diff --git a/.env.example b/.env.example index 9b23136..ad9d65d 100644 --- a/.env.example +++ b/.env.example @@ -24,3 +24,11 @@ GITHUB_TOKEN= # Production and the deploy workflow use `/` (the site is served from the apex # domain). Override only when previewing under a sub-path, e.g. `/my-fork/`. ASTRO_BASE=/ + +# Public analytics identifiers injected at build time. These values are visible +# in the generated HTML/browser and are identifiers, not secrets. +PUBLIC_CLOUDFLARE_WEB_ANALYTICS_TOKEN= +PUBLIC_GOOGLE_ANALYTICS_ID= + +# Optional Google Search Console HTML-tag verification value. +PUBLIC_GOOGLE_SITE_VERIFICATION= diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 85ffb02..841b490 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -4,7 +4,14 @@ ## Validation +- [ ] `npm test` - [ ] `npm run build` +- [ ] Cloudflare branch preview checked on desktop and mobile + +## Content checks + +- [ ] Claims, organizations, sponsors, and metrics have a verifiable source +- [ ] New internal links work with `ASTRO_BASE` ## Notes diff --git a/.github/workflows/deploy-pages.yml b/.github/workflows/deploy-pages.yml deleted file mode 100644 index 6fbd802..0000000 --- a/.github/workflows/deploy-pages.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: Deploy to GitHub Pages - -on: - push: - branches: - - main - workflow_dispatch: - -permissions: - contents: read - pages: write - id-token: write - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - -concurrency: - group: pages - cancel-in-progress: false - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v7 - - - name: Setup Node - uses: actions/setup-node@v7 - with: - node-version: 22 - cache: npm - - - name: Setup Pages - uses: actions/configure-pages@v6 - - - name: Install dependencies - run: npm ci - - - name: Build site - env: - ASTRO_BASE: / - run: npm run build - - - name: Upload artifact - uses: actions/upload-pages-artifact@v5 - with: - path: dist - - deploy: - needs: build - runs-on: ubuntu-latest - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v5 diff --git a/.github/workflows/sync-content.yml b/.github/workflows/sync-content.yml index 6e5164b..9183bb3 100644 --- a/.github/workflows/sync-content.yml +++ b/.github/workflows/sync-content.yml @@ -23,8 +23,6 @@ on: permissions: contents: write - pages: write - id-token: write env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true @@ -48,10 +46,6 @@ jobs: node-version: 22 cache: npm - - name: Setup Pages - if: ${{ github.ref == 'refs/heads/main' }} - uses: actions/configure-pages@v6 - - name: Install dependencies run: npm ci @@ -112,14 +106,3 @@ jobs: - name: Push synced data if: ${{ steps.commit.outputs.changed == 'true' }} run: git push - - - name: Upload Pages artifact - if: ${{ steps.commit.outputs.changed == 'true' && github.ref == 'refs/heads/main' }} - uses: actions/upload-pages-artifact@v5 - with: - path: dist - - - name: Deploy synced data to GitHub Pages - if: ${{ steps.commit.outputs.changed == 'true' && github.ref == 'refs/heads/main' }} - id: deployment - uses: actions/deploy-pages@v5 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9f701b9..125625e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,13 +6,13 @@ Thanks for helping improve IndopenSource.org. ```bash npm install -npm run sync:projects npm run dev ``` Before opening a pull request: ```bash +npm test npm run build ``` @@ -35,13 +35,41 @@ Use `GITHUB_TOKEN` or `GH_TOKEN` to avoid low GitHub API rate limits. `main` commit to production. - Keep changes focused. - Prefer Astro components in `src/components` over large page-only markup. -- Keep internal links compatible with GitHub Pages base paths by using `withBase()`. -- Run `npm run build` and include the result in the PR description. +- Use `withBase()` for internal links that must remain valid when `ASTRO_BASE` + is set by CI or a preview environment. +- Run `npm test` and `npm run build`, then include the results in the PR description. - For content or roadmap changes, link the relevant issue or discussion. +## Community Directory + +The Community page reads `src/data/communities.json`. To propose one community, +add one object containing: + +- `name` +- `description` +- `url` pointing to an official channel +- optional `focus` +- optional `location` + +Keep one community per pull request and explain your relationship to it or add a +source that reviewers can verify. Do not add inferred or inactive communities. + +## Content Quality + +- Lead with a direct summary before background detail. +- Cite primary sources for factual claims and include the relevant date. +- Use descriptive headings and stable internal links to related pages. +- Identify authors and keep publication and update dates traceable. +- Do not add speculative facts, generated statistics, or special AI-search + markup that is not supported by search-engine documentation. + ## Production Release The `release` branch is reserved for Cloudflare production deployments. Only IndopenSource organization members may update it, through a pull request from `main` that passes CI. Merge production promotion pull requests with a merge commit so `main` remains in the `release` history. + +Cloudflare creates a branch preview for contribution pull requests. Review that +preview before promoting `main` to `release`; use +[`docs/release-checklist.md`](docs/release-checklist.md) for the production pass. diff --git a/README.md b/README.md index ddba074..07453ef 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,42 @@ +![IndopenSource — Komunitas Open Source Indonesia](docs/assets/indopensource-banner.png) + # IndopenSource.org -[![Deploy to GitHub Pages](https://github.com/IndopenSource/indopensource.org/actions/workflows/deploy-pages.yml/badge.svg)](https://github.com/IndopenSource/indopensource.org/actions/workflows/deploy-pages.yml) +[![CI](https://github.com/IndopenSource/indopensource.org/actions/workflows/ci.yml/badge.svg)](https://github.com/IndopenSource/indopensource.org/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) -Website roadmap untuk `https://indopensource.org`. +Website komunitas dan direktori open source Indonesia di `https://indopensource.org`. ## Tech Stack - Astro - Tailwind CSS - TypeScript -- GitHub Pages - GitHub Actions +- Cloudflare Workers Static Assets ## Pages -- Home: ringkasan roadmap dan arah website. +- Home: pintu masuk ke direktori, roadmap, pembelajaran, dan kontribusi. - Falsafah: prinsip komunitas dan kurasi open source Indonesia. -- Projects: direktori dari `IndopenSource/awesome-indonesia`. -- Blog: placeholder untuk repo khusus `IndopenSource/Blog-IndopenSource`. -- Forum: rencana integrasi `https://github.com/orgs/IndopenSource/discussions`. -- Contact: kanal GitHub organization, projects, dan discussions. +- Projects: direktori proyek dari `IndopenSource/awesome-indonesia`. +- Top User: peringkat pemilik repository berdasarkan akumulasi stars. +- Komunitas: direktori komunitas yang diusulkan melalui pull request. +- Roadmap: rencana kerja publik 2026–2027. +- Belajar dan Blog: materi serta artikel yang disinkronkan dari repository sumber. +- Donasi: informasi OpenSawer, sponsorship, CSR, dan dukungan non-dana. +- Forum dan Kontak: GitHub Discussions serta kanal resmi IndopenSource. ## Development ```bash npm install -npm run sync:projects npm run dev ``` +Sinkronisasi data bersifat opsional untuk pengembangan UI. File hasil sinkronisasi +yang diperlukan sudah tersedia di `src/data/`. + ## Project Structure ```txt @@ -45,38 +52,34 @@ src/ ## Checks ```bash +npm test npm run check npm run build ``` -## MVP Pre-release - -MVP awal berfokus pada homepage sebagai pintu masuk roadmap IndopenSource: - -- Home menjelaskan posisi `indopensource.org`. -- Projects sudah punya data awal dari `awesome-indonesia`. -- Falsafah, Blog, Forum, dan Contact tersedia sebagai halaman roadmap. -- Deployment memakai GitHub Pages bawaan repo lewat GitHub Actions. - -Rilis pre-release bisa dibuat dari tag `v0.1.0-mvp` setelah workflow Pages hijau. -URL produksi setelah custom domain aktif adalah `https://indopensource.org/`. - ## Deployment -Deployment saat ini masih memakai GitHub Pages melalui workflow -`.github/workflows/deploy-pages.yml`. Cloudflare production berikutnya akan -dibangun dari branch `release`. +Cloudflare terhubung langsung ke repository dan membuat preview untuk pull +request atau branch non-production. Production hanya dibangun dari branch +`release`. - Build command: `npm run build` +- Deploy command: `npx wrangler deploy` - Output directory: `dist` -- Source: GitHub Actions +- Konfigurasi Worker: `wrangler.jsonc` +- Production branch: `release` - Custom domain: `indopensource.org` -- Pages base path: `/` -Semua kontribusi tetap masuk ke `main`. Rilis produksi dipromosikan oleh anggota -organisasi melalui pull request dari `main` ke `release`; jangan membuka pull +Semua kontribusi masuk ke `main`. Anggota organisasi mempromosikan versi yang +sudah diuji melalui pull request dari `main` ke `release`; jangan membuka pull request kontribusi langsung ke `release`. +GitHub Pages sudah tidak digunakan. Deployment dan preview website ditangani +oleh Cloudflare; workflow sinkronisasi konten hanya memperbarui data di `main`. + +Lihat [checklist release](docs/release-checklist.md) sebelum mempromosikan +`main` ke `release`. + ## Project Sync `npm run sync:projects` membaca `repos.json` dari `IndopenSource/awesome-indonesia`, @@ -88,8 +91,14 @@ Gunakan `GITHUB_TOKEN` atau `GH_TOKEN` untuk rate limit yang lebih lega. ## Blog Sync `npm run sync:blog` membaca artikel Markdown dari -`IndopenSource/Blog-IndopenSource`, mengambil metadata commit penulis/rilis, -dan menulis hasilnya ke `src/data/blog-posts.json`. +`IndopenSource/Blog-IndopenSource`, mengambil tanggal commit pertama, commit +terbaru, dan seluruh kontributor unik setiap artikel, lalu menulis hasilnya ke +`src/data/blog-posts.json`. Halaman artikel menautkan commit terbaru tersebut +agar riwayat sumber dapat diperiksa langsung. + +Artikel berstatus `draft` tetap dibangun pada URL `/blog/preview/{slug}/`, +tetapi tidak ditampilkan di indeks blog, tidak masuk sitemap, dan memakai +`noindex, nofollow, noarchive` sampai statusnya diterbitkan. ## Auto Sync @@ -103,8 +112,20 @@ Workflow `.github/workflows/sync-content.yml` memperbarui data secara otomatis. - `sync-content` Jika data berubah, workflow membuat commit `Sync content data`. Setelah commit -masuk `main`, workflow yang sama akan upload artifact dan deploy ulang GitHub -Pages agar perubahan data langsung tayang. +masuk `main`, perubahan mengikuti validasi dan alur promosi `main` ke `release`. +Cloudflare kemudian membuat preview `main`; production tetap dipromosikan lewat +pull request ke `release`. + +## Analytics & Search Verification + +Analytics aktif saat build Cloudflare menerima environment variable berikut: + +- `PUBLIC_CLOUDFLARE_WEB_ANALYTICS_TOKEN` +- `PUBLIC_GOOGLE_ANALYTICS_ID` (format `G-XXXXXXXXXX`) +- `PUBLIC_GOOGLE_SITE_VERIFICATION` (opsional) + +Nilainya merupakan identifier publik dan tidak perlu dimasukkan ke Git. Isi di +pengaturan build Cloudflare untuk production dan preview sesuai kebutuhan. ## Contributing diff --git a/docs/assets/indopensource-banner.png b/docs/assets/indopensource-banner.png new file mode 100644 index 0000000..9ff739a Binary files /dev/null and b/docs/assets/indopensource-banner.png differ diff --git a/docs/release-checklist.md b/docs/release-checklist.md new file mode 100644 index 0000000..557a3d9 --- /dev/null +++ b/docs/release-checklist.md @@ -0,0 +1,33 @@ +# Release checklist + +Production is promoted from `main` to `release` through a pull request opened by +an IndopenSource organization member. + +## Before promotion + +- [ ] Contribution pull requests are merged into `main` and CI is green. +- [ ] Cloudflare branch preview has been checked on desktop and mobile. +- [ ] `npm test` and `npm run build` pass from the intended release commit. +- [ ] Navigation, project detail pages, `/donasi/`, `/komunitas/`, `/roadmap/`, + `/sitemap.xml`, and the `/sponsors/` redirect have been checked. +- [ ] `admin@`, `press@`, `sponsor@`, and `legal@indopensource.org` can receive + mail, or links to inactive addresses have been removed. +- [ ] DNS, custom domain, TLS, and the Cloudflare production branch are correct. +- [ ] Cloudflare build variables for the Web Analytics token and Google + Analytics Measurement ID are configured. + +## Promote + +1. Open a pull request from `main` to `release`. +2. Require green CI and review the diff for unexpected generated data. +3. Merge with a merge commit so `main` remains in the `release` history. +4. Wait for the Cloudflare production deployment to pass. + +## After deployment + +- [ ] Smoke-test `https://indopensource.org/` and the primary navigation. +- [ ] Confirm canonical URLs, robots, sitemap, favicon, and social previews. +- [ ] Confirm Cloudflare Web Analytics and Google Analytics receive a test page + view without CSP errors. +- [ ] Send a test message to each published email address. +- [ ] Record the production commit or tag as the rollback point. diff --git a/public/_redirects b/public/_redirects new file mode 100644 index 0000000..54b9661 --- /dev/null +++ b/public/_redirects @@ -0,0 +1 @@ +/sponsors/ /donasi/ 301 diff --git a/public/brand/apple-touch-icon.png b/public/brand/apple-touch-icon.png index e36db39..b50d69a 100644 Binary files a/public/brand/apple-touch-icon.png and b/public/brand/apple-touch-icon.png differ diff --git a/public/brand/favicon-96.png b/public/brand/favicon-96.png new file mode 100644 index 0000000..4c9183a Binary files /dev/null and b/public/brand/favicon-96.png differ diff --git a/public/brand/favicon.ico b/public/brand/favicon.ico index 815d094..29eb739 100644 Binary files a/public/brand/favicon.ico and b/public/brand/favicon.ico differ diff --git a/public/brand/tokengratis-icon.png b/public/brand/tokengratis-icon.png new file mode 100644 index 0000000..bfebf7c Binary files /dev/null and b/public/brand/tokengratis-icon.png differ diff --git a/scripts/sync-blog-posts.mjs b/scripts/sync-blog-posts.mjs index 661eaac..a6153a9 100644 --- a/scripts/sync-blog-posts.mjs +++ b/scripts/sync-blog-posts.mjs @@ -161,6 +161,20 @@ async function getCommitMeta(path) { const firstCommit = commits.at(-1); const latestCommit = commits.at(0); const author = firstCommit?.author; + const authors = []; + const seenAuthors = new Set(); + + for (const item of commits.toReversed()) { + const name = item.author?.login || item.commit?.author?.name; + if (!name || seenAuthors.has(name.toLowerCase())) continue; + seenAuthors.add(name.toLowerCase()); + authors.push({ + name, + avatarUrl: item.author?.avatar_url || '', + url: item.author?.html_url || '', + committedAt: item.commit?.author?.date || '' + }); + } return { author: { @@ -169,6 +183,9 @@ async function getCommitMeta(path) { url: author?.html_url || firstCommit?.html_url || `https://github.com/${BLOG_REPO}`, committedAt: firstCommit?.commit?.author?.date || '' }, + authors, + latestCommitSha: latestCommit?.sha || '', + latestCommitUrl: latestCommit?.html_url || '', // The latest commit timestamp is the date of the most recent EDIT, not a // publication/release event (CC-9). Name it honestly as "last modified" so // the rendered label can stop calling an edit timestamp a release date. @@ -244,6 +261,14 @@ for (const path of articleFiles) { const commitMeta = await getCommitMeta(path); const editorialDate = (data.date || '').trim(); const resolvedAuthor = resolveAuthor(commitMeta.author, data.authors); + const resolvedAuthors = resolvedAuthor.fromFrontmatter + ? [ + resolvedAuthor.author, + ...commitMeta.authors.filter( + (author) => author.name.toLowerCase() !== (resolvedAuthor.author?.name || '').toLowerCase() + ) + ] + : commitMeta.authors.length ? commitMeta.authors : [resolvedAuthor.author]; posts.push({ slug: slugFromPath(path), @@ -264,7 +289,10 @@ for (const path of articleFiles) { // edit timestamp separately for provenance (CC-9). releasedAt: editorialDate || commitMeta.author.committedAt || commitMeta.lastModifiedAt, lastModifiedAt: commitMeta.lastModifiedAt, + latestCommitSha: commitMeta.latestCommitSha, + latestCommitUrl: commitMeta.latestCommitUrl, author: resolvedAuthor.author, + authors: resolvedAuthors, authorFromFrontmatter: resolvedAuthor.fromFrontmatter }); } diff --git a/src/components/DonationPage.astro b/src/components/DonationPage.astro new file mode 100644 index 0000000..752b9db --- /dev/null +++ b/src/components/DonationPage.astro @@ -0,0 +1,254 @@ +--- +import BaseLayout from '../layouts/BaseLayout.astro'; +import BaseButton from './BaseButton.astro'; +import PageHeader from './PageHeader.astro'; +import { withBase } from '../lib/urls'; + +const tiers = [ + { + name: 'Utama', + min: 'Rp 5.000.000/tahun', + benefits: [ + 'Logo di homepage & footer website', + 'Profil organisasi di halaman sponsor', + 'Kolaborasi satu program atau event', + 'Laporan penggunaan dan dampak berkala' + ], + highlight: true + }, + { + name: 'Partner', + min: 'Rp 2.500.000/tahun', + benefits: [ + 'Logo di halaman sponsor & footer', + 'Apresiasi di kanal komunitas', + 'Laporan penggunaan dana berkala' + ], + highlight: false + }, + { + name: 'Pendukung', + min: 'Rp 1.000.000/tahun', + benefits: [ + 'Logo di halaman sponsor', + 'Apresiasi publik', + 'Akses laporan terbuka' + ], + highlight: false + }, + { + name: 'Sahabat', + min: 'Rp 250.000/tahun', + benefits: [ + 'Nama di halaman sponsor', + 'Apresiasi publik', + 'Akses laporan terbuka' + ], + highlight: false + } +]; + +const currentSponsors = [ + { name: 'Tokengratis.id', since: '2026', url: 'https://tokengratis.id/', logo: withBase('/brand/tokengratis-icon.png') } +]; + +const openSawerIcon = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20width='64'%20height='64'%20viewBox='0%200%2040%2040'%3e%3ctitle%3eOpenSawer%3c/title%3e%3ccircle%20cx='20'%20cy='20'%20r='20'%20fill='%230f694e'/%3e%3ccircle%20cx='20'%20cy='11.5'%20r='3.2'%20fill='%23e8b83f'/%3e%3cpath%20d='M10.5%2019.5c1.4%207.2%205%2010.8%209.5%2010.8s8.1-3.6%209.5-10.8M12%2019.5h16'%20fill='none'%20stroke='%23fff'%20stroke-width='3.2'%20stroke-linecap='round'/%3e%3c/svg%3e"; +--- + + + +

IndopenSource dijalankan oleh komunitas sukarela. Dukungan individu, sponsorship, CSR, dan kolaborasi membantu kami merawat infrastruktur, mengadakan event, dan mendukung proyek-proyek potensial.

+
+ +
+
+
+ Donasi per proyek +

Dukung proyek yang Anda gunakan.

+

OpenSawer akan dihubungkan ke halaman setiap proyek yang terdaftar di direktori IndopenSource. Setelah integrasinya siap, siapa pun dapat memilih proyek lalu berdonasi langsung melalui kanal OpenSawer proyek tersebut.

+

Fitur ini masih dalam pengembangan dan belum tersedia di seluruh halaman proyek.

+
+ + +

Buka OpenSawer

+

Lihat kanal donasi terbuka yang sedang disiapkan untuk komunitas dan proyek.

+ sawer.indopensource.org ↗ +
+
+
+ +
+
+ Dampak bersama +

Setiap kontribusi membantu menjaga open source Indonesia tetap hidup dan berkembang.

+
+
+ +
+
+
+
+ Program CSR +

Kami menerima dukungan CSR.

+

+ Dana CSR dapat diarahkan untuk infrastruktur komunitas, dokumentasi dan materi belajar, + event, serta dukungan bagi proyek open source Indonesia yang membutuhkan ruang tumbuh. + Program dan indikator hasil disepakati bersama perusahaan sebelum pelaksanaan. +

+
+ Diskusikan program CSR +
+
+ +
+

Dasar hukum dan cakupannya

+
+

+ Pasal 74 UU No. 40 Tahun 2007 + mewajibkan Perseroan yang menjalankan usaha di bidang dan/atau berkaitan dengan sumber daya alam melaksanakan Tanggung Jawab Sosial dan Lingkungan. Anggarannya diperhitungkan sebagai biaya Perseroan dengan memperhatikan kepatutan dan kewajaran. +

+

+ PP No. 47 Tahun 2012 + mengatur pelaksanaannya melalui rencana kerja tahunan dan pemuatan hasil pelaksanaan dalam laporan tahunan Perseroan. +

+

+ Pasal 15 huruf b UU No. 25 Tahun 2007 + juga mewajibkan setiap penanam modal melaksanakan tanggung jawab sosial perusahaan. Penerapan kewajiban tetap mengikuti status, bidang usaha, dan aturan sektoral perusahaan masing-masing. +

+
+

Informasi umum · perusahaan tetap perlu menilai kewajibannya bersama penasihat hukum.

+
+
+
+
+ +
+
+
+
+ Transparansi terbuka +

Dana masuk, penggunaan, dan dampaknya dapat dilihat.

+

+ Penerimaan dan penggunaan dana CSR direncanakan dipublikasikan melalui OpenSawer. Laporan program, + rincian penyaluran, bukti kegiatan, dan pembaruan dampak akan dibuka setelah kanal pelaporan siap. +

+
+ +
+ + + +
+

OpenSawer

+

Kanal transparansi yang sedang disiapkan

+
+
+

Lihat aplikasi open source yang akan digunakan untuk membuka catatan dana dan laporan program.

+ Buka OpenSawer → +
+
+
+
+ +
+
+
+ Pendukung kami +

Organisasi yang mendukung.

+

Terima kasih kepada organisasi yang telah mendukung misi IndopenSource.

+
+
+ {currentSponsors.map((s) => ( + +
+
+ +
+
+

{s.name}

+

Mendukung sejak {s.since}

+
+
+
+ ))} +
+
+
+ +
+ +
+ +
+
+
+ Dukungan organisasi +

Pilih tingkat kemitraan.

+

Semua paket memberikan akses ke laporan dampak dan apresiasi publik.

+
+
+ {tiers.map((tier) => ( +
+ {tier.highlight && Populer} +

{tier.name}

+

{tier.min}

+
    + {tier.benefits.map((b) => ( +
  • + + {b} +
  • + ))} +
+
+ ))} +
+
+
+ +
+
+
+ Tertarik bermitra? +

Mari diskusikan kemitraan Anda.

+

Kami terbuka untuk paket kustom, sponsorship event, atau bentuk kolaborasi lain yang sejalan dengan misi open source.

+
+ Hubungi kami + Media Kit +
+
+
+
+
diff --git a/src/components/HomeHero.astro b/src/components/HomeHero.astro index 5a96446..9f287a5 100644 --- a/src/components/HomeHero.astro +++ b/src/components/HomeHero.astro @@ -19,25 +19,23 @@ import { heroImage, HERO_PRELOAD_WIDTH, HERO_QUALITY } from '../lib/hero';

