skrypt-ai 0.5.0 → 0.6.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.
Files changed (120) hide show
  1. package/dist/auth/index.js +8 -1
  2. package/dist/autofix/index.d.ts +0 -4
  3. package/dist/autofix/index.js +0 -21
  4. package/dist/capture/browser.d.ts +11 -0
  5. package/dist/capture/browser.js +173 -0
  6. package/dist/capture/diff.d.ts +23 -0
  7. package/dist/capture/diff.js +52 -0
  8. package/dist/capture/index.d.ts +23 -0
  9. package/dist/capture/index.js +210 -0
  10. package/dist/capture/naming.d.ts +17 -0
  11. package/dist/capture/naming.js +45 -0
  12. package/dist/capture/parser.d.ts +15 -0
  13. package/dist/capture/parser.js +80 -0
  14. package/dist/capture/types.d.ts +57 -0
  15. package/dist/capture/types.js +1 -0
  16. package/dist/cli.js +4 -0
  17. package/dist/commands/autofix.js +136 -120
  18. package/dist/commands/cron.js +58 -47
  19. package/dist/commands/deploy.js +123 -102
  20. package/dist/commands/generate.js +88 -6
  21. package/dist/commands/heal.d.ts +10 -0
  22. package/dist/commands/heal.js +201 -0
  23. package/dist/commands/i18n.js +146 -111
  24. package/dist/commands/lint.js +50 -44
  25. package/dist/commands/llms-txt.js +59 -49
  26. package/dist/commands/login.js +61 -43
  27. package/dist/commands/mcp.js +6 -0
  28. package/dist/commands/monitor.js +13 -8
  29. package/dist/commands/qa.d.ts +2 -0
  30. package/dist/commands/qa.js +43 -0
  31. package/dist/commands/review-pr.js +114 -103
  32. package/dist/commands/sdk.js +128 -122
  33. package/dist/commands/security.js +86 -80
  34. package/dist/commands/test.js +91 -92
  35. package/dist/commands/version.js +104 -75
  36. package/dist/commands/watch.js +130 -114
  37. package/dist/config/types.js +2 -2
  38. package/dist/context-hub/index.d.ts +23 -0
  39. package/dist/context-hub/index.js +179 -0
  40. package/dist/context-hub/mappings.d.ts +8 -0
  41. package/dist/context-hub/mappings.js +55 -0
  42. package/dist/context-hub/types.d.ts +33 -0
  43. package/dist/context-hub/types.js +1 -0
  44. package/dist/generator/generator.js +39 -6
  45. package/dist/generator/types.d.ts +7 -0
  46. package/dist/generator/writer.d.ts +3 -1
  47. package/dist/generator/writer.js +24 -4
  48. package/dist/llm/anthropic-client.d.ts +1 -0
  49. package/dist/llm/anthropic-client.js +3 -1
  50. package/dist/llm/index.d.ts +6 -4
  51. package/dist/llm/index.js +76 -261
  52. package/dist/llm/openai-client.d.ts +1 -0
  53. package/dist/llm/openai-client.js +7 -2
  54. package/dist/qa/checks.d.ts +10 -0
  55. package/dist/qa/checks.js +492 -0
  56. package/dist/qa/fixes.d.ts +30 -0
  57. package/dist/qa/fixes.js +277 -0
  58. package/dist/qa/index.d.ts +29 -0
  59. package/dist/qa/index.js +187 -0
  60. package/dist/qa/types.d.ts +24 -0
  61. package/dist/qa/types.js +1 -0
  62. package/dist/scanner/csharp.d.ts +23 -0
  63. package/dist/scanner/csharp.js +421 -0
  64. package/dist/scanner/index.js +16 -2
  65. package/dist/scanner/java.d.ts +39 -0
  66. package/dist/scanner/java.js +318 -0
  67. package/dist/scanner/kotlin.d.ts +23 -0
  68. package/dist/scanner/kotlin.js +389 -0
  69. package/dist/scanner/php.d.ts +57 -0
  70. package/dist/scanner/php.js +351 -0
  71. package/dist/scanner/ruby.d.ts +36 -0
  72. package/dist/scanner/ruby.js +431 -0
  73. package/dist/scanner/swift.d.ts +25 -0
  74. package/dist/scanner/swift.js +392 -0
  75. package/dist/scanner/types.d.ts +1 -1
  76. package/dist/template/content/docs/_navigation.json +46 -0
  77. package/dist/template/content/docs/_sidebars.json +684 -0
  78. package/dist/template/content/docs/core.md +4544 -0
  79. package/dist/template/content/docs/index.mdx +89 -0
  80. package/dist/template/content/docs/integrations.md +1158 -0
  81. package/dist/template/content/docs/llms-full.md +403 -0
  82. package/dist/template/content/docs/llms.txt +4588 -0
  83. package/dist/template/content/docs/other.md +10379 -0
  84. package/dist/template/content/docs/tools.md +746 -0
  85. package/dist/template/content/docs/types.md +531 -0
  86. package/dist/template/docs.json +13 -11
  87. package/dist/template/mdx-components.tsx +27 -2
  88. package/dist/template/package.json +6 -0
  89. package/dist/template/public/search-index.json +1 -1
  90. package/dist/template/scripts/build-search-index.mjs +84 -6
  91. package/dist/template/src/app/api/chat/route.ts +83 -128
  92. package/dist/template/src/app/docs/[...slug]/page.tsx +75 -20
  93. package/dist/template/src/app/docs/llms-full.md +151 -4
  94. package/dist/template/src/app/docs/llms.txt +2464 -847
  95. package/dist/template/src/app/docs/page.mdx +48 -38
  96. package/dist/template/src/app/layout.tsx +3 -1
  97. package/dist/template/src/app/page.tsx +22 -8
  98. package/dist/template/src/components/ai-chat.tsx +73 -64
  99. package/dist/template/src/components/breadcrumbs.tsx +21 -23
  100. package/dist/template/src/components/copy-button.tsx +13 -9
  101. package/dist/template/src/components/copy-page-button.tsx +54 -0
  102. package/dist/template/src/components/docs-layout.tsx +37 -25
  103. package/dist/template/src/components/header.tsx +51 -10
  104. package/dist/template/src/components/mdx/card.tsx +17 -3
  105. package/dist/template/src/components/mdx/code-block.tsx +13 -9
  106. package/dist/template/src/components/mdx/code-group.tsx +13 -8
  107. package/dist/template/src/components/mdx/heading.tsx +15 -2
  108. package/dist/template/src/components/mdx/highlighted-code.tsx +13 -8
  109. package/dist/template/src/components/mdx/index.tsx +2 -0
  110. package/dist/template/src/components/mdx/mermaid.tsx +110 -0
  111. package/dist/template/src/components/mdx/screenshot.tsx +150 -0
  112. package/dist/template/src/components/scroll-to-hash.tsx +48 -0
  113. package/dist/template/src/components/sidebar.tsx +12 -18
  114. package/dist/template/src/components/table-of-contents.tsx +9 -0
  115. package/dist/template/src/lib/highlight.ts +3 -88
  116. package/dist/template/src/lib/navigation.ts +159 -0
  117. package/dist/template/src/styles/globals.css +17 -6
  118. package/dist/utils/validation.d.ts +0 -3
  119. package/dist/utils/validation.js +0 -26
  120. package/package.json +3 -2
