Javis OS
Javis OS is an Agentic AI that assists entrepreneurs in business tasks, helping them grow their work.
AI translation
Community outreach command center for Illumin8 Chiropractic
Translation pending
BusinessTypeScript · CSSGitHub @chirocarterOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗A custom CRM / outreach command center for the Community Outreach Coordinator at Illumin8 Chiropractic (Albuquerque: NE Heights, Westside, Downtown).
Built to replace the ClickUp system: track restaurant drop box partnerships, lunch-and-learns, gym screening events, the outreach pipeline, leads, and outreach-attributed appointments — with fully deterministic reporting. No AI anywhere in the numbers: every metric is a readable SQL query, and every number on screen links to the exact filtered records that produced it.
npm install
npm run db:migrate # create the SQLite database (data/outreach.db)
npm run db:seed # realistic Illumin8 sample data
npm run dev # http://localhost:3000
Login: [email protected] / illumin8 (change it in Settings → Profile).
Other commands:
| Command | What it does |
|---|---|
npm run build / npm start | Production build / serve |
npm run db:reset | Wipe the database, re-migrate, re-seed |
npm run db:generate | Generate a new migration after editing src/db/schema.ts |
src/lib/auth.ts, src/middleware.ts)Set SESSION_SECRET in .env.local for production use.
| Path | Purpose |
|---|---|
src/db/schema.ts | All 16 tables (accounts, contacts, opportunities, activities, tasks, partners, campaigns, events, leads, appointments, tags, goals…) |
src/db/seed.ts | Sample data — dates are relative to "today" so the dashboard is always alive |
src/lib/taxonomy.ts | Single source of truth for every status/stage/type list. Reports reference these exact strings |
src/lib/metrics.ts | The metric engine: each metric = one SQL query + its drill-down URL. Dashboard, weekly reports, and goals all use it, so numbers always agree |
src/lib/focus.ts | "Today's Focus" scoring — plain, documented rules (due dates, stage weight, deal value, high-value verticals, event proximity) |
src/lib/lists.ts | URL-driven list queries shared by every list page and CSV export |
src/app/actions.ts | All mutations (server actions) |
src/app/(app)/… | Pages: dashboard, accounts, contacts, pipeline, calendar, activities, tasks, partners, campaigns, events, leads, appointments, reports (7), settings, search |
src/components/ActivityWizard.tsx | Log Activity funnel — one question per screen (type → business → contact → outcome → follow-up → notes), mobile-first |
src/app/(app)/calendar | Unified calendar: events, appointments, follow-up tasks, and drop box pickups (month grid on desktop, agenda on mobile) |
src/app/api/export | CSV export — runs the same query builders as the page you're looking at |
Documented here so reporting stays honest (also shown in the reports UI):
bookedAt timestamp (stamped the first time an event
reaches Booked/Confirmed) falls in range.src/lib/dates.ts; nothing else hard-codes a day.The app runs in two database modes with zero code changes:
data/outreach.db.TURSO_DATABASE_URL + TURSO_AUTH_TOKEN and it talks to
Turso (hosted SQLite) instead. This is the deploy path.libsql://…) and create an auth token.$env:TURSO_DATABASE_URL="libsql://…"; $env:TURSO_AUTH_TOKEN="…"
# --prod is required: without it this migrates the LOCAL file, never Turso.
npm run db:migrate -- --prod
# Clean start for real use — admin login + Albuquerque/clinics + goals, no demo data.
# Set ADMIN_PASSWORD first to choose the password, or read the temp one it prints.
$env:ADMIN_PASSWORD="choose-a-strong-one"; npm run db:bootstrap -- --prod
# (`npm run db:seed` is LOCAL-ONLY and refuses --prod — demo data never goes live.)
TURSO_DATABASE_URL, TURSO_AUTH_TOKEN, and
SESSION_SECRET (see .env.example for how to generate one). Deploy.Note: Vercel's free Hobby tier is licensed for non-commercial use; for a business tool the Pro plan ($20/mo) is the by-the-book choice.
Run it as one always-on container with a persistent volume mounted at
/app/data — the SQLite file just works, no env vars beyond SESSION_SECRET.
Simple and business-license-clean; slightly more setup (a Dockerfile).
Every campaign has a public sign-up page at /join/<token> (no login) and its
QR code is displayed on the campaign detail page (Download PNG to print).
Scans open a branded mobile form; submissions become Leads with source
"QR Code", attributed to the campaign, partner, and business automatically.
Create one campaign per placement (lunch-and-learn slides, website, in-office
ads) so each surface reports separately. Print QR codes from the deployed
site, not localhost — the code encodes the domain it was viewed on.
Settings → Team (admins only): add users with a name, email, temporary password, and role (Admin or Member). Everyone shares the same outreach data; admins additionally manage users and settings. New teammates change their password in Settings → Profile after first sign-in.
One convention, every script. A bare command never touches production.
| Command | Target | Notes |
|---|---|---|
npm run db:migrate | LOCAL | applies pending migrations to data/outreach.db |
npm run db:migrate -- --prod | PRODUCTION | banner names the host before anything runs |
npm run db:seed | LOCAL | demo data |
npm run db:seed -- --prod | refused | demo data must never reach the live CRM |
npm run db:bootstrap | LOCAL | admin + city + goals, only if the database is empty |
npm run db:bootstrap -- --prod | PRODUCTION | the documented first-deploy step |
npm run db:reset | LOCAL only | refuses whenever any Turso config is present |
npx tsx scripts/create-agent-user.ts --city "X" | LOCAL | dry run |
… --city "X" --apply | LOCAL | creates the identity |
… --city "X" --rotate --apply --prod | PRODUCTION | rotates that city's bearer credential |
A --prod credential operation additionally asks the live runtime before it reports
success: it writes the new hash, calls /api/agent/whoami on the deployed app with the new
credential, and if the runtime rejects it, puts the previous hash back and exits non-zero.
AGENT_KEY_SECRET exists in two places — this machine and the deployment — and nothing local
can prove they match. Vercel also snapshots environment variables at build time, so even a
correctly copied value can differ from what the running build holds until it is redeployed.
Only the runtime knows, so the runtime is asked. Override the URL with --verify-url for a
preview deployment.
Rules the shared resolver (src/db/target.ts) enforces:
--prod. --production, --live, --turso and
unknown flags are refused, never quietly treated as local.--prod with no TURSO_DATABASE_URL refuses rather than falling back to local — a
silent fallback would report success while production stayed untouched.TURSO_DATABASE_URL and TURSO_AUTH_TOKEN from the
process. src/db/index.ts picks its driver from that variable, so the hosted database is not
merely unselected, it is unreachable. That is the actual guarantee; the banner is just courtesy.Hard-won, each one having actually bitten:
Backing up the local SQLite file needs a WAL checkpoint. data/outreach.db runs in
WAL mode, so recent writes live in outreach.db-wal until they are checkpointed. A plain
cp data/outreach.db backup.db silently captures a stale database — a backup taken this
way once read 21 rows against a live 19, which looks exactly like data loss when you
compare them. Copy data/outreach.db* (all three files), or run
PRAGMA wal_checkpoint(TRUNCATE); first. The same reason .gitignore uses data/*.db*
rather than data/*.db.
drizzle-kit generate is not trusted here. The meta snapshots for migrations 0007–0012
were never created, so it diffs against 0006 and tries to replay six migrations — its output
once included DROP TABLE campaigns. Migrations 0007 onward are hand-written. Always read
generated SQL before applying it, and never run drizzle-kit push.
npm run db:migrate used to target PRODUCTION. loadEnvLocal() reads .env.turso
before .env.local, so TURSO_DATABASE_URL is set on every dev machine and the plain
command silently migrated Turso. This was written down here as a warning and the warning
still failed — a local-only migration landed on production anyway. The script now decides by
flag rather than by environment:
npm run db:migrate # LOCAL data/outreach.db, always
npm run db:migrate -- --prod # hosted Turso, printed loudly before it runs
Without --prod a configured TURSO_DATABASE_URL is ignored and said so out loud. With
--prod and no URL configured, it refuses rather than quietly migrating the local file.
The same trap existed in create-agent-user, db:seed and db:bootstrap, and all three now
use the same resolver — see the command matrix above. db:reset was already safe.
Foreign keys are enforced on BOTH drivers — but only one asks for it. better-sqlite3
defaults foreign_keys to OFF, which is why src/db/index.ts sets it; hosted Turso pins it
ON and ignores PRAGMA foreign_keys = OFF entirely (verified against the live database).
Do not assume a constraint is decorative in production. The practical consequence: deleting a
row that agent ledger entries reference will fail in production too, so anything that deletes
events or accounts has to clear first.
agent_activitiesTransactions are not reliable across the two drivers. The local better-sqlite3 handle is
cast to the libsql type, and its transaction() is synchronous — an async callback commits
before a thrown error can roll anything back (verified: a forced failure left the row). The
production libsql driver is async. Code that needs atomicity would therefore behave
differently in production than locally, so nothing here depends on it.
Timestamps are local time, not UTC. Columns default to datetime('now','localtime').
Comparing them against a cutoff built from toISOString() is wrong by the machine's offset —
on a UTC-6 host that made every agent run look six hours old the moment it was created.
Parse stored stamps as local (Date.parse(s.replace(" ", "T"))).
Unlayered CSS beats Tailwind utilities. globals.css rules written outside @layer
win over any layered utility regardless of specificity. That is why .no-scrollbar exists
as a rule rather than a utility, and why the grid min-width reset lives in @layer base.