@terasky/backstage-plugin-ai-rules 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,7 +4,7 @@ Welcome to the AI Coding Rules plugin!
4
4
 
5
5
  [![npm latest version](https://img.shields.io/npm/v/@terasky/backstage-plugin-ai-rules/latest.svg)](https://www.npmjs.com/package/@terasky/backstage-plugin-ai-rules)
6
6
 
7
- The `AI Coding Rules` plugin for Backstage provides comprehensive visualization and management of AI coding rules from various sources like Cursor, GitHub Copilot, and Cline. It enables teams to discover, view, and manage their AI coding guidelines directly within the Backstage interface, featuring modern UI with filtering capabilities, statistics overview, and expandable rule cards with metadata display.
7
+ The `AI Coding Rules` plugin for Backstage provides comprehensive visualization and management of AI coding rules from various sources like Cursor, GitHub Copilot, Cline, and Claude Code. It enables teams to discover, view, and manage their AI coding guidelines directly within the Backstage interface, featuring modern UI with filtering capabilities, statistics overview, and expandable rule cards with metadata display.
8
8
 
9
9
  For detailed docs go to https://terasky-oss.github.io/backstage-plugins/plugins/ai-rules-plugin/overview
10
10
 
package/config.d.ts CHANGED
@@ -7,9 +7,17 @@ export interface Config {
7
7
  aiRules?: {
8
8
  /**
9
9
  * Allowed rule types to search for in repositories
10
- * Defaults to ["cursor", "copilot"] if not specified
10
+ * Defaults to ["cursor", "copilot", "cline", "claude-code"] if not specified
11
11
  * @visibility frontend
12
12
  */
13
- allowedRuleTypes: string[]; // e.g. ["cursor", "copilot", "cline"]
13
+ allowedRuleTypes: string[]; // e.g. ["cursor", "copilot", "cline", "claude-code"]
14
+ /**
15
+ * Default rule types to be pre-selected when the component loads
16
+ * If not provided, defaults to empty array (no rules pre-selected)
17
+ * If empty array, no rule types are pre-selected
18
+ * If specific array, those rule types are pre-selected
19
+ * @visibility frontend
20
+ */
21
+ defaultRuleTypes?: string[]; // e.g. ["cursor", "copilot"]
14
22
  }
15
23
  }
@@ -1,7 +1,7 @@
1
- import { jsx, jsxs } from 'react/jsx-runtime';
1
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import { useAiRules } from '../../hooks/useAiRules.esm.js';
3
3
  import { InfoCard, Progress, EmptyState, MarkdownContent } from '@backstage/core-components';
4
- import { makeStyles, Button, Typography, FormControlLabel, Checkbox, Card, CardContent, useTheme, Accordion, AccordionSummary, Chip, Tooltip, IconButton, AccordionDetails } from '@material-ui/core';
4
+ import { makeStyles, Typography, FormControlLabel, Checkbox, Button, Card, CardContent, useTheme, Accordion, AccordionSummary, Chip, Tooltip, IconButton, AccordionDetails } from '@material-ui/core';
5
5
  import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
6
6
  import CodeIcon from '@material-ui/icons/Code';
7
7
  import LaunchIcon from '@material-ui/icons/Launch';
@@ -81,13 +81,17 @@ const useStyles = makeStyles((theme) => ({
81
81
  "& > *": {
82
82
  marginRight: theme.spacing(1)
83
83
  }
84
+ },
85
+ applyFilterButton: {
86
+ marginTop: theme.spacing(1)
84
87
  }
85
88
  }));
86
89
  const RuleTypeIcon = ({ type }) => {
87
90
  const colors = {
88
91
  [AIRuleType.CURSOR]: "#0066CC",
89
92
  [AIRuleType.COPILOT]: "#6F42C1",
90
- [AIRuleType.CLINE]: "#28A745"
93
+ [AIRuleType.CLINE]: "#28A745",
94
+ [AIRuleType.CLAUDE_CODE]: "#FF6B35"
91
95
  };
92
96
  return /* @__PURE__ */ jsx(CodeIcon, { style: { color: colors[type] } });
93
97
  };
@@ -271,9 +275,35 @@ const RuleComponent = ({ rule }) => {
271
275
  /* @__PURE__ */ jsx("div", { className: styles.ruleContent, children: /* @__PURE__ */ jsx(MarkdownContent, { content: rule2.content }) })
272
276
  ] }) })
273
277
  ] });
278
+ const renderClaudeCodeRule = (rule2) => /* @__PURE__ */ jsxs(Accordion, { className: styles.ruleCard, children: [
279
+ /* @__PURE__ */ jsx(AccordionSummary, { expandIcon: /* @__PURE__ */ jsx(ExpandMoreIcon, {}), children: /* @__PURE__ */ jsxs("div", { className: styles.ruleHeader, children: [
280
+ /* @__PURE__ */ jsxs("div", { className: styles.ruleHeaderContent, children: [
281
+ /* @__PURE__ */ jsx(RuleTypeIcon, { type: rule2.type }),
282
+ /* @__PURE__ */ jsx(Typography, { variant: "h6", children: rule2.title || rule2.fileName }),
283
+ /* @__PURE__ */ jsx(Chip, { label: rule2.type, size: "small", className: styles.ruleType })
284
+ ] }),
285
+ rule2.gitUrl && /* @__PURE__ */ jsx(Tooltip, { title: "Open file in repository", children: /* @__PURE__ */ jsx(
286
+ IconButton,
287
+ {
288
+ size: "small",
289
+ onClick: (e) => {
290
+ e.stopPropagation();
291
+ window.open(constructFileUrl(rule2.gitUrl, rule2.filePath), "_blank");
292
+ },
293
+ children: /* @__PURE__ */ jsx(LaunchIcon, { fontSize: "small" })
294
+ }
295
+ ) })
296
+ ] }) }),
297
+ /* @__PURE__ */ jsx(AccordionDetails, { children: /* @__PURE__ */ jsxs("div", { children: [
298
+ /* @__PURE__ */ jsx("div", { className: styles.ruleMetadata, children: /* @__PURE__ */ jsx(Chip, { label: `Path: ${rule2.filePath}`, size: "small", variant: "outlined" }) }),
299
+ /* @__PURE__ */ jsx("div", { className: styles.ruleContent, children: /* @__PURE__ */ jsx(MarkdownContent, { content: rule2.content }) })
300
+ ] }) })
301
+ ] });
274
302
  switch (rule.type) {
275
303
  case AIRuleType.CURSOR:
276
304
  return renderCursorRule(rule);
305
+ case AIRuleType.CLAUDE_CODE:
306
+ return renderClaudeCodeRule(rule);
277
307
  case AIRuleType.COPILOT:
278
308
  return renderCopilotRule(rule);
279
309
  case AIRuleType.CLINE:
@@ -283,8 +313,21 @@ const RuleComponent = ({ rule }) => {
283
313
  }
284
314
  };
285
315
  const AIRulesComponent = ({ title = "AI Coding Rules" } = {}) => {
286
- const { rulesByType, loading, error, hasGitUrl, totalRules, allowedRuleTypes, selectedRuleTypes, setSelectedRuleTypes } = useAiRules();
316
+ const { rulesByType, loading, error, hasGitUrl, totalRules, allowedRuleTypes, selectedRuleTypes, setSelectedRuleTypes, applyFilters, resetFilters, hasUnappliedChanges, hasSearched } = useAiRules();
287
317
  const styles = useStyles();
318
+ const ruleTypeDisplayOrder = [AIRuleType.CURSOR, AIRuleType.CLAUDE_CODE, AIRuleType.COPILOT, AIRuleType.CLINE];
319
+ const formatRuleTypeName = (type) => {
320
+ switch (type) {
321
+ case AIRuleType.CURSOR:
322
+ return "Cursor";
323
+ case AIRuleType.CLAUDE_CODE:
324
+ return "Claude Code";
325
+ case AIRuleType.COPILOT:
326
+ return "Copilot";
327
+ case AIRuleType.CLINE:
328
+ return "Cline";
329
+ }
330
+ };
288
331
  const handleTypeToggle = (type, checked) => {
289
332
  const newTypes = checked ? [...selectedRuleTypes, type] : selectedRuleTypes.filter((t) => t !== type);
290
333
  setSelectedRuleTypes(newTypes);
@@ -312,24 +355,6 @@ const AIRulesComponent = ({ title = "AI Coding Rules" } = {}) => {
312
355
  }
313
356
  ) });
314
357
  }
315
- if (totalRules === 0) {
316
- return /* @__PURE__ */ jsx(InfoCard, { title, children: /* @__PURE__ */ jsx(
317
- EmptyState,
318
- {
319
- missing: "content",
320
- title: "No AI Rules Found",
321
- description: "No AI rules were found in this repository for the selected rule types.",
322
- action: /* @__PURE__ */ jsx(
323
- Button,
324
- {
325
- variant: "outlined",
326
- onClick: () => setSelectedRuleTypes(allowedRuleTypes),
327
- children: "Reset Filters"
328
- }
329
- )
330
- }
331
- ) });
332
- }
333
358
  return /* @__PURE__ */ jsxs(InfoCard, { title, className: styles.root, children: [
334
359
  /* @__PURE__ */ jsxs("div", { className: styles.filterSection, children: [
335
360
  /* @__PURE__ */ jsx(Typography, { variant: "h6", gutterBottom: true, children: "Filter Rule Types" }),
@@ -343,30 +368,69 @@ const AIRulesComponent = ({ title = "AI Coding Rules" } = {}) => {
343
368
  onChange: (e) => handleTypeToggle(type, e.target.checked)
344
369
  }
345
370
  ),
346
- label: type.charAt(0).toUpperCase() + type.slice(1)
371
+ label: formatRuleTypeName(type)
347
372
  },
348
373
  type
349
- )) })
350
- ] }),
351
- /* @__PURE__ */ jsxs("div", { className: styles.statsContainer, children: [
352
- /* @__PURE__ */ jsx(Card, { className: styles.statCard, children: /* @__PURE__ */ jsxs(CardContent, { children: [
353
- /* @__PURE__ */ jsx(Typography, { variant: "h4", children: totalRules }),
354
- /* @__PURE__ */ jsx(Typography, { color: "textSecondary", children: "Total Rules" })
355
- ] }) }),
356
- Object.entries(rulesByType).map(([type, typeRules]) => /* @__PURE__ */ jsx(Card, { className: styles.statCard, children: /* @__PURE__ */ jsxs(CardContent, { children: [
357
- /* @__PURE__ */ jsx(Typography, { variant: "h4", children: typeRules.length }),
358
- /* @__PURE__ */ jsx(Typography, { color: "textSecondary", children: type.charAt(0).toUpperCase() + type.slice(1) })
359
- ] }) }, type))
374
+ )) }),
375
+ /* @__PURE__ */ jsxs("div", { className: styles.applyFilterButton, children: [
376
+ /* @__PURE__ */ jsx(
377
+ Button,
378
+ {
379
+ variant: "contained",
380
+ color: "primary",
381
+ onClick: applyFilters,
382
+ disabled: !hasUnappliedChanges,
383
+ children: "Apply Filter"
384
+ }
385
+ ),
386
+ hasUnappliedChanges && /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "textSecondary", style: { marginTop: 8 }, children: 'You have unsaved filter changes. Click "Apply Filter" to update the results.' }),
387
+ !hasUnappliedChanges && selectedRuleTypes.length === 0 && /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "textSecondary", style: { marginTop: 8 }, children: "Select at least one rule type to search for AI rules." })
388
+ ] })
360
389
  ] }),
361
- Object.entries(rulesByType).map(([type, typeRules]) => /* @__PURE__ */ jsxs("div", { children: [
362
- /* @__PURE__ */ jsxs(Typography, { variant: "h5", gutterBottom: true, style: { marginTop: 16 }, children: [
363
- type.charAt(0).toUpperCase() + type.slice(1),
364
- " Rules (",
365
- typeRules.length,
366
- ")"
390
+ hasSearched && totalRules === 0 ? /* @__PURE__ */ jsx(
391
+ EmptyState,
392
+ {
393
+ missing: "content",
394
+ title: "No AI Rules Found",
395
+ description: "No AI rules were found in this repository for the selected rule types.",
396
+ action: /* @__PURE__ */ jsx(
397
+ Button,
398
+ {
399
+ variant: "outlined",
400
+ onClick: resetFilters,
401
+ children: "Reset Filters"
402
+ }
403
+ )
404
+ }
405
+ ) : totalRules > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
406
+ /* @__PURE__ */ jsxs("div", { className: styles.statsContainer, children: [
407
+ /* @__PURE__ */ jsx(Card, { className: styles.statCard, children: /* @__PURE__ */ jsxs(CardContent, { children: [
408
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", children: totalRules }),
409
+ /* @__PURE__ */ jsx(Typography, { color: "textSecondary", children: "Total Rules" })
410
+ ] }) }),
411
+ ruleTypeDisplayOrder.map((type) => {
412
+ const typeRules = rulesByType[type] || [];
413
+ if (typeRules.length === 0) return null;
414
+ return /* @__PURE__ */ jsx(Card, { className: styles.statCard, children: /* @__PURE__ */ jsxs(CardContent, { children: [
415
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", children: typeRules.length }),
416
+ /* @__PURE__ */ jsx(Typography, { color: "textSecondary", children: formatRuleTypeName(type) })
417
+ ] }) }, type);
418
+ })
367
419
  ] }),
368
- typeRules.map((rule) => /* @__PURE__ */ jsx(RuleComponent, { rule }, rule.id))
369
- ] }, type))
420
+ ruleTypeDisplayOrder.map((type) => {
421
+ const typeRules = rulesByType[type] || [];
422
+ if (typeRules.length === 0) return null;
423
+ return /* @__PURE__ */ jsxs("div", { children: [
424
+ /* @__PURE__ */ jsxs(Typography, { variant: "h5", gutterBottom: true, style: { marginTop: 16 }, children: [
425
+ formatRuleTypeName(type),
426
+ " Rules (",
427
+ typeRules.length,
428
+ ")"
429
+ ] }),
430
+ typeRules.map((rule) => /* @__PURE__ */ jsx(RuleComponent, { rule }, rule.id))
431
+ ] }, type);
432
+ })
433
+ ] }) : /* @__PURE__ */ jsx("div", { style: { marginTop: 16 }, children: /* @__PURE__ */ jsx(Typography, { variant: "body1", color: "textSecondary", children: 'Select rule types above and click "Apply Filter" to search for AI coding rules in this repository.' }) })
370
434
  ] });
