@grove-dev/astro 0.2.6 → 0.2.13

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 (136) hide show
  1. package/dist/lib/display.d.ts +11 -17
  2. package/dist/lib/display.d.ts.map +1 -1
  3. package/dist/lib/display.js +43 -43
  4. package/dist/lib/display.js.map +1 -1
  5. package/dist/lib/format.d.ts +6 -7
  6. package/dist/lib/format.d.ts.map +1 -1
  7. package/dist/lib/format.js +11 -12
  8. package/dist/lib/format.js.map +1 -1
  9. package/dist/lib/lenses.d.ts +35 -73
  10. package/dist/lib/lenses.d.ts.map +1 -1
  11. package/dist/lib/lenses.js +144 -126
  12. package/dist/lib/lenses.js.map +1 -1
  13. package/dist/lib/repo.d.ts +11 -22
  14. package/dist/lib/repo.d.ts.map +1 -1
  15. package/dist/lib/repo.js +12 -25
  16. package/dist/lib/repo.js.map +1 -1
  17. package/dist/lib/scores.d.ts +15 -16
  18. package/dist/lib/scores.d.ts.map +1 -1
  19. package/dist/lib/scores.js +4 -11
  20. package/dist/lib/scores.js.map +1 -1
  21. package/dist/lib/search.d.ts +47 -129
  22. package/dist/lib/search.d.ts.map +1 -1
  23. package/dist/lib/search.js +195 -180
  24. package/dist/lib/search.js.map +1 -1
  25. package/dist/lib/taxonomy-counts.d.ts +7 -21
  26. package/dist/lib/taxonomy-counts.d.ts.map +1 -1
  27. package/dist/lib/taxonomy-counts.js +12 -31
  28. package/dist/lib/taxonomy-counts.js.map +1 -1
  29. package/package.json +4 -4
  30. package/src/components/AppsIndexRow.astro +55 -184
  31. package/src/components/AppsPagination.astro +45 -108
  32. package/src/components/CategoryGrid.astro +43 -31
  33. package/src/components/ContributorsGrid.astro +3 -3
  34. package/src/components/CurationGrid.astro +41 -0
  35. package/src/components/ExploreByStack.astro +6 -0
  36. package/src/components/FilterGroupMenu.astro +49 -0
  37. package/src/components/FilterOptions.astro +44 -0
  38. package/src/components/Hero.astro +198 -147
  39. package/src/components/Icon.astro +10 -3
  40. package/src/components/ItemCard.astro +266 -248
  41. package/src/components/RecordSection.astro +68 -33
  42. package/src/components/RefinePanel.astro +115 -141
  43. package/src/components/StackGrid.astro +32 -24
  44. package/src/components/WhyThisExists.astro +32 -32
  45. package/src/layouts/BaseLayout.astro +104 -49
  46. package/src/layouts/Container.astro +5 -44
  47. package/src/layouts/Footer.astro +31 -20
  48. package/src/layouts/Header.astro +40 -19
  49. package/src/layouts/SectionHeader.astro +27 -43
  50. package/src/layouts/Seo.astro +38 -56
  51. package/src/layouts/ThemeToggle.astro +35 -48
  52. package/src/lib/display.ts +46 -53
  53. package/src/lib/format.ts +11 -12
  54. package/src/lib/lenses.ts +151 -172
  55. package/src/lib/repo.ts +16 -28
  56. package/src/lib/scores.ts +15 -19
  57. package/src/lib/search.ts +222 -290
  58. package/src/lib/taxonomy-counts.ts +12 -46
  59. package/src/styles.css +207 -1
  60. package/templates/default/.github/ISSUE_TEMPLATE/bug_report.md +33 -0
  61. package/templates/default/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  62. package/templates/default/.github/ISSUE_TEMPLATE/record_submission.md +48 -0
  63. package/templates/default/.github/pull_request_template.md +27 -0
  64. package/templates/default/.github/workflows/build.yml +55 -0
  65. package/templates/default/.github/workflows/cleanup-stale-records.yml +48 -0
  66. package/templates/default/.github/workflows/daily-refresh.yml +53 -0
  67. package/templates/default/.github/workflows/sync-contributors.yml +53 -0
  68. package/templates/default/.github/workflows/sync-github-metadata.yml +58 -0
  69. package/templates/default/.github/workflows/validate-data.yml +35 -0
  70. package/templates/default/data/records/coolify.yml +46 -0
  71. package/templates/default/data/records/gitea.yml +46 -0
  72. package/templates/default/data/records/hoppscotch.yml +47 -0
  73. package/templates/default/data/records/inventree.yml +47 -0
  74. package/templates/default/data/records/label-studio.yml +46 -0
  75. package/templates/default/data/records/logseq.yml +48 -0
  76. package/templates/default/data/records/private-gpt.yml +47 -0
  77. package/templates/default/data/records/wakapi.yml +44 -0
  78. package/templates/default/data/taxonomy/categories.yml +47 -0
  79. package/templates/default/data/taxonomy/distribution-channels.yml +43 -0
  80. package/templates/default/data/taxonomy/platforms.yml +27 -0
  81. package/templates/default/data/taxonomy/stacks.yml +41 -0
  82. package/templates/default/grove.config.ts +1 -1
  83. package/templates/default/package.json +5 -4
  84. package/templates/default/public/icons/stacks/bun.svg +1 -0
  85. package/templates/default/public/icons/stacks/clojure.svg +1 -0
  86. package/templates/default/public/icons/stacks/clojurescript.svg +1 -0
  87. package/templates/default/public/icons/stacks/deno.svg +1 -0
  88. package/templates/default/public/icons/stacks/django.svg +1 -0
  89. package/templates/default/public/icons/stacks/docker.svg +1 -0
  90. package/templates/default/public/icons/stacks/go.svg +1 -0
  91. package/templates/default/public/icons/stacks/llm.svg +1 -0
  92. package/templates/default/public/icons/stacks/node.js.svg +1 -0
  93. package/templates/default/public/icons/stacks/sveltekit.svg +1 -0
  94. package/templates/default/public/icons/stacks/vue.svg +1 -0
  95. package/templates/default/public/llms-full.txt +197 -5
  96. package/templates/default/public/llms.txt +107 -6
  97. package/templates/default/public/sitemap.xml +63 -0
  98. package/templates/default/scripts/build-llms.mjs +123 -0
  99. package/templates/default/scripts/build-records-json.mjs +27 -0
  100. package/templates/default/scripts/build-sitemap.mjs +20 -0
  101. package/templates/default/scripts/cleanup-stale-records.mjs +20 -0
  102. package/templates/default/scripts/enrich-github-metadata.mjs +99 -0
  103. package/templates/default/scripts/fetch-icons.mjs +137 -0
  104. package/templates/default/scripts/migrate-legacy-to-schema-v1.mjs +86 -0
  105. package/templates/default/scripts/parse-legacy-readme.mjs +59 -0
  106. package/templates/default/scripts/refresh-records-activity.mjs +24 -0
  107. package/templates/default/scripts/repair-license-format.mjs +72 -0
  108. package/templates/default/scripts/report-cleanup-candidates.mjs +21 -0
  109. package/templates/default/scripts/seed-from-github.mjs +62 -0
  110. package/templates/default/scripts/sync-contributors.mjs +145 -0
  111. package/templates/default/scripts/sync-github-metadata.mjs +28 -0
  112. package/templates/default/scripts/validate-records.mjs +27 -0
  113. package/templates/default/src/data/records.ts +134 -51
  114. package/templates/default/src/pages/404.astro +64 -0
  115. package/templates/default/src/pages/{projects/[slug].astro → [slug]/[itemSlug].astro} +236 -66
  116. package/templates/default/src/pages/[slug]/index.astro +476 -0
  117. package/templates/default/src/pages/about.astro +165 -82
  118. package/templates/default/src/pages/apps/[itemSlug].astro +301 -0
  119. package/templates/default/src/pages/apps/index.astro +146 -0
  120. package/templates/default/src/pages/contributors.astro +184 -0
  121. package/templates/default/src/pages/index.astro +119 -162
  122. package/templates/default/src/pages/submit.astro +276 -114
  123. package/templates/default/wrangler.jsonc +28 -0
  124. package/LICENSE +0 -21
  125. package/templates/default/data/generated/apps.json +0 -1
  126. package/templates/default/data/generated/records.full.json +0 -8
  127. package/templates/default/data/generated/records.index.json +0 -7
  128. package/templates/default/data/generated/records.json +0 -8
  129. package/templates/default/data/generated/repo-stats.json +0 -1
  130. package/templates/default/data/generated/site-config.json +0 -31
  131. package/templates/default/src/components/layout/BaseLayout.astro +0 -47
  132. package/templates/default/src/components/layout/Container.astro +0 -18
  133. package/templates/default/src/components/layout/Footer.astro +0 -27
  134. package/templates/default/src/components/layout/Header.astro +0 -43
  135. package/templates/default/src/lib/markdown.ts +0 -171
  136. package/templates/default/src/pages/projects/index.astro +0 -193
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="none"><rect width="100" height="100" rx="18" fill="#0F172A"/><circle cx="50" cy="42" r="18" fill="none" stroke="#A78BFA" stroke-width="4"/><circle cx="35" cy="38" r="3" fill="#A78BFA"/><circle cx="65" cy="38" r="3" fill="#A78BFA"/><path d="M40 50 Q50 56 60 50" stroke="#A78BFA" stroke-width="3" fill="none" stroke-linecap="round"/><text x="50" y="82" font-family="ui-sans-serif, system-ui, sans-serif" font-size="16" font-weight="700" fill="#A78BFA" text-anchor="middle">LLM</text></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="none"><rect width="100" height="100" rx="18" fill="#339933"/><path fill="#FFFFFF" d="M30 50c0-4 3-7 7-7s7 3 7 7-3 7-7 7-7-3-7-7zm28 0c0-4 3-7 7-7s7 3 7 7-3 7-7 7-7-3-7-7zm14-22c0-2 1-3 3-3s3 1 3 3-1 3-3 3-3-1-3-3zm0 44c0-2 1-3 3-3s3 1 3 3-1 3-3 3-3-1-3-3z"/><text x="50" y="82" font-family="ui-sans-serif, system-ui, sans-serif" font-size="14" font-weight="700" fill="#FFFFFF" text-anchor="middle">node.js</text></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="none"><rect width="100" height="100" rx="20" fill="#FF3E00"/><path fill="#FFFFFF" d="M50 18c-9 0-17 4.5-22.5 11.5l-6 7.5 6 7.5C33 51.5 41 56 50 56s17-4.5 22.5-11.5l6-7.5-6-7.5C67 22.5 59 18 50 18zm0 28.5c-5.5 0-10-4.5-10-10s4.5-10 10-10 10 4.5 10 10-4.5 10-10 10z"/><text x="50" y="82" font-family="Arial, sans-serif" font-size="14" font-weight="700" fill="#FFFFFF" text-anchor="middle">SVELTE</text></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="none"><path fill="#42B883" d="M75.4 28.7 50 73.3 24.6 28.7h11.3L50 51.1l14.1-22.4z"/><path fill="#35495E" d="M50 73.3 75.4 28.7H89L50 95.1 11 28.7h13.6z"/></svg>
@@ -1,14 +1,206 @@
1
- # Grove Directory — full directory
1
+ # example.com — full directory
2
2
 
