@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.
- package/dist/lib/display.d.ts +11 -17
- package/dist/lib/display.d.ts.map +1 -1
- package/dist/lib/display.js +43 -43
- package/dist/lib/display.js.map +1 -1
- package/dist/lib/format.d.ts +6 -7
- package/dist/lib/format.d.ts.map +1 -1
- package/dist/lib/format.js +11 -12
- package/dist/lib/format.js.map +1 -1
- package/dist/lib/lenses.d.ts +35 -73
- package/dist/lib/lenses.d.ts.map +1 -1
- package/dist/lib/lenses.js +144 -126
- package/dist/lib/lenses.js.map +1 -1
- package/dist/lib/repo.d.ts +11 -22
- package/dist/lib/repo.d.ts.map +1 -1
- package/dist/lib/repo.js +12 -25
- package/dist/lib/repo.js.map +1 -1
- package/dist/lib/scores.d.ts +15 -16
- package/dist/lib/scores.d.ts.map +1 -1
- package/dist/lib/scores.js +4 -11
- package/dist/lib/scores.js.map +1 -1
- package/dist/lib/search.d.ts +47 -129
- package/dist/lib/search.d.ts.map +1 -1
- package/dist/lib/search.js +195 -180
- package/dist/lib/search.js.map +1 -1
- package/dist/lib/taxonomy-counts.d.ts +7 -21
- package/dist/lib/taxonomy-counts.d.ts.map +1 -1
- package/dist/lib/taxonomy-counts.js +12 -31
- package/dist/lib/taxonomy-counts.js.map +1 -1
- package/package.json +4 -4
- package/src/components/AppsIndexRow.astro +55 -184
- package/src/components/AppsPagination.astro +45 -108
- package/src/components/CategoryGrid.astro +43 -31
- package/src/components/ContributorsGrid.astro +3 -3
- package/src/components/CurationGrid.astro +41 -0
- package/src/components/ExploreByStack.astro +6 -0
- package/src/components/FilterGroupMenu.astro +49 -0
- package/src/components/FilterOptions.astro +44 -0
- package/src/components/Hero.astro +198 -147
- package/src/components/Icon.astro +10 -3
- package/src/components/ItemCard.astro +266 -248
- package/src/components/RecordSection.astro +68 -33
- package/src/components/RefinePanel.astro +115 -141
- package/src/components/StackGrid.astro +32 -24
- package/src/components/WhyThisExists.astro +32 -32
- package/src/layouts/BaseLayout.astro +104 -49
- package/src/layouts/Container.astro +5 -44
- package/src/layouts/Footer.astro +31 -20
- package/src/layouts/Header.astro +40 -19
- package/src/layouts/SectionHeader.astro +27 -43
- package/src/layouts/Seo.astro +38 -56
- package/src/layouts/ThemeToggle.astro +35 -48
- package/src/lib/display.ts +46 -53
- package/src/lib/format.ts +11 -12
- package/src/lib/lenses.ts +151 -172
- package/src/lib/repo.ts +16 -28
- package/src/lib/scores.ts +15 -19
- package/src/lib/search.ts +222 -290
- package/src/lib/taxonomy-counts.ts +12 -46
- package/src/styles.css +207 -1
- package/templates/default/.github/ISSUE_TEMPLATE/bug_report.md +33 -0
- package/templates/default/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- package/templates/default/.github/ISSUE_TEMPLATE/record_submission.md +48 -0
- package/templates/default/.github/pull_request_template.md +27 -0
- package/templates/default/.github/workflows/build.yml +55 -0
- package/templates/default/.github/workflows/cleanup-stale-records.yml +48 -0
- package/templates/default/.github/workflows/daily-refresh.yml +53 -0
- package/templates/default/.github/workflows/sync-contributors.yml +53 -0
- package/templates/default/.github/workflows/sync-github-metadata.yml +58 -0
- package/templates/default/.github/workflows/validate-data.yml +35 -0
- package/templates/default/data/records/coolify.yml +46 -0
- package/templates/default/data/records/gitea.yml +46 -0
- package/templates/default/data/records/hoppscotch.yml +47 -0
- package/templates/default/data/records/inventree.yml +47 -0
- package/templates/default/data/records/label-studio.yml +46 -0
- package/templates/default/data/records/logseq.yml +48 -0
- package/templates/default/data/records/private-gpt.yml +47 -0
- package/templates/default/data/records/wakapi.yml +44 -0
- package/templates/default/data/taxonomy/categories.yml +47 -0
- package/templates/default/data/taxonomy/distribution-channels.yml +43 -0
- package/templates/default/data/taxonomy/platforms.yml +27 -0
- package/templates/default/data/taxonomy/stacks.yml +41 -0
- package/templates/default/grove.config.ts +1 -1
- package/templates/default/package.json +5 -4
- package/templates/default/public/icons/stacks/bun.svg +1 -0
- package/templates/default/public/icons/stacks/clojure.svg +1 -0
- package/templates/default/public/icons/stacks/clojurescript.svg +1 -0
- package/templates/default/public/icons/stacks/deno.svg +1 -0
- package/templates/default/public/icons/stacks/django.svg +1 -0
- package/templates/default/public/icons/stacks/docker.svg +1 -0
- package/templates/default/public/icons/stacks/go.svg +1 -0
- package/templates/default/public/icons/stacks/llm.svg +1 -0
- package/templates/default/public/icons/stacks/node.js.svg +1 -0
- package/templates/default/public/icons/stacks/sveltekit.svg +1 -0
- package/templates/default/public/icons/stacks/vue.svg +1 -0
- package/templates/default/public/llms-full.txt +197 -5
- package/templates/default/public/llms.txt +107 -6
- package/templates/default/public/sitemap.xml +63 -0
- package/templates/default/scripts/build-llms.mjs +123 -0
- package/templates/default/scripts/build-records-json.mjs +27 -0
- package/templates/default/scripts/build-sitemap.mjs +20 -0
- package/templates/default/scripts/cleanup-stale-records.mjs +20 -0
- package/templates/default/scripts/enrich-github-metadata.mjs +99 -0
- package/templates/default/scripts/fetch-icons.mjs +137 -0
- package/templates/default/scripts/migrate-legacy-to-schema-v1.mjs +86 -0
- package/templates/default/scripts/parse-legacy-readme.mjs +59 -0
- package/templates/default/scripts/refresh-records-activity.mjs +24 -0
- package/templates/default/scripts/repair-license-format.mjs +72 -0
- package/templates/default/scripts/report-cleanup-candidates.mjs +21 -0
- package/templates/default/scripts/seed-from-github.mjs +62 -0
- package/templates/default/scripts/sync-contributors.mjs +145 -0
- package/templates/default/scripts/sync-github-metadata.mjs +28 -0
- package/templates/default/scripts/validate-records.mjs +27 -0
- package/templates/default/src/data/records.ts +134 -51
- package/templates/default/src/pages/404.astro +64 -0
- package/templates/default/src/pages/{projects/[slug].astro → [slug]/[itemSlug].astro} +236 -66
- package/templates/default/src/pages/[slug]/index.astro +476 -0
- package/templates/default/src/pages/about.astro +165 -82
- package/templates/default/src/pages/apps/[itemSlug].astro +301 -0
- package/templates/default/src/pages/apps/index.astro +146 -0
- package/templates/default/src/pages/contributors.astro +184 -0
- package/templates/default/src/pages/index.astro +119 -162
- package/templates/default/src/pages/submit.astro +276 -114
- package/templates/default/wrangler.jsonc +28 -0
- package/LICENSE +0 -21
- package/templates/default/data/generated/apps.json +0 -1
- package/templates/default/data/generated/records.full.json +0 -8
- package/templates/default/data/generated/records.index.json +0 -7
- package/templates/default/data/generated/records.json +0 -8
- package/templates/default/data/generated/repo-stats.json +0 -1
- package/templates/default/data/generated/site-config.json +0 -31
- package/templates/default/src/components/layout/BaseLayout.astro +0 -47
- package/templates/default/src/components/layout/Container.astro +0 -18
- package/templates/default/src/components/layout/Footer.astro +0 -27
- package/templates/default/src/components/layout/Header.astro +0 -43
- package/templates/default/src/lib/markdown.ts +0 -171
- 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
|
-
#
|
|
1
|
+
# example.com — full directory
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Generated: 2026-06-13T10:16:37.910Z
|
|
4
|
+
Records: 8
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
---
|
|
7
7
|
|
|
8
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
##
|
|
11
|
+
## Main URLs
|
|
10
12
|
|
|
11
|
-
|
|
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);
|