- Rumah terbuka untuk merawat daftar proyek, mendokumentasikan falsafah, - menulis pembelajaran, dan menghubungkan kontributor open source Indonesia. + Rumah terbuka untuk merawat daftar proyek, menulis pembelajaran, + dan menghubungkan kontributor open source Indonesia.

Lihat proyek - Roadmap GitHub
-
-
-
Direktori
-
Awesome ID
-
-
-
Lisensi
-
Open / Publik
-
-
+ + Lisensi + Open / Publik ↗ +
diff --git a/src/components/ProjectsDirectory.astro b/src/components/ProjectsDirectory.astro index 665bf52..9fff852 100644 --- a/src/components/ProjectsDirectory.astro +++ b/src/components/ProjectsDirectory.astro @@ -10,6 +10,7 @@ interface Props { const { projects } = Astro.props; const languages = [...new Set(projects.map((project) => project.language).filter((language): language is string => Boolean(language)))].sort(); const topics = [...new Set(projects.flatMap((project) => project.topics || []))].sort().slice(0, 80); +const repoName = (fullName: string) => fullName.split('/').at(-1) || fullName; const formatDate = (value?: string) => value ? new Intl.DateTimeFormat('id-ID', { dateStyle: 'medium' }).format(new Date(value)) : '-'; @@ -110,7 +111,8 @@ const formatDate = (value?: string) => value ? : } - {project.fullName} + {repoName(project.fullName)} + {project.owner && oleh {project.owner}} {project.description} {!project.syncFailed && ( diff --git a/src/components/SiteFooter.astro b/src/components/SiteFooter.astro index 17f1dbd..5b14a7c 100644 --- a/src/components/SiteFooter.astro +++ b/src/components/SiteFooter.astro @@ -2,8 +2,14 @@ import { withBase } from '../lib/urls'; const links: { label: string; href: string; external?: boolean }[] = [ + { label: 'Proyek', href: '/projects/' }, + { label: 'Belajar', href: '/belajar/' }, { label: 'Tentang', href: '/tentang/' }, + { label: 'Falsafah', href: '/falsafah/' }, { label: 'Komunitas', href: '/komunitas/' }, + { label: 'Top User', href: '/users/' }, + { label: 'Blog', href: '/blog/' }, + { label: 'Forum', href: '/forum/' }, { label: 'Cara Berkontribusi', href: '/cara-berkontribusi/' }, { label: 'Kode Etik', href: '/kode-etik/' }, { label: 'Donasi', href: '/donasi/' }, @@ -11,13 +17,13 @@ const links: { label: string; href: string; external?: boolean }[] = [ { label: 'Program', href: '/program/' }, { label: 'Tata Kelola', href: '/tata-kelola/' }, { label: 'Event', href: '/events/' }, - { label: 'Sponsors', href: '/sponsors/' }, + { label: 'Kontak', href: '/contact/' }, { label: 'Press', href: '/press/' }, { label: 'Legal', href: '/legal/' }, { label: 'Security', href: '/security/' }, + { label: 'Roadmap', href: '/roadmap/' }, { label: 'GitHub', href: 'https://github.com/IndopenSource', external: true }, { label: 'Discussions', href: 'https://github.com/orgs/IndopenSource/discussions', external: true }, - { label: 'Roadmap', href: 'https://github.com/orgs/IndopenSource/projects', external: true } ]; const year = new Date().getFullYear(); @@ -44,7 +50,7 @@ const year = new Date().getFullYear(); bareng, dirawat bareng.

- Open Source ID + Open source · Indonesia {/* Thick rule between mission and the colophon grid */} diff --git a/src/components/SiteHeader.astro b/src/components/SiteHeader.astro index 372f2f1..2653ce3 100644 --- a/src/components/SiteHeader.astro +++ b/src/components/SiteHeader.astro @@ -4,24 +4,20 @@ import { withBase } from '../lib/urls'; import brandIcon from '../assets/brand/icon-192.png'; const navItems = [ - ['Home', '/'], - ['Falsafah', '/falsafah/'], - ['Proyek', '/projects/'], - ['Belajar', '/belajar/'], - ['Kontribusi', '/cara-berkontribusi/'], - ['Blog', '/blog/'], - ['Forum', '/forum/'], - ['FAQ', '/faq/'], - ['Donasi', '/donasi/'], - ['Kontak', '/contact/'] + ['Home', '/', 'ph-house'], + ['Proyek', '/projects/', 'ph-grid-four'], + ['Blog', '/blog/', 'ph-article'], + ['Belajar', '/belajar/', 'ph-books'], + ['FAQ', '/faq/', 'ph-question'], + ['Donasi', '/donasi/', 'ph-heart'] ]; const mobileNavItems = [ ['Home', '/', 'ph-house'], ['Proyek', '/projects/', 'ph-grid-four'], - ['Belajar', '/belajar/', 'ph-books'], ['Blog', '/blog/', 'ph-article'], - ['Forum', '/forum/', 'ph-chats-circle'] + ['Belajar', '/belajar/', 'ph-books'], + ['Top User', '/users/', 'ph-ranking'] ]; const currentPath = Astro.url.pathname; @@ -65,26 +61,44 @@ const currentPathWithoutBase = currentPath.replace(new RegExp(`^${baseUrl.replac {/* Desktop nav — mono uppercase labels, riso-underline reveal, sticker active state */}
{ - navItems.map(([label, href]) => { + navItems.map(([label, href, icon]) => { const isCurrent = currentPathWithoutBase === href; + const isDonation = href === '/donasi/'; return isCurrent ? ( + {label} ) : ( + {label} ); }) } + + +
diff --git a/src/data/blog-posts.json b/src/data/blog-posts.json index b39a31f..82752bc 100644 --- a/src/data/blog-posts.json +++ b/src/data/blog-posts.json @@ -19,12 +19,22 @@ "sourceUrl": "https://github.com/IndopenSource/Blog-IndopenSource/blob/main/content/2026/07/bagaimana-open-source-mampu-bertahan.md", "releasedAt": "2026-07-16", "lastModifiedAt": "2026-07-16T11:08:44Z", + "latestCommitSha": "95d3147d541c05b1ab6f683ccddd6b84d816aabd", + "latestCommitUrl": "https://github.com/IndopenSource/Blog-IndopenSource/commit/95d3147d541c05b1ab6f683ccddd6b84d816aabd", "author": { "name": "wauputr4", "avatarUrl": "https://avatars.githubusercontent.com/u/103489788?v=4", "url": "https://github.com/wauputr4", "committedAt": "2026-07-16T11:02:36Z" }, + "authors": [ + { + "name": "wauputr4", + "avatarUrl": "https://avatars.githubusercontent.com/u/103489788?v=4", + "url": "https://github.com/wauputr4", + "committedAt": "2026-07-16T11:02:36Z" + } + ], "authorFromFrontmatter": false }, { @@ -45,12 +55,22 @@ "sourceUrl": "https://github.com/IndopenSource/Blog-IndopenSource/blob/main/content/2026/06/memperkenalkan-blog-indopensource.md", "releasedAt": "2026-06-14", "lastModifiedAt": "2026-07-16T11:02:50Z", + "latestCommitSha": "a411deee259a2d8b9a8b1150d17f5dd5e2d6aab1", + "latestCommitUrl": "https://github.com/IndopenSource/Blog-IndopenSource/commit/a411deee259a2d8b9a8b1150d17f5dd5e2d6aab1", "author": { "name": "wauputr4", "avatarUrl": "https://avatars.githubusercontent.com/u/103489788?v=4", "url": "https://github.com/wauputr4", "committedAt": "2026-06-13T17:37:55Z" }, + "authors": [ + { + "name": "wauputr4", + "avatarUrl": "https://avatars.githubusercontent.com/u/103489788?v=4", + "url": "https://github.com/wauputr4", + "committedAt": "2026-06-13T17:37:55Z" + } + ], "authorFromFrontmatter": false } ] diff --git a/src/data/communities.json b/src/data/communities.json new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/src/data/communities.json @@ -0,0 +1 @@ +[] diff --git a/src/data/legacy.json b/src/data/legacy.json index f69cbd6..9143d05 100644 --- a/src/data/legacy.json +++ b/src/data/legacy.json @@ -115,7 +115,7 @@ "url": "https://github.com/wauputr4/awesome-llm-gateways", "homepage": "", "stars": 3, - "forks": 2, + "forks": 3, "language": null, "topics": [], "license": "NOASSERTION", diff --git a/src/data/projects.json b/src/data/projects.json index c4f0918..8178ea3 100644 --- a/src/data/projects.json +++ b/src/data/projects.json @@ -156,10 +156,10 @@ "url": "https://github.com/jipraks/yt-short-clipper", "homepage": "", "language": "Python", - "stars": 895, + "stars": 896, "forks": 279, "topics": [], - "updatedAt": "2026-07-20T02:08:17Z", + "updatedAt": "2026-07-20T13:29:52Z", "pushedAt": "2026-07-18T02:05:39Z", "latestRelease": { "name": "YT Short Clipper v2.0.5-beta", @@ -200,7 +200,7 @@ "archived": false, "licenseSpdx": "MIT", "createdAt": "2016-07-15T09:39:20Z", - "openIssues": 1, + "openIssues": 2, "openPullRequests": 1, "subscribers": 22, "communityHealth": 37, @@ -291,7 +291,7 @@ "url": "https://github.com/fajarhide/omni", "homepage": "https://omni.weekndlabs.com", "language": "Rust", - "stars": 307, + "stars": 309, "forks": 29, "topics": [ "ai-agents", @@ -313,8 +313,8 @@ "token-reduction", "token-savings" ], - "updatedAt": "2026-07-20T02:45:42Z", - "pushedAt": "2026-07-17T11:39:55Z", + "updatedAt": "2026-07-20T12:02:08Z", + "pushedAt": "2026-07-20T13:01:34Z", "latestRelease": { "name": "v0.6.2", "tagName": "v0.6.2", @@ -324,8 +324,8 @@ "archived": false, "licenseSpdx": "MIT", "createdAt": "2026-03-15T03:38:04Z", - "openIssues": 0, - "openPullRequests": 0, + "openIssues": 13, + "openPullRequests": 2, "subscribers": 4, "communityHealth": 71, "readmeHtml": "
\n \"OMNI

OMNI

\n

\n Noise-canceling context and long-term memory for your AI agent. Stop paying Claude to read 10,000 lines of terminal noise like a headphone for AI agent\n

🇺🇸 English | 🇯🇵 日本語 | 🇨🇳 简体中文 | 🇸🇦 العربية | 🇮🇩 Bahasa Indonesia | 🇻🇳 Tiếng Việt | 🇰🇷 한국어

\n

\"CI\"\n\"Release\"\n \"Rust\"\n \"MCP\"\n \"License:\n \"Hits\"\n

\n\nUp to 85% less tokens · Cross-Session Memory · ~40% faster · Zero hallucination triggers

\n

\n

Every AI coding assistant has two massive problems.

\n

1. They read everything.
Build logs.
Docker logs.
CI logs.
Progress bars.
ANSI colors.
Thousands of tokens... to find one line. Claude isn't expensive. Your terminal is.

\n

2. They forget everything.
Every time you restart Cursor, or switch from Claude Code to Windsurf, your agent gets amnesia. You have to re-explain the project goal. You have to remind them of the same framework gotchas over and over again.

\n

OMNI fixes both.

\n
\n

The Difference

\n

Problem 1: Your terminal drowns out the signal

\n

npm install

\n

Without OMNI: 10,000 lines of \"Downloading...\", \"Extracting...\", and warnings. AI reads everything.
With OMNI: Package conflict. Node 20 required.

\n

terraform apply

\n

Without OMNI: 4,500 lines of unchanged execution plans.
With OMNI: The 3 resources that failed IAM permissions.

\n

docker build

\n

Without OMNI: Endless cache hits, layer hashes, and download progress bars.
With OMNI: Missing dependency libpq-dev at layer 12.

\n

pytest

\n

Without OMNI: 500 passing tests and verbose setup logs.
With OMNI: Only the 2 failed assertions and their stack traces.

\n

cargo build

\n

Without OMNI: 300 lines of compiling dependencies and warnings.
With OMNI: The exact line where the borrow checker failed.

\n

kubectl logs

\n

Without OMNI: Thousands of successful health checks and normal traffic logs.
With OMNI: The crash loop and panic stack trace.

\n

git diff

\n

Without OMNI: Formatting tweaks, generated lockfiles, and whitespace changes.
With OMNI: Only the core business logic changes.

\n

go test

\n

Without OMNI: Pages of standard output from passing packages.
With OMNI: The single nil pointer dereference.

\n

mvn package

\n

Without OMNI: Megabytes of \"Downloading from maven central\".
With OMNI: Compilation error in UserService.java.

\n

pip install

\n

Without OMNI: Resolution logs and wheel building outputs.
With OMNI: Dependency conflict with numpy.

\n

webpack / vite

\n

Without OMNI: 2,000 chunk asset lists and build times.
With OMNI: Missing module resolution in App.tsx.

\n

helm install

\n

Without OMNI: Entire rendered YAML output of all templates.
With OMNI: Pod scheduling failure due to missing secret.

\n

ansible-playbook

\n

Without OMNI: \"ok\" and \"skipped\" statuses for 50 servers.
With OMNI: The single \"failed\" task on web-03.

\n

GitHub Actions (CI/CD)

\n

Without OMNI: Complete workflow logs including environment setup.
With OMNI: Only the specific step that exited with code 1.

\n

Problem 2: Your agent forgets everything overnight

\n

Starting a new session

\n

Without OMNI: \"Please re-explain the project structure, the auth module is broken, and we use Postgres not MySQL.\"
With OMNI: The agent already knows. It picks up where you left off.

\n

Fixing the same bug twice

\n

Without OMNI: Agent hits the same framework gotcha it already solved yesterday because it has no memory.
With OMNI: The fix is already stored. omni recall surfaces the exact solution in under 10ms.

\n

Multi-IDE workflows (Cursor → Claude Code)

\n

Without OMNI: New IDE, new agent, zero context. You're starting from scratch.
With OMNI: Session summary is injected automatically. New agent is immediately up to speed.

\n
\n

Why This Matters

\n

The code you don't send to the AI is just as important as the code you do.

\n

When you feed an AI megabytes of terminal noise, it suffers from context bloat—hallucinating fixes for the wrong warnings and burning your API budget on irrelevant output.

\n

When you restart an agent and it has no memory, you lose hours re-establishing context that should have been preserved automatically.

\n

OMNI solves both, invisibly:

\n\n
\n

Benchmarks

\n

The honest headline, measured on the release binary against 1,810 real command\nexecutions replayed from one developer's actual usage:

\n\n

That second bullet is the number most tools in this category do not print. A tool\nthat claims to save 90% of every command is telling you it summarises output you\nneeded.

\n
\n\"OMNI\"\n

Where the saving actually comes from, over the same 1,810 executions:

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
CommandCallsInputOutputSaved
cargo29424 KB13 KB96.8%
git2565.9 MB509 KB91.3%
ls5271 KB29 KB59.5%
kubectl2124.4 MB2.3 MB48.0%
find3983 KB53 KB36.2%
grep184534 KB385 KB27.8%
cat85515 KB468 KB9.1%
\n

git and cargo carry the result; cat and grep are close to a no-op. OMNI\nearns its place on noisy, repetitive tooling output and gets out of the way\neverywhere else.

\n

Single fixtures from tests/fixtures/, if you want to reproduce one by hand:

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Command / ContextInputOutputSaved
cargo build (large, successful)3,220 B9 B99.7%
cargo test (490 passed, 10 failed)16.5 KB1,100 B93.3%
pytest (failures)730 B136 B81.4%
git status (dirty)496 B113 B77.2%
git diff (multi-file)397 B220 B44.6%
docker build (heavy noise)9.2 KB5.8 KB37.2%
kubectl get pods (mixed)840 B762 B9.3%
\n

Latency is a real cost, not zero. OMNI runs on every hooked command, and the\nprice grows with your history: a 496-byte git status takes ~82 ms against a\nfresh database and ~308 ms against a 97 MB one. A 16.5 KB cargo test takes\n~276 ms. Budget for it.

\n

To see your own actual token savings, just run omni stats after a few days of usage.

\n
\n

Quick Start & Installation

\n

Omni is incredibly easy to set up. It natively integrates into your terminal.

\n

macOS / Linux:

\n
# 1. Install via Homebrew\nbrew install fajarhide/tap/omni\n\n# 2. Setup Omni (Interactive Menu for Claude, VS Code, OpenCode, Codex, Antigravity)\nomni init\n\n# 3. Verify it's working\nomni doctor\n\n# 4. Or auto-fix any issues\nomni doctor --fix\n\n# 5. Check Current Status\nomni init --status\n
\n

Universal Installer (macOS / Linux / WSL):

\n
curl -fsSL omni.weekndlabs.com/install | bash\n
\n

Windows (PowerShell):

\n
irm omni.weekndlabs.com/install.ps1 | iex\n
\n
\n

Integrations

\n

OMNI works seamlessly with the agentic tools you already use. It intercepts their terminal executions automatically.

\n\n
\n

Adaptive Memory OS

\n

OMNI isn't just a terminal filter—it's a cure for AI amnesia.

\n

If you've ever worked with an AI agent for more than an hour, you know the pain of context loss. You restart the agent, and suddenly it forgets what you were working on. It forgets the project goal. It starts making the exact same mistakes it made yesterday because it forgot the repository's undocumented quirks.

\n

OMNI's Memory OS runs silently in the background to solve this:

\n\n

Your agent gets smarter about your codebase every single day, and you never have to repeat yourself again.

\n
\n

How it works

\n

Omni operates purely locally using a deterministic Read → Guard → Score → Collapse → Distill → Persist pipeline.

\n
flowchart LR\n    Command[Raw Tool Output] --> Hook[Omni Hook]\n    Hook --> Score[Scorer Engine]\n    Score -->|Critical=1.0, Noise=0.1| Distill[Content Distiller]\n    Distill --> Clean[Clean Context]\n    Command --> SQLite[(RewindStore SQLite)]\n
\n

If the AI really needs the dropped noise, OMNI's local SQLite RewindStore keeps the full uncompressed log safely hashed, allowing the agent to retrieve it anytime.

\n
\n

Architecture

\n
\n \"OMNI\n

Built in Rust for imperceptible latency.

\n\n
# Development\ncargo build --release\ncargo test --all\nmake fmt && make clippy\n
\n
\n

FAQ

\n

Does Omni permanently delete my logs?
No. The raw logs are compressed and stored locally in the SQLite RewindStore. The AI receives a hash and can retrieve the full log if needed.

\n

Will this slow down my terminal?
No. OMNI is written in Rust and executes the distillation pipeline in under 10ms.

\n

Can I add my own filters?
Yes. You can teach OMNI to strip noise specific to your internal tools using TOML:

\n
# ~/.omni/signals/custom.toml\n[filters.my_tool]\nmatch_command = \"^internal-tool\\\\b\"\nstrip_lines_matching = [\"^DEBUG\", \"syncing...\"]\n
\n

Contributing & License

\n

This is a passion project built for the era of Agentic AI. Whether you're here to save money on tokens, test out free models, or help build the ultimate agentic toolbelt, contributions are always welcome!

\n\n\n

\n \n \n \n \n \"Star\n \n \n

\n\nBuild with ❤️ by [Fajar Hidayat](https://github.com/fajarhide)\n" @@ -340,7 +340,7 @@ "url": "https://github.com/hadziqmtqn/erd-builder-pro", "homepage": "https://www.erdbuilderpro.com", "language": "TypeScript", - "stars": 168, + "stars": 169, "forks": 32, "topics": [ "coding", @@ -352,8 +352,8 @@ "productivity", "tiptap-editor" ], - "updatedAt": "2026-07-18T14:00:08Z", - "pushedAt": "2026-07-20T00:13:11Z", + "updatedAt": "2026-07-20T09:52:59Z", + "pushedAt": "2026-07-20T13:51:15Z", "latestRelease": { "name": "v3.1.3", "tagName": "v3.1.3", @@ -396,7 +396,7 @@ "terminal-emulator", "workspace-manager" ], - "updatedAt": "2026-07-19T15:08:06Z", + "updatedAt": "2026-07-20T13:04:18Z", "pushedAt": "2026-07-20T02:15:44Z", "latestRelease": { "name": "Termul Manager v0.4.8", @@ -449,7 +449,7 @@ "homepage": "", "language": "Java", "stars": 132, - "forks": 97, + "forks": 98, "topics": [], "updatedAt": "2026-07-13T09:56:33Z", "pushedAt": "2026-07-01T22:57:27Z", @@ -473,7 +473,7 @@ "url": "https://github.com/codecoradev/uteke", "homepage": "https://codecora.dev", "language": "Rust", - "stars": 117, + "stars": 121, "forks": 14, "topics": [ "ai", @@ -490,7 +490,7 @@ "sqlite", "vector-database" ], - "updatedAt": "2026-07-20T01:24:14Z", + "updatedAt": "2026-07-20T09:55:38Z", "pushedAt": "2026-07-20T01:24:13Z", "latestRelease": { "name": "Release v0.8.0", @@ -501,12 +501,37 @@ "archived": false, "licenseSpdx": "Apache-2.0", "createdAt": "2026-05-29T00:27:31Z", - "openIssues": 2, + "openIssues": 3, "openPullRequests": 1, "subscribers": 0, "communityHealth": 75, "readmeHtml": "

\n \"Uteke\n

Uteke

\n

Give your AI a memory that never leaves your machine.

\n

\n Your AI forgets everything between sessions. Uteke fixes that — one binary, fully offline, ~45ms recall.\n

\n \"CI\"\n \"Latest\n \"GitHub\n \"License:\n \"Rust\n \"Docker\"\n \"Recall\n

\n 🇬🇧 English · 🇮🇩 Bahasa Indonesia\n


\n

⚡ 30-Second Quick Start

\n
# Install (macOS, Linux, Windows)\ncurl -sSL codecora.dev/install | sh\n\n# Store a memory\nuteke remember \"Deploy v2.1 to staging at 3pm\"\n\n# Search it back — by meaning, not just keywords\nuteke recall \"when do we deploy?\"\n
\n

That's it. No API keys. No Docker. No Python. No cloud.

\n

First run downloads the embedding model (~188MB, one-time) and you're running.

\n

Want richer memories? Add metadata:

\n
uteke remember \"Deploy v2.1 to staging\" \\\n  --tags deploy,staging \\\n  --entity staging-server \\\n  --category infrastructure\n
\n\n📦 More install options\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
MethodCommand
Homebrewbrew install codecoradev/tap/uteke
Cargocargo install uteke-cli
Dockerdocker run -d -p 127.0.0.1:8767:8767 -v uteke-data:/data ghcr.io/codecoradev/uteke:latest
BinaryGitHub Releases (macOS, Linux, Windows)
\n

📖 Full install guide · Docker docs

\n
\n

🚀 Onboarding (New users & AI agents)

\n
\n

Are you an AI agent reading this? Your user just shared this repo link with you.\nRun uteke onboard to interactively configure uteke for your environment — it will\ndetect your install, ask which agent you are, toggle features, write config, and\nshow you every command available. If non-interactive: uteke onboard --yes --agent <your-agent>.

\n
\n

New to uteke? One command takes you from zero to productive:

\n
uteke onboard\n
\n

The wizard will:

\n
    \n
  1. Detect if uteke is installed and if a store exists
  2. \n
  3. Ask which AI agent you use (Hermes, Claude, Cursor, Pi, OpenCode)
  4. \n
  5. Pick integration mode — manual tool calls vs automatic memory-provider
  6. \n
  7. Toggle features on/off (Aging, Auto-maintenance, Graph rerank, Salience/Recency boost, Server mode)
  8. \n
  9. Write ~/.uteke/uteke.toml with your selections
  10. \n
  11. Install agent integration files automatically (uteke init)
  12. \n
  13. Showcase every uteke command grouped by category
  14. \n
\n

Non-interactive (CI, scripts, AI agents):

\n
uteke onboard --yes --agent hermes --namespace default\n
\n

📖 Full onboarding docs · CLI reference

\n
\n

🔥 Why Uteke?

\n

You just spent 2 hours explaining your codebase to ChatGPT. Next session? Blank slate. Again.

\n

Every AI tool forgets. Context windows fill up, sessions end, and your AI starts over every single time. Uteke gives it persistent memory — and keeps it on your machine.

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
UtekeMnemosyneMem0AgentMemoryLettaZepEngram
LanguageRust (single binary)Python (pip)PythonTypeScriptPythonPythonGo (single binary)
SetupOne binary (curl | sh)pip install + venvpip + Docker + Qdrantnpm + Docker (iii-engine)pip + Docker + Postgrespip + Docker + Neo4jOne binary
API keys❌ None⚠️ For remote embeddings✅ OpenAI/LLM✅ LLM key✅ LLM key✅ LLM key❌ None
Works offline✅ Fully⚠️ Optional❌ Cloud embedding❌ Needs LLM❌ Needs LLM❌ Needs LLM + vector DB✅ Fully
SearchHybrid (Vector + FTS5 + RRF)sqlite-vec + FTS5Vector + GraphVector + GraphVectorTemporal GraphFTS5 only
Recall speed~45ms~50ms+Network round-tripNetwork round-tripNetwork round-tripNetwork round-trip~Fast (local)
Multi-agent✅ Rooms (built-in collaboration)⚠️ Shared API
Time-travel✅ Native point-in-time⚠️ Temporal triples
MCP server✅ JSON-RPC + HTTP✅ stdio + SSE
Your data✅ Never leaves machine✅ Local-first⚠️ Sent to LLM cloud⚠️ Sent to LLM cloud⚠️ Sent to LLM cloud⚠️ Sent to LLM cloud✅ Local
LicenseApache 2.0MITApache 2.0Apache 2.0Apache 2.0Apache 2.0Apache 2.0
\n
\n

Uteke vs Mnemosyne: Both are local-first with semantic + FTS5 search. Mnemosyne is the closest competitor (~1.5K stars, Python). Uteke wins on single binary (no Python runtime), rooms, time-travel queries, and zero runtime dependencies.

\n
\n
\n

Uteke vs Engram: Both are single-binary, offline, no-API-key tools. But Engram is FTS5-only (keyword search). Uteke adds vector semantic search + RRF fusion + rooms + time-travel + graph relationships + smart decay + document engine + batch import. Same simplicity thesis, 10× the features.

\n
\n
\n

Uteke vs AgentMemory/Mem0/Letta/Zep: Those are powerful — but all require cloud LLM API keys and Docker infrastructure. Your data goes to OpenAI/Anthropic. Uteke runs fully offline with local ONNX embeddings. No Docker, no Python, no API keys.

\n
\n

\n \"Uteke\n


\n

💡 What Can You Do With Uteke?

\n

🤖 Building AI agents? Give them persistent memory without cloud dependencies. Your agent remembers user preferences, past decisions, and context — across sessions, fully offline.

\n

👥 Working in a team? Use Rooms to share knowledge. Meeting notes, project decisions, architecture choices — searchable by everyone, attributed by author.

\n

🔒 Building for privacy-sensitive domains? Healthcare, finance, legal — data stays on your machine. No API calls, no telemetry, no cloud. Local embeddings (ONNX, 768d).

\n

⌨️ Power user who lives in the terminal? Uteke is your personal knowledge graph. Remember anything, recall by meaning, link related thoughts. All from the command line.

\n
\n

✨ Features

\n

Core Memory

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
FeatureWhat it does
🧠 Hybrid SearchVector similarity + FTS5 full-text search, merged by Reciprocal Rank Fusion (RRF). Finds by meaning AND exact keywords.
🏠 RoomsGroup memories by context (meetings, projects, clients) with author attribution.
Time-travelRecall memories as they existed at any point in time. uteke recall \"deploy\" --at 2025-01-15
🏷️ Rich MetadataTags, entities, categories, key:value pairs on every memory.
🧩 Memory TypesTyped categories (fact, procedure, decision, etc.) with auto-inference.
✏️ Partial UpdatesUpdate content, tags, metadata, importance, or type without full rewrite.
📎 CitationsSource attribution on every memory (URL, file, user, import batch).
\n

Search & Intelligence

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
FeatureWhat it does
🔗 Relationship GraphLink memories with typed edges (supersedes, contradicts, references). Auto-backlinks.
🔗 Cross-Entity LinkingBidirectional memory↔document references via [[doc-slug]] wikilinks.
🤖 Cosine Auto-LinkingAutomatically creates similar_to edges between related memories.
📉 Smart DecayComposite importance scoring. Pin what matters, let stale memories fade.
📈 Salience + RecencyDual-axis recall boost by memory type and age.
🔍 Orphan DetectionFind disconnected, low-importance memories for cleanup.
🌙 Dream CycleOne-command maintenance: lint → backlinks → dedup → orphans.
\n

Integrations

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
FeatureWhat it does
🔌 MCP ServerJSON-RPC over stdio + Streamable HTTP. Works with Claude Code, Cursor, Hermes.
🖥️ Server ModePersistent daemon — eliminates cold-start embedding load on every call.
📂 Batch ImportImport entire directories with auto-strategy routing (document vs. memory extraction).
📝 Document EngineWiki/knowledge base with uteke doc create/get/list and auto-chunking.
📥 Import/ExportJSONL-based backup and restore.
🔑 View-Only API KeysRead-only tokens for safe GET-only access to the server.
\n

Performance & Privacy

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
FeatureWhat it does
📦 Single BinaryZero dependencies. No Docker needed, no database server, no Python, no API keys.
🔒 Fully OfflineLocal ONNX embeddings (EmbeddingGemma Q4, 768d). No telemetry, no cloud.
Recall CacheLRU cache eliminates redundant embedding for repeated queries.
🔥 Tiered MemoryHot/Warm/Cold tracking with auto-cleanup of stale memories.
🔄 Embed FallbackGracefully degrades to no-op embedder if local model fails (never crashes).
👥 Multi-Agent NamespacesFully isolated memory per agent, zero overhead.
📊 BenchmarksBuilt-in uteke bench for perf testing. See results.
\n\n🔌 MCP Server config — connect to Claude Code, Cursor, Hermes
// .mcp.json (Claude Code, Cursor)\n{ \"mcpServers\": { \"uteke\": { \"command\": \"uteke-mcp\" } } }\n
\n

For Claude Desktop, Hermes, and HTTP transport, see MCP docs.

\n

📖 Full documentation · CLI reference · Configuration

\n
\n

🏗️ Architecture

\n
graph LR\n    Input[User Query] --> Embed[Local ONNX Embedder<br/>768d, EmbeddingGemma Q4]\n    Embed --> HNSW[HNSW Vector Index<br/>usearch]\n    Embed --> FTS5[FTS5 Full-Text<br/>SQLite]\n    HNSW --> RRF[Reciprocal Rank Fusion<br/>k=60]\n    FTS5 --> RRF\n    RRF --> Results[Ranked Results]\n\n    style Input fill:#4a9eff,color:#fff\n    style Results fill:#4aff9e,color:#000\n    style RRF fill:#ff9e4a,color:#fff\n
\n

How hybrid search works:

\n
    \n
  1. HNSW (usearch) — finds by meaning (\"deploy\" matches \"rollout\")
  2. \n
  3. FTS5 (SQLite) — finds by exact terms (\"deploy\" matches \"deploy\")
  4. \n
  5. RRF (k=60) — merges both ranked lists → best of both worlds
  6. \n
\n

Everything runs in-process. No network. No cloud. No server required (unless you want server mode).

\n

\n \"Uteke\n


\n

❓ FAQ

\n\nHow is Uteke different from Mem0 or Letta?

Mem0 and Letta are great — but they require cloud API keys (OpenAI/LLM) and external infrastructure (Docker, Postgres, Qdrant). Your data gets sent to a cloud LLM provider. Uteke is a single binary with zero API keys. All embeddings run locally via ONNX. Your data never leaves your machine. See comparison table.

\n\nHow is Uteke different from AgentMemory?

AgentMemory (25K stars) is a TypeScript/Node.js platform with 53 MCP tools and 12 auto-hooks. It's feature-rich but requires Docker + the iii-engine + LLM API keys. Uteke is Rust, zero dependencies, and works fully offline. If you want maximum integrations and don't mind cloud dependency → AgentMemory. If you want privacy, speed, and zero setup → Uteke.

\n\nHow is Uteke different from Engram?

Engram (2.4K stars, Go) shares our philosophy: single binary, zero deps, MCP server, local-first. The key difference is search: Engram uses FTS5 only (keyword matching). Uteke uses hybrid search (HNSW vector similarity + FTS5 + Reciprocal Rank Fusion) — meaning you can search by meaning, not just exact words. Uteke also adds rooms, time-travel, graph relationships, smart decay, document engine, and batch import.

\n\nWhat can Uteke remember?

Anything text-based: decisions, meeting notes, code snippets, project context, personal notes, agent state. You can tag, categorize, and link memories. The --batch-dir flag lets you import entire document directories.

\n\nDoes it really work offline?

Yes. The embedding model (EmbeddingGemma Q4, 768d) downloads once (~188MB) on first run. After that, zero network calls. No telemetry. If the local model fails, Uteke degrades gracefully to a no-op embedder — it never crashes and never calls a cloud API.

\n\nHow fast is recall?

~45ms as a library (measured at 100–10K memories). No network round-trip because everything is local. The LRU recall cache eliminates redundant embedding computation for repeated queries.

\n\nCan I use Uteke with my existing AI tools?

Yes. Uteke ships with an MCP server that works with Claude Code, Cursor, and Hermes. You can also use the HTTP API directly in any language. See MCP setup →

\n\nIs it production-ready?

Uteke is at v0.7.3 with 206 tests, CI/CD on every commit, and benchmark harness. It's used in production by the CodeCora team and other early adopters. Still in 0.x — expect rough edges, but the core is stable.

\n
\n

🤝 Contributing

\n
cargo build --workspace        # Build\ncargo test --workspace         # Test (206 tests)\ncargo clippy -- -D warnings    # Lint\ncargo fmt                      # Format\n
\n

Contributions welcome! Read CONTRIBUTING.md for the full guide.

\n
\n

📄 License

\n

Apache License 2.0 — use it, fork it, ship it.

\n
\n

⭐ Star History

\n\n \n \n \n \"Star\n \n
\n

\n Found this useful? ⭐ Star this repo — it helps others discover Uteke.\n

\n

\n \n \"Star\n \n

\n" }, + { + "fullName": "jipraks/kasirgratisan", + "name": "kasirgratisan", + "owner": "jipraks", + "ownerAvatarUrl": "https://avatars.githubusercontent.com/u/10278519?v=4", + "description": "Free, Open Source, Offline Point of Sales Apps", + "metaDescription": "Free, Open Source, Offline Point of Sales Apps", + "url": "https://github.com/jipraks/kasirgratisan", + "homepage": "", + "language": "TypeScript", + "stars": 112, + "forks": 57, + "topics": [], + "updatedAt": "2026-07-20T04:58:20Z", + "pushedAt": "2026-07-13T00:57:27Z", + "latestRelease": null, + "archived": false, + "licenseSpdx": "MIT", + "createdAt": "2026-02-12T11:02:43Z", + "openIssues": 0, + "openPullRequests": 1, + "subscribers": 1, + "communityHealth": 42, + "readmeHtml": "

🧾 FreeKasir

\n

A free, offline-first, open source Point of Sale (POS) Progressive Web App built for Indonesian Micro, Small, and Medium Enterprises (UMKM). All data is stored locally on the user's device — no server, no registration, no cost.

\n
\n

✨ Features

\n\n
\n

🛠️ Tech Stack

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
LayerTechnology
FrameworkReact 18 + TypeScript
Build ToolVite
StylingTailwind CSS + shadcn/ui
Themingnext-themes (dark mode)
DatabaseIndexedDB via Dexie.js
ChartsRecharts
RoutingReact Router DOM v6
Forms & ValidationReact Hook Form + Zod
State@tanstack/react-query
IconsLucide React
i18ni18next + react-i18next
Datedate-fns (id, en-US, ms locales)
PWAvite-plugin-pwa (Workbox)
Barcodehtml5-qrcode (camera scanner + manual input)
Receipthtml2canvas (to PNG), Web Bluetooth Print (PWA), Bluetooth Classic (Android APK via Capacitor)
FontPlus Jakarta Sans
Native WrapperCapacitor 8 (Android)
\n
\n

🚀 Getting Started

\n

Prerequisites

\n\n

Installation

\n
# Clone the repository\ngit clone https://github.com/user/kasirgratisan.git\ncd kasirgratisan\n\n# Install dependencies\nnpm install\n\n# Start the development server\nnpm run dev\n
\n

The app will be running at http://localhost:8080.

\n

Production Build (PWA/Web)

\n
npm run build\nnpm run preview\n
\n

Android Build (Capacitor)

\n

This project can also run as a native Android app using Capacitor while keeping the PWA/web version working from the same codebase.

\n

Requirements:

\n\n

Set JAVA_HOME

\n

macOS / Linux:

\n
export JAVA_HOME=\"/Applications/Android Studio.app/Contents/jbr/Contents/Home\"\n
\n

Windows (PowerShell):

\n
$env:JAVA_HOME = \"C:\\Program Files\\Android\\Android Studio\\jbr\"\n
\n
\n

Adjust the path if your Android Studio is installed elsewhere.

\n
\n

Build debug APK

\n
npm run build\nnpx cap sync android\ncd android\n./gradlew assembleDebug\n
\n

Output: android/app/build/outputs/apk/debug/app-debug.apk

\n

Build release AAB (for Play Store)

\n
npm run build\nnpx cap sync android\ncd android\n./gradlew bundleRelease\n
\n

Output: android/app/build/outputs/bundle/release/app-release.aab

\n
\n

The release AAB must be signed before uploading to Google Play. See Android signing docs.

\n
\n

Useful scripts

\n
npm run cap:sync      # build web bundle and sync Capacitor\nnpm run cap:android   # build, sync, then open Android Studio\nnpm run cap:run       # build, sync, then run on connected Android device/emulator\n
\n
\n

📁 Project Structure

\n
src/\n├── App.tsx                  # Root component & routing\n├── main.tsx                 # Entry point\n├── index.css                # Design tokens (HSL CSS variables)\n├── lib/\n│   ├── db.ts                # Dexie database schema, interfaces, seed data\n│   ├── auth.ts              # Multi-user auth helpers (PIN hashing, sessions, validation)\n│   ├── utils.ts             # Utility functions (cn, etc.)\n│   ├── image-utils.ts       # Image compression utility\n│   └── version-check.ts     # Version check webhook\n├── components/\n│   ├── layout/\n│   │   ├── AppLayout.tsx    # Main layout (responsive: max-w-lg mobile, max-w-6xl tablet/landscape)\n│   │   └── BottomNav.tsx    # Bottom nav (5 tabs, center cashier CTA)\n│   ├── Onboarding.tsx       # First-run tutorial & store setup\n│   ├── LoginScreen.tsx      # Multi-user login (username + PIN)\n│   ├── LockedPage.tsx       # Permission-gated route fallback\n│   ├── NavLink.tsx          # Permission-aware nav link\n│   ├── BackupReminder.tsx   # Backup reminder & export utility\n│   ├── Receipt.tsx          # Receipt component (view, download, share, Bluetooth print)\n│   ├── BarcodeScanner.tsx   # Barcode/QR scanner with PWA-aware permission handling\n│   ├── ThemeColorPicker.tsx # Accent color picker (8 presets)\n│   ├── LanguageSwitcher.tsx # Language picker (ID, EN, MS)\n│   └── ui/                  # shadcn/ui components (40+)\n├── i18n/\n│   ├── index.ts             # i18next initialization\n│   └── locales/\n│       ├── id/               # Bahasa Indonesia\n│       ├── en/               # English\n│       └── ms/               # Bahasa Malaysia\n├── pages/\n│   ├── Dashboard.tsx        # Home: stats, quick actions, low stock alerts\n│   ├── Cashier.tsx          # POS / cashier (barcode scan input, camera scanner, side-by-side cart on landscape)\n│   ├── Products.tsx         # Product CRUD (with description, SKU, units, photos)\n│   ├── Reports.tsx          # Sales reports & charts\n│   ├── Settings.tsx         # Settings (store, payments, categories, units, theme, backup, install PWA)\n│   ├── Users.tsx            # Multi-user management (owner only)\n│   ├── Supplier.tsx         # Supplier CRUD\n│   ├── StockIn.tsx          # Stock in + COGS calculation\n│   ├── StockOut.tsx         # Stock out\n│   ├── StockReport.tsx      # Stock movement reports\n│   ├── TransactionHistory.tsx # Transaction history with open bill filter tabs\n│   └── NotFound.tsx         # 404 page\n└── hooks/\n    ├── use-auth.tsx         # Multi-user auth context (current user, permissions, login/logout)\n    ├── use-pwa-install.ts   # PWA install prompt + standalone detection (incl. iOS)\n    ├── use-theme-color.ts   # Accent color persistence\n    ├── use-mobile.tsx       # Mobile breakpoint detection\n    └── use-toast.ts         # Toast helper\n
\n
\n

💾 Database

\n

All data is stored locally in the browser using IndexedDB (via Dexie.js). No data is ever sent to any server.

\n

Tables

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
TableDescription
usersMulti-user accounts (owner/staff role, hashed PIN, granular permissions)
categoriesProduct categories (name, color, icon)
productsMaster products (name, SKU, sell price, COGS, stock, unit, description)
unitsMaster units of measurement
suppliersSupplier data
stockInsStock-in records
stockOutsStock-out records
hppHistoryCOGS change audit trail
paymentMethodsPayment methods (Cash, Bank Transfer, QRIS, etc.)
transactionsSales transactions (status: open/completed, customer name, table number, remarks)
transactionItemsIndividual items within each transaction (per-item notes & discount)
storeSettingsStore settings & app state (incl. multi-user toggle)
\n

COGS Calculation (Weighted Average)

\n

When stock is received, COGS is automatically recalculated:

\n
New COGS = ((Old Stock × Old COGS) + (New Qty × Buy Price)) / (Old Stock + New Qty)\n
\n
\n

💬 Feedback & Feature Requests

\n

Got suggestions, feature ideas, or found a bug? Submit and vote on our board:

\n

👉 kasirgratisan.fider.io

\n
\n

👥 Community

\n

Join the Telegram group to discuss the app, ask questions, and share tips with other users:

\n

👉 t.me/kasirgratisan

\n
\n

💎 Sponsors

\n

FreeKasir is proudly supported by:

\n\n \"Sumopod\"\n

Want to sponsor FreeKasir and have your logo featured here? Reach out at sponsorship@freekasir.com.

\n
\n

☕ Support the Developer

\n

FreeKasir is built and maintained for free. If you find it useful, you can buy the developer a coffee to support continued development:

\n

👉 traktir.jipraks.com

\n
\n

🤝 Contributing

\n

Contributions are welcome! Here's how:

\n
    \n
  1. Fork this repository
  2. \n
  3. Create a feature branch (git checkout -b feature/new-feature)
  4. \n
  5. Commit your changes (git commit -m 'Add new feature')
  6. \n
  7. Push to the branch (git push origin feature/new-feature)
  8. \n
  9. Open a Pull Request
  10. \n
\n

Guidelines

\n\n
\n

📄 License

\n

MIT License

\n
\n

🙏 Credits

\n

Built with ❤️ for Indonesian small businesses.

\n\n" + }, { "fullName": "IlhamriSKY/PDDIKTI-kemdikbud-API", "name": "PDDIKTI-kemdikbud-API", @@ -541,31 +566,6 @@ "communityHealth": 42, "readmeHtml": "

🎓 PDDIKTI API Python Library

\n

\"Codacy\n\"python3.x\"\n\"Version\n\"Downloads\"\n\"Author\"\n\"License\"

\n
\n

Library Python untuk mengakses data PDDIKTI Kemdikbud dengan mudah, aman, dan terdokumentasi lengkap

\n
\n

Wrapper API Python yang powerful dan user-friendly untuk mengambil data dari PDDIKTI Kemdikbud. Library ini menyediakan interface yang mudah digunakan untuk mengakses data mahasiswa, dosen, perguruan tinggi, dan program studi di Indonesia dengan dukungan type hints, error handling yang komprehensif, dan dokumentasi lengkap.

\n

📋 Daftar Isi

\n\n

🚀 Fitur Utama

\n\n

📦 Instalasi

\n
pip install pddiktipy\n
\n

Requirements:

\n\n

⚡ Quick Start

\n
from pddiktipy import api\nfrom pprint import pprint\n\n# Menggunakan context manager (recommended)\nwith api() as client:\n    # Cari semua data dengan keyword\n    hasil = client.search_all('Unika Soegijapranata')\n    pprint(hasil)\n    \n    # Cari mahasiswa spesifik\n    mahasiswa = client.search_mahasiswa('Ilham Riski Wibowo')\n    pprint(mahasiswa)\n
\n

⚠️ Error Handling

\n

Library ini menyediakan error handling yang komprehensif:

\n
from pddiktipy import api\nfrom pddiktipy.exceptions import (\n    ValidationError, \n    APIConnectionError, \n    APITimeoutError,\n    PDDIKTIError\n)\n\ntry:\n    with api() as client:\n        # Ini akan raise ValidationError karena keyword kosong\n        result = client.search_mahasiswa(\"\")\n        \nexcept ValidationError as e:\n    print(f\"Error validasi: {e}\")\nexcept APIConnectionError as e:\n    print(f\"Error koneksi: {e}\")\nexcept APITimeoutError as e:\n    print(f\"Request timeout: {e}\")\nexcept PDDIKTIError as e:\n    print(f\"Error PDDIKTI API: {e}\")\n
\n

📚 Dokumentasi Lengkap

\n

➡️ API Documentation - 63 method API dengan dokumentasi komprehensif, contoh penggunaan, dan struktur data response

\n

📊 Struktur Data Response

\n

Semua response API menggunakan TypedDict untuk type safety dan konsistensi. Struktur data disesuaikan dengan konteks pendidikan Indonesia dan standar PDDIKTI.

\n

📝 Changelog

\n

📝 Changelog - Riwayat versi dan roadmap pengembangan

\n

📋 Requirements

\n\n

🧪 Testing

\n

🧪 Testing Guide - Panduan testing dan quality assurance

\n

🤝 Contributing

\n

🤝 Contributing Guide - Panduan berkontribusi pada proyek

\n

📄 License

\n

📄 MIT License - Distributed under the MIT License

\n
\n

📞 Support & Contact

\n\n
\n

⭐ Jika library ini membantu proyek Anda, jangan lupa untuk memberikan star di GitHub!

\n" }, - { - "fullName": "jipraks/kasirgratisan", - "name": "kasirgratisan", - "owner": "jipraks", - "ownerAvatarUrl": "https://avatars.githubusercontent.com/u/10278519?v=4", - "description": "Free, Open Source, Offline Point of Sales Apps", - "metaDescription": "Free, Open Source, Offline Point of Sales Apps", - "url": "https://github.com/jipraks/kasirgratisan", - "homepage": "", - "language": "TypeScript", - "stars": 111, - "forks": 57, - "topics": [], - "updatedAt": "2026-07-18T10:04:32Z", - "pushedAt": "2026-07-13T00:57:27Z", - "latestRelease": null, - "archived": false, - "licenseSpdx": "MIT", - "createdAt": "2026-02-12T11:02:43Z", - "openIssues": 0, - "openPullRequests": 1, - "subscribers": 1, - "communityHealth": 42, - "readmeHtml": "

🧾 FreeKasir

\n

A free, offline-first, open source Point of Sale (POS) Progressive Web App built for Indonesian Micro, Small, and Medium Enterprises (UMKM). All data is stored locally on the user's device — no server, no registration, no cost.

\n
\n

✨ Features

\n\n
\n

🛠️ Tech Stack

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
LayerTechnology
FrameworkReact 18 + TypeScript
Build ToolVite
StylingTailwind CSS + shadcn/ui
Themingnext-themes (dark mode)
DatabaseIndexedDB via Dexie.js
ChartsRecharts
RoutingReact Router DOM v6
Forms & ValidationReact Hook Form + Zod
State@tanstack/react-query
IconsLucide React
i18ni18next + react-i18next
Datedate-fns (id, en-US, ms locales)
PWAvite-plugin-pwa (Workbox)
Barcodehtml5-qrcode (camera scanner + manual input)
Receipthtml2canvas (to PNG), Web Bluetooth Print (PWA), Bluetooth Classic (Android APK via Capacitor)
FontPlus Jakarta Sans
Native WrapperCapacitor 8 (Android)
\n
\n

🚀 Getting Started

\n

Prerequisites

\n\n

Installation

\n
# Clone the repository\ngit clone https://github.com/user/kasirgratisan.git\ncd kasirgratisan\n\n# Install dependencies\nnpm install\n\n# Start the development server\nnpm run dev\n
\n

The app will be running at http://localhost:8080.

\n

Production Build (PWA/Web)

\n
npm run build\nnpm run preview\n
\n

Android Build (Capacitor)

\n

This project can also run as a native Android app using Capacitor while keeping the PWA/web version working from the same codebase.

\n

Requirements:

\n\n

Set JAVA_HOME

\n

macOS / Linux:

\n
export JAVA_HOME=\"/Applications/Android Studio.app/Contents/jbr/Contents/Home\"\n
\n

Windows (PowerShell):

\n
$env:JAVA_HOME = \"C:\\Program Files\\Android\\Android Studio\\jbr\"\n
\n
\n

Adjust the path if your Android Studio is installed elsewhere.

\n
\n

Build debug APK

\n
npm run build\nnpx cap sync android\ncd android\n./gradlew assembleDebug\n
\n

Output: android/app/build/outputs/apk/debug/app-debug.apk

\n

Build release AAB (for Play Store)

\n
npm run build\nnpx cap sync android\ncd android\n./gradlew bundleRelease\n
\n

Output: android/app/build/outputs/bundle/release/app-release.aab

\n
\n

The release AAB must be signed before uploading to Google Play. See Android signing docs.

\n
\n

Useful scripts

\n
npm run cap:sync      # build web bundle and sync Capacitor\nnpm run cap:android   # build, sync, then open Android Studio\nnpm run cap:run       # build, sync, then run on connected Android device/emulator\n
\n
\n

📁 Project Structure

\n
src/\n├── App.tsx                  # Root component & routing\n├── main.tsx                 # Entry point\n├── index.css                # Design tokens (HSL CSS variables)\n├── lib/\n│   ├── db.ts                # Dexie database schema, interfaces, seed data\n│   ├── auth.ts              # Multi-user auth helpers (PIN hashing, sessions, validation)\n│   ├── utils.ts             # Utility functions (cn, etc.)\n│   ├── image-utils.ts       # Image compression utility\n│   └── version-check.ts     # Version check webhook\n├── components/\n│   ├── layout/\n│   │   ├── AppLayout.tsx    # Main layout (responsive: max-w-lg mobile, max-w-6xl tablet/landscape)\n│   │   └── BottomNav.tsx    # Bottom nav (5 tabs, center cashier CTA)\n│   ├── Onboarding.tsx       # First-run tutorial & store setup\n│   ├── LoginScreen.tsx      # Multi-user login (username + PIN)\n│   ├── LockedPage.tsx       # Permission-gated route fallback\n│   ├── NavLink.tsx          # Permission-aware nav link\n│   ├── BackupReminder.tsx   # Backup reminder & export utility\n│   ├── Receipt.tsx          # Receipt component (view, download, share, Bluetooth print)\n│   ├── BarcodeScanner.tsx   # Barcode/QR scanner with PWA-aware permission handling\n│   ├── ThemeColorPicker.tsx # Accent color picker (8 presets)\n│   ├── LanguageSwitcher.tsx # Language picker (ID, EN, MS)\n│   └── ui/                  # shadcn/ui components (40+)\n├── i18n/\n│   ├── index.ts             # i18next initialization\n│   └── locales/\n│       ├── id/               # Bahasa Indonesia\n│       ├── en/               # English\n│       └── ms/               # Bahasa Malaysia\n├── pages/\n│   ├── Dashboard.tsx        # Home: stats, quick actions, low stock alerts\n│   ├── Cashier.tsx          # POS / cashier (barcode scan input, camera scanner, side-by-side cart on landscape)\n│   ├── Products.tsx         # Product CRUD (with description, SKU, units, photos)\n│   ├── Reports.tsx          # Sales reports & charts\n│   ├── Settings.tsx         # Settings (store, payments, categories, units, theme, backup, install PWA)\n│   ├── Users.tsx            # Multi-user management (owner only)\n│   ├── Supplier.tsx         # Supplier CRUD\n│   ├── StockIn.tsx          # Stock in + COGS calculation\n│   ├── StockOut.tsx         # Stock out\n│   ├── StockReport.tsx      # Stock movement reports\n│   ├── TransactionHistory.tsx # Transaction history with open bill filter tabs\n│   └── NotFound.tsx         # 404 page\n└── hooks/\n    ├── use-auth.tsx         # Multi-user auth context (current user, permissions, login/logout)\n    ├── use-pwa-install.ts   # PWA install prompt + standalone detection (incl. iOS)\n    ├── use-theme-color.ts   # Accent color persistence\n    ├── use-mobile.tsx       # Mobile breakpoint detection\n    └── use-toast.ts         # Toast helper\n
\n
\n

💾 Database

\n

All data is stored locally in the browser using IndexedDB (via Dexie.js). No data is ever sent to any server.

\n

Tables

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
TableDescription
usersMulti-user accounts (owner/staff role, hashed PIN, granular permissions)
categoriesProduct categories (name, color, icon)
productsMaster products (name, SKU, sell price, COGS, stock, unit, description)
unitsMaster units of measurement
suppliersSupplier data
stockInsStock-in records
stockOutsStock-out records
hppHistoryCOGS change audit trail
paymentMethodsPayment methods (Cash, Bank Transfer, QRIS, etc.)
transactionsSales transactions (status: open/completed, customer name, table number, remarks)
transactionItemsIndividual items within each transaction (per-item notes & discount)
storeSettingsStore settings & app state (incl. multi-user toggle)
\n

COGS Calculation (Weighted Average)

\n

When stock is received, COGS is automatically recalculated:

\n
New COGS = ((Old Stock × Old COGS) + (New Qty × Buy Price)) / (Old Stock + New Qty)\n
\n
\n

💬 Feedback & Feature Requests

\n

Got suggestions, feature ideas, or found a bug? Submit and vote on our board:

\n

👉 kasirgratisan.fider.io

\n
\n

👥 Community

\n

Join the Telegram group to discuss the app, ask questions, and share tips with other users:

\n

👉 t.me/kasirgratisan

\n
\n

💎 Sponsors

\n

FreeKasir is proudly supported by:

\n\n \"Sumopod\"\n

Want to sponsor FreeKasir and have your logo featured here? Reach out at sponsorship@freekasir.com.

\n
\n

☕ Support the Developer

\n

FreeKasir is built and maintained for free. If you find it useful, you can buy the developer a coffee to support continued development:

\n

👉 traktir.jipraks.com

\n
\n

🤝 Contributing

\n

Contributions are welcome! Here's how:

\n
    \n
  1. Fork this repository
  2. \n
  3. Create a feature branch (git checkout -b feature/new-feature)
  4. \n
  5. Commit your changes (git commit -m 'Add new feature')
  6. \n
  7. Push to the branch (git push origin feature/new-feature)
  8. \n
  9. Open a Pull Request
  10. \n
\n

Guidelines

\n\n
\n

📄 License

\n

MIT License

\n
\n

🙏 Credits

\n

Built with ❤️ for Indonesian small businesses.

\n\n" - }, { "fullName": "adenaufal/anti-slop-writing", "name": "anti-slop-writing", @@ -805,36 +805,6 @@ "communityHealth": 100, "readmeHtml": "
\n \"TEDI\"\n

TEDI

\n

One lightweight app. Eight features. Your whole dev workflow in a single window.

\n \"license\"\n \"platform\"\n \"no\n \"no\n

\n

\n

What is TEDI?

\n

TEDI (Terminal Environment & Development Infrastructure) folds eight tools you reach for every day (a terminal, SSH client, DB browser, editor, AI agent, browser, and Git) into one window, so you stop alt-tabbing. Built on Tauri 2, so a Rust core owns every OS resource and the UI is a single webview: no Node runtime, no bundled Chromium, and a resident footprint closer to a terminal than to an IDE. No telemetry; API keys stay in the OS keychain, and it runs fully offline against a local model if you want.

\n

The eight features

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
#FeatureWhat it does
1Terminal multiplexerNative PTY terminals (zsh / bash / fish / pwsh) on xterm.js + WebGL: split panes, tab groups, shell integration (OSC 7 / 133), inline search, and link detection. Inactive tabs keep streaming.
2SSH connectionConnect to remote hosts (russh), open remote shells, and browse/transfer files over an integrated SFTP explorer, all from a saved connection manager.
3SQL explorer (extension)Browse and query databases from a dedicated panel. Ships as an extension (feature 8), so it installs and updates at runtime.
4Code editorCodeMirror 6 for TS/JS, Rust, Python, PHP, HTML/CSS, JSON, Markdown, C/C++, Java, C#, SQL, and more, with inline AI autocomplete, diff view, Vim mode, and Markdown/image preview.
5AI-native agentBring-your-own-key agent across ten providers (OpenAI, Anthropic, Google, xAI, Cerebras, Groq, DeepSeek, SumoPod) and local models via LM Studio or any OpenAI-compatible server (Ollama, llama.cpp, vLLM). Plan mode, skills, MCP, ten sub-agents, voice input, project memory via TEDI.md, and tools gated by approval.
6AI browser controlA real in-app browser (native webview) the agent drives end to end: navigate, read, type, click, scroll, screenshot the tab to see it, and read its console errors to find out why your page broke.
7WorkspacesEach workspace keeps its own project session (tab layout + working dirs) and switches instantly. The header folder picker spawns a terminal rooted there.
8Source control + extensionsInline Git diff / SCM pane, and a first-class extension system: install from a .zip or GitHub release to add settings, AI tools, commands, keybindings, panels, and status / header / sidebar items.
\n

Everything is fully themeable (presets, custom colors, transparency) while staying lightweight.

\n

Install

\n

Pre-built binaries: Releases (Windows, macOS, Linux .deb / .rpm / .AppImage). Download for your OS and install; TEDI auto-checks for updates.

\n

Screenshots

\n

\n \"TEDI\n \"TEDI\n

\n

\n \"TEDI\n \"TEDI\n

\n

\n \"TEDI\n

Configure AI

\n

Settings → AI, pick a provider and paste your API key. Keys go to the OS keychain via keyring, never to disk or localStorage. Full list: PROVIDERS in src/modules/ai/config.ts.

\n

Running fully local? Two routes, both keyless:

\n\n

The same providers back inline autocomplete (Settings → AI → Autocomplete), so ghost text works on a cloud key or entirely offline. Set the model id yourself when your server names models its own way (qwen2.5-coder:7b, a GGUF filename).

\n

Extensions

\n

TEDI ships no extensions in the binary; every one (including the SQL explorer) installs at runtime. Re-installing the same manifest.id replaces the old copy, so the same path handles updates.

\n
Settings → Extensions → From file       (pick a local .zip)\nSettings → Extensions → From GitHub     (paste owner/repo)\nSettings → Extensions → Check updates   (re-check releases/latest)\n
\n

Per-extension icon, namespaced settings/secrets/storage, and a permission-gated host API. Authoring guide: extensions/README.md. Reference extension: Discord Rich Presence.

\n

CLI

\n
tedi [PATH]          # open a folder or file in TEDI\ntedi .               # open the current directory\ntedi ext <subcmd>    # manage extensions headlessly (install / list / update / ...)\ntedi theme <subcmd>  # manage themes from the terminal\ntedi --help | --version | --update\n
\n

If TEDI is already running, the request forwards to the existing window (no second instance). On macOS / Linux AppImage the tedi command is not on PATH by default: Settings → General → \"Install tedi command in PATH\" creates a shim at ~/.local/bin/tedi. Windows' installer handles this.

\n

Architecture

\n

A Tauri 2 app: a React 19 webview (src/) talks to a Rust backend (src-tauri/) via invoke() and streaming Channels. See ARCHITECTURE.md for a one-page map, then TEDI.md for the per-module reference.

\n

Build from source

\n

Prereqs: Rust stable (rustup), Node 20.19+ / 22.12+ with pnpm, and Tauri's platform prereqs.

\n
pnpm install\npnpm tauri:dev     # dev (isolated data dir)\npnpm tauri build   # production bundle\n
\n

Pre-PR checks (full list in CONTRIBUTING.md):

\n
pnpm exec tsc --noEmit && pnpm lint:imports && pnpm format:check\ncd src-tauri && cargo clippy && cargo fmt\n
\n

Notes per platform

\n\n

Credits

\n

TEDI is a fork of crynta/terax-ai@v0.5.9 by Crynta. The Tauri/Rust backend, the xterm.js terminal, the CodeMirror editor, and the AI agent pipeline are the work of Crynta and the Terax contributors. Same Apache-2.0 license; if TEDI is useful, please star upstream Terax.

\n

License

\n

Apache-2.0. See LICENSE and NOTICE for required attribution.

\n" }, - { - "fullName": "giosakti/duragent", - "name": "duragent", - "owner": "giosakti", - "ownerAvatarUrl": "https://avatars.githubusercontent.com/u/524078?v=4", - "description": "Durable agent runtime. Sessions survive crashes. Agents are just files. One binary, zero dependencies.", - "metaDescription": "Durable agent runtime. Sessions survive crashes. Agents are just files. One binary, zero dependencies.", - "url": "https://github.com/giosakti/duragent", - "homepage": "http://docs.duragent.ai/", - "language": "Rust", - "stars": 48, - "forks": 2, - "topics": [], - "updatedAt": "2026-06-25T07:10:40Z", - "pushedAt": "2026-02-18T08:47:11Z", - "latestRelease": { - "name": "v0.5.4", - "tagName": "v0.5.4", - "url": "https://github.com/giosakti/duragent/releases/tag/v0.5.4", - "publishedAt": "2026-02-18T09:07:37Z" - }, - "archived": false, - "licenseSpdx": "MIT", - "createdAt": "2026-01-11T07:23:01Z", - "openIssues": 0, - "openPullRequests": 0, - "subscribers": 1, - "communityHealth": 100, - "readmeHtml": "

Duragent

\n

\"CI\"\n\"Docs\"\n\"Rust\"\n\"License\"

\n
\n

Duragent — A durable, self-contained runtime for AI agents.

\n
\n

Sessions survive crashes. Agents are just files. One binary, zero dependencies.

\n

Use it as a personal AI assistant, or as the foundation for agent-powered products.

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
What you getHow
Sessions that survive crashesAppend-only event log, attach/detach like tmux
Agents you can read and versionYAML + Markdown — no code required
State you can inspectJust files on disk — cat, grep, git diff
Deploy anywhereSingle binary, ~10MB, no Python/Node/Docker
Your choice of partsSwap LLM providers, gateways, and storage backends or bring your own
\n

Quick Start

\n

1. Install and initialize

\n

Download a prebuilt binary from GitHub Releases (Linux x86_64, macOS Intel/ARM), or build from source:

\n
cargo install --git https://github.com/giosakti/duragent.git\n\nduragent init\n# Follow the interactive setup\n
\n

2. Authenticate

\n
export OPENROUTER_API_KEY=your-key  # or: duragent login anthropic\n
\n

3. Chat with your agent (server starts automatically)

\n
duragent chat --agent <YOUR_AGENT_NAME>\n
\n

4. Attach to a session later

\n
duragent attach --list       # List attachable sessions\nduragent attach SESSION_ID   # Reconnect to existing session\n
\n

Features

\n\n

Modular by Design

\n

Use the built-ins, or swap in your own:

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
ComponentDefaultSwappable
GatewaysCLI, HTTP, SSE, Telegram, DiscordAny platform via gateway plugins
LLMOpenRouterAnthropic, OpenAI, Ollama, or any provider
SandboxTrust modebubblewrap, Docker (planned)
StorageFilesystemPostgres, S3 (planned)
\n

Workspace Layout

\n
./.duragent/\n├── agents/<agent-name>/\n│   ├── agent.yaml           # Agent definition (Duragent Format)\n│   ├── SOUL.md              # \"Who the agent IS\" (identity and personality)\n│   ├── SYSTEM_PROMPT.md     # \"What the agent DOES\" (core system prompt)\n│   ├── INSTRUCTIONS.md      # Additional runtime instructions (optional)\n│   ├── policy.yaml          # Tool execution policy (optional)\n│   ├── skills/              # Modular capabilities (SKILL.md files)\n│   ├── tools/               # Agent-specific auto-discovered tools\n│   └── memory/\n│       ├── MEMORY.md        # Curated long-term memory\n│       └── daily/           # Daily experience logs\n├── sessions/\n│   └── <session_id>/\n│       ├── events.jsonl     # Append-only event log\n│       └── state.json       # Snapshot for fast resume\n├── tools/                   # Workspace-level auto-discovered tools\n├── schedules/               # Scheduled tasks and run logs\n├── processes/               # Background process metadata and logs\n└── memory/\n    └── world/               # Shared knowledge across all agents\n
\n

Documentation

\n

Read the Duragent Guide for installation, configuration, and usage.

\n

For contributors: CONTRIBUTING.md | Project Status | Internal Specs

\n

License

\n

MIT

\n" - }, { "fullName": "wauputr4/bansos", "name": "bansos", @@ -845,7 +815,7 @@ "url": "https://github.com/wauputr4/bansos", "homepage": "https://bansos.dev", "language": "Svelte", - "stars": 48, + "stars": 49, "forks": 6, "topics": [ "bansos", @@ -859,8 +829,8 @@ "static-site", "sveltekit" ], - "updatedAt": "2026-07-17T10:10:27Z", - "pushedAt": "2026-07-14T11:37:42Z", + "updatedAt": "2026-07-20T09:11:40Z", + "pushedAt": "2026-07-20T09:10:23Z", "latestRelease": { "name": "Bansos v0.0.13", "tagName": "v0.0.13", @@ -871,11 +841,41 @@ "licenseSpdx": "MIT", "createdAt": "2026-06-11T13:28:54Z", "openIssues": 3, - "openPullRequests": 3, + "openPullRequests": 2, "subscribers": 0, "communityHealth": 100, "readmeHtml": "

bansos.dev

\n

\"npm\n\"License:\n\"Built\n\"Deploy:\n\"Discord\"\n\"Telegram\"\n\"WhatsApp\"

\n

\"Bansos

\n

🇮🇩 Indonesia (Default) · 🌐 English

\n
\n

🌏 Bahasa Indonesia (Default)

\n

Bantuan sosial untuk developer jelata

\n

bansos.dev adalah open-source katalog info bagi-bagi berkah, promo gratisan, dan diskonan tools coding paling legit khusus untuk developer jelata di Indonesia. Dibuat biar portofolio kita-kita tetep menyala walau dompet lagi sekarat. Nyari domain gratis, hosting free-tier, cloud credits, API credits, database gratisan, atau startup credits? Di sini tempat ngumpulnya! 100% Gratisan, No Clickbait, No Ribet. fr fr 🚀

\n

Situs ini dibangun sebagai static SvelteKit site yang super SEO-friendly, data-driven, aman di mode terang/gelap, dan gampang banget buat dikontribusikan lewat email atau merge request.

\n

Keyword cepat

\n

bansos developer, promo developer Indonesia, domain gratis, cloud credits gratis, API credits, hosting free tier, startup credits, developer tools gratis, open source Indonesia, SvelteKit static site.

\n

Fitur utama

\n\n

Deploy dan Hosting

\n

Situs ini di-deploy dan di-hosting menggunakan Cloudflare Pages dengan adapter @sveltejs/adapter-cloudflare. Setiap kali ada merge request atau push ke branch main, Cloudflare secara otomatis memicu build dan mendistribusikan situs statis super cepat beserta seluruh dynamic OG image yang sudah di-prerender.

\n

Menjalankan proyek

\n
npm install\nnpm run dev\nnpm run build\n
\n

Validasi lokal:

\n
npm run check\nnpm run lint\n
\n

Struktur penting

\n
src/lib/data/bansos.json       # data utama listing bansos\nsrc/lib/data/bansos.ts         # helper selector, sorting, dan contributor stats\nsrc/lib/components/            # komponen UI reusable\nsrc/routes/list/               # halaman list dan detail bansos\nsrc/routes/contribute/         # panduan kontribusi publik\nscripts/add-bansos.mjs         # script lokal tambah data\npackages/bansosdev-cli/        # CLI bansosdev (disabled untuk submit publik)\n
\n

Cara Menambah Bansos

\n

Untuk saat ini, submit publik yang aktif adalah via email dan Git clone. Jalur form, npx CLI, dan bot dinonaktifkan sementara karena spam.

\n
\n

[!TIP]\nSoon: Submisi via Discord & Telegram Bot!\nKami sedang membangun integrasi bot agar kamu bisa mengirimkan bansos baru secara otomatis langsung dari server Discord atau channel Telegram.\nSembari menunggu, yuk gabung ke komunitas kami:

\n\n
\n

1. Opsi 1: Lewat Email

\n

Opsi ini sangat cocok buat kamu yang ingin berbagi info dengan cepat tanpa perlu menyentuh terminal.

\n
    \n
  1. Buka halaman kontribusi di browser: bansos.dev/contribute.
  2. \n
  3. Pilih tab Email.
  4. \n
  5. Kirim usulan ke submit@bansos.dev memakai template yang tersedia.
  6. \n
  7. Pastikan semua field penting terisi: judul, provider, benefit, syarat klaim, link resmi, status, sumber, dan kontributor.
  8. \n
\n
\n

2. Opsi 2: Lewat Command Line (npx CLI) - Dinonaktifkan

\n

Submit publik via npx bansosdev add sedang dinonaktifkan sementara karena spam. Dokumentasi CLI tetap disimpan untuk maintainer dan pengujian lokal, tetapi jangan dipakai untuk submit publik saat ini.

\n
npx bansosdev add\n
\n

CLI akan menuntunmu mengisi field demi field untuk menyiapkan data lokal.

\n

Kamu juga bisa mengirimkan data langsung menggunakan argumen CLI:

\n
npx bansosdev add \\\n  --id contoh-bansos \\\n  --title \"Contoh Bansos Developer\" \\\n  --provider \"Example Provider\" \\\n  --description \"Deskripsi singkat bansos.\" \\\n  --benefits \"Benefit satu|Benefit dua\" \\\n  --validity-type fixed \\\n  --validity-date 2026-06-30 \\\n  --validity-desc \"Berlaku khusus pelajar\" \\\n  --published-at 2026-06-13 \\\n  --requirements \"Buat akun|Klaim program\" \\\n  --cta-link \"https://example.com\" \\\n  --contributor-name \"Nama Kamu\" \\\n  --contributor-url \"https://example.com\" \\\n  --tags \"Cloud,Gratisan\"\n
\n

Parameter validity

\n\n
\n

Catatan Otomatisasi:

\n\n
\n

Cek payload JSON

\n
npx bansosdev add ... --mode json\n
\n
\n

3. Opsi 3: Lewat Git Clone (Manual Merge Request)

\n

Opsi ini bagi kamu yang ingin menguji kode secara lokal atau memodifikasi file secara langsung.

\n
    \n
  1. Clone repositori ini ke komputermu:

    \n
    git clone https://gitlab.com/wauputr4/bansos.git\ncd bansos\nnpm install\n
    \n
  2. \n
  3. Tambahkan data secara lokal menggunakan helper script:

    \n
    npm run add:bansos -- \\\n  --id contoh-bansos \\\n  --title \"Contoh Bansos Developer\" \\\n  --provider \"Example Provider\" \\\n  --description \"Deskripsi singkat bansos.\" \\\n  --benefits \"Benefit satu|Benefit dua\" \\\n  --validity-type fixed \\\n  --validity-date 2026-06-30 \\\n  --requirements \"Buat akun|Klaim program\" \\\n  --cta-link \"https://example.com\" \\\n  --contributor-name \"Nama Kamu\" \\\n  --contributor-url \"https://example.com\" \\\n  --tags \"Cloud,Gratisan\"\n
    \n

    Script ini akan memvalidasi data dan menyimpannya di file data terstruktur src/lib/data/bansos.json.

    \n

    Argumen --benefits dan --requirements dipisahkan dengan |.\nArgumen --tags dipisahkan dengan koma.

    \n
  4. \n
  5. Buat branch baru, tambahkan commit, push ke fork, dan kirim merge request ke repositori utama.

    \n
  6. \n
\n
\n

Maintainer mode (Khusus Admin / Maintainer)

\n

Mode direct untuk submit otomatis sedang dinonaktifkan. Untuk perubahan maintainer, gunakan Git clone, commit manual, dan merge request ke main.

\n
npx bansosdev add ... --mode json\n
\n

Perintah di atas hanya untuk mengecek payload JSON secara lokal.

\n

Detail lengkap CLI lihat docs/bansosdev-cli.md.

\n

Panduan kualitas listing

\n

Listing yang baik sebaiknya menyertakan:

\n\n

Kontribusi

\n\n

Kode etik komunitas

\n

Ikuti Code of Conduct.

\n

Sponsor & Dukungan

\n

Proyek bansos.dev dibangun secara gratis oleh komunitas. Jika proyek ini membantumu menghemat budget developer-mu, silakan kirim dukungan via email ke me@wau.my.id.

\n
\n

[!NOTE]\nSoon: Kami berencana menghadirkan fitur di mana donatur/pengunjung bisa mengirimkan dukungan (donasi) langsung ke masing-masing kontributor yang mendaftarkan/menulis listing bansos tersebut.

\n
\n

Lisensi

\n

MIT. Lihat LICENSE.

\n

Disclaimer

\n

bansos.dev adalah platform komunitas open-source yang bertujuan membantu sesama developer Indonesia menemukan program bantuan sosial yang sah dan legal dari provider resmi. Kami tidak terafiliasi dengan provider mana pun.

\n

Kami dengan tegas melarang:

\n\n

Semua informasi yang ditampilkan bersifat referensi. Selalu verifikasi langsung ke situs resmi provider sebelum melakukan klaim. Kami tidak bertanggung jawab atas perubahan kebijakan sepihak dari provider, interpretasi manfaat yang keliru, ataupun penyalahgunaan informasi oleh pihak tidak bertanggung jawab.

\n

Dengan menggunakan bansos.dev, Anda menyetujui bahwa platform ini hanyalah katalog komunitas dan segala klaim, transaksi, atau interaksi dengan provider sepenuhnya merupakan tanggung jawab pribadi masing-masing pengguna.

\n" }, + { + "fullName": "giosakti/duragent", + "name": "duragent", + "owner": "giosakti", + "ownerAvatarUrl": "https://avatars.githubusercontent.com/u/524078?v=4", + "description": "Durable agent runtime. Sessions survive crashes. Agents are just files. One binary, zero dependencies.", + "metaDescription": "Durable agent runtime. Sessions survive crashes. Agents are just files. One binary, zero dependencies.", + "url": "https://github.com/giosakti/duragent", + "homepage": "http://docs.duragent.ai/", + "language": "Rust", + "stars": 48, + "forks": 2, + "topics": [], + "updatedAt": "2026-06-25T07:10:40Z", + "pushedAt": "2026-02-18T08:47:11Z", + "latestRelease": { + "name": "v0.5.4", + "tagName": "v0.5.4", + "url": "https://github.com/giosakti/duragent/releases/tag/v0.5.4", + "publishedAt": "2026-02-18T09:07:37Z" + }, + "archived": false, + "licenseSpdx": "MIT", + "createdAt": "2026-01-11T07:23:01Z", + "openIssues": 0, + "openPullRequests": 0, + "subscribers": 1, + "communityHealth": 100, + "readmeHtml": "

Duragent

\n

\"CI\"\n\"Docs\"\n\"Rust\"\n\"License\"

\n
\n

Duragent — A durable, self-contained runtime for AI agents.

\n
\n

Sessions survive crashes. Agents are just files. One binary, zero dependencies.

\n

Use it as a personal AI assistant, or as the foundation for agent-powered products.

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
What you getHow
Sessions that survive crashesAppend-only event log, attach/detach like tmux
Agents you can read and versionYAML + Markdown — no code required
State you can inspectJust files on disk — cat, grep, git diff
Deploy anywhereSingle binary, ~10MB, no Python/Node/Docker
Your choice of partsSwap LLM providers, gateways, and storage backends or bring your own
\n

Quick Start

\n

1. Install and initialize

\n

Download a prebuilt binary from GitHub Releases (Linux x86_64, macOS Intel/ARM), or build from source:

\n
cargo install --git https://github.com/giosakti/duragent.git\n\nduragent init\n# Follow the interactive setup\n
\n

2. Authenticate

\n
export OPENROUTER_API_KEY=your-key  # or: duragent login anthropic\n
\n

3. Chat with your agent (server starts automatically)

\n
duragent chat --agent <YOUR_AGENT_NAME>\n
\n

4. Attach to a session later

\n
duragent attach --list       # List attachable sessions\nduragent attach SESSION_ID   # Reconnect to existing session\n
\n

Features

\n\n

Modular by Design

\n

Use the built-ins, or swap in your own:

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
ComponentDefaultSwappable
GatewaysCLI, HTTP, SSE, Telegram, DiscordAny platform via gateway plugins
LLMOpenRouterAnthropic, OpenAI, Ollama, or any provider
SandboxTrust modebubblewrap, Docker (planned)
StorageFilesystemPostgres, S3 (planned)
\n

Workspace Layout

\n
./.duragent/\n├── agents/<agent-name>/\n│   ├── agent.yaml           # Agent definition (Duragent Format)\n│   ├── SOUL.md              # \"Who the agent IS\" (identity and personality)\n│   ├── SYSTEM_PROMPT.md     # \"What the agent DOES\" (core system prompt)\n│   ├── INSTRUCTIONS.md      # Additional runtime instructions (optional)\n│   ├── policy.yaml          # Tool execution policy (optional)\n│   ├── skills/              # Modular capabilities (SKILL.md files)\n│   ├── tools/               # Agent-specific auto-discovered tools\n│   └── memory/\n│       ├── MEMORY.md        # Curated long-term memory\n│       └── daily/           # Daily experience logs\n├── sessions/\n│   └── <session_id>/\n│       ├── events.jsonl     # Append-only event log\n│       └── state.json       # Snapshot for fast resume\n├── tools/                   # Workspace-level auto-discovered tools\n├── schedules/               # Scheduled tasks and run logs\n├── processes/               # Background process metadata and logs\n└── memory/\n    └── world/               # Shared knowledge across all agents\n
\n

Documentation

\n

Read the Duragent Guide for installation, configuration, and usage.

\n

For contributors: CONTRIBUTING.md | Project Status | Internal Specs

\n

License

\n

MIT

\n" + }, { "fullName": "mmenghancurkan/dynaQRIS", "name": "dynaQRIS", @@ -1042,7 +1042,7 @@ "url": "https://github.com/rahmanef63/os-vps", "homepage": "", "language": "TypeScript", - "stars": 40, + "stars": 41, "forks": 7, "topics": [ "browser-shell", @@ -1065,7 +1065,7 @@ "web-desktop", "web-terminal" ], - "updatedAt": "2026-07-19T15:00:07Z", + "updatedAt": "2026-07-20T12:46:27Z", "pushedAt": "2026-07-18T00:55:36Z", "latestRelease": null, "archived": false, @@ -1362,8 +1362,8 @@ "pmtiles", "wildlife" ], - "updatedAt": "2026-07-19T10:57:46Z", - "pushedAt": "2026-07-19T10:57:43Z", + "updatedAt": "2026-07-20T10:31:49Z", + "pushedAt": "2026-07-20T10:31:33Z", "latestRelease": { "name": "Mandum Rimba v1.0.0", "tagName": "v1.0.0", @@ -1440,7 +1440,7 @@ "linux", "omarchy" ], - "updatedAt": "2026-07-19T05:16:48Z", + "updatedAt": "2026-07-20T06:57:09Z", "pushedAt": "2026-07-10T12:37:02Z", "latestRelease": { "name": "v0.2.2", @@ -1633,8 +1633,8 @@ "windows-app", "windows-desktop" ], - "updatedAt": "2026-07-20T01:25:17Z", - "pushedAt": "2026-07-20T01:25:13Z", + "updatedAt": "2026-07-20T14:10:16Z", + "pushedAt": "2026-07-20T14:11:33Z", "latestRelease": { "name": "v0.3.2", "tagName": "v0.3.2", @@ -1644,7 +1644,7 @@ "archived": false, "licenseSpdx": "MIT", "createdAt": "2026-02-23T13:59:12Z", - "openIssues": 3, + "openIssues": 1, "openPullRequests": 0, "subscribers": 0, "communityHealth": 75, @@ -2454,8 +2454,8 @@ "stars": 1, "forks": 0, "topics": [], - "updatedAt": "2026-07-17T08:16:56Z", - "pushedAt": "2026-07-17T08:16:32Z", + "updatedAt": "2026-07-20T13:35:57Z", + "pushedAt": "2026-07-20T13:35:09Z", "latestRelease": { "name": "v1.1.0", "tagName": "v1.1.0", @@ -2554,7 +2554,7 @@ "forks": 0, "topics": [], "updatedAt": "2026-06-26T22:59:58Z", - "pushedAt": "2026-06-29T05:58:13Z", + "pushedAt": "2026-07-20T05:54:59Z", "latestRelease": { "name": "Masday CLI v0.3.89", "tagName": "v0.3.89", @@ -2565,7 +2565,7 @@ "licenseSpdx": "", "createdAt": "2026-05-31T08:23:44Z", "openIssues": 0, - "openPullRequests": 11, + "openPullRequests": 12, "subscribers": 0, "communityHealth": 42, "readmeHtml": "

masday-workflow-rust

\n

Current version: 0.3.74

\n

\"Rust\"\n\"PostgreSQL\"\n\"MCP\"\n\"License:

\n

Unified AI coding agent platform built on Model Context Protocol (MCP) — Rust Implementation

\n
\n

Migration Status: Complete. Backend migrated from TypeScript to Rust.

\n
\n

This is the Rust implementation of masday-workflow, providing a robust, type-safe backend with the MCP protocol. The project combines a multi-agent workflow system with 4-layer memory (working, episodic, long-term, graph) and exposes MCP tools for AI agents.

\n
\n

Install

\n

One-line install (Linux/macOS/Windows Git Bash):

\n
curl -fsSL https://github.com/dayartcrew-web/masday-workflow-rust/releases/latest/download/install.sh | bash\n
\n

Auto-detects: Linux / macOS / Windows × x86_64 / aarch64

\n

Manual download: Linux x86_64 · Windows x86_64 · All releases

\n

📖 Full install guide: docs/install-guide.md

\n
masday quickstart          # One-command setup (db + agents + MCP)\nmasday --version           # Check version\nmasday status              # Check health\n
\n
\n

Quick Start

\n

Prerequisites

\n\n

Setup

\n
# Clone and setup\ngit clone <repo-url>\ncd masday-workflow-rust\nbash scripts/setup.sh\n\n# Build Rust crates\ncargo build --workspace\n\n# Run MCP server (exposes tools via stdio, uses SQLite — no database setup needed)\ncargo run -p masday-mcp\n\n# Run API server (REST endpoints, requires PostgreSQL)\nDATABASE_URL=postgresql://USER:PASS@localhost:54341/masday_workflow \\\n  cargo run -p masday-api\n\n# Build release binaries\ncargo build --release --workspace\n
\n
\n

Note: The MCP stdio server (masday-mcp) runs in SQLite-only mode — it creates ~/.masday/data.db automatically. No PostgreSQL or API server required for local use. PostgreSQL is only needed for the API server (masday-api) or remote deployments.

\n
\n
\n

Architecture

\n

Architecture — Local Mode (stdio, SQLite)

\n
┌──────────────┐     ┌──────────────────────────────────┐\n│  MCP Client  │────>│  masday-mcp (standalone binary)   │\n│  (Claude/etc)│stdio│  ┌──────────┐  ┌──────────────┐  │\n└──────────────┘     │  │ 20 Tool  │  │   SQLite     │  │\n                     │  │ Domains  │──│  ~/.masday/  │  │\n                     │  └──────────┘  │  data.db     │  │\n                     └──────────────────────────────────┘\n
\n

Architecture — Remote Mode (HTTP/SSE → API → PostgreSQL)

\n
┌──────────────┐     ┌──────────────────────────────┐     ┌─────────────┐\n│  MCP Client  │────>│  API Server (masday-api)     │────>│  PostgreSQL │\n│  (Claude/etc)│HTTP │  (Axum)                      │      │             │\n└──────────────┘     │                              │     └─────────────┘\n                     │  ┌────────┐  ┌───────────┐  │\n┌──────────────┐     │  │Service │  │ 20 Tool   │  │     ┌─────────────┐\n│  Dashboard   │────>│  │ Layer  │  │ Domains   │  │────>│ Redis Cache │\n│  (Next.js)   │HTTP │  └────────┘  └───────────┘  │     └─────────────┘\n└──────────────┘     └──────────────────────────────┘\n
\n

Remote mode env vars (API server only):

\n
DATABASE_URL=\"postgresql://USER:PASS@localhost:54341/masday_workflow\"\nMASDAY_API_KEY=\"your-api-key\"\n
\n

Rust Crates

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
CrateDescription
masday-coreShared types, errors, constants
masday-dbRepository layer (sqlx, PostgreSQL, deadpool)
masday-serviceBusiness logic (workflow, memory, policy, capability)
masday-apiHTTP API layer (Axum, REST endpoints)
masday-mcpMCP server (stdio/local: 70 tools; HTTP/remote: 92 tools; SQLite + PostgreSQL)
masday-cliCommand-line interface
\n

Memory Stack

\n

Masday uses a 3-layer persistence architecture:

\n

Layer 1: File-based Memory (Claude Code native)

\n
~/.claude/projects/<project>/memory/\n├── MEMORY.md              ← Index (auto-loaded every session)\n├── decision-name.md       ← Individual memory files\n└── another-fact.md\n
\n\n

Layer 2: SQLite/PostgreSQL Memory (MCP memory_* tools)

\n
Local:  ~/.masday/data.db → table: memories\nRemote: PostgreSQL        → table: memories (with pgvector)\n
\n\n

Layer 3: Knowledge Graph (MCP graph_* tools)

\n
Local:  ~/.masday/data.db → tables: graph_nodes, graph_edges\nRemote: PostgreSQL        → tables: graph_nodes, graph_edges\n
\n\n

4-Layer Memory Model

\n
  +----------------------------------------------------------+\n  |                   WORKING MEMORY                         |\n  |              In-process RAM, per session                 |\n  +----------------------------------------------------------+\n                           |\n  +----------------------------------------------------------+\n  |                  EPISODIC MEMORY                         |\n  |            Last N messages per session                   |\n  |         Persisted to EpisodicMemory table                |\n  +----------------------------------------------------------+\n                           |\n  +----------------------------------------------------------+\n  |                 LONG-TERM MEMORY                         |\n  |   Scoring: similarity×0.6 + importance×0.2               |\n  |            + recency×0.1 + usage×0.1                     |\n  |   Memory table (SQLite BLOB or PostgreSQL pgvector)      |\n  +----------------------------------------------------------+\n                           |\n  +----------------------------------------------------------+\n  |                 KNOWLEDGE GRAPH                          |\n  |             Nodes & edges, auto-linked                   |\n  |        GraphNode + GraphEdge tables                      |\n  +----------------------------------------------------------+\n
\n

Embedding Implementation

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
ModeVectorizerStorageSearchScale
Local (SQLite)Feature hashing (768-dim)BLOB columnRust brute-force cosine~100s
Remote (PostgreSQL)ONNX nomic-embed-text or OpenAIpgvector columnORDER BY <=> (IVFFlat/HNSW index)~millions
\n

Workflow States

\n
INIT --> ANALYZE --> PLAN --> EXECUTE --> VERIFY --> DONE\n  |                    |    |      |          |\n  |--> DONE            |    |      |--> FIX --|\n  |--> FAILED          |    |--> PAUSED       |--> FIX --> EXECUTE\n                       |--> FAILED    |\n                                      |--> FAILED\n                                         FIX --> DONE\n                                         FIX --> FAILED\n
\n
\n

MCP Tools (92 HTTP / 70 stdio, across 20 domains)

\n

The masday-mcp crate exposes 70 tools in stdio/local mode (masday mcp, SQLite) and 92 tools in HTTP/remote mode (via masday-api, PostgreSQL). The per-domain counts below are the HTTP/remote superset; stdio mode registers a subset of each domain.

\n

Tool Namespaces

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
NamespaceToolsDescription
workflow23Workflow lifecycle, tasks, plans, parallel branches
memory114-layer memory (working, episodic, long-term, graph)
semantic-search3Context packs, code search, fingerprinting
policy6Validation, drift detection, workflow audit
capability11Agent/skill registry, system health, scaffolding
filesystem5File operations (read, write, list, delete)
review2Review submission, decision tracking
session3Session state management
local4Local file-based state (.masday/)
git3Git operations
npm2Package manager operations
docker3Docker operations
cicd3CI/CD operations
github3GitHub operations
tests1Test runner
reminder3Stale/stuck workflow detection
\n
\n

CLI Commands Reference

\n

Setup & Install

\n
masday setup                # Interactive setup wizard (first-time)\nmasday quickstart           # One-command setup: db + migrate + install + ready\nmasday install              # Install masday into current project\nmasday install --force      # Force overwrite existing configs\nmasday install --standalone # Standalone mode — templates only, no build\nmasday install --remote URL # Connect to remote API server\nmasday install --skip-build # Skip cargo build, use existing binaries\nmasday uninstall            # Remove masday from current project\n
\n

Update

\n
masday update               # Download latest binary from GitHub Releases\n                            # Copies to ~/.masday/bin/, re-syncs config\n
\n

Services

\n
masday status               # Check health of all services\nmasday serve                # Start API server + dashboard (port 30101)\nmasday serve --port 8080    # Start on custom port\nmasday mcp                  # Start MCP server (stdio — used by AI platforms)\n
\n

Database

\n
masday db start             # Start PostgreSQL and Redis containers\nmasday db stop              # Stop all containers\nmasday db reset             # Delete data and recreate containers\n
\n

Local Embeddings

\n
masday embed setup                    # Download ONNX Runtime + default model\nmasday embed setup --model bge-small-en-v1.5  # Download specific model\nmasday embed status                   # Show embedding setup status\nmasday embed remove                   # Remove all embedding artifacts\nmasday embed remove --models-only     # Remove models, keep ONNX Runtime\n
\n

Supported models:

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
ModelDimensionsDescription
all-MiniLM-L6-v2 (default)384Fast, good quality
bge-small-en-v1.5384BGE small variant
bge-base-en-v1.5768BGE base, higher quality
\n

Developer Commands (from source)

\n
# Build\ncargo build --workspace              # Build all crates\ncargo build --release               # Optimized release build\ncargo build -p masday-mcp           # Build specific crate\n\n# Run\ncargo run -p masday-mcp             # Start MCP server\ncargo run -p masday-api             # Start API server\n\n# Test\ncargo test --workspace              # Run all tests\ncargo test -p masday-service        # Test specific crate\ncargo test -- --nocapture          # Show test output\n\n# Lint\ncargo clippy --workspace -- -D warnings  # Lint with warnings as errors\ncargo fmt --all                     # Format code\ncargo fmt --all -- --check         # Check formatting\n\n# Clean\ncargo clean                         # Remove build artifacts\n
\n

Release Commands

\n
# Build and release from project root (Linux + Windows)\nbash scripts/release.sh v0.3.74\n\n# Build Linux only\nbash scripts/release.sh v0.3.74 --linux-only\n\n# Test without uploading\nbash scripts/release.sh v0.3.74 --dry-run\n
\n

Release Artifacts

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
BinaryLinuxWindowsSize
masday (CLI)masday-linux-x86_64 (39MB)masday-windows-x86_64.exe (15MB)CLI + MCP server wrapper
masday-mcp (standalone)masday-mcp-linux-x86_64 (3.3MB)masday-mcp-windows-x86_64.exe (2.9MB)Lightweight MCP server
\n
\n

Note: Windows binary is built without local ONNX embeddings. Use remote embedding provider (Ollama/OpenAI) on Windows.

\n
\n
\n

Configuration

\n

All config lives under ~/.masday/:

\n
~/.masday/\n├── config.toml       ← Configuration (auto-created by quickstart)\n├── bin/\n│   └── masday       ← CLI binary\n└── data.db          ← SQLite database (local mode, auto-created)\n
\n

Quickstart Wizard

\n

Run masday quickstart for an interactive setup wizard:

\n\n

The wizard automatically:

\n\n

Config File (~/.masday/config.toml)

\n
mode = \"local\"                          # local | remote | standalone\napi_url = \"http://localhost:30101\"\napi_key = \"***\"\ndatabase_url = \"postgresql://USER:PASS@localhost:54341/masday_workflow\"\nembedding_provider = \"local\"             # local | ollama | openai\nembedding_model = \"all-MiniLM-L6-v2\"\nembedding_dimensions = 384\nport = 30101\nplatforms = [\"claude-code\"]\n
\n

Environment Variables

\n

Local mode (default) — the MCP stdio server requires no configuration. It uses SQLite at ~/.masday/data.db automatically.

\n

Remote mode — only the API server needs env vars. MCP clients connect directly via HTTP/SSE:

\n
# ── API Server (masday-api) ──────────────────────────────\nDATABASE_URL=\"postgresql://USER:PASS@localhost:54341/masday_workflow\"\nMASDAY_API_KEY=\"your-api-key\"           # Auth key for MCP clients\n\n# ── Optional (both modes) ────────────────────────────────\nRUST_LOG=\"info\"                         # debug, info, warn, error\nRUST_BACKTRACE=\"1\"                      # Enable backtrace on panic\n
\n
\n

Note: The MCP binary in local mode needs zero env vars. For remote deployments, skip the binary entirely and point MCP clients directly at the API server via HTTP/SSE.

\n
\n

MCP Stdio Binary (Zero Config)

\n

The masday-mcp binary runs standalone:

\n\n

MCP Configuration

\n

The scripts/setup.sh script generates MCP configuration files for different platforms:

\n\n

All configurations point to the Rust MCP binary: target/debug/masday-mcp (or target/release/masday-mcp for release builds).

\n

MCP Binary Distribution

\n

The masday binary wraps the MCP server — run masday mcp to start. A separate lightweight masday-mcp binary is also available.

\n\n

Download from GitHub Releases:

\n
# Linux — full CLI (includes MCP server)\ncurl -fsSL -o masday \\\n  https://github.com/dayartcrew-web/masday-workflow-rust/releases/latest/download/masday-linux-x86_64\nchmod +x masday\n\n# Linux — standalone MCP server only\ncurl -fsSL -o masday-mcp \\\n  https://github.com/dayartcrew-web/masday-workflow-rust/releases/latest/download/masday-mcp-linux-x86_64\nchmod +x masday-mcp\n\n# Windows PowerShell\nInvoke-WebRequest -Uri \"https://github.com/dayartcrew-web/masday-workflow-rust/releases/latest/download/masday-windows-x86_64.exe\" -OutFile \"masday.exe\"\n
\n

For stdio mode (recommended — SQLite, no external services needed):

\n
{\n  \"mcpServers\": {\n    \"masday\": {\n      \"type\": \"stdio\",\n      \"command\": \"/path/to/masday-mcp\"\n    }\n  }\n}\n
\n

No environment variables needed — the binary creates ~/.masday/data.db on first run.

\n

For remote deployments (no MCP binary needed — point directly to API server):

\n
{\n  \"mcpServers\": {\n    \"masday\": {\n      \"url\": \"http://localhost:30101/mcp\"\n    }\n  }\n}\n
\n
\n

Database

\n

Local Mode — SQLite (default for stdio)

\n

The MCP stdio binary (masday-mcp) uses SQLite for persistence. No setup required.

\n\n
# Check your database\nsqlite3 ~/.masday/data.db \".tables\"\n
\n

Remote Mode — PostgreSQL + pgvector (for API server)

\n

The API server (masday-api) uses PostgreSQL 16 with pgvector for multi-user deployments.

\n
# Start PostgreSQL with pgvector\ndocker-compose up -d\n\n# Run migrations\ncargo run -p masday-db -- migrate\n
\n

Schema (16 tables, shared between SQLite and PostgreSQL)

\n\n
\n

Tech Stack

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
LayerTechnology
LanguageRust (2021 edition)
RuntimeTokio async runtime
DatabaseSQLite (stdio, local mode) / PostgreSQL 16 + pgvector (API, remote mode)
APIAxum (REST HTTP)
ProtocolModel Context Protocol (MCP) over stdio
CacheRedis (optional)
ValidationSerde + serde_json
Loggingtracing
Testingbuilt-in cargo test
CLIclap 4.5
Error Handlingthiserror (lib) + anyhow (app)
\n
\n

Development Workflow

\n

Adding New MCP Tools

\n
    \n
  1. Define types in masday-core/src/types.rs
  2. \n
  3. Add repository methods in masday-db/src/repos/
  4. \n
  5. Add service logic in masday-service/src/
  6. \n
  7. Add HTTP endpoint in masday-api/src/routes/
  8. \n
  9. Add MCP tool handler in masday-mcp/src/tools/
  10. \n
  11. Register tool in masday-mcp/src/main.rs
  12. \n
  13. Add tests in each crate's tests/ module
  14. \n
\n

Running Tests

\n
# Unit tests (all crates)\ncargo test --workspace\n\n# Integration tests\ncargo test --workspace --test '*_integration'\n\n# With output\ncargo test --workspace -- --nocapture\n\n# Specific test\ncargo test -p masday-service test_workflow_create\n
\n

Linting and Formatting

\n
# Format all code\ncargo fmt --all\n\n# Check formatting (CI)\ncargo fmt --all -- --check\n\n# Lint with clippy\ncargo clippy --workspace -- -D warnings\n\n# Fix clippy warnings\ncargo clippy --workspace --fix\n
\n
\n

Platform Support

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
PlatformAgentsSkillsMCP ConfigLocation
Claude Code.claude/agents/*.md.claude/skills/*/SKILL.md.mcp.jsonProject root
Gemini CLI.gemini/agents/.gemini/skills/.gemini/settings.json~/.gemini/
VS Code Copilot.github/agents/N/A.vscode/mcp.jsonProject root
OpenCode.opencode/agent/.opencode/skills/Custom~/.config/opencode/
\n

Run bash scripts/setup.sh to install to all platforms.

\n
\n

Migration from TypeScript

\n

This project was migrated from a TypeScript monorepo to Rust. The migration is complete:

\n

Removed (TypeScript)

\n\n

Replaced by Rust

\n\n

Kept (TypeScript)

\n\n

Migration Benefits

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
BenefitTypeScript → Rust
Type SafetyRuntime type errors → Compile-time guarantees
PerformanceSingle-threaded event loop → Multi-core Tokio
DatabaseDrizzle runtime checks → sqlx compile-time checks
MemoryJSON fallback → Proper API error handling
ConnectionsStale connections → deadpool with health checks
\n
\n

Contributing

\n
    \n
  1. Fork the repository
  2. \n
  3. Create a feature branch (git checkout -b feat/my-feature)
  4. \n
  5. Follow Rust conventions:\n
  6. \n
  7. Submit a pull request
  8. \n
\n

Conventions

\n\n
\n

Troubleshooting

\n

Build Errors

\n
# Clear build cache\ncargo clean\n\n# Update dependencies\ncargo update\n\n# Check Rust version\nrustc --version  # Should be 1.85+\n
\n

Database Connection (API Server Only)

\n
# Check PostgreSQL is running (API server mode only)\ndocker-compose ps\n\n# Check DATABASE_URL in .env\necho $DATABASE_URL\n\n# Test connection\npsql $DATABASE_URL\n
\n

MCP Server Not Starting

\n
# Check binary exists\nls -la target/debug/masday-mcp\n\n# Build if missing\ncargo build -p masday-mcp\n\n# Check logs\nRUST_LOG=debug cargo run -p masday-mcp\n\n# Verify SQLite database\nsqlite3 ~/.masday/data.db \".tables\"\n
\n
\n

License

\n

MIT

\n
\n

Credits

\n

Built on top of outstanding open source software:

\n

Core

\n\n

Database

\n\n

Protocol

\n\n

Frontend

\n\n" diff --git a/src/data/roadmap.ts b/src/data/roadmap.ts new file mode 100644 index 0000000..f2e0551 --- /dev/null +++ b/src/data/roadmap.ts @@ -0,0 +1,37 @@ +export const roadmapPeriods = [ + { + period: 'Jul–Sep 2026', + title: 'Rapikan fondasi data', + items: [ + 'Jaga sinkronisasi direktori proyek', + 'Publikasikan roadmap dan kanal transparansi', + 'Rapikan jalur kontribusi komunitas', + 'Uji OpenSawer bersama proyek terpilih', + 'Rekrut penggerak komunitas', + 'Adakan event perdana' + ] + }, + { + period: 'Okt–Des 2026', + title: 'Buka dukungan komunitas', + items: [ + 'Kurasi kebutuhan proyek potensial', + 'Mulai laporan sponsor dan CSR terbuka', + 'Perluas artikel dan materi belajar' + ] + }, + { + period: 'Jan–Mar 2027', + title: 'Hubungkan proyek dan organisasi', + items: [ + 'Adakan konferensi perangkat terbuka', + 'Buka kolaborasi program dengan komunitas', + 'Dokumentasikan hasil program' + ] + }, + { + period: 'Apr–Jun 2027', + title: 'Ukur dan tentukan langkah berikutnya', + items: ['Terbitkan laporan satu tahun', 'Evaluasi program yang berdampak', 'Susun roadmap periode selanjutnya'] + } +]; diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro index 31dc3e4..90ff76a 100644 --- a/src/layouts/BaseLayout.astro +++ b/src/layouts/BaseLayout.astro @@ -10,6 +10,11 @@ interface Props { image?: string; /** Open Graph `og:type` (e.g. `website`, `article`). */ type?: string; + /** Search-engine indexing policy for this page. */ + robots?: string; + /** ISO timestamps used by article Open Graph metadata. */ + publishedAt?: string; + modifiedAt?: string; /** * Structured data injected as a JSON-LD `` inside synced data (repo description, post // title) can't terminate the inline block. Escaped before hashing, so the CSP @@ -82,15 +123,16 @@ for (const byte of new Uint8Array(jsonLdDigest)) { jsonLdBinary += String.fromCharCode(byte); } const jsonLdHash = btoa(jsonLdBinary); +const styleSrc = import.meta.env.DEV ? "style-src 'self' 'unsafe-inline'" : "style-src 'self'"; const csp = [ "default-src 'self'", "base-uri 'self'", - `script-src 'self' 'sha256-${jsonLdHash}'`, - "style-src 'self'", + `script-src 'self' 'sha256-${jsonLdHash}'${cloudflareBeaconToken ? ' https://static.cloudflareinsights.com' : ''}${googleAnalyticsId ? ' https://www.googletagmanager.com' : ''}`, + styleSrc, // First-party + GitHub avatar/raw image hosts only — no third-party beacons. - "img-src 'self' data: https://raw.githubusercontent.com https://avatars.githubusercontent.com", + `img-src 'self' data: https://raw.githubusercontent.com https://avatars.githubusercontent.com${googleAnalyticsId ? ' https://*.google-analytics.com https://*.googletagmanager.com' : ''}`, "font-src 'self' data:", - "connect-src 'self'", + `connect-src 'self'${cloudflareBeaconToken ? ' https://cloudflareinsights.com' : ''}${googleAnalyticsId ? ' https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com' : ''}`, "frame-src https://drive.google.com", "form-action 'self'", "frame-ancestors 'none'", @@ -108,7 +150,8 @@ const csp = [ - + + {googleSiteVerification && } @@ -124,15 +167,18 @@ const csp = [ ) } - + + + {type === 'article' && publishedAt && } + {type === 'article' && modifiedAt && } @@ -202,6 +248,32 @@ const csp = [ observer.observe(el); } } + + const cloudflareBeaconToken = import.meta.env.PUBLIC_CLOUDFLARE_WEB_ANALYTICS_TOKEN; + if (cloudflareBeaconToken) { + const beacon = document.createElement('script'); + beacon.defer = true; + beacon.src = 'https://static.cloudflareinsights.com/beacon.min.js'; + beacon.dataset.cfBeacon = JSON.stringify({ token: cloudflareBeaconToken }); + document.head.append(beacon); + } + + const googleAnalyticsId = import.meta.env.PUBLIC_GOOGLE_ANALYTICS_ID; + if (googleAnalyticsId) { + const tag = document.createElement('script'); + tag.async = true; + tag.src = `https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent(googleAnalyticsId)}`; + document.head.append(tag); + + const analyticsWindow = window as typeof window & { + dataLayer?: unknown[][]; + gtag?: (...args: unknown[]) => void; + }; + const dataLayer = analyticsWindow.dataLayer ||= []; + const gtag = analyticsWindow.gtag ||= (...args: unknown[]) => dataLayer.push(args); + gtag('js', new Date()); + gtag('config', googleAnalyticsId); + } diff --git a/src/lib/content.ts b/src/lib/content.ts index 62724f9..afbaece 100644 --- a/src/lib/content.ts +++ b/src/lib/content.ts @@ -45,7 +45,11 @@ export interface BlogPost { releasedAt: string; /** ISO timestamp of the most recent edit commit; distinct from `releasedAt`. */ lastModifiedAt?: string; + latestCommitSha?: string; + latestCommitUrl?: string; author: PostAuthor; + /** Unique contributors derived from the Markdown file's commit history. */ + authors?: PostAuthor[]; /** * Whether `author` was taken from frontmatter `authors[]` (true) or * derived from git commit metadata (false/undefined). Lets the UI label diff --git a/src/lib/learning.ts b/src/lib/learning.ts index 5774571..e79c615 100644 --- a/src/lib/learning.ts +++ b/src/lib/learning.ts @@ -9,6 +9,7 @@ import smartDigitalEnergyOg from '../assets/og/learning/smart-digital-energy.jpg export interface LearningResource { slug: string; title: string; + seoTitle?: string; year: number; topic: string; description: string; @@ -38,6 +39,7 @@ export const learningResources: LearningResource[] = [ { slug: 'ai-yang-proaktif', title: 'AI yang Proaktif: Memahami Agentic AI dengan Contoh Sederhana', + seoTitle: 'Agentic AI Proaktif dengan Contoh Sederhana', year: 2025, topic: 'Agentic AI', description: 'Pengenalan agentic AI melalui contoh yang mudah diikuti.', @@ -47,6 +49,7 @@ export const learningResources: LearningResource[] = [ { slug: 'menulis-jurnal-dibantu-ai', title: 'Menulis Jurnal Dibantu AI: Data-First Anti-Halusinasi', + seoTitle: 'Menulis Jurnal dengan AI Tanpa Halusinasi', year: 2025, topic: 'Literasi AI', description: 'Panduan menggunakan AI secara berbasis data untuk penulisan jurnal.', @@ -56,6 +59,7 @@ export const learningResources: LearningResource[] = [ { slug: 'roadmap-ai-gotong-royong', title: 'Roadmap AI Gotong Royong bagi Bangsa Indonesia', + seoTitle: 'Roadmap AI Gotong Royong Indonesia', year: 2025, topic: 'Transformasi Digital', description: 'Pandangan tentang arah pengembangan AI yang kolaboratif di Indonesia.', @@ -65,6 +69,7 @@ export const learningResources: LearningResource[] = [ { slug: 'smart-digital-energy', title: 'Smart Digital Energy: Inovasi Teknologi Open Source untuk Pencapaian Target Produksi Migas', + seoTitle: 'Smart Digital Energy dengan Open Source', year: 2025, topic: 'Energi', description: 'Contoh pemanfaatan teknologi open source untuk sektor energi.', diff --git a/src/lib/projects.ts b/src/lib/projects.ts index 43c52ec..77e5937 100644 Binary files a/src/lib/projects.ts and b/src/lib/projects.ts differ diff --git a/src/pages/belajar/[slug].astro b/src/pages/belajar/[slug].astro index 6485125..9d8ef6c 100644 --- a/src/pages/belajar/[slug].astro +++ b/src/pages/belajar/[slug].astro @@ -19,7 +19,7 @@ const jsonLd = { }; --- - +
Ruang Belajar
diff --git a/src/pages/blog.astro b/src/pages/blog.astro index 38da69d..a0087f7 100644 --- a/src/pages/blog.astro +++ b/src/pages/blog.astro @@ -185,7 +185,9 @@ const statusLegend = [

Artikel disimpan per tahun dan bulan supaya arsip tetap mudah dipindai, - dipreview, dan diintegrasikan ke halaman blog nanti. + dipreview, dan disinkronkan ke website. Draf dapat dibuka melalui + /blog/preview/slug-artikel/ + tanpa ditampilkan di daftar blog atau sitemap.

content/
   2026/
diff --git a/src/pages/blog/[slug].astro b/src/pages/blog/[...slug].astro
similarity index 52%
rename from src/pages/blog/[slug].astro
rename to src/pages/blog/[...slug].astro
index e44748a..8717ff2 100644
--- a/src/pages/blog/[slug].astro
+++ b/src/pages/blog/[...slug].astro
@@ -6,21 +6,29 @@ import { withBase, safeHref } from '../../lib/urls';
 import { isPublished, renderArticle, type BlogPost } from '../../lib/content';
 
 export function getStaticPaths() {
-  // Only build pages for published posts so drafts are never reachable.
-  return (blogPosts as BlogPost[]).filter(isPublished).map((post) => ({
-    params: { slug: post.slug },
+  // Drafts use a distinct preview route and stay out of listings and the sitemap.
+  return (blogPosts as BlogPost[]).map((post) => ({
+    params: { slug: isPublished(post) ? post.slug : `preview/${post.slug}` },
     props: { post }
   }));
 }
 
 const { post } = Astro.props;
+const published = isPublished(post);
+const articlePath = published ? `/blog/${post.slug}` : `/blog/preview/${post.slug}`;
 // Render Markdown through the shared sanitizing pipeline (no raw `set:html`).
 const html = renderArticle(post.content);
-// Author link comes from frontmatter/git — allowlist the scheme before href.
-const authorUrl = safeHref(post.author?.url);
+const authors = post.authors?.length ? post.authors : post.author ? [post.author] : [];
 const authorDate = post.author?.committedAt
   ? new Intl.DateTimeFormat('id-ID', { dateStyle: 'long' }).format(new Date(post.author.committedAt))
   : '';
+const modifiedDate = post.lastModifiedAt
+  ? new Intl.DateTimeFormat('id-ID', { dateStyle: 'long' }).format(new Date(post.lastModifiedAt))
+  : '';
+const latestCommitUrl = safeHref(post.latestCommitUrl);
+const seoTitle = post.slug === 'bagaimana-open-source-mampu-bertahan'
+  ? 'Mengapa Open Source Bertahan Puluhan Tahun - IndopenSource'
+  : `${post.title} - IndopenSource`;
 const releaseDate = post.releasedAt
   ? new Intl.DateTimeFormat('id-ID', { dateStyle: 'long' }).format(new Date(post.releasedAt))
   : '';
@@ -30,7 +38,7 @@ const heroAlt = `Thumbnail artikel: ${post.title}`;
 // Provenance-aware author byline: frontmatter `authors[]` vs git commit metadata.
 const authorByline = post.authorFromFrontmatter
   ? `Disebutkan di frontmatter artikel${authorDate ? ` (commit pertama ${authorDate})` : ''}.`
-  : `Diambil otomatis dari commit pertama file artikel${authorDate ? ` pada ${authorDate}` : ''}.`;
+  : `Kontributor diambil dari riwayat commit sumber Markdown${authorDate ? published ? `. Terbit ${authorDate}` : `. Commit pertama ${authorDate}` : ''}${modifiedDate ? ` · diperbarui ${modifiedDate}` : ''}.`;
 
 // BlogPosting structured data (passed to BaseLayout's hashed jsonLd prop so it
 // is CSP-allow-listed in , not a separate inline script).
@@ -40,22 +48,27 @@ const jsonLd = {
   headline: post.title,
   description: post.description,
   image: thumbnailUrl,
-  ...(post.releasedAt ? { datePublished: post.releasedAt } : {}),
+  ...(published && post.releasedAt ? { datePublished: post.releasedAt } : {}),
   ...(post.lastModifiedAt ? { dateModified: post.lastModifiedAt } : {}),
-  ...(post.author?.name
-    ? { author: { '@type': 'Person', name: post.author.name, ...(authorUrl ? { url: authorUrl } : {}) } }
+  ...(authors.length
+    ? { author: authors.map((author) => ({ '@type': 'Person', name: author.name, ...(safeHref(author.url) ? { url: safeHref(author.url) } : {}) })) }
     : {}),
   publisher: { '@type': 'Organization', name: 'IndopenSource' }
 };
 ---
 
-
+
   
+    {!published && (
+      
+ Preview draf. Artikel ini masih ditinjau, belum diterbitkan, dan tidak diindeks mesin pencari. +
+ )}

{post.description}

- /blog/{post.slug} - {releaseDate && <>Rilis {releaseDate}} - {post.author?.name && <>oleh {post.author.name}} + {articlePath} + {releaseDate && <>{published ? 'Rilis' : 'Tanggal draf'} {releaseDate}} + {authors.length > 0 && <>oleh {authors.map((author) => author.name).join(' & ')}}
{post.status} @@ -74,19 +87,29 @@ const jsonLd = { Penulis
-
- { - post.author?.avatarUrl && ( - {`${post.author.name} - ) - } -
- {authorUrl - ? {post.author?.name} - : {post.author?.name}} -

{authorByline}

-
+
+ {authors.map((author) => { + const authorUrl = safeHref(author.url); + return ( +
+ {author.avatarUrl && ( + + )} +
+ {authorUrl + ? {author.name} + : {author.name}} +
+
+ ); + })}
+

{authorByline}

+ {latestCommitUrl && post.latestCommitSha && ( + + Commit terbaru {post.latestCommitSha.slice(0, 7)} ↗ + + )}
{c.tag} @@ -102,7 +103,7 @@ const channels = [ }`} > {c.label} - (situs eksternal) + {c.href.startsWith('http') && (situs eksternal)}
diff --git a/src/pages/donasi.astro b/src/pages/donasi.astro index 9c3b6be..b1ab0de 100644 --- a/src/pages/donasi.astro +++ b/src/pages/donasi.astro @@ -1,173 +1,5 @@ --- -import BaseLayout from '../layouts/BaseLayout.astro'; -import BaseButton from '../components/BaseButton.astro'; -import PageHeader from '../components/PageHeader.astro'; -import InfoCard from '../components/InfoCard.astro'; -import { withBase } from '../lib/urls'; - -const highlights = [ - { - title: 'Open Source', - desc: 'Sawer App adalah perangkat donasi yang source code-nya terbuka — bisa diaudit, difork, dan dipasang mandiri oleh proyek mana pun.' - }, - { - title: 'Untuk Komunitas', - desc: 'Dirancang khusus untuk kebutuhan ekosistem open source Indonesia. Ringan, transparan, tanpa iklan.' - }, - { - title: 'Bisa Dipasang Mandiri', - desc: 'Setiap proyek yang listing di IndopenSource nantinya bisa menggunakan Sawer App sebagai kanal donasi resmi proyek mereka.' - } -]; +import DonationPage from '../components/DonationPage.astro'; --- - - -

- IndopenSource adalah inisiatif komunitas yang dijalankan secara sukarela. - Ada dua cara untuk mendukung — donasi dari individu lewat Sawer App, - atau sponsor dari organisasi yang sejalan dengan misi open source Indonesia. -

-
- - {/* PREVIEW — banner mencolok, semua fitur masih preview */} -
-
-
-
- Preview -

Halaman dukungan masih tahap awal.

-

- Baik kanal donasi lewat Sawer App maupun paket sponsor - organisasi masih dalam persiapan. Yang bisa kamu lihat sekarang adalah - preview awal — pantau terus untuk info selanjutnya. -

-
- - Kunjungi Sawer App → - -
-
-
- - {/* DUA JALUR — donasi individu ⬄ sponsor organisasi */} -
-
-
- Dua jalur dukungan - // donasi & sponsor -

- Individu sawer, organisasi sponsor. -

-

- Baik kamu kontributor individu yang ingin menyawer proyek favorit, - maupun organisasi yang ingin berpartner secara resmi — ada jalurnya. - Dua bentuk dukungan ini menjaga IndopenSource tetap independen dan terbuka. -

-
- -
-
- -

Donasi individu

-

- Lewat Sawer App, kamu bisa mendukung proyek open source - secara langsung. Sawer App adalah platform donasi terbuka — source code-nya - publik, bisa dipasang mandiri, dan dirancang untuk ekosistem Indonesia. -

-
- - Coba Sawer App - -
-
- -
- -

Sponsor organisasi

-

- Organisasi bisa menjadi sponsor resmi IndopenSource — dukungan finansial - untuk infrastruktur, event komunitas, dan program pengembangan. - Tersedia beberapa tingkat sponsorship dengan benefit masing-masing. -

-
- - Lihat paket sponsor - -
-
-
-
-
- - {/* TENTANG SAWER — app open source untuk proyek yang listing */} -
-
-
- Sawer App - // platform donasi terbuka -

- Sawer dulu, baru saweran. -

-

- Sawer App adalah aplikasi donasi yang source code-nya terbuka untuk umum. - Bukan sekadar platform — ini adalah perangkat yang bisa dipakai ulang - oleh proyek open source Indonesia untuk menerima dukungan dari komunitas. -

-
- -
- {highlights.map((h) => ( - -

{h.desc}

-
- ))} -
- -
-
-

- Sawer App — open source, MIT license. - Setiap proyek yang listing di IndopenSource nantinya bisa menggunakan Sawer - sebagai kanal donasi resmi langsung dari halaman proyek mereka. -

-

- Repositori sedang disiapkan · pantau GitHub IndopenSource -

-
- - Daftarkan proyekmu - -
-
-
- - {/* CLOSING — coming soon dengan tiga aksi */} -
-
- Coming soon -

- Siap-siap. Sawer & Sponsor segera hadir. -

-

- Halaman ini masih preview — baik donasi individu lewat Sawer App - maupun paket sponsor organisasi sedang disiapkan. - Pantau GitHub dan website untuk info selanjutnya. -

-
- - Coba Sawer App → - - - Info sponsor - - - Ikuti blog - -
-
-
-
+ diff --git a/src/pages/falsafah.astro b/src/pages/falsafah.astro index af91978..7f69b0b 100644 --- a/src/pages/falsafah.astro +++ b/src/pages/falsafah.astro @@ -3,32 +3,43 @@ import BaseLayout from '../layouts/BaseLayout.astro'; import PageHeader from '../components/PageHeader.astro'; import LightSvgMotion from '../components/LightSvgMotion.astro'; -// Three pillars, numbered editorial-style. The page logic is purely -// presentational (static content) so the restyle is the whole job; an array -// keeps the numbered rhythm consistent and easy to read. const prinsip = [ { no: '01', - tag: 'Terbuka', - title: 'Kode, jejak, dan keputusan dibuka.', - body: 'Kode, diskusi, keputusan, dan roadmap dibuat mudah ditemukan oleh publik — bukan rahasia tim, tapi catatan bersama yang bisa dibaca siapa saja.', - meta: '// transparansi default' + tag: 'Kebebasan', + title: 'Boleh dipelajari, diubah, dan dibagikan.', + body: 'Akses ke kode sumber bukan tujuan akhir. Orang perlu punya ruang nyata untuk memahami cara kerja perangkat lunak, menyesuaikannya dengan kebutuhan, dan meneruskan perbaikan kepada orang lain.', + meta: '// pengguna juga pelaku' }, { no: '02', - tag: 'Terawat', - title: 'Daftar yang dijaga, bukan ditumpuk.', - body: 'Daftar proyek perlu kurasi, metadata yang jelas, dan ritme pembaruan yang sehat. Yang dirawat tetap berguna; yang ditinggalkan jadi beban.', - meta: '// kurasi > akumulasi' + tag: 'Terbuka', + title: 'Kerja terlihat, agar orang bisa ikut.', + body: 'Kode, diskusi, keputusan, dokumentasi, dan roadmap sebaiknya meninggalkan jejak publik. Keterbukaan membuat masalah lebih mudah ditemukan, pengetahuan tidak berhenti pada satu orang, dan kontribusi dapat diperiksa bersama.', + meta: '// catatan adalah infrastruktur' }, { no: '03', - tag: 'Bermanfaat', - title: 'Dipakai orang Indonesia, hari ini.', - body: 'Fokus pada perangkat, pustaka, data, dan praktik yang membantu pengguna Indonesia secara nyata — bukan demo yang mengkilap lalu terlupakan.', - meta: '// manfaat lokal nyata' + tag: 'Terawat', + title: 'Rilis bukan garis akhir.', + body: 'Proyek yang berguna memerlukan kurasi, dokumentasi, review, keamanan, dan regenerasi pengelola. Merawat pekerjaan lama sama berharganya dengan memulai repository baru.', + meta: '// keberlanjutan adalah kerja' + }, + { + no: '04', + tag: 'Berakar', + title: 'Terbuka untuk kebutuhan Indonesia.', + body: 'Open source memberi ruang untuk membangun kemampuan sendiri, memakai ulang pengetahuan, dan menyesuaikan teknologi dengan bahasa serta kebutuhan lokal. Kemandirian tidak berarti bekerja sendirian; ia tumbuh lewat gotong royong lintas proyek dan komunitas.', + meta: '// lokal, bukan terisolasi' } ]; + +const references = [ + { title: 'History of the Open Source Initiative', note: 'sejarah istilah dan gerakan open source', href: 'https://opensource.org/about/history-of-the-open-source-initiative' }, + { title: 'GNU Manifesto', note: 'kebebasan mempelajari, mengubah, dan membagikan perangkat lunak', href: 'https://web.cs.ucdavis.edu/~rogaway/classes/188/materials/gnu-manifesto.pdf' }, + { title: 'The Cathedral and the Bazaar', note: 'pengembangan terbuka, umpan balik, dan kolaborasi publik', href: 'https://firstmonday.org/ojs/index.php/fm/article/download/578/499?inline=1' }, + { title: 'Indonesia, Go Open Source', note: 'jejak gerakan dan kemandirian teknologi di Indonesia', href: 'https://id.wikipedia.org/wiki/Indonesia,_Go_Open_Source' } +]; ---
- {/* Three numbered principles — editorial grid, asymmetric: oversized index + + {/* Numbered principles — editorial grid, asymmetric: oversized index + sticker tag on the left rail, the statement and body on the right. Thick ink rule separates each entry. */}
- Tiga prinsip + Empat prinsip - +
@@ -97,6 +108,39 @@ const prinsip = [
+
+
+ Bacaan dasar +

Falsafah yang bisa ditelusuri.

+

Prinsip di atas dirumuskan untuk konteks IndopenSource dari sejarah dan praktik gerakan open source. Ini bukan kutipan atau definisi hukum lisensi, melainkan pegangan kerja komunitas.

+
+ {references.map((reference) => ( + +

{reference.title}

+

{reference.note}

+ Baca sumber ↗ +
+ ))} +
+
+
+ +
+
+
+ +
+ Segera hadir +

Buku Manifesto Perangkat Terbuka.

+

Sebuah buku yang sedang disiapkan untuk merangkum gagasan, praktik, dan arah gerakan perangkat terbuka dalam konteks Indonesia.

+

COMING SOON · belum ada tanggal terbit

+
+
+
+
+ {/* Closing stamp — a halftone poster band with a rubber-stamp sign-off, so the manifesto ends on a printed, community-made note. ink/brand on canvas all AA. */} diff --git a/src/pages/faq.astro b/src/pages/faq.astro index d30d979..087a573 100644 --- a/src/pages/faq.astro +++ b/src/pages/faq.astro @@ -17,28 +17,28 @@ const faqs = [ { category: 'Proyek', items: [ - { q: 'Bagaimana cara mendaftarkan proyek saya?', a: 'Buka issue di repository awesome-indonesia dengan template yang tersedia. Tim kurasi akan mereview dan membantu menambahkan proyek Anda ke direktori.' }, - { q: 'Apa syarat proyek bisa masuk direktori?', a: 'Proyek harus open source (lisensi OSI-approved), dibuat atau dipelihara oleh orang Indonesia, dan memiliki dokumentasi dasar (README, cara install). Tidak harus besar atau populer.' }, - { q: 'Proyek saya tidak aktif, apakah bisa dihapus?', a: 'Kami memiliki kategori "revival" untuk proyek potensial yang perlu dihidupkan kembali. Jika proyek benar-benar tidak relevan, kami bisa memindahkannya ke arsip.' }, - { q: 'Bolehkah proyek komersial masuk?', a: 'Boleh, selama ada komponen open source yang jelas dan lisensinya transparan. Kami juga menerima proyek dengan model open-core.' } + { q: 'Bagaimana cara mendaftarkan proyek saya?', a: 'Tambahkan nama repository ke repos.json di repository awesome-indonesia, jalankan validasi, lalu kirim pull request dengan ringkasan proyek.' }, + { q: 'Apa syarat proyek bisa masuk direktori?', a: 'Repository harus publik, relevan dengan ekosistem Indonesia, aktif atau fresh pada 2026, dan bukan duplikasi entri yang sudah ada.' }, + { q: 'Di mana resource dan proyek lama ditempatkan?', a: 'Resource non-app dan materi belajar masuk ke Awesome Indonesia Legacy. Proyek lama atau kandidat yang potensial dihidupkan kembali masuk ke Awesome Indonesia Revival.' }, + { q: 'Apakah metadata README harus diedit manual?', a: 'Tidak. Contributor cukup mengubah repos.json. Metadata README akan dibuat dari GitHub API melalui workflow setelah perubahan digabungkan.' } ] }, { category: 'Kontribusi', items: [ { q: 'Saya pemula, bisa mulai dari mana?', a: 'Cari issue berlabel "good first issue" di repository yang menarik. Atau mulai dari kontribusi non-kode: perbaiki dokumentasi, laporkan bug, atau bantu terjemahan.' }, - { q: 'Bagaimana cara menjadi maintainer?', a: 'Mulai dengan kontribusi konsisten di satu proyek. Setelah beberapa PR di-merge dan Anda memahami kodebase, maintainer existing bisa mengundang Anda menjadi co-maintainer.' }, - { q: 'PR saya tidak di-review, harus bagaimana?', a: 'Bisa mention maintainer di PR atau tanya di Discussions. Kadang review tertunda karena maintainer sibuk. Bersabar dan sopan selalu membantu.' }, - { q: 'Apakah ada mentoring untuk kontributor baru?', a: 'Ya! Kami memiliki program mentoring melalui Discussions. Anda bisa meminta guidance dari maintainer berpengalaman atau kontributor senior.' } + { q: 'Bagaimana cara membantu repository lain?', a: 'Baca panduan kontribusi repository tersebut, pilih issue yang sesuai, lalu kirim perubahan kecil yang mudah ditinjau.' }, + { q: 'PR saya belum ditinjau, harus bagaimana?', a: 'Tambahkan konteks yang kurang di PR lalu tanyakan dengan sopan kepada maintainer. Setiap repository memiliki ritme review berbeda.' }, + { q: 'Apakah ada program mentoring?', a: 'Belum ada program mentoring resmi. Pertanyaan dan usulan bantuan dapat dibuka melalui GitHub Discussions.' } ] }, { category: 'Teknis', items: [ - { q: 'Lisensi apa yang direkomendasikan?', a: 'Untuk proyek software: MIT, Apache 2.0, atau GPL. Untuk dokumentasi: CC-BY-SA. Pilih yang sesuai dengan tujuan dan komunitas target Anda.' }, + { q: 'Lisensi apa yang sebaiknya dipilih?', a: 'Pilih lisensi yang sesuai dengan tujuan proyek dan pahami kewajibannya. Direktori menampilkan lisensi dari metadata repository, tetapi tidak memberikan nasihat hukum.' }, { q: 'Bagaimana cara menangani security vulnerability?', a: 'Laporkan secara privat ke maintainer atau via halaman Security kami. Jangan buka issue publik untuk vulnerability sebelum ada patch.' }, - { q: 'Apakah ada CI/CD yang bisa dipakai?', a: 'GitHub Actions tersedia gratis untuk repository publik. Kami juga menyediakan template workflow untuk testing, linting, dan deployment.' }, - { q: 'Bagaimana cara hosting dokumentasi?', a: 'GitHub Pages, Netlify, atau Vercel menyediakan hosting gratis untuk static site. Astro (yang kami pakai) bisa di-deploy ke mana saja.' } + { q: 'Apakah IndopenSource menyediakan template CI/CD?', a: 'Belum ada template CI/CD resmi. Lihat workflow repository IndopenSource sebagai referensi, lalu sesuaikan dengan kebutuhan proyek.' }, + { q: 'Bagaimana website ini dibuat?', a: 'Website ini menggunakan Astro. Kode dan workflow deployment tersedia di repository indopensource.org.' } ] } ]; diff --git a/src/pages/forum.astro b/src/pages/forum.astro index 27ba0bb..6c2f78c 100644 --- a/src/pages/forum.astro +++ b/src/pages/forum.astro @@ -15,7 +15,7 @@ import PageHeader from '../components/PageHeader.astro';

Buka discussions - Buka project board + Lihat roadmap
diff --git a/src/pages/index.astro b/src/pages/index.astro index 6fa4bbb..cd47e24 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -4,15 +4,20 @@ import BaseLayout from '../layouts/BaseLayout.astro'; import HomeHero from '../components/HomeHero.astro'; import InfoCard from '../components/InfoCard.astro'; import ProjectCard from '../components/ProjectCard.astro'; -import RoadmapTimeline from '../components/RoadmapTimeline.astro'; import SectionHeader from '../components/SectionHeader.astro'; import BaseButton from '../components/BaseButton.astro'; import LightSvgMotion from '../components/LightSvgMotion.astro'; -import projects from '../data/projects.json'; +import projectsData from '../data/projects.json'; +import { roadmapPeriods } from '../data/roadmap'; import { heroImage, HERO_PRELOAD_WIDTH, HERO_QUALITY } from '../lib/hero'; +import { rankProjectOwners, type Project } from '../lib/projects'; +import { withBase } from '../lib/urls'; +const projects = projectsData as Project[]; const totalProjects = projects.length; const topProjects = projects.slice(0, 5); +const roadmapPreview = roadmapPeriods.slice(0, 2); +const topUsers = rankProjectOwners(projects).slice(0, 5); // Pre-generate the AVIF the hero will paint as its LCP candidate so we // can the exact same /_astro/* file from . Same src, @@ -69,11 +74,13 @@ const heroPreload = await getImage({ // kenapa kami ada
- Open source bukan cuma kode di GitHub — ini soal merawat catatan, - membuka pintu kontribusi, dan menulis ulang cerita teknologi Indonesia - secara terbuka. + Open source Indonesia itu soal merawat catatan, membuka pintu kontribusi, + kolaborasi dan menulis ulang cerita teknologi Indonesia secara terbuka.
-

— IndopenSource · komunitas terbuka

+
+

— IndopenSource · komunitas terbuka

+ Baca falsafah +
@@ -101,7 +108,7 @@ const heroPreload = await getImage({ Semua orang boleh ikut menulis catatannya
- Mulai kontribusi + Mulai kontribusi

@@ -119,13 +126,26 @@ const heroPreload = await getImage({

- +
- Gabung diskusi + Lihat selengkapnya
- +
+ {roadmapPreview.map((item, index) => ( +
+
+ {item.period} + +
+

{item.title}

+
    + {item.items.map((entry) =>
  • {entry}
  • )} +
+
+ ))} +
@@ -149,7 +169,9 @@ const heroPreload = await getImage({
- + + Pilihan proyek dari katalog awesome-indonesia. +
Buka semua @@ -169,6 +191,59 @@ const heroPreload = await getImage({
+
+
+
+
+ + Peringkat berdasarkan akumulasi stars pada repository yang tercatat di direktori. + +
+
Lihat semua
+
+ +
+
+ +
+ +
+
diff --git a/src/pages/kode-etik.astro b/src/pages/kode-etik.astro index f0f1d3b..49b8483 100644 --- a/src/pages/kode-etik.astro +++ b/src/pages/kode-etik.astro @@ -20,11 +20,6 @@ const unacceptable = [ 'Perilaku lain yang secara wajar dapat dianggap tidak pantas dalam lingkungan profesional' ]; -const enforcement = [ - { level: 'Koreksi', desc: 'Peringatan tertulis dari maintainer. Permintaan untuk menghentikan perilaku.', action: 'Peringatan verbal/written' }, - { level: 'Peringatan', desc: 'Pelanggaran serius atau berulang. Dilarang berpartisipasi di ruang komunitas untuk periode tertentu.', action: 'Suspend sementara' }, - { level: 'Larangan', desc: 'Pelanggaran berat atau berkelanjutan. Dilarang permanen dari semua ruang komunitas.', action: 'Ban permanen' } -]; --- @@ -85,15 +80,9 @@ const enforcement = [

Tindakan untuk pelanggaran.

Maintainer komunitas berhak mengambil tindakan yang sesuai untuk menegakkan kode etik ini.

-
- {enforcement.map((e, i) => ( -
- Level {i + 1} -

{e.level}

-

{e.desc}

-

{e.action}

-
- ))} +
+

Maintainer dapat memoderasi issue, Discussions, pull request, dan ruang komunitas lain agar kolaborasi tetap sehat dan berguna. Tindakan disesuaikan dengan konteks pelanggaran.

+ Baca dokumen sumber →
@@ -103,7 +92,7 @@ const enforcement = [
Laporan

Melaporkan pelanggaran.

-

Jika Anda mengalami atau menyaksikan pelanggaran kode etik, silakan laporkan kepada tim maintainer. Semua laporan akan ditangani secara rahasia dan serius.

+

Jika Anda mengalami atau menyaksikan pelanggaran kode etik, laporkan melalui kanal privat atau kepada maintainer IndopenSource yang Anda percaya.

Laporkan via Email Diskusi publik diff --git a/src/pages/komunitas.astro b/src/pages/komunitas.astro index e92d00f..b7fbb38 100644 --- a/src/pages/komunitas.astro +++ b/src/pages/komunitas.astro @@ -2,77 +2,73 @@ import BaseLayout from '../layouts/BaseLayout.astro'; import BaseButton from '../components/BaseButton.astro'; import PageHeader from '../components/PageHeader.astro'; -import LightSvgMotion from '../components/LightSvgMotion.astro'; +import communitiesData from '../data/communities.json'; +import { safeHref } from '../lib/urls'; -const paths = [ - { no: '01', tag: 'Temukan', title: 'Kurasi satu proyek.', body: 'Tambahkan proyek Indonesia yang layak dipakai, atau rapikan informasi proyek yang sudah ada.', href: '/projects/', action: 'Buka direktori' }, - { no: '02', tag: 'Tulis', title: 'Bagikan yang dipelajari.', body: 'Tulis catatan teknis, pengalaman merawat proyek, atau panduan praktis untuk pembaca berikutnya.', href: '/blog/', action: 'Baca dan kirim artikel' }, - { no: '03', tag: 'Diskusikan', title: 'Bawa pertanyaan ke ruang terbuka.', body: 'Mulai dari ide kecil, kendala kontribusi, atau usulan kolaborasi lintas komunitas.', href: 'https://github.com/orgs/IndopenSource/discussions', action: 'Buka Discussions' }, - { no: '04', tag: 'Rawat', title: 'Bantu pekerjaan yang sering tak terlihat.', body: 'Dokumentasi, terjemahan, pengujian, desain, dan review sama pentingnya dengan menulis kode.', href: 'https://github.com/IndopenSource', action: 'Lihat repository' } -]; +interface Community { + name: string; + description: string; + focus?: string; + location?: string; + url: string; +} -const spaces = [ - { label: 'Proyek', note: 'direktori dan metadata', href: '/projects/' }, - { label: 'Blog', note: 'catatan dan pembelajaran', href: '/blog/' }, - { label: 'Discussions', note: 'tanya, usul, koordinasi', href: 'https://github.com/orgs/IndopenSource/discussions' }, - { label: 'Roadmap', note: 'pekerjaan yang sedang dibuka', href: 'https://github.com/orgs/IndopenSource/projects' } -]; +const communities = communitiesData as Community[]; --- - -

IndopenSource adalah ruang kerja terbuka. Pilih kontribusi yang sesuai dengan waktu, pengalaman, dan rasa ingin tahu Anda—lalu tinggalkan jejak yang bisa diteruskan orang lain.

+ +

Tempat menemukan komunitas, kelompok pengguna, dan ruang belajar terbuka dari berbagai daerah dan bidang. Daftarnya sedang kami siapkan bersama.

-
- -
- Mulai di sini -

Kontribusi kecil yang terbuka lebih berguna daripada ide besar yang tidak pernah dibagikan.

-
- Mulai diskusi - Cari proyek -
-
-
- -
+
-
-
Empat jalur

Pilih pintu yang paling dekat.

- tidak perlu menunggu undangan -
-
- {paths.map((path) => ( - - ))} -
+ {communities.length === 0 ? ( +
+ Segera hadir +

Belum ada komunitas yang ditampilkan.

+

Kami sengaja memulai dari daftar kosong agar setiap entri punya sumber, tautan aktif, dan diajukan secara terbuka—bukan hasil asumsi.

+
+ ) : ( + + )}
-
-
+
+
- Ritme sederhana -

Baca, bicara, lalu buat perubahan.

+ Tambahkan komunitas +

Buka pull request di repository website.

+

Pengurus atau anggota komunitas boleh mengusulkan entri. Sertakan informasi yang bisa diverifikasi dan pastikan Anda memang berwenang membagikan profil tersebut.

    -
  1. 01 — Pilih satu hal. Temukan proyek, tulisan, atau percakapan yang benar-benar ingin Anda bantu.
  2. -
  3. 02 — Tinggalkan konteks. Ajukan pertanyaan atau usulan secara publik agar orang lain dapat menambahkan pengetahuan.
  4. -
  5. 03 — Kirim kontribusi kecil. Mulai dari koreksi, tautan, contoh penggunaan, atau pull request yang mudah ditinjau.
  6. +
  7. 01 — Fork repository. Buka IndopenSource/indopensource.org lalu buat branch baru.
  8. +
  9. 02 — Tambahkan data. Edit src/data/communities.json dengan nama, deskripsi, wilayah atau fokus, dan tautan resmi.
  10. +
  11. 03 — Verifikasi lokal. Jalankan npm test dan npm run build.
  12. +
  13. 04 — Buka PR ke main. Jelaskan hubungan Anda dengan komunitas serta sumber yang dapat dipakai reviewer untuk memeriksa entri.
diff --git a/src/pages/legal.astro b/src/pages/legal.astro index 617b979..643722a 100644 --- a/src/pages/legal.astro +++ b/src/pages/legal.astro @@ -5,56 +5,34 @@ import PageHeader from '../components/PageHeader.astro'; import { withBase } from '../lib/urls'; const policies = [ - { - icon: 'ph-copyright', - title: 'Lisensi Konten', - desc: 'Konten di website IndopenSource (artikel, dokumentasi, kurasi) dilisensikan di bawah Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0). Anda bebas membagikan dan mengadaptasi konten dengan syarat atribusi dan lisensi yang sama.', - link: 'https://creativecommons.org/licenses/by-sa/4.0/', - isExternal: true - }, { icon: 'ph-code', title: 'Lisensi Kode', - desc: 'Kode sumber website ini dan repository komunitas dilisensikan di bawah MIT License. Proyek yang terkurasi di direktori memiliki lisensi masing-masing yang tercantum di metadata proyek.', + desc: 'Kode sumber website ini dilisensikan di bawah MIT License. Setiap proyek dalam direktori memiliki lisensinya sendiri seperti tercantum pada metadata repository asal.', link: 'https://github.com/IndopenSource/indopensource.org/blob/main/LICENSE', isExternal: true }, { icon: 'ph-shield-warning', title: 'Pelaporan Kerentanan', - desc: 'Jika Anda menemukan kerentanan keamanan di website atau proyek komunitas, laporkan secara bertanggung jawab melalui halaman Security atau email ke security@indopensource.org. Jangan buka issue publik.', + desc: 'Jangan membuka issue publik untuk laporan sensitif. Ikuti halaman Security atau kirim detail melalui kanal privat yang tersedia.', link: '/security/', isExternal: false }, - { - icon: 'ph-gavel', - title: 'DMCA & Hak Cipta', - desc: 'Kami menghormati hak kekayaan intelektual. Jika Anda yakin konten di platform kami melanggar hak cipta, kirimkan notifikasi DMCA ke legal@indopensource.org dengan informasi lengkap.', - link: 'mailto:legal@indopensource.org', - isExternal: true - }, - { - icon: 'ph-trademark', - title: 'Merek Dagang', - desc: 'Nama "IndopenSource", logo, dan identitas visual adalah merek komunitas. Penggunaan harus mendapat izin tertulis. Proyek yang terkurasi tetap memegang merek mereka sendiri.', - link: 'mailto:admin@indopensource.org', - isExternal: true - }, { icon: 'ph-user-circle', title: 'Privasi & Data', - desc: 'Kami tidak mengumpulkan data pribadi beyond yang diperlukan untuk operasional (GitHub username untuk kontribusi). Tidak ada tracking analytics pihak ketiga. Cookie hanya untuk fungsionalitas dasar.', - link: '#', - isExternal: false + desc: 'Website publik ini dapat dibaca tanpa akun atau formulir data pribadi. Saat mengikuti tautan ke GitHub atau layanan lain, kebijakan layanan tersebut berlaku.', + link: 'https://github.com/IndopenSource/indopensource.org', + isExternal: true } ]; const disclaimers = [ - 'IndopenSource adalah inisiatif komunitas, bukan entitas hukum formal.', 'Konten yang dikurasi adalah tanggung jawab masing-masing maintainer proyek.', 'Kami tidak memberikan nasihat hukum atau lisensi profesional.', 'Tautan ke proyek eksternal tidak merupakan endorsement.', - 'Komunitas berhak menolak atau menghapus konten yang melanggar kode etik.' + 'Entri direktori dapat diperbarui atau dihapus jika tidak lagi memenuhi aturan kontribusi.' ]; --- @@ -89,7 +67,7 @@ const disclaimers = [

{p.title}

{p.desc}

- Selengkapnya → + Selengkapnya →
diff --git a/src/pages/press.astro b/src/pages/press.astro index febd715..598c4a9 100644 --- a/src/pages/press.astro +++ b/src/pages/press.astro @@ -1,89 +1,46 @@ --- import BaseLayout from '../layouts/BaseLayout.astro'; -import BaseButton from '../components/BaseButton.astro'; import PageHeader from '../components/PageHeader.astro'; import { withBase } from '../lib/urls'; import projectsData from '../data/projects.json'; -import legacyData from '../data/legacy.json'; -import revivalData from '../data/revival.json'; - -const allProjects = [...(projectsData as any[]), ...(legacyData as any[]), ...(revivalData as any[])]; -const totalProjects = allProjects.length; -const totalStars = allProjects.reduce((sum, p) => sum + (p.stars ?? 0), 0); -const totalForks = allProjects.reduce((sum, p) => sum + (p.forks ?? 0), 0); - -const brandAssets = [ - { name: 'Logo Utama (SVG)', desc: 'Format vektor untuk print & digital', icon: 'ph-file-svg', link: '/brand/indopensource-logo.svg', available: false }, - { name: 'Logo Utama (PNG)', desc: 'Resolusi tinggi, background transparan', icon: 'ph-file-png', link: '/brand/indopensource-logo.png', available: true }, - { name: 'Icon Only', desc: 'Mark tanpa teks untuk avatar & favicon', icon: 'ph-image', link: '/brand/indopensource-icon.svg', available: false }, - { name: 'Brand Guidelines', desc: 'Panduan penggunaan logo & warna', icon: 'ph-book-open', link: '/brand/brand-guidelines.pdf', available: false } -]; - -const colorPalette = [ - { name: 'Brand Green', hex: '#4d7c0f', tailwind: 'bg-[#4d7c0f]', usage: 'Primary brand color' }, - { name: 'Brand Dark', hex: '#365314', tailwind: 'bg-[#365314]', usage: 'Dark variant' }, - { name: 'Sun', hex: '#ecfccb', tailwind: 'bg-[#ecfccb]', usage: 'Highlight & accent' }, - { name: 'Ink', hex: '#101828', tailwind: 'bg-[#101828]', usage: 'Text & dark backgrounds' }, - { name: 'Paper', hex: '#ffffff', tailwind: 'bg-[#ffffff]', usage: 'Light backgrounds' }, - { name: 'Canvas', hex: '#f8fafc', tailwind: 'bg-[#f8fafc]', usage: 'Subtle backgrounds' } -]; - -const pressCoverage = [ - { title: 'IndopenSource: Menghidupkan Kembali Proyek Open Source Indonesia', source: 'Tech in Asia', date: '2025', link: '#' }, - { title: 'Kolaborasi Komunitas OSS Indonesia Melalui IndopenSource', source: 'Kompas Tech', date: '2025', link: '#' }, - { title: 'Direktori Proyek Open Source Buatan Anak Bangsa', source: 'IDN Times', date: '2025', link: '#' } -]; +const projects = (projectsData as any[]).filter((project) => !project.syncFailed); +const totalStars = projects.reduce((sum, project) => sum + (project.stars ?? 0), 0); +const totalForks = projects.reduce((sum, project) => sum + (project.forks ?? 0), 0); const keyFacts = [ - { label: 'Didirikan', value: 'Maret 2025' }, - { label: 'Proyek terkurasi', value: `${totalProjects}+` }, - { label: 'Total bintang GitHub', value: totalStars.toLocaleString('id-ID') }, - { label: 'Fork komunitas', value: totalForks.toLocaleString('id-ID') }, - { label: 'Kontributor aktif', value: '100+' }, - { label: 'Website', value: 'indopensource.org' } + { label: 'Proyek tersinkron', value: projects.length.toLocaleString('id-ID') }, + { label: 'Akumulasi stars', value: totalStars.toLocaleString('id-ID') }, + { label: 'Akumulasi forks', value: totalForks.toLocaleString('id-ID') } ]; - const contact = [ - { type: 'Umum', email: 'admin@indopensource.org', desc: 'Pertanyaan umum & kemitraan' }, - { type: 'Media', email: 'press@indopensource.org', desc: 'Wawancara, liputan, press release' }, - { type: 'Sponsor', email: 'sponsor@indopensource.org', desc: 'Sponsorship & dukungan finansial' } + { type: 'Umum', email: 'admin@indopensource.org', desc: 'Pertanyaan umum dan kemitraan' }, + { type: 'Media', email: 'press@indopensource.org', desc: 'Wawancara dan kebutuhan media' }, + { type: 'Sponsor', email: 'sponsor@indopensource.org', desc: 'Sponsorship dan dukungan finansial' } ]; --- - - -

Halaman ini menyediakan brand assets, fakta kunci, dan informasi kontak untuk jurnalis, blogger, dan organisasi yang ingin meliput atau bermitra dengan IndopenSource.

+ + +

Gunakan ringkasan, data direktori, dan logo yang tersedia di halaman ini. Statistik mengikuti snapshot data proyek terbaru.

- Media kit -

Semua yang Anda butuhkan untuk memberitakan IndopenSource.

+ Tentang IndopenSource +

Ruang terbuka untuk menemukan, mempelajari, dan merawat proyek open source Indonesia.

-
-
- Brand assets -

Logo & identitas visual.

-

Gunakan asset ini sesuai brand guidelines. Untuk penggunaan khusus, hubungi tim media.

+
+
+ Logo tersedia +

Logo IndopenSource.

+

Saat ini hanya logo PNG berikut yang disediakan sebagai aset resmi situs.

+ Download PNG →
-
- {brandAssets.map((asset) => ( -
-
- -
-

{asset.name}

-

{asset.desc}

- {asset.available ? ( - Download → - ) : ( - Segera hadir - )} -
- ))} +
+ Logo IndopenSource
@@ -91,33 +48,13 @@ const contact = [
- Color palette -

Warna brand IndopenSource.

-
-
- {colorPalette.map((color) => ( -
-
-
-

{color.name}

-

{color.hex}

-

{color.usage}

-
-
- ))} -
-
-
- -
-
-
- Fakta kunci -

Angka & informasi penting.

+ Snapshot direktori +

Angka yang dapat diperiksa.

+

Hanya menghitung repo utama yang berhasil disinkronkan; koleksi Legacy dan Revival tidak digabungkan.

-
+
{keyFacts.map((fact) => ( -
+

{fact.label}

{fact.value}

@@ -126,36 +63,15 @@ const contact = [
-
-
-
- Liputan media -

IndopenSource di berita.

-
-
- {pressCoverage.map((item) => ( - - ))} -
-
-
-
-
- Kontak media -

Hubungi tim kami.

-
+
Kontak

Hubungi kami.

- {contact.map((c) => ( + {contact.map((item) => ( ))}
diff --git a/src/pages/projects.astro b/src/pages/projects.astro index 2ce12db..296da95 100644 --- a/src/pages/projects.astro +++ b/src/pages/projects.astro @@ -2,7 +2,8 @@ import BaseLayout from '../layouts/BaseLayout.astro'; import PageHeader from '../components/PageHeader.astro'; import ProjectsDirectory from '../components/ProjectsDirectory.astro'; -import type { Project } from '../lib/projects'; +import { rankProjectOwners, type Project } from '../lib/projects'; +import { withBase } from '../lib/urls'; import projectsData from '../data/projects.json'; import legacyData from '../data/legacy.json'; import revivalData from '../data/revival.json'; @@ -27,6 +28,8 @@ const latestUpdate = refreshedProjects const latestUpdateLabel = latestUpdate ? new Intl.DateTimeFormat('id-ID', { dateStyle: 'medium' }).format(new Date(latestUpdate)) : ''; +const ownerAvatarUrl = (owner: string) => `https://avatars.githubusercontent.com/${encodeURIComponent(owner)}?size=80`; +const topUsers = rankProjectOwners(refreshedProjects).slice(0, 5); ---
+ +
+
+
+
+ Top user +

Lima penggerak teratas.

+
+ + Lihat semua + +
+ +
+
{legacyProjects.length > 0 && ( @@ -74,6 +105,7 @@ const latestUpdateLabel = latestUpdate

Resource dan proyek non-app open source Indonesia yang masih relevan sebagai referensi pembelajaran.

+ Buka repository Legacy
{legacyProjects.map((repo) => ( @@ -84,7 +116,13 @@ const latestUpdateLabel = latestUpdate rel="noopener noreferrer" >
-

{repo.name}

+
+ +
+

{repo.name}

+ oleh {repo.owner} +
+

{repo.description || 'Tidak ada deskripsi'}

@@ -109,6 +147,7 @@ const latestUpdateLabel = latestUpdate

Proyek open source Indonesia yang butuh kontribusi untuk aktif kembali.

+ Buka repository Revival
{revivalProjects.map((repo) => ( @@ -119,7 +158,13 @@ const latestUpdateLabel = latestUpdate rel="noopener noreferrer" >
-

{repo.name}

+
+ +
+

{repo.name}

+ oleh {repo.owner} +
+

{repo.description || 'Tidak ada deskripsi'}

diff --git a/src/pages/projects/[slug].astro b/src/pages/projects/[slug].astro index 55d2bcd..2a050e7 100644 --- a/src/pages/projects/[slug].astro +++ b/src/pages/projects/[slug].astro @@ -22,7 +22,10 @@ const repoName = project.fullName.split('/').at(-1) || project.fullName; // no longer fetches GitHub or parses Markdown. const readmeHtml = project.readmeHtml || ''; // Snippet-clamped description for /OG, falling back to the raw one (SEO-6). -const metaDescription = project.metaDescription || project.description; +const sourceDescription = project.metaDescription || project.description; +const metaDescription = sourceDescription === 'Proyek dari daftar awesome-indonesia.' + ? `${repoName} adalah proyek open source Indonesia yang terdaftar di direktori IndopenSource.` + : sourceDescription; const topics = project.topics ?? []; // SoftwareSourceCode structured data so search engines understand each project @@ -48,10 +51,11 @@ const jsonLd = { ---
diff --git a/src/pages/roadmap.astro b/src/pages/roadmap.astro new file mode 100644 index 0000000..18c3545 --- /dev/null +++ b/src/pages/roadmap.astro @@ -0,0 +1,38 @@ +--- +import BaseLayout from '../layouts/BaseLayout.astro'; +import BaseButton from '../components/BaseButton.astro'; +import PageHeader from '../components/PageHeader.astro'; +import { roadmapPeriods } from '../data/roadmap'; +--- + + + +

Rencana kerja publik untuk menjaga direktori, pengetahuan, dukungan proyek, dan transparansi komunitas. Urutan dapat berubah mengikuti kebutuhan dan kapasitas kontributor.

+
+ +
+
+
    + {roadmapPeriods.map((item, index) => ( +
  1. +
    + {item.period} + +
    +

    {item.title}

    +
      + {item.items.map((entry) =>
    • {entry}
    • )} +
    +
  2. + ))} +
+
+
+

Roadmap ini milik komunitas.

+

Usulkan perubahan, tawarkan bantuan, atau ceritakan kebutuhan proyekmu melalui kanal komunitas.

+
+
Buka diskusi
+
+
+
+
diff --git a/src/pages/robots.txt.ts b/src/pages/robots.txt.ts index 5be2f64..be84ffb 100644 --- a/src/pages/robots.txt.ts +++ b/src/pages/robots.txt.ts @@ -10,7 +10,16 @@ export const GET: APIRoute = ({ site }) => { const origin = site ?? new URL('https://indopensource.org'); const sitemapUrl = new URL('/sitemap.xml', origin).toString(); - return new Response(`User-agent: * + return new Response(`User-agent: OAI-SearchBot +Allow: / + +User-agent: ChatGPT-User +Allow: / + +User-agent: GPTBot +Allow: / + +User-agent: * Allow: / Sitemap: ${sitemapUrl} diff --git a/src/pages/sitemap.xml.ts b/src/pages/sitemap.xml.ts index 730929c..1368570 100644 --- a/src/pages/sitemap.xml.ts +++ b/src/pages/sitemap.xml.ts @@ -35,8 +35,23 @@ const staticPages: SitemapEntry[] = [ { path: '/' }, { path: '/falsafah/' }, { path: '/projects/' }, + { path: '/users/' }, + { path: '/belajar/' }, { path: '/blog/' }, { path: '/forum/' }, + { path: '/faq/' }, + { path: '/cara-berkontribusi/' }, + { path: '/kode-etik/' }, + { path: '/roadmap/' }, + { path: '/tentang/' }, + { path: '/komunitas/' }, + { path: '/program/' }, + { path: '/tata-kelola/' }, + { path: '/events/' }, + { path: '/donasi/' }, + { path: '/press/' }, + { path: '/legal/' }, + { path: '/security/' }, { path: '/contact/' } ]; @@ -44,7 +59,7 @@ const blogPages: SitemapEntry[] = blogPosts .filter((post) => isPublished(post)) .map((post) => ({ path: `/blog/${post.slug}/`, - lastmod: toLastmod(post.releasedAt) ?? toLastmod(post.date) + lastmod: toLastmod(post.lastModifiedAt) ?? toLastmod(post.releasedAt) ?? toLastmod(post.date) })); const projectPages: SitemapEntry[] = projects.map((project) => ({ diff --git a/src/pages/sponsors.astro b/src/pages/sponsors.astro index a55a42e..5d90dd7 100644 --- a/src/pages/sponsors.astro +++ b/src/pages/sponsors.astro @@ -1,165 +1,3 @@ --- -import BaseLayout from '../layouts/BaseLayout.astro'; -import BaseButton from '../components/BaseButton.astro'; -import PageHeader from '../components/PageHeader.astro'; -import { withBase } from '../lib/urls'; - -const tiers = [ - { - name: 'Platinum', - min: 'Rp 50.000.000+/tahun', - benefits: [ - 'Logo di homepage & footer website', - 'Sebutan di semua press release', - 'Booth di event komunitas', - 'Akses prioritas ke talent pool', - 'Co-branding di materi marketing' - ], - highlight: true - }, - { - name: 'Gold', - min: 'Rp 20.000.000/tahun', - benefits: [ - 'Logo di halaman sponsors', - 'Sebutan di newsletter bulanan', - 'Tiket VIP event komunitas', - 'Akses ke laporan tahunan' - ], - highlight: false - }, - { - name: 'Silver', - min: 'Rp 5.000.000/tahun', - benefits: [ - 'Logo di halaman sponsors', - 'Sebutan di media sosial', - 'Tiket event komunitas' - ], - highlight: false - }, - { - name: 'Supporter', - min: 'Rp 500.000/tahun', - benefits: [ - 'Nama di halaman sponsors', - 'Stiker eksklusif', - 'Badge "Supporter" di GitHub' - ], - highlight: false - } -]; - -const currentSponsors = [ - { name: 'Tokengratis.id', tier: 'Platinum', since: '2025', logo: '/brand/tokengratis.svg' }, - { name: 'IDCloudHost', tier: 'Gold', since: '2025', logo: '/brand/idcloudhost.svg' }, - { name: 'Komunitas OSS Indonesia', tier: 'Silver', since: '2025', logo: '/brand/komunitas.svg' } -]; - -const impact = [ - { label: 'Proyek terkurasi', value: '69+' }, - { label: 'Total bintang GitHub', value: '5.361' }, - { label: 'Kontributor aktif', value: '100+' }, - { label: 'Artikel & tutorial', value: '50+' } -]; +return Astro.redirect(`${import.meta.env.BASE_URL.replace(/\/?$/, '/')}donasi/`, 301); --- - - - -

IndopenSource dijalankan oleh komunitas sukarela. Sponsorship membantu kami merawat infrastruktur, mengadakan event, dan mendukung proyek-proyek potensial.

-
- -
-
- Dampak bersama -

Setiap kontribusi membantu menjaga open source Indonesia tetap hidup dan berkembang.

-
-
- -
-
-
- Dampak 2025 -

Apa yang sudah kita capai.

-
-
- {impact.map((item) => ( -
-

{item.value}

-

{item.label}

-
- ))} -
-
-
- -
-
-
- Pendukung kami -

Organisasi yang mendukung.

-

Terima kasih kepada organisasi yang telah mendukung misi IndopenSource.

-
-
- {currentSponsors.map((s) => ( -
-
-
- {s.logo ? ( - {s.name - ) : ( - - )} -
-
-

{s.name}

-

{s.tier} · Sejak {s.since}

-
-
-
- ))} -
-
-
- -
-
-
- Paket sponsorship -

Pilih tingkat dukungan Anda.

-

Semua paket memberikan akses ke laporan dampak dan apresiasi publik.

-
-
- {tiers.map((tier) => ( -
- {tier.highlight && Populer} -

{tier.name}

-

{tier.min}

-
    - {tier.benefits.map((b) => ( -
  • - - {b} -
  • - ))} -
-
- ))} -
-
-
- -
-
-
- Tertarik bermitra? -

Mari diskusikan kemitraan Anda.

-

Kami terbuka untuk paket kustom, sponsorship event, atau bentuk kolaborasi lain yang sejalan dengan misi open source.

-
- Hubungi Tim Sponsor - Media Kit -
-
-
-
-
diff --git a/src/pages/tentang.astro b/src/pages/tentang.astro index 031045b..3fa8346 100644 --- a/src/pages/tentang.astro +++ b/src/pages/tentang.astro @@ -17,5 +17,11 @@ const focus = [
Arah kerja

Open source menjadi lebih kuat ketika jejak kerjanya mudah ditemukan dan diteruskan.

-
01 / peta proyek02 / pengetahuan terbuka03 / kerja komunitas
{focus.map(([title, body], index) =>
0{index + 1}

{title}

{body}

)}
Masuk ke komunitas
+
+
+
01 / peta proyek02 / pengetahuan terbuka03 / kerja komunitas
+
{focus.map(([title, body], index) =>
0{index + 1}

{title}

{body}

)}
+
Lihat direktori komunitasLihat roadmapHubungi kami
+
+
diff --git a/src/pages/users.astro b/src/pages/users.astro new file mode 100644 index 0000000..bccb583 --- /dev/null +++ b/src/pages/users.astro @@ -0,0 +1,92 @@ +--- +import BaseLayout from '../layouts/BaseLayout.astro'; +import PageHeader from '../components/PageHeader.astro'; +import { rankProjectOwners, type Project } from '../lib/projects'; +import projectsData from '../data/projects.json'; + +const projects = (projectsData as Project[]).filter((project) => !project.syncFailed && project.owner); +const users = rankProjectOwners(projects); +const topUsers = users.slice(0, 3); +const totalStars = users.reduce((sum, user) => sum + user.stars, 0); +const formatNumber = (value: number) => value.toLocaleString('id-ID'); +--- + + + +

+ Peringkat pemilik dan organisasi berdasarkan akumulasi repo yang tercatat di direktori IndopenSource. + Angka ini bukan peringkat seluruh aktivitas GitHub. +

+
+
+ User & organisasi + {formatNumber(users.length)} +
+
+ Repo terhitung + {formatNumber(projects.length)} +
+
+ Akumulasi stars + {formatNumber(totalStars)} +
+
+
+ +
+
+
+ Podium +

Tiga teratas.

+
+ + +
+ + + + + + + + + + + + {users.map((user, index) => ( + + + + + + + + ))} + +
#User / organisasiRepoStarsForks
{index + 1} + + + {user.owner} + + {formatNumber(user.repositories)}{formatNumber(user.stars)}{formatNumber(user.forks)}
+
+

Dihitung dari snapshot data direktori terbaru · repo yang gagal disinkronkan tidak disertakan.

+
+
+
diff --git a/test/lib.test.mjs b/test/lib.test.mjs index d570eba..e24024a 100644 --- a/test/lib.test.mjs +++ b/test/lib.test.mjs @@ -12,7 +12,7 @@ import { strict as assert } from 'node:assert'; import { describe, it } from 'node:test'; -import { projectSlug } from '../src/lib/projects.ts'; +import { projectSlug, rankProjectOwners } from '../src/lib/projects.ts'; import { normalizeHref, safeHref } from '../src/lib/urls.ts'; import { isPublished, renderArticle, forbiddenTags } from '../src/lib/content.ts'; @@ -50,6 +50,22 @@ describe('projectSlug (slug)', () => { }); }); +describe('rankProjectOwners (project ranking)', () => { + it('groups case-insensitive owners and ignores failed sync records', () => { + const users = rankProjectOwners([ + { owner: 'Example', stars: 2, forks: 1, fullName: 'Example/a', description: '', url: '' }, + { owner: 'example', stars: 3, forks: 2, fullName: 'example/b', description: '', url: '' }, + { owner: 'Other', stars: 99, forks: 99, fullName: 'Other/c', description: '', url: '', syncFailed: true } + ]); + + assert.equal(users.length, 1); + assert.deepEqual( + { owner: users[0].owner, repositories: users[0].repositories, stars: users[0].stars, forks: users[0].forks }, + { owner: 'Example', repositories: 2, stars: 5, forks: 3 } + ); + }); +}); + describe('normalizeHref (urls)', () => { it('leaves absolute http(s) URLs untouched', () => { assert.equal(normalizeHref('https://example.com/x'), 'https://example.com/x');