3
- > Generated 2026-06-10T15:03:23.841Z from 0 records.
4
- > Source: https://example.com/projects · Regenerate with `grove generate`.
3
+ Generated: 2026-06-13T10:16:37.910Z
4
+ Records: 8
5
5
 
6
- Each section below mirrors one record detail page.
6
+ ---
7
7
 
8
- ## Index
8
+ ## Coolify
9
9
 
10
+ Open-source, self-hostable Heroku / Netlify / Vercel alternative — deploy apps and databases from a single dashboard.
10
11
 
12
+ - **URL**: https://example.com/coolify
13
+ - **Repository**: https://github.com/coollabsio/coolify
14
+ - **Stack**: SvelteKit
15
+ - **Platforms**: linux, self-host
16
+ - **Category**: DevOps
17
+ - **Tags**: self-host, paas, deployment, svelte
18
+ - **Stars**: 0
19
+ - **Last commit**: —
11
20
 
21
+ **Best for:**
22
+ - Self-hosted PaaS
23
+ - VPS deployments
24
+ - Docker-based stacks
12
25
 
26
+ **Why listed:**
27
+ - One-click app + DB deploys
28
+ - Active dev
29
+ - Clear upgrade path from manual Docker
13
30
 
31
+ ---
14
32
 
33
+ ## Gitea
34
+
35
+ Lightweight, self-hosted Git service — painless, single-binary, low-resource alternative to GitHub.
36
+
37
+ - **URL**: https://example.com/gitea
38
+ - **Repository**: https://github.com/go-gitea/gitea
39
+ - **Stack**: Go
40
+ - **Platforms**: linux, self-host, docker
41
+ - **Category**: DevOps
42
+ - **Tags**: git, forge, self-host, go
43
+ - **Stars**: 0
44
+ - **Last commit**: —
45
+
46
+ **Best for:**
47
+ - Self-hosted Git hosting
48
+ - Lightweight CI/CD integration
49
+ - Internal team forges
50
+
51
+ **Why listed:**
52
+ - Single binary, easy install
53
+ - Mature, stable releases
54
+ - Active community
55
+
56
+ ---
57
+
58
+ ## Hoppscotch
59
+
60
+ Open-source API development ecosystem — design, test, and debug APIs faster from a single interface.
61
+
62
+ - **URL**: https://example.com/hoppscotch
63
+ - **Repository**: https://github.com/hoppscotch/hoppscotch
64
+ - **Stack**: Vue
65
+ - **Platforms**: web, desktop, self-host
66
+ - **Category**: Web
67
+ - **Tags**: api, testing, developer-tools, vue
68
+ - **Stars**: 0
69
+ - **Last commit**: —
70
+
71
+ **Best for:**
72
+ - API exploration
73
+ - REST/GraphQL/WebSocket testing
74
+ - Self-hosted team workflows
75
+
76
+ **Why listed:**
77
+ - Lightweight, fast UI
78
+ - PWA + desktop builds
79
+ - Open-source Postman/Insomnia alternative
80
+
81
+ ---
82
+
83
+ ## InvenTree
84
+
85
+ Open-source inventory management system with parts, stock, and purchase orders.
86
+
87
+ - **URL**: https://example.com/inventree
88
+ - **Repository**: https://github.com/inventree/InvenTree
89
+ - **Stack**: Flutter
90
+ - **Platforms**: ios, android, web
91
+ - **Category**: Mobile
92
+ - **Tags**: inventory, stock, mobile, dart, flutter
93
+ - **Stars**: 0
94
+ - **Last commit**: —
95
+
96
+ **Best for:**
97
+ - Inventory tracking
98
+ - Manufacturing
99
+ - Hardware projects
100
+
101
+ **Why listed:**
102
+ - Production-grade mobile + web stack
103
+ - Strong community
104
+ - Cleanly factored Dart codebase
105
+
106
+ ---
107
+
108
+ ## Label Studio
109
+
110
+ Open-source data labeling platform — annotate images, text, audio, video, and time series for ML training.
111
+
112
+ - **URL**: https://example.com/label-studio
113
+ - **Repository**: https://github.com/HumanSignal/label-studio
114
+ - **Stack**: Python
115
+ - **Platforms**: web, self-host
116
+ - **Category**: AI/ML
117
+ - **Tags**: labeling, ml-ops, annotation, python
118
+ - **Stars**: 0
119
+ - **Last commit**: —
120
+
121
+ **Best for:**
122
+ - ML dataset labeling
123
+ - Multi-modal annotation
124
+ - Active learning pipelines
125
+
126
+ **Why listed:**
127
+ - Mature, production-grade
128
+ - Strong ML backend
129
+ - Active commercial + community backing
130
+
131
+ ---
132
+
133
+ ## Logseq
134
+
135
+ Privacy-first, open-source knowledge base that works on top of plain text Markdown and Org-mode files.
136
+
137
+ - **URL**: https://example.com/logseq
138
+ - **Repository**: https://github.com/logseq/logseq
139
+ - **Stack**: ClojureScript
140
+ - **Platforms**: web, desktop
141
+ - **Category**: Web
142
+ - **Tags**: notes, knowledge-base, productivity, clojure
143
+ - **Stars**: 0
144
+ - **Last commit**: —
145
+
146
+ **Best for:**
147
+ - PKM (personal knowledge management)
148
+ - Local-first note taking
149
+ - Graph-based outliner
150
+
151
+ **Why listed:**
152
+ - Local-first with sync
153
+ - Active community
154
+ - Strong plugin ecosystem
155
+
156
+ ---
157
+
158
+ ## PrivateGPT
159
+
160
+ Interact privately with your documents using LLMs — 100% private, no data leaves your execution environment.
161
+
162
+ - **URL**: https://example.com/private-gpt
163
+ - **Repository**: https://github.com/zylon-ai/private-gpt
164
+ - **Stack**: Python
165
+ - **Platforms**: linux, macos, self-host
166
+ - **Category**: AI/ML
167
+ - **Tags**: llm, rag, privacy, python
168
+ - **Stars**: 0
169
+ - **Last commit**: —
170
+
171
+ **Best for:**
172
+ - Private document Q&A
173
+ - Local LLM experimentation
174
+ - Enterprise privacy use cases
175
+
176
+ **Why listed:**
177
+ - Strict no-network mode
178
+ - Pluggable LLM providers
179
+ - Clean ingestion pipeline
180
+
181
+ ---
182
+
183
+ ## Wakapi
184
+
185
+ Open-source time tracker for developers — coding stats, language breakdowns, and project-level metrics.
186
+
187
+ - **URL**: https://example.com/wakapi
188
+ - **Repository**: https://github.com/muety/wakapi
189
+ - **Stack**: Kotlin
190
+ - **Platforms**: android
191
+ - **Category**: Mobile
192
+ - **Tags**: time-tracking, analytics, mobile, kotlin
193
+ - **Stars**: 0
194
+ - **Last commit**: —
195
+
196
+ **Best for:**
197
+ - Personal coding stats
198
+ - WakaTime alternative
199
+ - Privacy-first telemetry
200
+
201
+ **Why listed:**
202
+ - Self-host friendly
203
+ - Companion mobile app
204
+ - Clean MVVM architecture
205
+
206
+ ---
@@ -1,11 +1,112 @@
1
1
  # Grove Directory