@@ -0,0 +1,48 @@
1
+ 'use client'
2
+
3
+ import { useEffect } from 'react'
4
+ import { usePathname } from 'next/navigation'
5
+
6
+ /**
7
+ * Handles scrolling to hash targets after page navigation.
8
+ * Next.js App Router can intercept hash links before content is rendered.
9
+ * This component uses a MutationObserver to wait for the target element.
10
+ */
11
+ export function ScrollToHash() {
12
+ const pathname = usePathname()
13
+
14
+ useEffect(() => {
15
+ const hash = window.location.hash
16
+ if (!hash) return
17
+
18
+ const id = hash.slice(1)
19
+
20
+ // Try immediate scroll first
21
+ const target = document.getElementById(id)
22
+ if (target) {
23
+ target.scrollIntoView({ behavior: 'smooth', block: 'start' })
24
+ return
25
+ }
26
+
27
+ // If element not found yet (SSR/hydration timing), observe DOM
28
+ const observer = new MutationObserver(() => {
29
+ const el = document.getElementById(id)
30
+ if (el) {
31
+ observer.disconnect()
32
+ el.scrollIntoView({ behavior: 'smooth', block: 'start' })
33
+ }
34
+ })
35
+
36
+ observer.observe(document.body, { childList: true, subtree: true })
37
+
38
+ // Cleanup after 5 seconds
39
+ const timeout = setTimeout(() => observer.disconnect(), 5000)
40
+
41
+ return () => {
42
+ observer.disconnect()
43
+ clearTimeout(timeout)
44
+ }
45
+ }, [pathname])
46
+
47
+ return null
48
+ }
@@ -5,15 +5,13 @@ import { usePathname } from 'next/navigation'
5
5
  import { ChevronRight, BookOpen, Code, FileText, Settings, Key, Zap, Shield, Globe, Terminal, Database, Cloud, Lock, Rocket, Search, Star, Heart, Package, Puzzle, GitBranch, Cpu } from 'lucide-react'