371
435
  };
372
436
 
@@ -1 +1 @@
1
- {"version":3,"file":"AiRulesComponent.esm.js","sources":["../../../src/components/AiRulesComponent/AiRulesComponent.tsx"],"sourcesContent":["import { useAiRules } from '../../hooks/useAiRules';\nimport { InfoCard, Progress, EmptyState, MarkdownContent } from '@backstage/core-components';\nimport { Button, makeStyles, useTheme, Typography, Chip, Card, CardContent, Accordion, AccordionSummary, AccordionDetails, FormControlLabel, Checkbox, IconButton, Tooltip } from '@material-ui/core';\nimport { Entity } from '@backstage/catalog-model';\nimport ExpandMoreIcon from '@material-ui/icons/ExpandMore';\nimport CodeIcon from '@material-ui/icons/Code';\nimport LaunchIcon from '@material-ui/icons/Launch';\nimport { AIRuleType, AIRule, CursorRule, CopilotRule, ClineRule } from '../../types';\nexport interface AIRulesComponentProps {\n title?: string;\n}\n\nexport const isAIRulesAvailable = (entity: Entity): boolean => {\n const sourceAnnotation = entity.metadata?.annotations?.['backstage.io/source-location'] || '';\n return sourceAnnotation.startsWith('url:');\n};\n\nconst useStyles = makeStyles((theme) => ({\n root: {\n '& .MuiAccordion-root': {\n marginBottom: theme.spacing(1),\n '&:before': {\n display: 'none',\n },\n },\n },\n filterSection: {\n marginBottom: theme.spacing(2),\n padding: theme.spacing(2),\n backgroundColor: theme.palette.background.default,\n borderRadius: theme.shape.borderRadius,\n },\n ruleCard: {\n marginBottom: theme.spacing(1),\n border: `1px solid ${theme.palette.divider}`,\n },\n ruleHeader: {\n display: 'flex',\n alignItems: 'center',\n gap: theme.spacing(1),\n width: '100%',\n },\n ruleHeaderContent: {\n display: 'flex',\n alignItems: 'center',\n gap: theme.spacing(1),\n flex: 1,\n },\n ruleType: {\n textTransform: 'uppercase',\n fontWeight: 'bold',\n fontSize: '0.75rem',\n },\n ruleContent: {\n padding: theme.spacing(1),\n borderRadius: theme.shape.borderRadius,\n overflow: 'auto',\n maxHeight: '300px',\n '& > *': {\n backgroundColor: 'transparent !important',\n },\n },\n ruleMetadata: {\n display: 'flex',\n flexWrap: 'wrap',\n gap: theme.spacing(0.5),\n marginBottom: theme.spacing(1),\n },\n statsContainer: {\n display: 'flex',\n gap: theme.spacing(2),\n marginBottom: theme.spacing(2),\n },\n statCard: {\n minWidth: '120px',\n textAlign: 'center',\n },\n emptyStateIcon: {\n fontSize: '4rem',\n color: theme.palette.grey[400],\n },\n filterContainer: {\n display: 'flex',\n flexWrap: 'wrap',\n '& > *': {\n marginRight: theme.spacing(1),\n },\n },\n}));\n\nconst RuleTypeIcon = ({ type }: { type: AIRuleType }) => {\n const colors = {\n [AIRuleType.CURSOR]: '#0066CC',\n [AIRuleType.COPILOT]: '#6F42C1', \n [AIRuleType.CLINE]: '#28A745',\n };\n \n return <CodeIcon style={{ color: colors[type] }} />;\n};\n\nconst renderFrontmatter = (theme: any, frontmatter?: Record<string, any>) => {\n if (!frontmatter || Object.keys(frontmatter).length === 0) {\n return null;\n }\n\n // Filter out fields that are already displayed elsewhere (description, globs)\n const filteredEntries = Object.entries(frontmatter).filter(([key]) => \n !['description', 'globs'].includes(key)\n );\n\n if (filteredEntries.length === 0) {\n return null;\n }\n\n return (\n <div style={{ \n marginBottom: '16px', \n padding: '16px', \n backgroundColor: theme.palette.type === 'dark' ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)', \n borderRadius: '8px',\n border: `1px solid ${theme.palette.type === 'dark' ? 'rgba(255,255,255,0.12)' : 'rgba(0,0,0,0.12)'}`\n }}>\n <Typography variant=\"subtitle2\" style={{ \n marginBottom: '12px', \n fontWeight: 'bold',\n color: theme.palette.text.secondary,\n textTransform: 'uppercase',\n letterSpacing: '0.5px'\n }}>\n Metadata\n </Typography>\n {filteredEntries.map(([key, value], index) => (\n <div key={key} style={{ marginBottom: index < filteredEntries.length - 1 ? '12px' : '0' }}>\n <Typography variant=\"body2\" style={{ \n fontWeight: 'bold',\n textTransform: 'capitalize',\n color: theme.palette.primary.main,\n marginBottom: '4px'\n }}>\n {key}:\n </Typography>\n <Typography variant=\"body2\" style={{ \n lineHeight: '1.5',\n marginLeft: '8px',\n color: theme.palette.text.primary\n }}>\n {Array.isArray(value) ? value.join(', ') : String(value)}\n </Typography>\n </div>\n ))}\n </div>\n );\n};\n\n// Helper function to parse cursor rule content manually\nconst parseCursorContent = (content: string) => {\n return manualParseFrontmatter(content);\n};\n\n// Manual frontmatter parsing as fallback\nconst manualParseFrontmatter = (content: string) => {\n // Check if content starts with ---\n if (!content.trim().startsWith('---')) {\n return {\n frontmatter: undefined,\n content: content\n };\n }\n\n try {\n // Split by lines\n const lines = content.split('\\n');\n let frontmatterEndIndex = -1;\n \n // Find the closing ---\n for (let i = 1; i < lines.length; i++) {\n if (lines[i].trim() === '---') {\n frontmatterEndIndex = i;\n break;\n }\n }\n \n if (frontmatterEndIndex === -1) {\n return {\n frontmatter: undefined,\n content: content\n };\n }\n \n // Extract frontmatter lines (between the --- markers)\n const frontmatterLines = lines.slice(1, frontmatterEndIndex);\n const contentLines = lines.slice(frontmatterEndIndex + 1);\n \n // Parse the YAML manually (simple key: value pairs)\n const frontmatter: Record<string, any> = {};\n for (const line of frontmatterLines) {\n const trimmedLine = line.trim();\n if (trimmedLine && trimmedLine.includes(':')) {\n const colonIndex = trimmedLine.indexOf(':');\n const key = trimmedLine.substring(0, colonIndex).trim();\n const value = trimmedLine.substring(colonIndex + 1).trim();\n frontmatter[key] = value;\n }\n }\n \n return {\n frontmatter: Object.keys(frontmatter).length > 0 ? frontmatter : undefined,\n content: contentLines.join('\\n').trim()\n };\n } catch (error) {\n return {\n frontmatter: undefined,\n content: content\n };\n }\n};\n\nconst constructFileUrl = (gitUrl: string, filePath: string): string => {\n // Remove trailing slashes from gitUrl\n const cleanGitUrl = gitUrl.replace(/\\/+$/, '');\n \n // For GitHub URLs, convert to blob view\n if (cleanGitUrl.includes('github.com')) {\n return `${cleanGitUrl}/blob/main/${filePath}`;\n }\n \n // For GitLab URLs, convert to blob view\n if (cleanGitUrl.includes('gitlab.com')) {\n return `${cleanGitUrl}/-/blob/main/${filePath}`;\n }\n \n // For other git providers, try generic blob URL\n return `${cleanGitUrl}/blob/main/${filePath}`;\n};\n\nconst RuleComponent = ({ rule }: { rule: AIRule }) => {\n const styles = useStyles();\n const theme = useTheme();\n \n const renderCursorRule = (rule: CursorRule) => {\n // Parse the raw content to extract frontmatter and clean content\n const { frontmatter, content } = parseCursorContent(rule.content);\n \n return (\n <Accordion className={styles.ruleCard}>\n <AccordionSummary expandIcon={<ExpandMoreIcon />}>\n <div className={styles.ruleHeader}>\n <div className={styles.ruleHeaderContent}>\n <RuleTypeIcon type={rule.type} />\n <Typography variant=\"h6\">{rule.fileName}</Typography>\n <Chip label={rule.type} size=\"small\" className={styles.ruleType} />\n {frontmatter?.description && (\n <Typography variant=\"body2\" style={{ marginLeft: 8, color: theme.palette.text.secondary }}>\n {frontmatter.description}\n </Typography>\n )}\n </div>\n {rule.gitUrl && (\n <Tooltip title=\"Open file in repository\">\n <IconButton\n size=\"small\"\n onClick={(e) => {\n e.stopPropagation();\n window.open(constructFileUrl(rule.gitUrl!, rule.filePath), '_blank');\n }}\n >\n <LaunchIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n )}\n </div>\n </AccordionSummary>\n <AccordionDetails>\n <div>\n <div className={styles.ruleMetadata}>\n <Chip label={`Path: ${rule.filePath}`} size=\"small\" variant=\"outlined\" />\n {frontmatter?.globs && (\n <Chip label={`Globs: ${Array.isArray(frontmatter.globs) ? frontmatter.globs.join(', ') : frontmatter.globs}`} size=\"small\" variant=\"outlined\" />\n )}\n </div>\n {renderFrontmatter(theme, frontmatter)}\n <div className={styles.ruleContent}>\n <MarkdownContent content={content} />\n </div>\n </div>\n </AccordionDetails>\n </Accordion>\n );\n };\n\n const renderCopilotRule = (rule: CopilotRule) => (\n <Card className={styles.ruleCard}>\n <CardContent>\n <div className={styles.ruleHeader}>\n <div className={styles.ruleHeaderContent}>\n <RuleTypeIcon type={rule.type} />\n <Typography variant=\"h6\">Copilot Rule #{rule.order}</Typography>\n <Chip label={rule.type} size=\"small\" className={styles.ruleType} />\n </div>\n {rule.gitUrl && (\n <Tooltip title=\"Open file in repository\">\n <IconButton\n size=\"small\"\n onClick={() => window.open(constructFileUrl(rule.gitUrl!, rule.filePath), '_blank')}\n >\n <LaunchIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n )}\n </div>\n <div className={styles.ruleContent}>\n {rule.content}\n </div>\n </CardContent>\n </Card>\n );\n\n const renderClineRule = (rule: ClineRule) => (\n <Accordion className={styles.ruleCard}>\n <AccordionSummary expandIcon={<ExpandMoreIcon />}>\n <div className={styles.ruleHeader}>\n <div className={styles.ruleHeaderContent}>\n <RuleTypeIcon type={rule.type} />\n <Typography variant=\"h6\">{rule.title || rule.fileName}</Typography>\n <Chip label={rule.type} size=\"small\" className={styles.ruleType} />\n </div>\n {rule.gitUrl && (\n <Tooltip title=\"Open file in repository\">\n <IconButton\n size=\"small\"\n onClick={(e) => {\n e.stopPropagation();\n window.open(constructFileUrl(rule.gitUrl!, rule.filePath), '_blank');\n }}\n >\n <LaunchIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n )}\n </div>\n </AccordionSummary>\n <AccordionDetails>\n <div>\n <div className={styles.ruleMetadata}>\n <Chip label={`Path: ${rule.filePath}`} size=\"small\" variant=\"outlined\" />\n </div>\n <div className={styles.ruleContent}>\n <MarkdownContent content={rule.content} />\n </div>\n </div>\n </AccordionDetails>\n </Accordion>\n );\n\n switch (rule.type) {\n case AIRuleType.CURSOR:\n return renderCursorRule(rule as CursorRule);\n case AIRuleType.COPILOT:\n return renderCopilotRule(rule as CopilotRule);\n case AIRuleType.CLINE:\n return renderClineRule(rule as ClineRule);\n default:\n return null;\n }\n};\n\nexport const AIRulesComponent = ({ title = \"AI Coding Rules\" }: AIRulesComponentProps = {}) => {\n const { rulesByType, loading, error, hasGitUrl, totalRules, allowedRuleTypes, selectedRuleTypes, setSelectedRuleTypes } = useAiRules();\n const styles = useStyles();\n const handleTypeToggle = (type: AIRuleType, checked: boolean) => {\n const newTypes = checked \n ? [...selectedRuleTypes, type]\n : selectedRuleTypes.filter(t => t !== type);\n setSelectedRuleTypes(newTypes);\n };\n\n if (loading) {\n return (\n <InfoCard title={title}>\n <Progress />\n </InfoCard>\n );\n }\n\n if (!hasGitUrl) {\n return (\n <InfoCard title={title}>\n <EmptyState\n missing=\"content\"\n title=\"No Git Repository\"\n description=\"This component doesn't have a Git source URL configured.\"\n />\n </InfoCard>\n );\n }\n\n if (error) {\n return (\n <InfoCard title={title}>\n <EmptyState\n missing=\"content\"\n title=\"Error Loading Rules\"\n description={error}\n />\n </InfoCard>\n );\n }\n\n if (totalRules === 0) {\n return (\n <InfoCard title={title}>\n <EmptyState\n missing=\"content\"\n title=\"No AI Rules Found\"\n description=\"No AI rules were found in this repository for the selected rule types.\"\n action={\n <Button\n variant=\"outlined\"\n onClick={() => setSelectedRuleTypes(allowedRuleTypes)}\n >\n Reset Filters\n </Button>\n }\n />\n </InfoCard>\n );\n }\n\n return (\n <InfoCard title={title} className={styles.root}>\n <div className={styles.filterSection}>\n <Typography variant=\"h6\" gutterBottom>\n Filter Rule Types\n </Typography>\n <div className={styles.filterContainer}>\n {allowedRuleTypes.map(type => (\n <FormControlLabel\n key={type}\n control={\n <Checkbox\n checked={selectedRuleTypes.includes(type)}\n onChange={(e) => handleTypeToggle(type, e.target.checked)}\n />\n }\n label={type.charAt(0).toUpperCase() + type.slice(1)}\n />\n ))}\n </div>\n </div>\n\n <div className={styles.statsContainer}>\n <Card className={styles.statCard}>\n <CardContent>\n <Typography variant=\"h4\">{totalRules}</Typography>\n <Typography color=\"textSecondary\">Total Rules</Typography>\n </CardContent>\n </Card>\n {Object.entries(rulesByType).map(([type, typeRules]) => (\n <Card key={type} className={styles.statCard}>\n <CardContent>\n <Typography variant=\"h4\">{typeRules.length}</Typography>\n <Typography color=\"textSecondary\">{type.charAt(0).toUpperCase() + type.slice(1)}</Typography>\n </CardContent>\n </Card>\n ))}\n </div>\n\n {Object.entries(rulesByType).map(([type, typeRules]) => (\n <div key={type}>\n <Typography variant=\"h5\" gutterBottom style={{ marginTop: 16 }}>\n {type.charAt(0).toUpperCase() + type.slice(1)} Rules ({typeRules.length})\n </Typography>\n {typeRules.map(rule => (\n <RuleComponent key={rule.id} rule={rule} />\n ))}\n </div>\n ))}\n </InfoCard>\n );\n};"],"names":["rule"],"mappings":";;;;;;;;;AAYa,MAAA,kBAAA,GAAqB,CAAC,MAA4B,KAAA;AAC7D,EAAA,MAAM,gBAAmB,GAAA,MAAA,CAAO,QAAU,EAAA,WAAA,GAAc,8BAA8B,CAAK,IAAA,EAAA;AAC3F,EAAO,OAAA,gBAAA,CAAiB,WAAW,MAAM,CAAA;AAC3C;AAEA,MAAM,SAAA,GAAY,UAAW,CAAA,CAAC,KAAW,MAAA;AAAA,EACvC,IAAM,EAAA;AAAA,IACJ,sBAAwB,EAAA;AAAA,MACtB,YAAA,EAAc,KAAM,CAAA,OAAA,CAAQ,CAAC,CAAA;AAAA,MAC7B,UAAY,EAAA;AAAA,QACV,OAAS,EAAA;AAAA;AACX;AACF,GACF;AAAA,EACA,aAAe,EAAA;AAAA,IACb,YAAA,EAAc,KAAM,CAAA,OAAA,CAAQ,CAAC,CAAA;AAAA,IAC7B,OAAA,EAAS,KAAM,CAAA,OAAA,CAAQ,CAAC,CAAA;AAAA,IACxB,eAAA,EAAiB,KAAM,CAAA,OAAA,CAAQ,UAAW,CAAA,OAAA;AAAA,IAC1C,YAAA,EAAc,MAAM,KAAM,CAAA;AAAA,GAC5B;AAAA,EACA,QAAU,EAAA;AAAA,IACR,YAAA,EAAc,KAAM,CAAA,OAAA,CAAQ,CAAC,CAAA;AAAA,IAC7B,MAAQ,EAAA,CAAA,UAAA,EAAa,KAAM,CAAA,OAAA,CAAQ,OAAO,CAAA;AAAA,GAC5C;AAAA,EACA,UAAY,EAAA;AAAA,IACV,OAAS,EAAA,MAAA;AAAA,IACT,UAAY,EAAA,QAAA;AAAA,IACZ,GAAA,EAAK,KAAM,CAAA,OAAA,CAAQ,CAAC,CAAA;AAAA,IACpB,KAAO,EAAA;AAAA,GACT;AAAA,EACA,iBAAmB,EAAA;AAAA,IACjB,OAAS,EAAA,MAAA;AAAA,IACT,UAAY,EAAA,QAAA;AAAA,IACZ,GAAA,EAAK,KAAM,CAAA,OAAA,CAAQ,CAAC,CAAA;AAAA,IACpB,IAAM,EAAA;AAAA,GACR;AAAA,EACA,QAAU,EAAA;AAAA,IACR,aAAe,EAAA,WAAA;AAAA,IACf,UAAY,EAAA,MAAA;AAAA,IACZ,QAAU,EAAA;AAAA,GACZ;AAAA,EACA,WAAa,EAAA;AAAA,IACX,OAAA,EAAS,KAAM,CAAA,OAAA,CAAQ,CAAC,CAAA;AAAA,IACxB,YAAA,EAAc,MAAM,KAAM,CAAA,YAAA;AAAA,IAC1B,QAAU,EAAA,MAAA;AAAA,IACV,SAAW,EAAA,OAAA;AAAA,IACX,OAAS,EAAA;AAAA,MACP,eAAiB,EAAA;AAAA;AACnB,GACF;AAAA,EACA,YAAc,EAAA;AAAA,IACZ,OAAS,EAAA,MAAA;AAAA,IACT,QAAU,EAAA,MAAA;AAAA,IACV,GAAA,EAAK,KAAM,CAAA,OAAA,CAAQ,GAAG,CAAA;AAAA,IACtB,YAAA,EAAc,KAAM,CAAA,OAAA,CAAQ,CAAC;AAAA,GAC/B;AAAA,EACA,cAAgB,EAAA;AAAA,IACd,OAAS,EAAA,MAAA;AAAA,IACT,GAAA,EAAK,KAAM,CAAA,OAAA,CAAQ,CAAC,CAAA;AAAA,IACpB,YAAA,EAAc,KAAM,CAAA,OAAA,CAAQ,CAAC;AAAA,GAC/B;AAAA,EACA,QAAU,EAAA;AAAA,IACR,QAAU,EAAA,OAAA;AAAA,IACV,SAAW,EAAA;AAAA,GACb;AAAA,EACA,cAAgB,EAAA;AAAA,IACd,QAAU,EAAA,MAAA;AAAA,IACV,KAAO,EAAA,KAAA,CAAM,OAAQ,CAAA,IAAA,CAAK,GAAG;AAAA,GAC/B;AAAA,EACA,eAAiB,EAAA;AAAA,IACf,OAAS,EAAA,MAAA;AAAA,IACT,QAAU,EAAA,MAAA;AAAA,IACV,OAAS,EAAA;AAAA,MACP,WAAA,EAAa,KAAM,CAAA,OAAA,CAAQ,CAAC;AAAA;AAC9B;AAEJ,CAAE,CAAA,CAAA;AAEF,MAAM,YAAe,GAAA,CAAC,EAAE,IAAA,EAAiC,KAAA;AACvD,EAAA,MAAM,MAAS,GAAA;AAAA,IACb,CAAC,UAAW,CAAA,MAAM,GAAG,SAAA;AAAA,IACrB,CAAC,UAAW,CAAA,OAAO,GAAG,SAAA;AAAA,IACtB,CAAC,UAAW,CAAA,KAAK,GAAG;AAAA,GACtB;AAEA,EAAO,uBAAA,GAAA,CAAC,YAAS,KAAO,EAAA,EAAE,OAAO,MAAO,CAAA,IAAI,GAAK,EAAA,CAAA;AACnD,CAAA;AAEA,MAAM,iBAAA,GAAoB,CAAC,KAAA,EAAY,WAAsC,KAAA;AAC3E,EAAA,IAAI,CAAC,WAAe,IAAA,MAAA,CAAO,KAAK,WAAW,CAAA,CAAE,WAAW,CAAG,EAAA;AACzD,IAAO,OAAA,IAAA;AAAA;AAIT,EAAA,MAAM,eAAkB,GAAA,MAAA,CAAO,OAAQ,CAAA,WAAW,CAAE,CAAA,MAAA;AAAA,IAAO,CAAC,CAAC,GAAG,CAC9D,KAAA,CAAC,CAAC,aAAe,EAAA,OAAO,CAAE,CAAA,QAAA,CAAS,GAAG;AAAA,GACxC;AAEA,EAAI,IAAA,eAAA,CAAgB,WAAW,CAAG,EAAA;AAChC,IAAO,OAAA,IAAA;AAAA;AAGT,EACE,uBAAA,IAAA,CAAC,SAAI,KAAO,EAAA;AAAA,IACV,YAAc,EAAA,MAAA;AAAA,IACd,OAAS,EAAA,MAAA;AAAA,IACT,eAAiB,EAAA,KAAA,CAAM,OAAQ,CAAA,IAAA,KAAS,SAAS,wBAA2B,GAAA,kBAAA;AAAA,IAC5E,YAAc,EAAA,KAAA;AAAA,IACd,QAAQ,CAAa,UAAA,EAAA,KAAA,CAAM,QAAQ,IAAS,KAAA,MAAA,GAAS,2BAA2B,kBAAkB,CAAA;AAAA,GAElG,EAAA,QAAA,EAAA;AAAA,oBAAC,GAAA,CAAA,UAAA,EAAA,EAAW,OAAQ,EAAA,WAAA,EAAY,KAAO,EAAA;AAAA,MACrC,YAAc,EAAA,MAAA;AAAA,MACd,UAAY,EAAA,MAAA;AAAA,MACZ,KAAA,EAAO,KAAM,CAAA,OAAA,CAAQ,IAAK,CAAA,SAAA;AAAA,MAC1B,aAAe,EAAA,WAAA;AAAA,MACf,aAAe,EAAA;AAAA,OACd,QAEH,EAAA,UAAA,EAAA,CAAA;AAAA,IACC,gBAAgB,GAAI,CAAA,CAAC,CAAC,GAAK,EAAA,KAAK,GAAG,KAClC,qBAAA,IAAA,CAAC,SAAc,KAAO,EAAA,EAAE,cAAc,KAAQ,GAAA,eAAA,CAAgB,SAAS,CAAI,GAAA,MAAA,GAAS,KAClF,EAAA,QAAA,EAAA;AAAA,sBAAC,IAAA,CAAA,UAAA,EAAA,EAAW,OAAQ,EAAA,OAAA,EAAQ,KAAO,EAAA;AAAA,QACjC,UAAY,EAAA,MAAA;AAAA,QACZ,aAAe,EAAA,YAAA;AAAA,QACf,KAAA,EAAO,KAAM,CAAA,OAAA,CAAQ,OAAQ,CAAA,IAAA;AAAA,QAC7B,YAAc,EAAA;AAAA,OAEb,EAAA,QAAA,EAAA;AAAA,QAAA,GAAA;AAAA,QAAI;AAAA,OACP,EAAA,CAAA;AAAA,sBACC,GAAA,CAAA,UAAA,EAAA,EAAW,OAAQ,EAAA,OAAA,EAAQ,KAAO,EAAA;AAAA,QACjC,UAAY,EAAA,KAAA;AAAA,QACZ,UAAY,EAAA,KAAA;AAAA,QACZ,KAAA,EAAO,KAAM,CAAA,OAAA,CAAQ,IAAK,CAAA;AAAA,OAC5B,EACG,QAAM,EAAA,KAAA,CAAA,OAAA,CAAQ,KAAK,CAAA,GAAI,KAAM,CAAA,IAAA,CAAK,IAAI,CAAA,GAAI,MAAO,CAAA,KAAK,CACzD,EAAA;AAAA,KAAA,EAAA,EAfQ,GAgBV,CACD;AAAA,GACH,EAAA,CAAA;AAEJ,CAAA;AAGA,MAAM,kBAAA,GAAqB,CAAC,OAAoB,KAAA;AAC9C,EAAA,OAAO,uBAAuB,OAAO,CAAA;AACvC,CAAA;AAGA,MAAM,sBAAA,GAAyB,CAAC,OAAoB,KAAA;AAElD,EAAA,IAAI,CAAC,OAAQ,CAAA,IAAA,EAAO,CAAA,UAAA,CAAW,KAAK,CAAG,EAAA;AACrC,IAAO,OAAA;AAAA,MACL,WAAa,EAAA,KAAA,CAAA;AAAA,MACb;AAAA,KACF;AAAA;AAGF,EAAI,IAAA;AAEF,IAAM,MAAA,KAAA,GAAQ,OAAQ,CAAA,KAAA,CAAM,IAAI,CAAA;AAChC,IAAA,IAAI,mBAAsB,GAAA,CAAA,CAAA;AAG1B,IAAA,KAAA,IAAS,CAAI,GAAA,CAAA,EAAG,CAAI,GAAA,KAAA,CAAM,QAAQ,CAAK,EAAA,EAAA;AACrC,MAAA,IAAI,KAAM,CAAA,CAAC,CAAE,CAAA,IAAA,OAAW,KAAO,EAAA;AAC7B,QAAsB,mBAAA,GAAA,CAAA;AACtB,QAAA;AAAA;AACF;AAGF,IAAA,IAAI,wBAAwB,CAAI,CAAA,EAAA;AAC9B,MAAO,OAAA;AAAA,QACL,WAAa,EAAA,KAAA,CAAA;AAAA,QACb;AAAA,OACF;AAAA;AAIF,IAAA,MAAM,gBAAmB,GAAA,KAAA,CAAM,KAAM,CAAA,CAAA,EAAG,mBAAmB,CAAA;AAC3D,IAAA,MAAM,YAAe,GAAA,KAAA,CAAM,KAAM,CAAA,mBAAA,GAAsB,CAAC,CAAA;AAGxD,IAAA,MAAM,cAAmC,EAAC;AAC1C,IAAA,KAAA,MAAW,QAAQ,gBAAkB,EAAA;AACnC,MAAM,MAAA,WAAA,GAAc,KAAK,IAAK,EAAA;AAC9B,MAAA,IAAI,WAAe,IAAA,WAAA,CAAY,QAAS,CAAA,GAAG,CAAG,EAAA;AAC5C,QAAM,MAAA,UAAA,GAAa,WAAY,CAAA,OAAA,CAAQ,GAAG,CAAA;AAC1C,QAAA,MAAM,MAAM,WAAY,CAAA,SAAA,CAAU,CAAG,EAAA,UAAU,EAAE,IAAK,EAAA;AACtD,QAAA,MAAM,QAAQ,WAAY,CAAA,SAAA,CAAU,UAAa,GAAA,CAAC,EAAE,IAAK,EAAA;AACzD,QAAA,WAAA,CAAY,GAAG,CAAI,GAAA,KAAA;AAAA;AACrB;AAGF,IAAO,OAAA;AAAA,MACL,aAAa,MAAO,CAAA,IAAA,CAAK,WAAW,CAAE,CAAA,MAAA,GAAS,IAAI,WAAc,GAAA,KAAA,CAAA;AAAA,MACjE,OAAS,EAAA,YAAA,CAAa,IAAK,CAAA,IAAI,EAAE,IAAK;AAAA,KACxC;AAAA,WACO,KAAO,EAAA;AACd,IAAO,OAAA;AAAA,MACL,WAAa,EAAA,KAAA,CAAA;AAAA,MACb;AAAA,KACF;AAAA;AAEJ,CAAA;AAEA,MAAM,gBAAA,GAAmB,CAAC,MAAA,EAAgB,QAA6B,KAAA;AAErE,EAAA,MAAM,WAAc,GAAA,MAAA,CAAO,OAAQ,CAAA,MAAA,EAAQ,EAAE,CAAA;AAG7C,EAAI,IAAA,WAAA,CAAY,QAAS,CAAA,YAAY,CAAG,EAAA;AACtC,IAAO,OAAA,CAAA,EAAG,WAAW,CAAA,WAAA,EAAc,QAAQ,CAAA,CAAA;AAAA;AAI7C,EAAI,IAAA,WAAA,CAAY,QAAS,CAAA,YAAY,CAAG,EAAA;AACtC,IAAO,OAAA,CAAA,EAAG,WAAW,CAAA,aAAA,EAAgB,QAAQ,CAAA,CAAA;AAAA;AAI/C,EAAO,OAAA,CAAA,EAAG,WAAW,CAAA,WAAA,EAAc,QAAQ,CAAA,CAAA;AAC7C,CAAA;AAEA,MAAM,aAAgB,GAAA,CAAC,EAAE,IAAA,EAA6B,KAAA;AACpD,EAAA,MAAM,SAAS,SAAU,EAAA;AACzB,EAAA,MAAM,QAAQ,QAAS,EAAA;AAEvB,EAAM,MAAA,gBAAA,GAAmB,CAACA,KAAqB,KAAA;AAE7C,IAAA,MAAM,EAAE,WAAa,EAAA,OAAA,EAAY,GAAA,kBAAA,CAAmBA,MAAK,OAAO,CAAA;AAEhE,IAAA,uBACG,IAAA,CAAA,SAAA,EAAA,EAAU,SAAW,EAAA,MAAA,CAAO,QAC3B,EAAA,QAAA,EAAA;AAAA,sBAAC,GAAA,CAAA,gBAAA,EAAA,EAAiB,4BAAa,GAAA,CAAA,cAAA,EAAA,EAAe,GAC5C,QAAC,kBAAA,IAAA,CAAA,KAAA,EAAA,EAAI,SAAW,EAAA,MAAA,CAAO,UACrB,EAAA,QAAA,EAAA;AAAA,wBAAC,IAAA,CAAA,KAAA,EAAA,EAAI,SAAW,EAAA,MAAA,CAAO,iBACrB,EAAA,QAAA,EAAA;AAAA,0BAAC,GAAA,CAAA,YAAA,EAAA,EAAa,IAAMA,EAAAA,KAAAA,CAAK,IAAM,EAAA,CAAA;AAAA,8BAC9B,UAAW,EAAA,EAAA,OAAA,EAAQ,IAAM,EAAA,QAAA,EAAAA,MAAK,QAAS,EAAA,CAAA;AAAA,0BACxC,GAAA,CAAC,QAAK,KAAOA,EAAAA,KAAAA,CAAK,MAAM,IAAK,EAAA,OAAA,EAAQ,SAAW,EAAA,MAAA,CAAO,QAAU,EAAA,CAAA;AAAA,UAChE,aAAa,WACZ,oBAAA,GAAA,CAAC,UAAW,EAAA,EAAA,OAAA,EAAQ,SAAQ,KAAO,EAAA,EAAE,UAAY,EAAA,CAAA,EAAG,OAAO,KAAM,CAAA,OAAA,CAAQ,KAAK,SAAU,EAAA,EACrF,sBAAY,WACf,EAAA;AAAA,SAEJ,EAAA,CAAA;AAAA,QACCA,KAAK,CAAA,MAAA,oBACH,GAAA,CAAA,OAAA,EAAA,EAAQ,OAAM,yBACb,EAAA,QAAA,kBAAA,GAAA;AAAA,UAAC,UAAA;AAAA,UAAA;AAAA,YACC,IAAK,EAAA,OAAA;AAAA,YACL,OAAA,EAAS,CAAC,CAAM,KAAA;AACd,cAAA,CAAA,CAAE,eAAgB,EAAA;AAClB,cAAA,MAAA,CAAO,KAAK,gBAAiBA,CAAAA,KAAAA,CAAK,QAASA,KAAK,CAAA,QAAQ,GAAG,QAAQ,CAAA;AAAA,aACrE;AAAA,YAEA,QAAA,kBAAA,GAAA,CAAC,UAAW,EAAA,EAAA,QAAA,EAAS,OAAQ,EAAA;AAAA;AAAA,SAEjC,EAAA;AAAA,OAAA,EAEJ,CACF,EAAA,CAAA;AAAA,sBACA,GAAA,CAAC,gBACC,EAAA,EAAA,QAAA,kBAAA,IAAA,CAAC,KACC,EAAA,EAAA,QAAA,EAAA;AAAA,wBAAC,IAAA,CAAA,KAAA,EAAA,EAAI,SAAW,EAAA,MAAA,CAAO,YACrB,EAAA,QAAA,EAAA;AAAA,0BAAC,GAAA,CAAA,IAAA,EAAA,EAAK,OAAO,CAASA,MAAAA,EAAAA,KAAAA,CAAK,QAAQ,CAAI,CAAA,EAAA,IAAA,EAAK,OAAQ,EAAA,OAAA,EAAQ,UAAW,EAAA,CAAA;AAAA,UACtE,WAAA,EAAa,yBACX,GAAA,CAAA,IAAA,EAAA,EAAK,OAAO,CAAU,OAAA,EAAA,KAAA,CAAM,OAAQ,CAAA,WAAA,CAAY,KAAK,CAAA,GAAI,YAAY,KAAM,CAAA,IAAA,CAAK,IAAI,CAAI,GAAA,WAAA,CAAY,KAAK,CAAI,CAAA,EAAA,IAAA,EAAK,OAAQ,EAAA,OAAA,EAAQ,UAAW,EAAA;AAAA,SAElJ,EAAA,CAAA;AAAA,QACC,iBAAA,CAAkB,OAAO,WAAW,CAAA;AAAA,wBACrC,GAAA,CAAC,SAAI,SAAW,EAAA,MAAA,CAAO,aACrB,QAAC,kBAAA,GAAA,CAAA,eAAA,EAAA,EAAgB,SAAkB,CACrC,EAAA;AAAA,OAAA,EACF,CACF,EAAA;AAAA,KACF,EAAA,CAAA;AAAA,GAEJ;AAEA,EAAM,MAAA,iBAAA,GAAoB,CAACA,KACzB,qBAAA,GAAA,CAAC,QAAK,SAAW,EAAA,MAAA,CAAO,QACtB,EAAA,QAAA,kBAAA,IAAA,CAAC,WACC,EAAA,EAAA,QAAA,EAAA;AAAA,oBAAC,IAAA,CAAA,KAAA,EAAA,EAAI,SAAW,EAAA,MAAA,CAAO,UACrB,EAAA,QAAA,EAAA;AAAA,sBAAC,IAAA,CAAA,KAAA,EAAA,EAAI,SAAW,EAAA,MAAA,CAAO,iBACrB,EAAA,QAAA,EAAA;AAAA,wBAAC,GAAA,CAAA,YAAA,EAAA,EAAa,IAAMA,EAAAA,KAAAA,CAAK,IAAM,EAAA,CAAA;AAAA,wBAC/B,IAAA,CAAC,UAAW,EAAA,EAAA,OAAA,EAAQ,IAAK,EAAA,QAAA,EAAA;AAAA,UAAA,gBAAA;AAAA,UAAeA,KAAK,CAAA;AAAA,SAAM,EAAA,CAAA;AAAA,wBACnD,GAAA,CAAC,QAAK,KAAOA,EAAAA,KAAAA,CAAK,MAAM,IAAK,EAAA,OAAA,EAAQ,SAAW,EAAA,MAAA,CAAO,QAAU,EAAA;AAAA,OACnE,EAAA,CAAA;AAAA,MACCA,KAAK,CAAA,MAAA,oBACH,GAAA,CAAA,OAAA,EAAA,EAAQ,OAAM,yBACb,EAAA,QAAA,kBAAA,GAAA;AAAA,QAAC,UAAA;AAAA,QAAA;AAAA,UACC,IAAK,EAAA,OAAA;AAAA,UACL,OAAA,EAAS,MAAM,MAAA,CAAO,IAAK,CAAA,gBAAA,CAAiBA,MAAK,MAASA,EAAAA,KAAAA,CAAK,QAAQ,CAAA,EAAG,QAAQ,CAAA;AAAA,UAElF,QAAA,kBAAA,GAAA,CAAC,UAAW,EAAA,EAAA,QAAA,EAAS,OAAQ,EAAA;AAAA;AAAA,OAEjC,EAAA;AAAA,KAEJ,EAAA,CAAA;AAAA,wBACC,KAAI,EAAA,EAAA,SAAA,EAAW,OAAO,WACpB,EAAA,QAAA,EAAAA,MAAK,OACR,EAAA;AAAA,GAAA,EACF,CACF,EAAA,CAAA;AAGF,EAAA,MAAM,kBAAkB,CAACA,KAAAA,0BACtB,SAAU,EAAA,EAAA,SAAA,EAAW,OAAO,QAC3B,EAAA,QAAA,EAAA;AAAA,oBAAC,GAAA,CAAA,gBAAA,EAAA,EAAiB,4BAAa,GAAA,CAAA,cAAA,EAAA,EAAe,GAC5C,QAAC,kBAAA,IAAA,CAAA,KAAA,EAAA,EAAI,SAAW,EAAA,MAAA,CAAO,UACrB,EAAA,QAAA,EAAA;AAAA,sBAAC,IAAA,CAAA,KAAA,EAAA,EAAI,SAAW,EAAA,MAAA,CAAO,iBACrB,EAAA,QAAA,EAAA;AAAA,wBAAC,GAAA,CAAA,YAAA,EAAA,EAAa,IAAMA,EAAAA,KAAAA,CAAK,IAAM,EAAA,CAAA;AAAA,wBAC/B,GAAA,CAAC,cAAW,OAAQ,EAAA,IAAA,EAAM,UAAAA,KAAK,CAAA,KAAA,IAASA,MAAK,QAAS,EAAA,CAAA;AAAA,wBACtD,GAAA,CAAC,QAAK,KAAOA,EAAAA,KAAAA,CAAK,MAAM,IAAK,EAAA,OAAA,EAAQ,SAAW,EAAA,MAAA,CAAO,QAAU,EAAA;AAAA,OACnE,EAAA,CAAA;AAAA,MACCA,KAAK,CAAA,MAAA,oBACH,GAAA,CAAA,OAAA,EAAA,EAAQ,OAAM,yBACb,EAAA,QAAA,kBAAA,GAAA;AAAA,QAAC,UAAA;AAAA,QAAA;AAAA,UACC,IAAK,EAAA,OAAA;AAAA,UACL,OAAA,EAAS,CAAC,CAAM,KAAA;AACd,YAAA,CAAA,CAAE,eAAgB,EAAA;AAClB,YAAA,MAAA,CAAO,KAAK,gBAAiBA,CAAAA,KAAAA,CAAK,QAASA,KAAK,CAAA,QAAQ,GAAG,QAAQ,CAAA;AAAA,WACrE;AAAA,UAEA,QAAA,kBAAA,GAAA,CAAC,UAAW,EAAA,EAAA,QAAA,EAAS,OAAQ,EAAA;AAAA;AAAA,OAEjC,EAAA;AAAA,KAAA,EAEJ,CACF,EAAA,CAAA;AAAA,oBACA,GAAA,CAAC,gBACC,EAAA,EAAA,QAAA,kBAAA,IAAA,CAAC,KACC,EAAA,EAAA,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,KAAI,EAAA,EAAA,SAAA,EAAW,MAAO,CAAA,YAAA,EACrB,8BAAC,IAAK,EAAA,EAAA,KAAA,EAAO,CAASA,MAAAA,EAAAA,KAAAA,CAAK,QAAQ,CAAI,CAAA,EAAA,IAAA,EAAK,OAAQ,EAAA,OAAA,EAAQ,YAAW,CACzE,EAAA,CAAA;AAAA,sBACA,GAAA,CAAC,KAAI,EAAA,EAAA,SAAA,EAAW,MAAO,CAAA,WAAA,EACrB,8BAAC,eAAgB,EAAA,EAAA,OAAA,EAASA,KAAK,CAAA,OAAA,EAAS,CAC1C,EAAA;AAAA,KAAA,EACF,CACF,EAAA;AAAA,GACF,EAAA,CAAA;AAGF,EAAA,QAAQ,KAAK,IAAM;AAAA,IACjB,KAAK,UAAW,CAAA,MAAA;AACd,MAAA,OAAO,iBAAiB,IAAkB,CAAA;AAAA,IAC5C,KAAK,UAAW,CAAA,OAAA;AACd,MAAA,OAAO,kBAAkB,IAAmB,CAAA;AAAA,IAC9C,KAAK,UAAW,CAAA,KAAA;AACd,MAAA,OAAO,gBAAgB,IAAiB,CAAA;AAAA,IAC1C;AACE,MAAO,OAAA,IAAA;AAAA;AAEb,CAAA;AAEO,MAAM,mBAAmB,CAAC,EAAE,QAAQ,iBAAkB,EAAA,GAA2B,EAAO,KAAA;AAC7F,EAAM,MAAA,EAAE,WAAa,EAAA,OAAA,EAAS,KAAO,EAAA,SAAA,EAAW,YAAY,gBAAkB,EAAA,iBAAA,EAAmB,oBAAqB,EAAA,GAAI,UAAW,EAAA;AACrI,EAAA,MAAM,SAAS,SAAU,EAAA;AACzB,EAAM,MAAA,gBAAA,GAAmB,CAAC,IAAA,EAAkB,OAAqB,KAAA;AAC/D,IAAM,MAAA,QAAA,GAAW,OACb,GAAA,CAAC,GAAG,iBAAA,EAAmB,IAAI,CAAA,GAC3B,iBAAkB,CAAA,MAAA,CAAO,CAAK,CAAA,KAAA,CAAA,KAAM,IAAI,CAAA;AAC5C,IAAA,oBAAA,CAAqB,QAAQ,CAAA;AAAA,GAC/B;AAEA,EAAA,IAAI,OAAS,EAAA;AACX,IAAA,uBACG,GAAA,CAAA,QAAA,EAAA,EAAS,KACR,EAAA,QAAA,kBAAA,GAAA,CAAC,YAAS,CACZ,EAAA,CAAA;AAAA;AAIJ,EAAA,IAAI,CAAC,SAAW,EAAA;AACd,IACE,uBAAA,GAAA,CAAC,YAAS,KACR,EAAA,QAAA,kBAAA,GAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,OAAQ,EAAA,SAAA;AAAA,QACR,KAAM,EAAA,mBAAA;AAAA,QACN,WAAY,EAAA;AAAA;AAAA,KAEhB,EAAA,CAAA;AAAA;AAIJ,EAAA,IAAI,KAAO,EAAA;AACT,IACE,uBAAA,GAAA,CAAC,YAAS,KACR,EAAA,QAAA,kBAAA,GAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,OAAQ,EAAA,SAAA;AAAA,QACR,KAAM,EAAA,qBAAA;AAAA,QACN,WAAa,EAAA;AAAA;AAAA,KAEjB,EAAA,CAAA;AAAA;AAIJ,EAAA,IAAI,eAAe,CAAG,EAAA;AACpB,IACE,uBAAA,GAAA,CAAC,YAAS,KACR,EAAA,QAAA,kBAAA,GAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,OAAQ,EAAA,SAAA;AAAA,QACR,KAAM,EAAA,mBAAA;AAAA,QACN,WAAY,EAAA,wEAAA;AAAA,QACZ,MACE,kBAAA,GAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACC,OAAQ,EAAA,UAAA;AAAA,YACR,OAAA,EAAS,MAAM,oBAAA,CAAqB,gBAAgB,CAAA;AAAA,YACrD,QAAA,EAAA;AAAA;AAAA;AAED;AAAA,KAGN,EAAA,CAAA;AAAA;AAIJ,EAAA,uBACG,IAAA,CAAA,QAAA,EAAA,EAAS,KAAc,EAAA,SAAA,EAAW,OAAO,IACxC,EAAA,QAAA,EAAA;AAAA,oBAAC,IAAA,CAAA,KAAA,EAAA,EAAI,SAAW,EAAA,MAAA,CAAO,aACrB,EAAA,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,UAAW,EAAA,EAAA,OAAA,EAAQ,IAAK,EAAA,YAAA,EAAY,MAAC,QAEtC,EAAA,mBAAA,EAAA,CAAA;AAAA,0BACC,KAAI,EAAA,EAAA,SAAA,EAAW,OAAO,eACpB,EAAA,QAAA,EAAA,gBAAA,CAAiB,IAAI,CACpB,IAAA,qBAAA,GAAA;AAAA,QAAC,gBAAA;AAAA,QAAA;AAAA,UAEC,OACE,kBAAA,GAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cACC,OAAA,EAAS,iBAAkB,CAAA,QAAA,CAAS,IAAI,CAAA;AAAA,cACxC,UAAU,CAAC,CAAA,KAAM,iBAAiB,IAAM,EAAA,CAAA,CAAE,OAAO,OAAO;AAAA;AAAA,WAC1D;AAAA,UAEF,KAAA,EAAO,KAAK,MAAO,CAAA,CAAC,EAAE,WAAY,EAAA,GAAI,IAAK,CAAA,KAAA,CAAM,CAAC;AAAA,SAAA;AAAA,QAP7C;AAAA,OASR,CACH,EAAA;AAAA,KACF,EAAA,CAAA;AAAA,oBAEC,IAAA,CAAA,KAAA,EAAA,EAAI,SAAW,EAAA,MAAA,CAAO,cACrB,EAAA,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,IAAK,EAAA,EAAA,SAAA,EAAW,MAAO,CAAA,QAAA,EACtB,+BAAC,WACC,EAAA,EAAA,QAAA,EAAA;AAAA,wBAAC,GAAA,CAAA,UAAA,EAAA,EAAW,OAAQ,EAAA,IAAA,EAAM,QAAW,EAAA,UAAA,EAAA,CAAA;AAAA,wBACpC,GAAA,CAAA,UAAA,EAAA,EAAW,KAAM,EAAA,eAAA,EAAgB,QAAW,EAAA,aAAA,EAAA;AAAA,OAAA,EAC/C,CACF,EAAA,CAAA;AAAA,MACC,OAAO,OAAQ,CAAA,WAAW,CAAE,CAAA,GAAA,CAAI,CAAC,CAAC,IAAA,EAAM,SAAS,CAAA,yBAC/C,IAAgB,EAAA,EAAA,SAAA,EAAW,MAAO,CAAA,QAAA,EACjC,+BAAC,WACC,EAAA,EAAA,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,UAAW,EAAA,EAAA,OAAA,EAAQ,IAAM,EAAA,QAAA,EAAA,SAAA,CAAU,MAAO,EAAA,CAAA;AAAA,wBAC1C,GAAA,CAAA,UAAA,EAAA,EAAW,KAAM,EAAA,eAAA,EAAiB,QAAK,EAAA,IAAA,CAAA,MAAA,CAAO,CAAC,CAAA,CAAE,WAAY,EAAA,GAAI,IAAK,CAAA,KAAA,CAAM,CAAC,CAAE,EAAA;AAAA,OAClF,EAAA,CAAA,EAAA,EAJS,IAKX,CACD;AAAA,KACH,EAAA,CAAA;AAAA,IAEC,MAAA,CAAO,OAAQ,CAAA,WAAW,CAAE,CAAA,GAAA,CAAI,CAAC,CAAC,IAAM,EAAA,SAAS,CAChD,qBAAA,IAAA,CAAC,KACC,EAAA,EAAA,QAAA,EAAA;AAAA,sBAAC,IAAA,CAAA,UAAA,EAAA,EAAW,SAAQ,IAAK,EAAA,YAAA,EAAY,MAAC,KAAO,EAAA,EAAE,SAAW,EAAA,EAAA,EACvD,EAAA,QAAA,EAAA;AAAA,QAAA,IAAA,CAAK,OAAO,CAAC,CAAA,CAAE,aAAgB,GAAA,IAAA,CAAK,MAAM,CAAC,CAAA;AAAA,QAAE,UAAA;AAAA,QAAS,SAAU,CAAA,MAAA;AAAA,QAAO;AAAA,OAC1E,EAAA,CAAA;AAAA,MACC,SAAA,CAAU,IAAI,CACb,IAAA,qBAAA,GAAA,CAAC,iBAA4B,IAAT,EAAA,EAAA,IAAA,CAAK,EAAgB,CAC1C;AAAA,KAAA,EAAA,EANO,IAOV,CACD;AAAA,GACH,EAAA,CAAA;AAEJ;;;;"}
1
+ {"version":3,"file":"AiRulesComponent.esm.js","sources":["../../../src/components/AiRulesComponent/AiRulesComponent.tsx"],"sourcesContent":["import { useAiRules } from '../../hooks/useAiRules';\nimport { InfoCard, Progress, EmptyState, MarkdownContent } from '@backstage/core-components';\nimport { Button, makeStyles, useTheme, Typography, Chip, Card, CardContent, Accordion, AccordionSummary, AccordionDetails, FormControlLabel, Checkbox, IconButton, Tooltip } from '@material-ui/core';\nimport { Entity } from '@backstage/catalog-model';\nimport ExpandMoreIcon from '@material-ui/icons/ExpandMore';\nimport CodeIcon from '@material-ui/icons/Code';\nimport LaunchIcon from '@material-ui/icons/Launch';\nimport { AIRuleType, AIRule, CursorRule, CopilotRule, ClineRule, ClaudeCodeRule } from '../../types';\nexport interface AIRulesComponentProps {\n title?: string;\n}\n\nexport const isAIRulesAvailable = (entity: Entity): boolean => {\n const sourceAnnotation = entity.metadata?.annotations?.['backstage.io/source-location'] || '';\n return sourceAnnotation.startsWith('url:');\n};\n\nconst useStyles = makeStyles((theme) => ({\n root: {\n '& .MuiAccordion-root': {\n marginBottom: theme.spacing(1),\n '&:before': {\n display: 'none',\n },\n },\n },\n filterSection: {\n marginBottom: theme.spacing(2),\n padding: theme.spacing(2),\n backgroundColor: theme.palette.background.default,\n borderRadius: theme.shape.borderRadius,\n },\n ruleCard: {\n marginBottom: theme.spacing(1),\n border: `1px solid ${theme.palette.divider}`,\n },\n ruleHeader: {\n display: 'flex',\n alignItems: 'center',\n gap: theme.spacing(1),\n width: '100%',\n },\n ruleHeaderContent: {\n display: 'flex',\n alignItems: 'center',\n gap: theme.spacing(1),\n flex: 1,\n },\n ruleType: {\n textTransform: 'uppercase',\n fontWeight: 'bold',\n fontSize: '0.75rem',\n },\n ruleContent: {\n padding: theme.spacing(1),\n borderRadius: theme.shape.borderRadius,\n overflow: 'auto',\n maxHeight: '300px',\n '& > *': {\n backgroundColor: 'transparent !important',\n },\n },\n ruleMetadata: {\n display: 'flex',\n flexWrap: 'wrap',\n gap: theme.spacing(0.5),\n marginBottom: theme.spacing(1),\n },\n statsContainer: {\n display: 'flex',\n gap: theme.spacing(2),\n marginBottom: theme.spacing(2),\n },\n statCard: {\n minWidth: '120px',\n textAlign: 'center',\n },\n emptyStateIcon: {\n fontSize: '4rem',\n color: theme.palette.grey[400],\n },\n filterContainer: {\n display: 'flex',\n flexWrap: 'wrap',\n '& > *': {\n marginRight: theme.spacing(1),\n },\n },\n applyFilterButton: {\n marginTop: theme.spacing(1),\n },\n}));\n\nconst RuleTypeIcon = ({ type }: { type: AIRuleType }) => {\n const colors = {\n [AIRuleType.CURSOR]: '#0066CC',\n [AIRuleType.COPILOT]: '#6F42C1', \n [AIRuleType.CLINE]: '#28A745',\n [AIRuleType.CLAUDE_CODE]: '#FF6B35',\n };\n \n return <CodeIcon style={{ color: colors[type] }} />;\n};\n\nconst renderFrontmatter = (theme: any, frontmatter?: Record<string, any>) => {\n if (!frontmatter || Object.keys(frontmatter).length === 0) {\n return null;\n }\n\n // Filter out fields that are already displayed elsewhere (description, globs)\n const filteredEntries = Object.entries(frontmatter).filter(([key]) => \n !['description', 'globs'].includes(key)\n );\n\n if (filteredEntries.length === 0) {\n return null;\n }\n\n return (\n <div style={{ \n marginBottom: '16px', \n padding: '16px', \n backgroundColor: theme.palette.type === 'dark' ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)', \n borderRadius: '8px',\n border: `1px solid ${theme.palette.type === 'dark' ? 'rgba(255,255,255,0.12)' : 'rgba(0,0,0,0.12)'}`\n }}>\n <Typography variant=\"subtitle2\" style={{ \n marginBottom: '12px', \n fontWeight: 'bold',\n color: theme.palette.text.secondary,\n textTransform: 'uppercase',\n letterSpacing: '0.5px'\n }}>\n Metadata\n </Typography>\n {filteredEntries.map(([key, value], index) => (\n <div key={key} style={{ marginBottom: index < filteredEntries.length - 1 ? '12px' : '0' }}>\n <Typography variant=\"body2\" style={{ \n fontWeight: 'bold',\n textTransform: 'capitalize',\n color: theme.palette.primary.main,\n marginBottom: '4px'\n }}>\n {key}:\n </Typography>\n <Typography variant=\"body2\" style={{ \n lineHeight: '1.5',\n marginLeft: '8px',\n color: theme.palette.text.primary\n }}>\n {Array.isArray(value) ? value.join(', ') : String(value)}\n </Typography>\n </div>\n ))}\n </div>\n );\n};\n\n// Helper function to parse cursor rule content manually\nconst parseCursorContent = (content: string) => {\n return manualParseFrontmatter(content);\n};\n\n// Manual frontmatter parsing as fallback\nconst manualParseFrontmatter = (content: string) => {\n // Check if content starts with ---\n if (!content.trim().startsWith('---')) {\n return {\n frontmatter: undefined,\n content: content\n };\n }\n\n try {\n // Split by lines\n const lines = content.split('\\n');\n let frontmatterEndIndex = -1;\n \n // Find the closing ---\n for (let i = 1; i < lines.length; i++) {\n if (lines[i].trim() === '---') {\n frontmatterEndIndex = i;\n break;\n }\n }\n \n if (frontmatterEndIndex === -1) {\n return {\n frontmatter: undefined,\n content: content\n };\n }\n \n // Extract frontmatter lines (between the --- markers)\n const frontmatterLines = lines.slice(1, frontmatterEndIndex);\n const contentLines = lines.slice(frontmatterEndIndex + 1);\n \n // Parse the YAML manually (simple key: value pairs)\n const frontmatter: Record<string, any> = {};\n for (const line of frontmatterLines) {\n const trimmedLine = line.trim();\n if (trimmedLine && trimmedLine.includes(':')) {\n const colonIndex = trimmedLine.indexOf(':');\n const key = trimmedLine.substring(0, colonIndex).trim();\n const value = trimmedLine.substring(colonIndex + 1).trim();\n frontmatter[key] = value;\n }\n }\n \n return {\n frontmatter: Object.keys(frontmatter).length > 0 ? frontmatter : undefined,\n content: contentLines.join('\\n').trim()\n };\n } catch (error) {\n return {\n frontmatter: undefined,\n content: content\n };\n }\n};\n\nconst constructFileUrl = (gitUrl: string, filePath: string): string => {\n // Remove trailing slashes from gitUrl\n const cleanGitUrl = gitUrl.replace(/\\/+$/, '');\n \n // For GitHub URLs, convert to blob view\n if (cleanGitUrl.includes('github.com')) {\n return `${cleanGitUrl}/blob/main/${filePath}`;\n }\n \n // For GitLab URLs, convert to blob view\n if (cleanGitUrl.includes('gitlab.com')) {\n return `${cleanGitUrl}/-/blob/main/${filePath}`;\n }\n \n // For other git providers, try generic blob URL\n return `${cleanGitUrl}/blob/main/${filePath}`;\n};\n\nconst RuleComponent = ({ rule }: { rule: AIRule }) => {\n const styles = useStyles();\n const theme = useTheme();\n \n const renderCursorRule = (rule: CursorRule) => {\n // Parse the raw content to extract frontmatter and clean content\n const { frontmatter, content } = parseCursorContent(rule.content);\n \n return (\n <Accordion className={styles.ruleCard}>\n <AccordionSummary expandIcon={<ExpandMoreIcon />}>\n <div className={styles.ruleHeader}>\n <div className={styles.ruleHeaderContent}>\n <RuleTypeIcon type={rule.type} />\n <Typography variant=\"h6\">{rule.fileName}</Typography>\n <Chip label={rule.type} size=\"small\" className={styles.ruleType} />\n {frontmatter?.description && (\n <Typography variant=\"body2\" style={{ marginLeft: 8, color: theme.palette.text.secondary }}>\n {frontmatter.description}\n </Typography>\n )}\n </div>\n {rule.gitUrl && (\n <Tooltip title=\"Open file in repository\">\n <IconButton\n size=\"small\"\n onClick={(e) => {\n e.stopPropagation();\n window.open(constructFileUrl(rule.gitUrl!, rule.filePath), '_blank');\n }}\n >\n <LaunchIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n )}\n </div>\n </AccordionSummary>\n <AccordionDetails>\n <div>\n <div className={styles.ruleMetadata}>\n <Chip label={`Path: ${rule.filePath}`} size=\"small\" variant=\"outlined\" />\n {frontmatter?.globs && (\n <Chip label={`Globs: ${Array.isArray(frontmatter.globs) ? frontmatter.globs.join(', ') : frontmatter.globs}`} size=\"small\" variant=\"outlined\" />\n )}\n </div>\n {renderFrontmatter(theme, frontmatter)}\n <div className={styles.ruleContent}>\n <MarkdownContent content={content} />\n </div>\n </div>\n </AccordionDetails>\n </Accordion>\n );\n };\n\n const renderCopilotRule = (rule: CopilotRule) => (\n <Card className={styles.ruleCard}>\n <CardContent>\n <div className={styles.ruleHeader}>\n <div className={styles.ruleHeaderContent}>\n <RuleTypeIcon type={rule.type} />\n <Typography variant=\"h6\">Copilot Rule #{rule.order}</Typography>\n <Chip label={rule.type} size=\"small\" className={styles.ruleType} />\n </div>\n {rule.gitUrl && (\n <Tooltip title=\"Open file in repository\">\n <IconButton\n size=\"small\"\n onClick={() => window.open(constructFileUrl(rule.gitUrl!, rule.filePath), '_blank')}\n >\n <LaunchIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n )}\n </div>\n <div className={styles.ruleContent}>\n {rule.content}\n </div>\n </CardContent>\n </Card>\n );\n\n const renderClineRule = (rule: ClineRule) => (\n <Accordion className={styles.ruleCard}>\n <AccordionSummary expandIcon={<ExpandMoreIcon />}>\n <div className={styles.ruleHeader}>\n <div className={styles.ruleHeaderContent}>\n <RuleTypeIcon type={rule.type} />\n <Typography variant=\"h6\">{rule.title || rule.fileName}</Typography>\n <Chip label={rule.type} size=\"small\" className={styles.ruleType} />\n </div>\n {rule.gitUrl && (\n <Tooltip title=\"Open file in repository\">\n <IconButton\n size=\"small\"\n onClick={(e) => {\n e.stopPropagation();\n window.open(constructFileUrl(rule.gitUrl!, rule.filePath), '_blank');\n }}\n >\n <LaunchIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n )}\n </div>\n </AccordionSummary>\n <AccordionDetails>\n <div>\n <div className={styles.ruleMetadata}>\n <Chip label={`Path: ${rule.filePath}`} size=\"small\" variant=\"outlined\" />\n </div>\n <div className={styles.ruleContent}>\n <MarkdownContent content={rule.content} />\n </div>\n </div>\n </AccordionDetails>\n </Accordion>\n );\n\n const renderClaudeCodeRule = (rule: ClaudeCodeRule) => (\n <Accordion className={styles.ruleCard}>\n <AccordionSummary expandIcon={<ExpandMoreIcon />}>\n <div className={styles.ruleHeader}>\n <div className={styles.ruleHeaderContent}>\n <RuleTypeIcon type={rule.type} />\n <Typography variant=\"h6\">{rule.title || rule.fileName}</Typography>\n <Chip label={rule.type} size=\"small\" className={styles.ruleType} />\n </div>\n {rule.gitUrl && (\n <Tooltip title=\"Open file in repository\">\n <IconButton\n size=\"small\"\n onClick={(e) => {\n e.stopPropagation();\n window.open(constructFileUrl(rule.gitUrl!, rule.filePath), '_blank');\n }}\n >\n <LaunchIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n )}\n </div>\n </AccordionSummary>\n <AccordionDetails>\n <div>\n <div className={styles.ruleMetadata}>\n <Chip label={`Path: ${rule.filePath}`} size=\"small\" variant=\"outlined\" />\n </div>\n <div className={styles.ruleContent}>\n <MarkdownContent content={rule.content} />\n </div>\n </div>\n </AccordionDetails>\n </Accordion>\n );\n\n switch (rule.type) {\n case AIRuleType.CURSOR:\n return renderCursorRule(rule as CursorRule);\n case AIRuleType.CLAUDE_CODE:\n return renderClaudeCodeRule(rule as ClaudeCodeRule);\n case AIRuleType.COPILOT:\n return renderCopilotRule(rule as CopilotRule);\n case AIRuleType.CLINE:\n return renderClineRule(rule as ClineRule);\n default:\n return null;\n }\n};\n\nexport const AIRulesComponent = ({ title = \"AI Coding Rules\" }: AIRulesComponentProps = {}) => {\n const { rulesByType, loading, error, hasGitUrl, totalRules, allowedRuleTypes, selectedRuleTypes, setSelectedRuleTypes, applyFilters, resetFilters, hasUnappliedChanges, hasSearched } = useAiRules();\n const styles = useStyles();\n \n // Define the desired rendering order\n const ruleTypeDisplayOrder = [AIRuleType.CURSOR, AIRuleType.CLAUDE_CODE, AIRuleType.COPILOT, AIRuleType.CLINE];\n \n // Helper function to format rule type names for display\n const formatRuleTypeName = (type: AIRuleType): string => {\n switch (type) {\n case AIRuleType.CURSOR:\n return 'Cursor';\n case AIRuleType.CLAUDE_CODE:\n return 'Claude Code';\n case AIRuleType.COPILOT:\n return 'Copilot';\n case AIRuleType.CLINE:\n return 'Cline';\n }\n };\n const handleTypeToggle = (type: AIRuleType, checked: boolean) => {\n const newTypes = checked \n ? [...selectedRuleTypes, type]\n : selectedRuleTypes.filter(t => t !== type);\n setSelectedRuleTypes(newTypes);\n };\n\n if (loading) {\n return (\n <InfoCard title={title}>\n <Progress />\n </InfoCard>\n );\n }\n\n if (!hasGitUrl) {\n return (\n <InfoCard title={title}>\n <EmptyState\n missing=\"content\"\n title=\"No Git Repository\"\n description=\"This component doesn't have a Git source URL configured.\"\n />\n </InfoCard>\n );\n }\n\n if (error) {\n return (\n <InfoCard title={title}>\n <EmptyState\n missing=\"content\"\n title=\"Error Loading Rules\"\n description={error}\n />\n </InfoCard>\n );\n }\n\n return (\n <InfoCard title={title} className={styles.root}>\n <div className={styles.filterSection}>\n <Typography variant=\"h6\" gutterBottom>\n Filter Rule Types\n </Typography>\n <div className={styles.filterContainer}>\n {allowedRuleTypes.map(type => (\n <FormControlLabel\n key={type}\n control={\n <Checkbox\n checked={selectedRuleTypes.includes(type)}\n onChange={(e) => handleTypeToggle(type, e.target.checked)}\n />\n }\n label={formatRuleTypeName(type)}\n />\n ))}\n </div>\n <div className={styles.applyFilterButton}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={applyFilters}\n disabled={!hasUnappliedChanges}\n >\n Apply Filter\n </Button>\n {hasUnappliedChanges && (\n <Typography variant=\"body2\" color=\"textSecondary\" style={{ marginTop: 8 }}>\n You have unsaved filter changes. Click \"Apply Filter\" to update the results.\n </Typography>\n )}\n {!hasUnappliedChanges && selectedRuleTypes.length === 0 && (\n <Typography variant=\"body2\" color=\"textSecondary\" style={{ marginTop: 8 }}>\n Select at least one rule type to search for AI rules.\n </Typography>\n )}\n </div>\n </div>\n\n {hasSearched && totalRules === 0 ? (\n <EmptyState\n missing=\"content\"\n title=\"No AI Rules Found\"\n description=\"No AI rules were found in this repository for the selected rule types.\"\n action={\n <Button\n variant=\"outlined\"\n onClick={resetFilters}\n >\n Reset Filters\n </Button>\n }\n />\n ) : totalRules > 0 ? (\n <>\n <div className={styles.statsContainer}>\n <Card className={styles.statCard}>\n <CardContent>\n <Typography variant=\"h4\">{totalRules}</Typography>\n <Typography color=\"textSecondary\">Total Rules</Typography>\n </CardContent>\n </Card>\n {ruleTypeDisplayOrder.map(type => {\n const typeRules = rulesByType[type] || [];\n if (typeRules.length === 0) return null;\n return (\n <Card key={type} className={styles.statCard}>\n <CardContent>\n <Typography variant=\"h4\">{typeRules.length}</Typography>\n <Typography color=\"textSecondary\">{formatRuleTypeName(type)}</Typography>\n </CardContent>\n </Card>\n );\n })}\n </div>\n\n {ruleTypeDisplayOrder.map(type => {\n const typeRules = rulesByType[type] || [];\n if (typeRules.length === 0) return null;\n return (\n <div key={type}>\n <Typography variant=\"h5\" gutterBottom style={{ marginTop: 16 }}>\n {formatRuleTypeName(type)} Rules ({typeRules.length})\n </Typography>\n {typeRules.map(rule => (\n <RuleComponent key={rule.id} rule={rule} />\n ))}\n </div>\n );\n })}\n </>\n ) : (\n <div style={{ marginTop: 16 }}>\n <Typography variant=\"body1\" color=\"textSecondary\">\n Select rule types above and click \"Apply Filter\" to search for AI coding rules in this repository.\n </Typography>\n </div>\n )}\n </InfoCard>\n );\n};"],"names":["rule"],"mappings":"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"}
@@ -9,11 +9,22 @@ const useAiRules = () => {
9
9
  const discoveryApi = useApi(discoveryApiRef);
10
10
  const fetchApi = useApi(fetchApiRef);
11
11
  const [rules, setRules] = useState([]);
12
- const [loading, setLoading] = useState(true);
12
+ const [loading, setLoading] = useState(false);
13
13
  const [error, setError] = useState(null);
14
14
  const [selectedRuleTypes, setSelectedRuleTypes] = useState([]);
15
+ const [appliedRuleTypes, setAppliedRuleTypes] = useState([]);
16
+ const [initialized, setInitialized] = useState(false);
17
+ const [hasSearched, setHasSearched] = useState(false);
15
18
  const allowedRuleTypes = useMemo(() => {
16
- return configApi.getOptionalStringArray("aiRules.allowedRuleTypes") || [AIRuleType.CURSOR, AIRuleType.COPILOT];
19
+ return configApi.getOptionalStringArray("aiRules.allowedRuleTypes") || [AIRuleType.CURSOR, AIRuleType.COPILOT, AIRuleType.CLINE, AIRuleType.CLAUDE_CODE];
20
+ }, [configApi]);
21
+ const defaultRuleTypes = useMemo(() => {
22
+ const aiRulesConfig = configApi.getOptionalConfig("aiRules");
23
+ if (!aiRulesConfig) {
24
+ return [];
25
+ }
26
+ const configuredDefaults = configApi.getOptionalStringArray("aiRules.defaultRuleTypes");
27
+ return configuredDefaults !== void 0 ? configuredDefaults : [];
17
28
  }, [configApi]);
18
29
  const entityData = useMemo(() => {
19
30
  const sourceAnnotation = entity.metadata?.annotations?.["backstage.io/source-location"] || "";
@@ -40,8 +51,14 @@ const useAiRules = () => {
40
51
  };
41
52
  }, [entity.kind, entity.metadata.namespace, entity.metadata.name, entity.metadata?.annotations?.["backstage.io/source-location"]]);
42
53
  useEffect(() => {
43
- setSelectedRuleTypes(allowedRuleTypes);
44
- }, []);
54
+ const aiRulesConfig = configApi.getOptionalConfig("aiRules");
55
+ const configLoaded = aiRulesConfig !== void 0;
56
+ if (!initialized && allowedRuleTypes.length > 0 && configLoaded) {
57
+ setSelectedRuleTypes(defaultRuleTypes);
58
+ setAppliedRuleTypes(defaultRuleTypes);
59
+ setInitialized(true);
60
+ }
61
+ }, [defaultRuleTypes, allowedRuleTypes, initialized, configApi]);
45
62
  const fetchAiRules = useCallback(async (ruleTypes) => {
46
63
  if (!entityData.hasGitUrl || !entityData.gitUrl) {
47
64
  setLoading(false);
@@ -50,19 +67,18 @@ const useAiRules = () => {
50
67
  try {
51
68
  setLoading(true);
52
69
  setError(null);
70
+ setHasSearched(true);
53
71
  const baseUrl = await discoveryApi.getBaseUrl("ai-rules");
54
72
  const url = new URL(`${baseUrl}/rules`);
55
73
  url.searchParams.append("gitUrl", entityData.gitUrl);
56
74
  if (ruleTypes.length > 0) {
57
75
  url.searchParams.append("ruleTypes", ruleTypes.join(","));
58
76
  }
59
- console.log("Fetching AI rules from:", url.toString());
60
77
  const response = await fetchApi.fetch(url.toString());
61
78
  if (!response.ok) {
62
79
  throw new Error(`Failed to fetch AI rules: ${response.statusText}`);
63
80
  }
64
81
  const data = await response.json();
65
- console.log("Received AI rules:", data);
66
82
  setRules(data.rules);
67
83
  } catch (err) {
68
84
  const errorMessage = err instanceof Error ? err.message : "Unknown error occurred";
@@ -73,11 +89,28 @@ const useAiRules = () => {
73
89
  setLoading(false);
74
90
  }
75
91
  }, [entityData.hasGitUrl, entityData.gitUrl, discoveryApi, fetchApi]);
76
- useEffect(() => {
92
+ const applyFilters = useCallback(() => {
93
+ setAppliedRuleTypes([...selectedRuleTypes]);
77
94
  if (selectedRuleTypes.length > 0) {
78
95
  fetchAiRules(selectedRuleTypes);
96
+ } else {
97
+ setRules([]);
98
+ setError(null);
99
+ setHasSearched(true);
100
+ }
101
+ }, [selectedRuleTypes, fetchAiRules]);
102
+ const resetFilters = useCallback(() => {
103
+ setSelectedRuleTypes(allowedRuleTypes);
104
+ setAppliedRuleTypes([...allowedRuleTypes]);
105
+ if (allowedRuleTypes.length > 0) {
106
+ fetchAiRules(allowedRuleTypes);
107
+ }
108
+ }, [allowedRuleTypes, fetchAiRules]);
109
+ useEffect(() => {
110
+ if (appliedRuleTypes.length > 0) {
111
+ fetchAiRules(appliedRuleTypes);
79
112
  }
80
- }, [fetchAiRules, selectedRuleTypes]);
113
+ }, [fetchAiRules, appliedRuleTypes]);
81
114
  const rulesByType = useMemo(() => {
82
115
  return rules.reduce((acc, rule) => {
83
116
  if (!acc[rule.type]) {
@@ -95,9 +128,15 @@ const useAiRules = () => {
95
128
  hasGitUrl: entityData.hasGitUrl,
96
129
  componentName: entityData.name,
97
130
  allowedRuleTypes,
131
+ defaultRuleTypes,
98
132
  selectedRuleTypes,
99
133
  setSelectedRuleTypes,
100
- totalRules: rules.length
134
+ appliedRuleTypes,
135
+ applyFilters,
136
+ resetFilters,
137
+ totalRules: rules.length,
138
+ hasSearched,
139
+ hasUnappliedChanges: JSON.stringify(selectedRuleTypes.sort()) !== JSON.stringify(appliedRuleTypes.sort())
101
140
  };
102
141
  };
103
142
 
@@ -1 +1 @@
1
- {"version":3,"file":"useAiRules.esm.js","sources":["../../src/hooks/useAiRules.ts"],"sourcesContent":["import { useEntity } from '@backstage/plugin-catalog-react';\nimport { AIRuleType, AIRulesResponse, AIRule } from '../types';\nimport { useState, useEffect, useMemo, useCallback } from 'react';\nimport { useApi, configApiRef, discoveryApiRef, fetchApiRef } from '@backstage/core-plugin-api';\n\nexport const useAiRules = () => {\n const { entity } = useEntity();\n const configApi = useApi(configApiRef);\n const discoveryApi = useApi(discoveryApiRef);\n const fetchApi = useApi(fetchApiRef);\n \n const [rules, setRules] = useState<AIRule[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [selectedRuleTypes, setSelectedRuleTypes] = useState<AIRuleType[]>([]);\n\n // Stabilize allowed rule types\n const allowedRuleTypes = useMemo(() => {\n return configApi.getOptionalStringArray('aiRules.allowedRuleTypes') as AIRuleType[] || [AIRuleType.CURSOR, AIRuleType.COPILOT];\n }, [configApi]);\n\n // Extract stable entity properties\n const entityData = useMemo(() => {\n const sourceAnnotation = entity.metadata?.annotations?.['backstage.io/source-location'] || '';\n const hasGitUrl = sourceAnnotation.startsWith('url:');\n \n let gitUrl = hasGitUrl ? sourceAnnotation.substring(4) : undefined;\n if (gitUrl) {\n // Remove trailing slashes and normalize URL\n gitUrl = gitUrl.replace(/\\/+$/, '');\n // Handle GitHub tree/blob URLs - extract base repo URL\n const treeMatch = gitUrl.match(/^(.+)\\/tree\\/([^/]+)(?:\\/(.+))?$/);\n const blobMatch = gitUrl.match(/^(.+)\\/blob\\/([^/]+)(?:\\/(.+))?$/);\n if (treeMatch) {\n gitUrl = treeMatch[1]; // Just the base repo URL\n }\n if (blobMatch) {\n gitUrl = blobMatch[1]; // Just the base repo URL \n }\n }\n\n return {\n kind: entity.kind,\n namespace: entity.metadata.namespace || 'default',\n name: entity.metadata.name,\n sourceAnnotation,\n hasGitUrl,\n gitUrl,\n };\n }, [entity.kind, entity.metadata.namespace, entity.metadata.name, entity.metadata?.annotations?.['backstage.io/source-location']]);\n\n // Initialize selected rule types only once\n useEffect(() => {\n setSelectedRuleTypes(allowedRuleTypes);\n }, []); // Empty dependency - only run once\n\n // Stable fetch function\n const fetchAiRules = useCallback(async (ruleTypes: AIRuleType[]) => {\n if (!entityData.hasGitUrl || !entityData.gitUrl) {\n setLoading(false);\n return;\n }\n\n try {\n setLoading(true);\n setError(null);\n \n const baseUrl = await discoveryApi.getBaseUrl('ai-rules');\n const url = new URL(`${baseUrl}/rules`);\n \n // Send the Git URL directly to the backend\n url.searchParams.append('gitUrl', entityData.gitUrl);\n if (ruleTypes.length > 0) {\n url.searchParams.append('ruleTypes', ruleTypes.join(','));\n }\n\n console.log('Fetching AI rules from:', url.toString());\n\n const response = await fetchApi.fetch(url.toString());\n if (!response.ok) {\n throw new Error(`Failed to fetch AI rules: ${response.statusText}`);\n }\n\n const data: AIRulesResponse = await response.json();\n console.log('Received AI rules:', data);\n setRules(data.rules);\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';\n console.error('Error fetching AI rules:', errorMessage);\n setError(errorMessage);\n setRules([]);\n } finally {\n setLoading(false);\n }\n }, [entityData.hasGitUrl, entityData.gitUrl, discoveryApi, fetchApi]);\n\n // Fetch rules when dependencies change\n useEffect(() => {\n if (selectedRuleTypes.length > 0) {\n fetchAiRules(selectedRuleTypes);\n }\n }, [fetchAiRules, selectedRuleTypes]);\n\n const rulesByType = useMemo(() => {\n return rules.reduce((acc, rule) => {\n if (!acc[rule.type]) {\n acc[rule.type] = [];\n }\n acc[rule.type].push(rule);\n return acc;\n }, {} as Record<AIRuleType, AIRule[]>);\n }, [rules]);\n\n return {\n rules,\n rulesByType,\n loading,\n error,\n hasGitUrl: entityData.hasGitUrl,\n componentName: entityData.name,\n allowedRuleTypes,\n selectedRuleTypes,\n setSelectedRuleTypes,\n totalRules: rules.length,\n };\n};"],"names":[],"mappings":";;;;;AAKO,MAAM,aAAa,MAAM;AAC9B,EAAM,MAAA,EAAE,MAAO,EAAA,GAAI,SAAU,EAAA;AAC7B,EAAM,MAAA,SAAA,GAAY,OAAO,YAAY,CAAA;AACrC,EAAM,MAAA,YAAA,GAAe,OAAO,eAAe,CAAA;AAC3C,EAAM,MAAA,QAAA,GAAW,OAAO,WAAW,CAAA;AAEnC,EAAA,MAAM,CAAC,KAAO,EAAA,QAAQ,CAAI,GAAA,QAAA,CAAmB,EAAE,CAAA;AAC/C,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAS,IAAI,CAAA;AAC3C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAwB,IAAI,CAAA;AACtD,EAAA,MAAM,CAAC,iBAAmB,EAAA,oBAAoB,CAAI,GAAA,QAAA,CAAuB,EAAE,CAAA;AAG3E,EAAM,MAAA,gBAAA,GAAmB,QAAQ,MAAM;AACrC,IAAO,OAAA,SAAA,CAAU,uBAAuB,0BAA0B,CAAA,IAAqB,CAAC,UAAW,CAAA,MAAA,EAAQ,WAAW,OAAO,CAAA;AAAA,GAC/H,EAAG,CAAC,SAAS,CAAC,CAAA;AAGd,EAAM,MAAA,UAAA,GAAa,QAAQ,MAAM;AAC/B,IAAA,MAAM,gBAAmB,GAAA,MAAA,CAAO,QAAU,EAAA,WAAA,GAAc,8BAA8B,CAAK,IAAA,EAAA;AAC3F,IAAM,MAAA,SAAA,GAAY,gBAAiB,CAAA,UAAA,CAAW,MAAM,CAAA;AAEpD,IAAA,IAAI,MAAS,GAAA,SAAA,GAAY,gBAAiB,CAAA,SAAA,CAAU,CAAC,CAAI,GAAA,KAAA,CAAA;AACzD,IAAA,IAAI,MAAQ,EAAA;AAEV,MAAS,MAAA,GAAA,MAAA,CAAO,OAAQ,CAAA,MAAA,EAAQ,EAAE,CAAA;AAElC,MAAM,MAAA,SAAA,GAAY,MAAO,CAAA,KAAA,CAAM,kCAAkC,CAAA;AACjE,MAAM,MAAA,SAAA,GAAY,MAAO,CAAA,KAAA,CAAM,kCAAkC,CAAA;AACjE,MAAA,IAAI,SAAW,EAAA;AACb,QAAA,MAAA,GAAS,UAAU,CAAC,CAAA;AAAA;AAEtB,MAAA,IAAI,SAAW,EAAA;AACb,QAAA,MAAA,GAAS,UAAU,CAAC,CAAA;AAAA;AACtB;AAGF,IAAO,OAAA;AAAA,MACL,MAAM,MAAO,CAAA,IAAA;AAAA,MACb,SAAA,EAAW,MAAO,CAAA,QAAA,CAAS,SAAa,IAAA,SAAA;AAAA,MACxC,IAAA,EAAM,OAAO,QAAS,CAAA,IAAA;AAAA,MACtB,gBAAA;AAAA,MACA,SAAA;AAAA,MACA;AAAA,KACF;AAAA,GACC,EAAA,CAAC,MAAO,CAAA,IAAA,EAAM,OAAO,QAAS,CAAA,SAAA,EAAW,MAAO,CAAA,QAAA,CAAS,MAAM,MAAO,CAAA,QAAA,EAAU,WAAc,GAAA,8BAA8B,CAAC,CAAC,CAAA;AAGjI,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,oBAAA,CAAqB,gBAAgB,CAAA;AAAA,GACvC,EAAG,EAAE,CAAA;AAGL,EAAM,MAAA,YAAA,GAAe,WAAY,CAAA,OAAO,SAA4B,KAAA;AAClE,IAAA,IAAI,CAAC,UAAA,CAAW,SAAa,IAAA,CAAC,WAAW,MAAQ,EAAA;AAC/C,MAAA,UAAA,CAAW,KAAK,CAAA;AAChB,MAAA;AAAA;AAGF,IAAI,IAAA;AACF,MAAA,UAAA,CAAW,IAAI,CAAA;AACf,MAAA,QAAA,CAAS,IAAI,CAAA;AAEb,MAAA,MAAM,OAAU,GAAA,MAAM,YAAa,CAAA,UAAA,CAAW,UAAU,CAAA;AACxD,MAAA,MAAM,GAAM,GAAA,IAAI,GAAI,CAAA,CAAA,EAAG,OAAO,CAAQ,MAAA,CAAA,CAAA;AAGtC,MAAA,GAAA,CAAI,YAAa,CAAA,MAAA,CAAO,QAAU,EAAA,UAAA,CAAW,MAAM,CAAA;AACnD,MAAI,IAAA,SAAA,CAAU,SAAS,CAAG,EAAA;AACxB,QAAA,GAAA,CAAI,aAAa,MAAO,CAAA,WAAA,EAAa,SAAU,CAAA,IAAA,CAAK,GAAG,CAAC,CAAA;AAAA;AAG1D,MAAA,OAAA,CAAQ,GAAI,CAAA,yBAAA,EAA2B,GAAI,CAAA,QAAA,EAAU,CAAA;AAErD,MAAA,MAAM,WAAW,MAAM,QAAA,CAAS,KAAM,CAAA,GAAA,CAAI,UAAU,CAAA;AACpD,MAAI,IAAA,CAAC,SAAS,EAAI,EAAA;AAChB,QAAA,MAAM,IAAI,KAAA,CAAM,CAA6B,0BAAA,EAAA,QAAA,CAAS,UAAU,CAAE,CAAA,CAAA;AAAA;AAGpE,MAAM,MAAA,IAAA,GAAwB,MAAM,QAAA,CAAS,IAAK,EAAA;AAClD,MAAQ,OAAA,CAAA,GAAA,CAAI,sBAAsB,IAAI,CAAA;AACtC,MAAA,QAAA,CAAS,KAAK,KAAK,CAAA;AAAA,aACZ,GAAK,EAAA;AACZ,MAAA,MAAM,YAAe,GAAA,GAAA,YAAe,KAAQ,GAAA,GAAA,CAAI,OAAU,GAAA,wBAAA;AAC1D,MAAQ,OAAA,CAAA,KAAA,CAAM,4BAA4B,YAAY,CAAA;AACtD,MAAA,QAAA,CAAS,YAAY,CAAA;AACrB,MAAA,QAAA,CAAS,EAAE,CAAA;AAAA,KACX,SAAA;AACA,MAAA,UAAA,CAAW,KAAK,CAAA;AAAA;AAClB,GACF,EAAG,CAAC,UAAW,CAAA,SAAA,EAAW,WAAW,MAAQ,EAAA,YAAA,EAAc,QAAQ,CAAC,CAAA;AAGpE,EAAA,SAAA,CAAU,MAAM;AACd,IAAI,IAAA,iBAAA,CAAkB,SAAS,CAAG,EAAA;AAChC,MAAA,YAAA,CAAa,iBAAiB,CAAA;AAAA;AAChC,GACC,EAAA,CAAC,YAAc,EAAA,iBAAiB,CAAC,CAAA;AAEpC,EAAM,MAAA,WAAA,GAAc,QAAQ,MAAM;AAChC,IAAA,OAAO,KAAM,CAAA,MAAA,CAAO,CAAC,GAAA,EAAK,IAAS,KAAA;AACjC,MAAA,IAAI,CAAC,GAAA,CAAI,IAAK,CAAA,IAAI,CAAG,EAAA;AACnB,QAAI,GAAA,CAAA,IAAA,CAAK,IAAI,CAAA,GAAI,EAAC;AAAA;AAEpB,MAAA,GAAA,CAAI,IAAK,CAAA,IAAI,CAAE,CAAA,IAAA,CAAK,IAAI,CAAA;AACxB,MAAO,OAAA,GAAA;AAAA,KACT,EAAG,EAAkC,CAAA;AAAA,GACvC,EAAG,CAAC,KAAK,CAAC,CAAA;AAEV,EAAO,OAAA;AAAA,IACL,KAAA;AAAA,IACA,WAAA;AAAA,IACA,OAAA;AAAA,IACA,KAAA;AAAA,IACA,WAAW,UAAW,CAAA,SAAA;AAAA,IACtB,eAAe,UAAW,CAAA,IAAA;AAAA,IAC1B,gBAAA;AAAA,IACA,iBAAA;AAAA,IACA,oBAAA;AAAA,IACA,YAAY,KAAM,CAAA;AAAA,GACpB;AACF;;;;"}
1
+ {"version":3,"file":"useAiRules.esm.js","sources":["../../src/hooks/useAiRules.ts"],"sourcesContent":["import { useEntity } from '@backstage/plugin-catalog-react';\nimport { AIRuleType, AIRulesResponse, AIRule } from '../types';\nimport { useState, useEffect, useMemo, useCallback } from 'react';\nimport { useApi, configApiRef, discoveryApiRef, fetchApiRef } from '@backstage/core-plugin-api';\n\nexport const useAiRules = () => {\n const { entity } = useEntity();\n const configApi = useApi(configApiRef);\n const discoveryApi = useApi(discoveryApiRef);\n const fetchApi = useApi(fetchApiRef);\n \n const [rules, setRules] = useState<AIRule[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [selectedRuleTypes, setSelectedRuleTypes] = useState<AIRuleType[]>([]);\n const [appliedRuleTypes, setAppliedRuleTypes] = useState<AIRuleType[]>([]);\n const [initialized, setInitialized] = useState(false);\n const [hasSearched, setHasSearched] = useState(false);\n\n // Stabilize allowed rule types\n const allowedRuleTypes = useMemo(() => {\n return configApi.getOptionalStringArray('aiRules.allowedRuleTypes') as AIRuleType[] || [AIRuleType.CURSOR, AIRuleType.COPILOT, AIRuleType.CLINE, AIRuleType.CLAUDE_CODE];\n }, [configApi]);\n\n // Stabilize default rule types\n const defaultRuleTypes = useMemo(() => {\n // Check if the config has the aiRules section at all\n const aiRulesConfig = configApi.getOptionalConfig('aiRules');\n if (!aiRulesConfig) {\n // Config not loaded yet or aiRules section doesn't exist, use empty array\n return [];\n }\n \n const configuredDefaults = configApi.getOptionalStringArray('aiRules.defaultRuleTypes') as AIRuleType[];\n \n // If defaultRuleTypes is explicitly configured (even as empty array), use that\n // If not configured at all, use empty array\n return configuredDefaults !== undefined ? configuredDefaults : [];\n }, [configApi]);\n\n // Extract stable entity properties\n const entityData = useMemo(() => {\n const sourceAnnotation = entity.metadata?.annotations?.['backstage.io/source-location'] || '';\n const hasGitUrl = sourceAnnotation.startsWith('url:');\n \n let gitUrl = hasGitUrl ? sourceAnnotation.substring(4) : undefined;\n if (gitUrl) {\n // Remove trailing slashes and normalize URL\n gitUrl = gitUrl.replace(/\\/+$/, '');\n // Handle GitHub tree/blob URLs - extract base repo URL\n const treeMatch = gitUrl.match(/^(.+)\\/tree\\/([^/]+)(?:\\/(.+))?$/);\n const blobMatch = gitUrl.match(/^(.+)\\/blob\\/([^/]+)(?:\\/(.+))?$/);\n if (treeMatch) {\n gitUrl = treeMatch[1]; // Just the base repo URL\n }\n if (blobMatch) {\n gitUrl = blobMatch[1]; // Just the base repo URL \n }\n }\n\n return {\n kind: entity.kind,\n namespace: entity.metadata.namespace || 'default',\n name: entity.metadata.name,\n sourceAnnotation,\n hasGitUrl,\n gitUrl,\n };\n }, [entity.kind, entity.metadata.namespace, entity.metadata.name, entity.metadata?.annotations?.['backstage.io/source-location']]);\n\n // Initialize selected and applied rule types when defaultRuleTypes is available\n useEffect(() => {\n // Check if config is loaded\n const aiRulesConfig = configApi.getOptionalConfig('aiRules');\n const configLoaded = aiRulesConfig !== undefined;\n \n if (!initialized && allowedRuleTypes.length > 0 && configLoaded) {\n setSelectedRuleTypes(defaultRuleTypes);\n setAppliedRuleTypes(defaultRuleTypes);\n setInitialized(true);\n }\n }, [defaultRuleTypes, allowedRuleTypes, initialized, configApi]);\n\n // Stable fetch function\n const fetchAiRules = useCallback(async (ruleTypes: AIRuleType[]) => {\n if (!entityData.hasGitUrl || !entityData.gitUrl) {\n setLoading(false);\n return;\n }\n\n try {\n setLoading(true);\n setError(null);\n setHasSearched(true);\n \n const baseUrl = await discoveryApi.getBaseUrl('ai-rules');\n const url = new URL(`${baseUrl}/rules`);\n \n // Send the Git URL directly to the backend\n url.searchParams.append('gitUrl', entityData.gitUrl);\n if (ruleTypes.length > 0) {\n url.searchParams.append('ruleTypes', ruleTypes.join(','));\n }\n\n const response = await fetchApi.fetch(url.toString());\n if (!response.ok) {\n throw new Error(`Failed to fetch AI rules: ${response.statusText}`);\n }\n\n const data: AIRulesResponse = await response.json();\n setRules(data.rules);\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';\n console.error('Error fetching AI rules:', errorMessage);\n setError(errorMessage);\n setRules([]);\n } finally {\n setLoading(false);\n }\n }, [entityData.hasGitUrl, entityData.gitUrl, discoveryApi, fetchApi]);\n\n // Manual apply filter function\n const applyFilters = useCallback(() => {\n setAppliedRuleTypes([...selectedRuleTypes]);\n if (selectedRuleTypes.length > 0) {\n fetchAiRules(selectedRuleTypes);\n } else {\n // If no rule types selected, clear rules\n setRules([]);\n setError(null);\n setHasSearched(true);\n }\n }, [selectedRuleTypes, fetchAiRules]);\n\n // Reset filters to allowed rule types and apply immediately\n const resetFilters = useCallback(() => {\n setSelectedRuleTypes(allowedRuleTypes);\n setAppliedRuleTypes([...allowedRuleTypes]);\n if (allowedRuleTypes.length > 0) {\n fetchAiRules(allowedRuleTypes);\n }\n }, [allowedRuleTypes, fetchAiRules]);\n\n // Fetch rules when applied rule types change (triggered by applyFilters)\n useEffect(() => {\n if (appliedRuleTypes.length > 0) {\n fetchAiRules(appliedRuleTypes);\n }\n }, [fetchAiRules, appliedRuleTypes]);\n\n const rulesByType = useMemo(() => {\n return rules.reduce((acc, rule) => {\n if (!acc[rule.type]) {\n acc[rule.type] = [];\n }\n acc[rule.type].push(rule);\n return acc;\n }, {} as Record<AIRuleType, AIRule[]>);\n }, [rules]);\n\n return {\n rules,\n rulesByType,\n loading,\n error,\n hasGitUrl: entityData.hasGitUrl,\n componentName: entityData.name,\n allowedRuleTypes,\n defaultRuleTypes,\n selectedRuleTypes,\n setSelectedRuleTypes,\n appliedRuleTypes,\n applyFilters,\n resetFilters,\n totalRules: rules.length,\n hasSearched,\n hasUnappliedChanges: JSON.stringify(selectedRuleTypes.sort()) !== JSON.stringify(appliedRuleTypes.sort()),\n };\n};"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -15,7 +15,8 @@ declare const AIRulesComponent: ({ title }?: AIRulesComponentProps) => react_jsx
15
15
  declare enum AIRuleType {
16
16
  CURSOR = "cursor",
17
17
  COPILOT = "copilot",
18
- CLINE = "cline"
18
+ CLINE = "cline",
19
+ CLAUDE_CODE = "claude-code"
19
20
  }
20
21
  interface CursorRule {
21
22
  type: AIRuleType.CURSOR;
@@ -51,7 +52,16 @@ interface ClineRule {
51
52
  content: string;
52
53
  }>;
53
54
  }
54
- type AIRule = CursorRule | CopilotRule | ClineRule;
55
+ interface ClaudeCodeRule {
56
+ type: AIRuleType.CLAUDE_CODE;
57
+ id: string;
58
+ filePath: string;
59
+ fileName: string;
60
+ gitUrl?: string;
61
+ content: string;
62
+ title?: string;
63
+ }
64
+ type AIRule = CursorRule | CopilotRule | ClineRule | ClaudeCodeRule;
55
65
  interface AIRulesResponse {
56
66
  rules: AIRule[];
57
67
  totalCount: number;
@@ -61,4 +71,4 @@ interface AIRulesConfig {
61
71
  allowedRuleTypes?: AIRuleType[];
62
72
  }
63
73
 
64
- export { type AIRule, AIRuleType, AIRulesComponent, type AIRulesComponentProps, type AIRulesConfig, type AIRulesResponse, type ClineRule, type CopilotRule, type CursorRule, aiRulesPlugin, isAIRulesAvailable };
74
+ export { type AIRule, AIRuleType, AIRulesComponent, type AIRulesComponentProps, type AIRulesConfig, type AIRulesResponse, type ClaudeCodeRule, type ClineRule, type CopilotRule, type CursorRule, aiRulesPlugin, isAIRulesAvailable };
package/dist/types.esm.js CHANGED
@@ -2,6 +2,7 @@ var AIRuleType = /* @__PURE__ */ ((AIRuleType2) => {
2
2
  AIRuleType2["CURSOR"] = "cursor";
3
3
  AIRuleType2["COPILOT"] = "copilot";
4
4
  AIRuleType2["CLINE"] = "cline";
5
+ AIRuleType2["CLAUDE_CODE"] = "claude-code";
5
6
  return AIRuleType2;
6
7
  })(AIRuleType || {});
7
8
 
@@ -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}\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\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 type AIRule = CursorRule | CopilotRule | ClineRule;\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":"AAAY,IAAA,UAAA,qBAAAA,WAAL,KAAA;AACL,EAAAA,YAAA,QAAS,CAAA,GAAA,QAAA;AACT,EAAAA,YAAA,SAAU,CAAA,GAAA,SAAA;AACV,EAAAA,YAAA,OAAQ,CAAA,GAAA,OAAA;AAHE,EAAAA,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}\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\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":"AAAY,IAAA,UAAA,qBAAAA,WAAL,KAAA;AACL,EAAAA,YAAA,QAAS,CAAA,GAAA,QAAA;AACT,EAAAA,YAAA,SAAU,CAAA,GAAA,SAAA;AACV,EAAAA,YAAA,OAAQ,CAAA,GAAA,OAAA;AACR,EAAAA,YAAA,aAAc,CAAA,GAAA,aAAA;AAJJ,EAAAA,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": "0.1.0",
4
+ "version": "0.2.1",
5
5
  "main": "dist/index.esm.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "license": "Apache-2.0",
@@ -36,11 +36,11 @@
36
36
  "postpack": "backstage-cli package postpack"
37
37
  },
38
38
  "dependencies": {
39
- "@backstage/catalog-model": "^1.7.4",
40
- "@backstage/core-components": "^0.17.3",
41
- "@backstage/core-plugin-api": "^1.10.8",
42
- "@backstage/plugin-catalog-react": "^1.19.0",
43
- "@backstage/theme": "^0.6.6",
39
+ "@backstage/catalog-model": "^1.7.5",
40
+ "@backstage/core-components": "^0.17.5",
41
+ "@backstage/core-plugin-api": "^1.10.9",
42
+ "@backstage/plugin-catalog-react": "^1.20.1",
43
+ "@backstage/theme": "^0.6.8",
44
44
  "@material-ui/core": "^4.12.4",
45
45
  "@material-ui/icons": "^4.11.3",
46
46
  "@material-ui/lab": "4.0.0-alpha.61"
@@ -49,10 +49,10 @@
49
49
  "react": "^16.13.1 || ^17.0.0 || ^18.0.0"
50
50
  },
51
51
  "devDependencies": {
52
- "@backstage/cli": "^0.33.0",
53
- "@backstage/core-app-api": "^1.17.1",
54
- "@backstage/dev-utils": "^1.1.11",
55
- "@backstage/test-utils": "^1.7.9",
52
+ "@backstage/cli": "^0.34.1",
53
+ "@backstage/core-app-api": "^1.18.0",
54
+ "@backstage/dev-utils": "^1.1.13",
55
+ "@backstage/test-utils": "^1.7.11",
56
56
  "@testing-library/jest-dom": "^6.0.0",
57
57
  "@testing-library/react": "^14.0.0",
58
58
  "@testing-library/user-event": "^14.0.0",