2
2
 
3
- A curated, health-aware developer directory powered by Grove.
3
+ Grove Directory is a curated, health-aware developer directory powered by
4
+ the Grove framework. Every entry is real, runnable, and actively
5
+ maintained — not an SDK, not a template, not a toy demo.
4
6
 
5
- Directory: https://example.com/projects
6
- Records indexed: 0
7
- Categories: 0
7
+ The directory is built from plain YAML files in the consumer's
8
+ repository. The build pipeline renders a static Astro site. No
9
+ database, no JavaScript framework, no CDN with sticky sessions.
8
10
 
9
- ## Usage
11
+ ## Main URLs
10
12
 
11
- Use /llms-full.txt for record-level details. Prefer detail pages for citations.
13
+ - Home: https://example.com/
14
+ - Browse: https://example.com/projects
15
+ - About: https://example.com/about
16
+ - Contributors: https://example.com/contributors
17
+ - Submit a record: https://example.com/submit
18
+
19
+ ## Filter views (one URL = one curated lens)
20
+
21
+ - Trending: https://example.com/projects?label=hot
22
+ - Recently added: https://example.com/projects?label=new
23
+ - Established (mature): https://example.com/projects?label=mature
24
+ - Featured: https://example.com/projects?label=featured
25
+ - Good to learn: https://example.com/projects?lens=good-to-learn
26
+ - Production-like: https://example.com/projects?lens=production-like
27
+ - Beginner-friendly: https://example.com/projects?lens=beginner-friendly
28
+ - Contribution-ready: https://example.com/projects?lens=contribution-ready
29
+
30
+ ## Browse axes
31
+
32
+ - By stack: https://example.com/projects (use the `stack` query param)
33
+ - By category: https://example.com/projects (use the `category` query param)
34
+ - By platform: https://example.com/projects (use the `platform` query param)
35
+ - By license: https://example.com/projects (use the `license` query param)
36
+ - By health: https://example.com/projects (use the `status` query param)
37
+
38
+ ## What the directory contains
39
+
40
+ - Real, runnable open-source records (projects, resources, entities)
41
+ - Stack, language, and architecture at a glance
42
+ - Activity, health signals, and maturity tiers
43
+ - License, stars, and contribution readiness
44
+ - Curated notes: best for, why listed, caveats
45
+ - Filters by stack, platform, license, and status
46
+
47
+ ## Data layout (for AI agents that want to ingest the catalog)
48
+
49
+ - `data/records/*.yml` — human-edited source of truth (one file per record)
50
+ - `data/taxonomy/categories.yml` — 16 categories (Productivity, Finance, …)
51
+ - `data/taxonomy/stacks.yml` — 6 stacks (Flutter, React Native, …)
52
+ - `data/taxonomy/platforms.yml` — 7 platforms (iOS, Android, …)
53
+ - `data/taxonomy/distribution-channels.yml` — 11 distribution channels
54
+ - `data/generated/records.full.json` — full record projection
55
+ - `data/generated/records.index.json` — slim list-page projection
56
+ - `data/generated/site-config.json` — blueprint + site metadata
57
+
58
+ For per-record markdown (one section per record with description, repo,
59
+ stack, platforms, stars, last-commit), see `/llms-full.txt`.
60
+
61
+ ## Blueprint system
62
+
63
+ Grove supports three blueprints out of the box:
64
+
65
+ - `project-directory` (default) — `kind: project` records under
66
+ `/projects/<slug>`. Use this for code-reference showcases.
67
+ - `resource-hub` — `kind: resource` records (datasets, libraries,
68
+ tools) under `/resources/<slug>`.
69
+ - `ecosystem-map` — `kind: entity` records (companies, foundations,
70
+ people) under `/entities/<slug>`.
71
+
72
+ The blueprint is selected in `grove.config.ts` and propagates through
73
+ to every page, route, and component — no per-blueprint forks needed.
74
+
75
+ ## How the build runs
76
+
77
+ ```
78
+ pnpm run build:data # yml → JSON (grove generate)
79
+ pnpm run build:sitemap # emit public/sitemap.xml (grove sitemap)
80
+ pnpm run build:llms # emit public/llms-full.txt (committed llms.txt is preserved)
81
+ astro build # render the static site
82
+ ```
83
+
84
+ ## Contributing
85
+
86
+ Open a PR against `data/records/<slug>.yml`. The build pipeline will
87
+ fail if your record doesn't validate against the blueprint schema.
88
+ Read `grove.config.ts` and the `data/taxonomy/` files for the
89
+ available fields and enum values.
90
+
91
+ ## License
92
+
93
+ MIT — the directory content, schema, and source code are all
94
+ permissively licensed. The data is yours to fork, mirror, or
95
+ republish; attribution to the original author is appreciated but not
96
+ required.
97
+
98
+ ## Contact
99
+
100
+ Issues: open a ticket on the consumer repository's issue tracker.
101
+ Pull requests: see `CONTRIBUTING.md` for the workflow.
102
+
103
+ ## LLM-specific guidance
104
+
105
+ - Per-record markdown: `/llms-full.txt` (generated, ~2,000 lines for
106
+ ~150 records).
107
+ - Sitemap: `/sitemap.xml` (lists every record's detail URL).
108
+ - robots.txt: explicitly allows GPTBot, ChatGPT-User, ClaudeBot,
109
+ Claude-Web, anthropic-ai, PerplexityBot, Google-Extended,
110
+ Applebot-Extended, cohere-ai, CCBot, Amazonbot.
111
+ - Schema: each detail page emits a `SoftwareSourceCode` (or
112
+ `Dataset` / `Organization`) JSON-LD block for AI crawlers.
@@ -0,0 +1,63 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
3
+ <url>
4
+ <loc>https://example.com/</loc>
5
+ <lastmod>2026-06-13T10:16:37.466Z</lastmod>
6
+ <changefreq>daily</changefreq>
7
+ <priority>1.0</priority>
8
+ </url>
9
+ <url>
10
+ <loc>https://example.com/projects</loc>
11
+ <lastmod>2026-06-13T10:16:37.466Z</lastmod>
12
+ <changefreq>daily</changefreq>
13
+ <priority>0.9</priority>
14
+ </url>
15
+ <url>
16
+ <loc>https://example.com/projects/coolify</loc>
17
+ <lastmod>2026-06-13T10:16:37.466Z</lastmod>
18
+ <changefreq>weekly</changefreq>
19
+ <priority>0.7</priority>
20
+ </url>
21
+ <url>
22
+ <loc>https://example.com/projects/gitea</loc>
23
+ <lastmod>2026-06-13T10:16:37.466Z</lastmod>
24
+ <changefreq>weekly</changefreq>
25
+ <priority>0.7</priority>
26
+ </url>
27
+ <url>
28
+ <loc>https://example.com/projects/hoppscotch</loc>
29
+ <lastmod>2026-06-13T10:16:37.466Z</lastmod>
30
+ <changefreq>weekly</changefreq>
31
+ <priority>0.7</priority>
32
+ </url>
33
+ <url>
34
+ <loc>https://example.com/projects/inventree</loc>
35
+ <lastmod>2026-06-13T10:16:37.466Z</lastmod>
36
+ <changefreq>weekly</changefreq>
37
+ <priority>0.7</priority>
38
+ </url>
39
+ <url>
40
+ <loc>https://example.com/projects/label-studio</loc>
41
+ <lastmod>2026-06-13T10:16:37.466Z</lastmod>
42
+ <changefreq>weekly</changefreq>
43
+ <priority>0.7</priority>
44
+ </url>
45
+ <url>
46
+ <loc>https://example.com/projects/logseq</loc>
47
+ <lastmod>2026-06-13T10:16:37.466Z</lastmod>
48
+ <changefreq>weekly</changefreq>
49
+ <priority>0.7</priority>
50
+ </url>
51
+ <url>
52
+ <loc>https://example.com/projects/private-gpt</loc>
53
+ <lastmod>2026-06-13T10:16:37.466Z</lastmod>
54
+ <changefreq>weekly</changefreq>
55
+ <priority>0.7</priority>
56
+ </url>
57
+ <url>
58
+ <loc>https://example.com/projects/wakapi</loc>
59
+ <lastmod>2026-06-13T10:16:37.466Z</lastmod>
60
+ <changefreq>weekly</changefreq>
61
+ <priority>0.7</priority>
62
+ </url>
63
+ </urlset>
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ /**
5
+ * build-llms.mjs — emit `public/llms-full.txt` (per-record
6
+ * markdown mirror) only.
7
+ *
8
+ * `public/llms.txt` is a static file committed to the template —
9
+ * it does NOT get regenerated by this script. The build pipeline
10
+ * treats the committed version as the canonical, hand-curated
11
+ * LLM-facing index. The script only refreshes the per-record
12
+ * `llms-full.txt` from the latest `data/generated/records.full.json`.
13
+ *
14
+ * This is intentional: openapps' `llms.txt` is also a
15
+ * hand-curated 30+ line document, not an auto-generated stub.
16
+ *
17
+ * Usage: node scripts/build-llms.mjs
18
+ */
19
+ import { mkdir, readFile, writeFile, access } from "node:fs/promises";
20
+ import { dirname, join, resolve } from "node:path";
21
+ import { fileURLToPath } from "node:url";
22
+
23
+ const __dirname = dirname(fileURLToPath(import.meta.url));
24
+ const ROOT = resolve(__dirname, "..");
25
+ const SOURCE = join(ROOT, "data", "generated", "records.full.json");
26
+ const OUT_DIR = join(ROOT, "public");
27
+ const FULL = join(OUT_DIR, "llms-full.txt");
28
+
29
+ const SITE = process.env.GROVE_SITE ?? "https://example.com";
30
+
31
+ function compactStars(n) {
32
+ if (typeof n !== "number" || !Number.isFinite(n)) return "—";
33
+ if (n >= 1_000_000) return (n / 1_000_000).toFixed(1).replace(/\.0$/, "") + "M";
34
+ if (n >= 1_000) {
35
+ const k = n / 1_000;
36
+ return (k >= 10 ? Math.round(k).toString() : k.toFixed(1).replace(/\.0$/, "")) + "k";
37
+ }
38
+ return n.toString();
39
+ }
40
+
41
+ function relativeTime(iso) {
42
+ if (!iso) return "—";
43
+ const d = new Date(iso);
44
+ if (isNaN(d.valueOf())) return "—";
45
+ const days = Math.floor((Date.now() - d.valueOf()) / 86_400_000);
46
+ if (days === 0) return "today";
47
+ if (days === 1) return "1 day ago";
48
+ if (days < 30) return `${days} days ago`;
49
+ if (days < 365) return `${Math.floor(days / 30)} months ago`;
50
+ return `${Math.floor(days / 365)} years ago`;
51
+ }
52
+
53
+ function buildFullTxt(records) {
54
+ const lines = [];
55
+ lines.push(`# ${SITE.replace(/^https?:\/\//, "")} — full directory`);
56
+ lines.push("");
57
+ lines.push(`Generated: ${new Date().toISOString()}`);
58
+ lines.push(`Records: ${records.length}`);
59
+ lines.push("");
60
+ lines.push("---");
61
+ lines.push("");
62
+
63
+ for (const r of records) {
64
+ const name = r.name ?? r.title ?? r.slug;
65
+ const stack = r.stack ?? (Array.isArray(r.stacks) ? r.stacks.join(", ") : "");
66
+ const platforms = Array.isArray(r.platforms) ? r.platforms.join(", ") : "";
67
+ const tags = Array.isArray(r.tags) ? r.tags.join(", ") : "";
68
+ const stars = compactStars(r.stars ?? r.github?.stars ?? 0);
69
+ const updated = relativeTime(r.lastCommitAt ?? r.github?.pushedAt ?? null);
70
+ const repoUrl = r.repoUrl ?? r.links?.github ?? "";
71
+ const url = `${SITE.replace(/\/$/, "")}/${r.slug}`;
72
+ const desc = r.description ?? "";
73
+
74
+ lines.push(`## ${name}`);
75
+ lines.push("");
76
+ if (desc) lines.push(desc);
77
+ lines.push("");
78
+ lines.push(`- **URL**: ${url}`);
79
+ if (repoUrl) lines.push(`- **Repository**: ${repoUrl}`);
80
+ if (stack) lines.push(`- **Stack**: ${stack}`);
81
+ if (platforms) lines.push(`- **Platforms**: ${platforms}`);
82
+ if (r.category) lines.push(`- **Category**: ${r.category}`);
83
+ if (tags) lines.push(`- **Tags**: ${tags}`);
84
+ if (r.license) lines.push(`- **License**: ${r.license}`);
85
+ lines.push(`- **Stars**: ${stars}`);
86
+ lines.push(`- **Last commit**: ${updated}`);
87
+ lines.push("");
88
+ if (Array.isArray(r.bestFor) && r.bestFor.length) {
89
+ lines.push("**Best for:**");
90
+ for (const b of r.bestFor) lines.push(`- ${b}`);
91
+ lines.push("");
92
+ }
93
+ if (Array.isArray(r.whyListed) && r.whyListed.length) {
94
+ lines.push("**Why listed:**");
95
+ for (const w of r.whyListed) lines.push(`- ${w}`);
96
+ lines.push("");
97
+ }
98
+ lines.push("---");
99
+ lines.push("");
100
+ }
101
+ return lines.join("\n");
102
+ }
103
+
104
+ async function main() {
105
+ if (!(await access(SOURCE).then(() => true).catch(() => false))) {
106
+ console.error(`No ${SOURCE} found. Run \`grove generate\` first.`);
107
+ process.exit(1);
108
+ }
109
+ const raw = JSON.parse(await readFile(SOURCE, "utf8"));
110
+ const records = (raw.records ?? []).filter(
111
+ (r) => r.visibility !== "hide" && r.visibility !== "remove",
112
+ );
113
+
114
+ await mkdir(OUT_DIR, { recursive: true });
115
+ await writeFile(FULL, buildFullTxt(records), "utf8");
116
+ console.log(`[llms] ${records.length} indexed → ${FULL}`);
117
+ console.log(`[llms] public/llms.txt is committed (NOT regenerated) — see scripts/build-llms.mjs`);
118
+ }
119
+
120
+ main().catch((err) => {
121
+ console.error("build-llms failed:", err);
122
+ process.exit(1);
123
+ });
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ /**
5
+ * build-records-json.mjs — yml → normalized JSON.
6
+ *
7
+ * Wraps `@grove-dev/cli generate`, which produces:
8
+ * - data/generated/records.full.json (every record, all fields)
9
+ * - data/generated/records.index.json (slim projection, visible only)
10
+ * - data/generated/records.json (alias of records.full.json)
11
+ * - data/generated/site-config.json (current blueprint + stats)
12
+ *
13
+ * Run by `pnpm run build:data` (also pre-build).
14
+ */
15
+ import { spawnSync } from "node:child_process";
16
+
17
+ const result = spawnSync("grove", ["generate"], {
18
+ stdio: "inherit",
19
+ env: process.env,
20
+ });
21
+
22
+ if (result.error) {
23
+ console.error("Failed to spawn `grove generate`:", result.error.message);
24
+ process.exit(1);
25
+ }
26
+
27
+ process.exit(result.status ?? 0);
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ /**
5
+ * build-sitemap.mjs — emit public/sitemap.xml from the generated
6
+ * index JSON. Thin wrapper around `@grove-dev/cli sitemap`.
7
+ */
8
+ import { spawnSync } from "node:child_process";
9
+
10
+ const result = spawnSync("grove", ["sitemap"], {
11
+ stdio: "inherit",
12
+ env: process.env,
13
+ });
14
+
15
+ if (result.error) {
16
+ console.error("Failed to spawn `grove sitemap`:", result.error.message);
17
+ process.exit(1);
18
+ }
19
+
20
+ process.exit(result.status ?? 0);
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ /**
5
+ * cleanup-stale-records.mjs — flag records that look like they
6
+ * should be archived. Wrapper around `grove cleanup stale`.
7
+ */
8
+ import { spawnSync } from "node:child_process";
9
+
10
+ const result = spawnSync("grove", ["cleanup", "stale"], {
11
+ stdio: "inherit",
12
+ env: process.env,
13
+ });
14
+
15
+ if (result.error) {
16
+ console.error("Failed to spawn `grove cleanup stale`:", result.error.message);
17
+ process.exit(1);
18
+ }
19
+
20
+ process.exit(result.status ?? 0);