6
6
  import { useState, useMemo } from 'react'
7
7
  import { cn } from '@/lib/utils'
8
-
9
- interface NavPage { title: string; path: string; pages?: NavPage[] }
10
- interface NavGroup { group: string; icon?: string; pages: (NavPage | NavGroup)[] }
11
- interface DocsConfig { navigation: NavGroup[] }
8
+ import type { NavPage, NavGroup } from '@/lib/navigation'
9
+ import { isNavGroup } from '@/lib/navigation'
12
10
 
13
11
  interface SidebarProps {
14
12
  open?: boolean
15
13
  onClose?: () => void
16
- docsConfig: DocsConfig
14
+ groups: NavGroup[]
17
15
  }
18
16
 
19
17
  const iconMap: Record<string, React.ComponentType<{ size?: number; className?: string }>> = {
@@ -22,10 +20,6 @@ const iconMap: Record<string, React.ComponentType<{ size?: number; className?: s
22
20
  Package, Puzzle, GitBranch, Cpu,
23
21
  }
24
22
 
25
- function isNavGroup(item: NavPage | NavGroup): item is NavGroup {
26
- return 'group' in item && !('path' in item)
27
- }
28
-
29
23
  /** Check if any descendant page matches the current pathname */
30
24
  function hasActivePage(pages: (NavPage | NavGroup)[], pathname: string): boolean {
31
25
  for (const item of pages) {
@@ -39,7 +33,7 @@ function hasActivePage(pages: (NavPage | NavGroup)[], pathname: string): boolean
39
33
  return false
40
34
  }
41
35
 
42
- export function Sidebar({ open, onClose, docsConfig }: SidebarProps) {
36
+ export function Sidebar({ open, onClose, groups }: SidebarProps) {
43
37
  const pathname = usePathname()
44
38
 
45
39
  return (
@@ -61,8 +55,8 @@ export function Sidebar({ open, onClose, docsConfig }: SidebarProps) {
61
55
  open ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'
62
56
  )}
63
57
  >
64
- <nav className="py-4 pr-2 space-y-6 divide-y divide-[var(--color-border)]">
65
- {docsConfig.navigation.map((group) => (
58
+ <nav className="py-4 pr-2 space-y-5">
59
+ {groups.map((group) => (
66
60
  <SidebarGroup key={group.group} group={group} pathname={pathname} onNavigate={onClose} level={0} />
67
61
  ))}
68
62
  </nav>
@@ -80,7 +74,7 @@ function SidebarGroup({ group, pathname, onNavigate, level }: { group: NavGroup;
80
74
  // Limit nesting to 3 levels
81
75
  if (level > 2) return null
82
76
 
83
- const paddingLeft = level === 0 ? 'pl-4' : level === 1 ? 'pl-10' : 'pl-14'
77
+ const paddingLeft = level === 0 ? 'pl-4' : level === 1 ? 'pl-8' : 'pl-12'
84
78
 
85
79
  return (
86
80
  <div>
@@ -88,15 +82,15 @@ function SidebarGroup({ group, pathname, onNavigate, level }: { group: NavGroup;
88
82
  onClick={() => setExpanded(!expanded)}
89
83
  aria-expanded={expanded}
90
84
  className={cn(
91
- 'flex items-center gap-2.5 w-full mb-2 text-[0.8125rem] font-medium text-[var(--color-text)] hover:text-[var(--color-text)] transition-colors',
85
+ 'flex items-center gap-2 w-full mb-1 py-1 text-[0.6875rem] font-semibold uppercase tracking-wider text-[var(--color-text-tertiary)] hover:text-[var(--color-text-secondary)] transition-colors',
92
86
  paddingLeft
93
87
  )}
94
88
  >
95
89
  <ChevronRight
96
- size={14}
97
- className={cn('text-[var(--color-text-tertiary)] transition-transform duration-100', expanded && 'rotate-90')}
90
+ size={10}
91
+ className={cn('shrink-0 transition-transform duration-100', expanded && 'rotate-90')}
98
92
  />
99
- {Icon && <Icon size={14} className="text-[var(--color-text-tertiary)]" />}
93
+ {Icon && <Icon size={12} className="shrink-0" />}
100
94
  {group.group}
101
95
  </button>
102
96
  {expanded && (
@@ -141,7 +135,7 @@ function SidebarPageItem({ page, pathname, onNavigate, level }: { page: NavPage;
141
135
  }, [page, pathname])
142
136
  const [expanded, setExpanded] = useState(isActive)
143
137
 
144
- const paddingClass = level === 0 ? 'pl-10' : level === 1 ? 'pl-14' : 'pl-18'
138
+ const paddingClass = level === 0 ? 'pl-7' : level === 1 ? 'pl-11' : 'pl-14'
145
139
 
146
140
  return (
147
141
  <div>
@@ -21,6 +21,7 @@ export function TableOfContents() {
21
21
 
22
22
  const elements = article.querySelectorAll('h2, h3')
23
23
  const items: Heading[] = []
24
+ const seenIds = new Set<string>()
24
25
 
25
26
  // Generic headings that repeat in API reference pages — skip them in TOC
26
27
  const genericHeadings = new Set([
@@ -28,6 +29,7 @@ export function TableOfContents() {
28
29
  'returned validator function', 'requirements',
29
30
  'when results are returned', 'when each value is returned',
30
31
  'validationresult shape',
32
+ 'example', 'related', 'notes', 'properties',
31
33
  ])
32
34
 
33
35
  elements.forEach((el) => {
@@ -37,9 +39,16 @@ export function TableOfContents() {
37
39
  // Skip generic/repeated headings
38
40
  if (genericHeadings.has(normalized)) return
39
41
 
42
+ // Skip headings inside Card components (Cards use h3 for titles, not doc headings)
43
+ if (el.closest('.card-link') || el.closest('[data-card]')) return
44
+
40
45
  const id = el.id || normalized.replace(/\s+/g, '-')
41
46
  if (!el.id) el.id = id
42
47
 
48
+ // Deduplicate — skip if we've already seen this ID
49
+ if (seenIds.has(id)) return
50
+ seenIds.add(id)
51
+
43
52
  items.push({
44
53
  id,
45
54
  text,
@@ -1,7 +1,4 @@
1
- import { createHighlighter, type Highlighter, type BundledTheme } from 'shiki'
2
-
3
- // Cache highlighter instances per loaded theme set
4
- const highlighterCache = new Map<string, Highlighter>()
1
+ import { codeToHtml, type BundledTheme } from 'shiki'
5
2
 
6
3
  export type ThemeName =
7
4
  | 'catppuccin-mocha'
@@ -28,98 +25,16 @@ export const AVAILABLE_THEMES: { name: ThemeName; label: string; isDark: boolean
28
25
 
29
26
  export const DEFAULT_THEME: ThemeName = 'catppuccin-mocha'
30
27
 
31
- const SUPPORTED_LANGS = [
32
- 'typescript',
33
- 'javascript',
34
- 'python',
35
- 'go',
36
- 'rust',
37
- 'json',
38
- 'yaml',
39
- 'bash',
40
- 'shell',
41
- 'markdown',
42
- 'html',
43
- 'css',
44
- 'jsx',
45
- 'tsx',
46
- 'sql',
47
- 'graphql',
48
- 'diff',
49
- ]
50
-
51
- // Start with just the default theme; load others on demand
52
- let defaultHighlighter: Highlighter | null = null
53
- let defaultHighlighterPromise: Promise<Highlighter> | null = null
54
-
55
- async function getDefaultHighlighter(): Promise<Highlighter> {
56
- if (defaultHighlighter) return defaultHighlighter
57
- if (defaultHighlighterPromise) return defaultHighlighterPromise
58
-
59
- defaultHighlighterPromise = createHighlighter({
60
- themes: [DEFAULT_THEME] as BundledTheme[],
61
- langs: SUPPORTED_LANGS,
62
- }).then(hl => {
63
- defaultHighlighter = hl
64
- highlighterCache.set(DEFAULT_THEME, hl)
65
- return hl
66
- })
67
-
68
- return defaultHighlighterPromise
69
- }
70
-
71
- async function getHighlighterForTheme(theme: ThemeName): Promise<Highlighter> {
72
- if (theme === DEFAULT_THEME || highlighterCache.has(DEFAULT_THEME)) {
73
- // Try to load the theme into the existing default highlighter
74
- const hl = await getDefaultHighlighter()
75
- try {
76
- // Check if the theme is already loaded by attempting to use it
77
- const loadedThemes = hl.getLoadedThemes()
78
- if (!loadedThemes.includes(theme as BundledTheme)) {
79
- await hl.loadTheme(theme as BundledTheme)
80
- }
81
- return hl
82
- } catch {
83
- // If loading into existing highlighter fails, create a new one
84
- }
85
- }
86
-
87
- // Check cache
88
- const cached = highlighterCache.get(theme)
89
- if (cached) return cached
90
-
91
- // Create a new highlighter with this theme
92
- const hl = await createHighlighter({
93
- themes: [theme] as BundledTheme[],
94
- langs: SUPPORTED_LANGS,
95
- })
96
- highlighterCache.set(theme, hl)
97
- return hl
98
- }
99
-
100
- export async function getHighlighter(): Promise<Highlighter> {
101
- return getDefaultHighlighter()
102
- }
103
-
104
28
  export async function highlight(
105
29
  code: string,
106
30
  language: string,
107
31
  theme: ThemeName = DEFAULT_THEME
108
32
  ): Promise<string> {
109
- const hl = await getHighlighterForTheme(theme)
110
-
111
33
  const lang = normalizeLanguage(language)
112
-
113
34
  try {
114
- return hl.codeToHtml(code, {
115
- lang,
116
- theme: theme as BundledTheme,
117
- })
35
+ return await codeToHtml(code, { lang, theme: theme as BundledTheme })
118
36
  } catch {
119
- return hl.codeToHtml(code, {
120
- lang: 'text',
121
- theme: theme as BundledTheme,
122
- })
37
+ return await codeToHtml(code, { lang: 'text', theme: theme as BundledTheme })
123
38
  }
124
39
  }
125
40
 
@@ -0,0 +1,159 @@
1
+ export interface NavPage {
2
+ title: string
3
+ path: string
4
+ description?: string
5
+ pages?: NavPage[]
6
+ }
7
+
8
+ export interface NavGroup {
9
+ group: string
10
+ icon?: string
11
+ pages: (NavPage | NavGroup)[]
12
+ }
13
+
14
+ export interface NavTab {
15
+ tab: string
16
+ icon?: string
17
+ groups: NavGroup[]
18
+ href?: string
19
+ }
20
+
21
+ export type Navigation = NavGroup[] | NavTab[]
22
+
23
+ export function isNavGroup(item: NavPage | NavGroup): item is NavGroup {
24
+ return 'group' in item && !('path' in item)
25
+ }
26
+
27
+ /** Check if navigation uses tabs format */
28
+ export function hasTabs(navigation: Navigation): navigation is NavTab[] {
29
+ return navigation.length > 0 && 'tab' in navigation[0]
30
+ }
31
+
32
+ /** Recursively check if a page path exists within a list of groups */
33
+ function groupsContainPath(groups: NavGroup[], pathname: string): boolean {
34
+ for (const group of groups) {
35
+ for (const item of group.pages) {
36
+ if (isNavGroup(item)) {
37
+ if (groupsContainPath([item], pathname)) return true
38
+ } else {
39
+ if (item.path === pathname) return true
40
+ if (item.pages && pagesContainPath(item.pages, pathname)) return true
41
+ }
42
+ }
43
+ }
44
+ return false
45
+ }
46
+
47
+ function pagesContainPath(pages: NavPage[], pathname: string): boolean {
48
+ for (const page of pages) {
49
+ if (page.path === pathname) return true
50
+ if (page.pages && pagesContainPath(page.pages, pathname)) return true
51
+ }
52
+ return false
53
+ }
54
+
55
+ /** Find the active tab based on current pathname */
56
+ export function getActiveTab(pathname: string, tabs: NavTab[]): NavTab | null {
57
+ for (const tab of tabs) {
58
+ if (tab.href && tab.href === pathname) return tab
59
+ if (tab.groups && groupsContainPath(tab.groups, pathname)) return tab
60
+ }
61
+ // Default to first tab if no match
62
+ return tabs.length > 0 ? tabs[0] : null
63
+ }
64
+
65
+ /** Get sidebar groups for current pathname (works for both flat and tabbed nav) */
66
+ export function getTabGroups(navigation: Navigation, pathname: string): NavGroup[] {
67
+ if (!hasTabs(navigation)) return navigation
68
+ const active = getActiveTab(pathname, navigation)
69
+ return active?.groups ?? []
70
+ }
71
+
72
+ /** Get first page path for a tab (for linking) */
73
+ export function getTabHref(tab: NavTab): string {
74
+ if (tab.href) return tab.href
75
+ for (const group of tab.groups) {
76
+ for (const item of group.pages) {
77
+ if (isNavGroup(item)) {
78
+ for (const subItem of item.pages) {
79
+ if (!isNavGroup(subItem) && subItem.path) return subItem.path
80
+ }
81
+ } else {
82
+ return item.path
83
+ }
84
+ }
85
+ }
86
+ return '/'
87
+ }
88
+
89
+ /** Flatten all pages across all tabs/groups for prev/next navigation */
90
+ export function getAllPagesFlat(navigation: Navigation): Array<{ title: string; path: string }> {
91
+ const pages: Array<{ title: string; path: string }> = []
92
+
93
+ function collectFromPages(items: (NavPage | NavGroup)[]) {
94
+ for (const item of items) {
95
+ if (isNavGroup(item)) {
96
+ collectFromPages(item.pages)
97
+ } else {
98
+ pages.push({ title: item.title, path: item.path })
99
+ if (item.pages) collectFromPages(item.pages)
100
+ }
101
+ }
102
+ }
103
+
104
+ if (hasTabs(navigation)) {
105
+ for (const tab of navigation) {
106
+ if (tab.groups) {
107
+ for (const group of tab.groups) {
108
+ collectFromPages(group.pages)
109
+ }
110
+ }
111
+ }
112
+ } else {
113
+ for (const group of navigation) {
114
+ collectFromPages(group.pages)
115
+ }
116
+ }
117
+
118
+ return pages
119
+ }
120
+
121
+ /** Look up page info from navigation (works with both formats) */
122
+ export function findPageInNavigation(
123
+ navigation: Navigation,
124
+ pathname: string
125
+ ): { title?: string; description?: string } {
126
+ function searchPages(items: (NavPage | NavGroup)[]): { title?: string; description?: string } | null {
127
+ for (const item of items) {
128
+ if (isNavGroup(item)) {
129
+ const found = searchPages(item.pages)
130
+ if (found) return found
131
+ } else {
132
+ if (item.path === pathname) return { title: item.title, description: item.description }
133
+ if (item.pages) {
134
+ const found = searchPages(item.pages)
135
+ if (found) return found
136
+ }
137
+ }
138
+ }
139
+ return null
140
+ }
141
+
142
+ if (hasTabs(navigation)) {
143
+ for (const tab of navigation) {
144
+ if (tab.groups) {
145
+ for (const group of tab.groups) {
146
+ const found = searchPages(group.pages)
147
+ if (found) return found
148
+ }
149
+ }
150
+ }
151
+ } else {
152
+ for (const group of navigation) {
153
+ const found = searchPages(group.pages)
154
+ if (found) return found
155
+ }
156
+ }
157
+
158
+ return {}
159
+ }
@@ -80,14 +80,14 @@
80
80
  --color-bg: #0f1117;
81
81
  --color-bg-secondary: #1a1d27;
82
82
  --color-bg-tertiary: #252833;
83
- --color-text: #f3f4f6;
84
- --color-text-secondary: #d1d5db;
83
+ --color-text: #e0e2e6;
84
+ --color-text-secondary: #b0b4bc;
85
85
  --color-text-tertiary: #6b7280;
86
86
  --color-border: rgba(255, 255, 255, 0.07);
87
87
  --color-border-strong: rgba(255, 255, 255, 0.12);
88
88
  --color-code-bg: #0b0c0e;
89
89
  --color-code-border: rgba(255, 255, 255, 0.1);
90
- --color-code-text: #f3f4f6;
90
+ --color-code-text: #e0e2e6;
91
91
  }
92
92
  }
93
93
 
@@ -95,14 +95,14 @@
95
95
  --color-bg: #0f1117;
96
96
  --color-bg-secondary: #1a1d27;
97
97
  --color-bg-tertiary: #252833;
98
- --color-text: #f3f4f6;
99
- --color-text-secondary: #d1d5db;
98
+ --color-text: #e0e2e6;
99
+ --color-text-secondary: #b0b4bc;
100
100
  --color-text-tertiary: #6b7280;
101
101
  --color-border: rgba(255, 255, 255, 0.07);
102
102
  --color-border-strong: rgba(255, 255, 255, 0.12);
103
103
  --color-code-bg: #0b0c0e;
104
104
  --color-code-border: rgba(255, 255, 255, 0.1);
105
- --color-code-text: #f3f4f6;
105
+ --color-code-text: #e0e2e6;
106
106
  }
107
107
 
108
108
  /* Error page icon — dark mode overrides (avoids Tailwind dark: variant mismatch) */
@@ -257,6 +257,17 @@ code {
257
257
  border-bottom-width: 2px;
258
258
  }
259
259
 
260
+ /* Cards and card groups should not inherit prose link styling */
261
+ .prose .card-link,
262
+ .prose a:has(> .group) {
263
+ border-bottom: none !important;
264
+ font-weight: inherit;
265
+ }
266
+ .prose .card-link:hover,
267
+ .prose a:has(> .group):hover {
268
+ border-bottom: none !important;
269
+ }
270
+
260
271
  .prose p {
261
272
  margin-top: 1.25em;
262
273
  margin-bottom: 1.25em;
@@ -1,4 +1 @@
1
- export declare function validatePath(input: string, mustExist?: boolean): string;
2
1
  export declare function validateUrl(input: string): string;
3
- export declare function validateSlug(input: string): string;
4
- export declare function sanitizeForShell(input: string): string;
@@ -1,16 +1,3 @@
1
- import { existsSync } from 'fs';
2
- import { resolve } from 'path';
3
- export function validatePath(input, mustExist = true) {
4
- const resolved = resolve(input);
5
- // Prevent path traversal outside cwd
6
- if (!resolved.startsWith(process.cwd()) && !resolved.startsWith('/tmp')) {
7
- // Allow absolute paths but warn
8
- }
9
- if (mustExist && !existsSync(resolved)) {
10
- throw new Error(`Path does not exist: ${resolved}`);
11
- }
12
- return resolved;
13
- }
14
1
  export function validateUrl(input) {
15
2
  try {
16
3
  const url = new URL(input);
@@ -23,16 +10,3 @@ export function validateUrl(input) {
23
10
  throw new Error(`Invalid URL: ${input}`);
24
11
  }
25
12
  }
26
- export function validateSlug(input) {
27
- if (!/^[a-zA-Z0-9_-]+$/.test(input)) {
28
- throw new Error(`Invalid slug: ${input}. Only alphanumeric, hyphens, and underscores allowed.`);
29
- }
30
- return input;
31
- }
32
- export function sanitizeForShell(input) {
33
- // Only allow safe characters for git refs, filenames, etc.
34
- if (!/^[a-zA-Z0-9/_~.^@{}-]+$/.test(input)) {
35
- throw new Error(`Unsafe characters in input: ${input}`);
36
- }
37
- return input;
38
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skrypt-ai",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "description": "AI-powered documentation generator with code examples",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",
@@ -55,7 +55,8 @@
55
55
  "typescript": "^5.9.3"
56
56
  },
57
57
  "optionalDependencies": {
58
- "@napi-rs/keyring": "^1.1.6"
58
+ "@napi-rs/keyring": "^1.1.6",
59
+ "playwright": "^1.52.0"
59
60
  },
60
61
  "devDependencies": {
61
62
  "@eslint/js": "^10.0.1",