Javis OS
Javis OS는 기업가를 돕는 에이전시 AI로, 업무를 지원하고 함께 성장합니다
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Briefs, rounds and delivery for the Neuroid creative studio — one place for every brand and every round. Strategists raise tickets, designers work them on a board, and the time each piece takes is measured from that work rather than from anything anyone has to fill in.
Formats tracked: Video · Static · Carousel · GIF.
Designers push back on time tracking when it feels like surveillance, and they're usually right to — a stopwatch you have to remember is both annoying and inaccurate. So there isn't one.
Deliverables live in Frame.io, not in this tool. Each ticket carries a review link instead of the files themselves — the designer pastes it when they submit, and it's the first thing on the ticket for both sides. Submitting for review prompts for the link if it isn't set yet, so the strategist gets a ticket they can act on rather than one they have to chase.
The small Working files panel further down is for the odd source file, font or reference that should sit with the ticket. It isn't where finished work goes.
| Admin | Operator | Strategist | Designer | |
|---|---|---|---|---|
| Raise / edit tickets | ✅ | — | ✅ | — |
| Approve, request revisions, send to client | ✅ | — | ✅ | — |
| Work tickets, submit for review | ✅ | — | ✅ | ✅ |
| Team analytics (volume, on-time, first-pass) | ✅ | ✅ | ✅ | — |
| Per-designer timing and scorecards | ✅ | ✅ | — | own only |
| Nightly scorecard email | ✅ | ✅ | — | — |
| Invite people, set roles and benchmarks | ✅ | — | — | — |
| Block bandwidth, write call sheets | ✅ | ✅ | with shoot ops | — |
Operator reads the numbers without running the board — analytics, scorecards and timing, plus the nightly digest, but no ability to move work through the pipeline. They also keep Brands and Team tidy, with one line they can't cross: an admin's role, an admin's access, and handing out shoot ops all stay with admins, enforced in the database rather than by a hidden button.
Shoot ops is a flag, not a role. An admin can give it to a strategist who effectively runs production, and it unlocks exactly two things — the Shoot section, and the ability to block a designer's day.
Timing is management information, not a scoreboard, and the visibility rules say so:
Enforced in row-level security as well as in the UI, so it holds over a direct database connection too.
A Break button sits in the sidebar for every designer. Taking one stops the clock, blanks the screen, and returns them to the same ticket where they left off — the total continues rather than restarting. The heartbeat is refused server-side while a break is open, so a tab left running in the background can't quietly restart it either.
Lunch simply isn't in the numbers. Nobody should have to choose between taking a proper break and looking slow.
Enforced by Postgres row-level security and by triggers on the tickets table — a designer can't edit a brief or approve their own work even with a direct database connection.
At supabase.com, create a project and note the URL and keys from Project Settings → API.
Paste the files in supabase/migrations/ into the SQL Editor and run them
in filename order, one at a time. 0001_init.sql creates every table, the
RLS policies, the time-tracking triggers and the reporting functions; each later
file adds what its name says. All of them are safe to run more than once, so
re-running one you're unsure about costs nothing. Optionally run
supabase/seed.sql for a few starter brands.
Two notes on the order:
0002_review_links.sql is only needed if you ran an earlier copy of 0001
before review links existed. A fresh project gets everything from 0001
alone, and it's safe either way.0009 is split into STEP-1 and STEP-2 because Postgres won't let a new
enum value be used in the same transaction that adds it. Run STEP-1, then
STEP-2, as two separate statements.In the SQL Editor:
alter database postgres set app.bootstrap_admin_email = '[email protected]';
That address becomes an admin on first sign-in. (If the instance is completely empty, whoever signs in first becomes the admin anyway — the setting just makes it deterministic.) Everyone after that is invited from the Team page and lands on the role you picked for them.
Authentication → Providers → Email: enable it, and turn Confirm email on so the six-digit code gets sent. Under Authentication → URL Configuration add your deployed domain to the redirect allow-list.
Copy .env.example to .env.local and fill it in:
cp .env.example .env.local
npm install
npm run dev
Optional, and worth doing before the team settles in.
studio.neuroidmedia.com.neuroidmedia.com is hosted → a CNAME record,
name studio, value cname.vercel-dns.com. Vercel issues the certificate
once it resolves.https://studio.neuroidmedia.com and add https://studio.neuroidmedia.com/**
to Redirect URLs. Sign-in links are built from these, so a stale Site
URL sends everyone back to the old address.CANONICAL_HOST=studio.neuroidmedia.com,
then redeploy.https://<project-ref>.supabase.co/auth/v1/callback) — Google redirects to
Supabase, not to the app. Add the new domain under Authorized JavaScript
origins if you like; don't put it in redirect URIs in place of Supabase's.Everyone signs in again afterwards, and everyone re-grants desktop alerts and
re-installs the app: cookies, notification permission, the service worker and
localStorage are all scoped to the origin and none of it follows the domain.
CANONICAL_HOST is what stops half the team drifting back to the old address
and quietly running on a second set of all of it.
Push to a Vercel project and set the same four variables in Settings →
Environment Variables. vercel.json already registers the two cron jobs:
| Job | Schedule | What it does |
|---|---|---|
/api/cron/sweep | every 15 min | Ends sessions whose tab went quiet, rewound to the last heartbeat |
/api/cron/scorecards | 19:30 UTC daily | Writes the previous day's scorecard for every designer |
Both are protected by CRON_SECRET. Move the scorecard schedule to whatever
lands after your team's working day — it rolls up each designer against their
local day, so a team spread across timezones still gets correct cards.
To regenerate a specific day by hand:
curl -H "Authorization: Bearer $CRON_SECRET" \
"https://your-app.vercel.app/api/cron/scorecards?day=2026-08-19"
Re-running a day overwrites rather than duplicates, so a retry is always safe.
The complaint that started this was lag, so the architecture is shaped around round trips:
UPDATE
with a midpoint instead of renumbering every card.src/
app/
(app)/ board · my-day · tickets/[id] · analytics · scorecards · team · brands
api/cron/ idle sweep + nightly scorecard roll-up
login/ six-digit email code
components/
board/ kanban, cards, ticket composer
ticket/ detail view, live clock, revisions, comments, attachments
analytics/ team charts + per-designer table
scorecards/ daily cards, live for today
charts/ hand-built bar and trend charts
ui/ primitives shared across the app
lib/ types, formatting, query helpers, Supabase clients
supabase/migrations/ the whole schema, RLS and triggers in one file
New Request → In Progress → Ready for Approval → Sent to Client → Approved
↑ │ │
└── Needs Edit ───┴──────────────────┘
└── Size Changes ─┘
Side states, none of which run the clock:
Awaiting Assets · On Hold
In Progress is the only status where the clock runs. Ready for Approval stops it; Needs Edit sends the work back, and the next In Progress adds to the same running total rather than starting a fresh one.
Needs Edit increments the revision counter and clears any sign-off — that's what makes the first-pass approval rate worth reading. Size Changes deliberately does not: a resize request isn't the designer missing the brief, and folding it in would make that number meaningless.
A designer may hold several tickets In Progress at once — that's what a day's plan looks like. Only one of them has a running clock; starting a second stops the first's timer and leaves its status alone.
Every work session is stamped with what kind of work it was, so a ticket's total breaks down rather than sitting as one opaque number:
| Phase | When it applies |
|---|---|
| Original build | From New Request through the first submission |
| Revisions | Everything after a Needs Edit |
| Size changes | Everything after a Size Changes request |
The phase lives on the ticket rather than being inferred from the previous status, so a designer who pauses mid-revision and resumes tomorrow is still doing revision work.
This is what makes "how long does a static take us" answerable: the original build is the number to benchmark, and revisions and resizes are separate costs worth seeing on their own.
Every submission creates a new version — V1, V2, V3 — rather than overwriting one link. Marking a ticket Ready for Approval always asks for the Frame.io link and records it against the round it belongs to, so a strategist can open V1 next to V3 and see whether the notes landed.
Written by database triggers, not by the browser, so a ping can't be lost to a dropped request or a tab closed at the wrong moment. Delivered live to the bell in the sidebar.
| Event | Who hears |
|---|---|
| Ready for Approval | Whoever raised the brief |
| Needs Edit | The designer |
| Size Changes | The designer |
| Approved / Sent to Client | The designer |
| Assigned | The new assignee |
| Somebody booked out for a shoot | Every strategist and admin, and the person themselves |
| An estimate asked for | The designer on the ticket |
| An estimate given or moved | Whoever raised the brief, and whoever asked |
The shoot ping is written once per marking, not once per day blocked: a three-day shoot is one fact, and three identical pings is how a bell gets ignored.
Anything somebody is waiting on also arrives as a desktop notification, through a service worker, so it reaches a tab that isn't in front. They are opt-in per person and per browser — everyone turns them on once in Profile → Desktop alerts, and again on any other machine they use. The same card checks every link in the chain and names the one that's broken when nothing appears.
Desktop notifications are captioned by the browser with wherever the app is served from, which no API can change. Installing it (Profile → Desktop alerts → Install, or the install icon in the address bar) captions them Creative Creative Studio instead and gives it the Neuroid mark and its own window. A custom domain fixes the caption for anyone who'd rather stay in a tab.
Every brief carries a due date: the promise made when it was raised. It's set by the strategist and it doesn't move.
Some briefs also carry an estimate — the designer's own read on when the thing will actually be ready. It moves as often as reality does. The distance between the two is the entire point: nobody needs a timesheet, they need to know the Thursday promise became Monday while there's still time to tell the client.
It is deliberately not a field on every ticket. A strategist chasing one particular brief presses Ask for an ETA on it; the designer gets a notification and a row at the top of My Day with four one-click answers — end of today, end of tomorrow, in two days, end of the week — and can pick any other date instead. Answering clears the question. Moving it later tells the same people again, with a reason if the designer gave one.
The reason is offered and never required. Mandatory reasons train people to type "busy"; optional ones get filled in when there's something real to say, and "waiting on product shots" three briefs running is the sort of fact worth having.
Every answer is kept. A date that has moved three times is its own warning, and the reasons stack up over a quarter into a straight answer about where the weeks actually go.
The estimate never touches the due date. Only the designer on the ticket (or an admin) can set one; only staff can ask for one. Both halves are enforced in the database.
Three emails, and only one of them is sent by this app.
| Sent by | Branding lives in | |
|---|---|---|
| Daily scorecard | This app, via Resend | src/lib/scorecard-email.ts |
| Call sheet | This app, via Resend | src/lib/call-sheet-email.ts |
| Sign-in code | Supabase Auth | Supabase dashboard template |
| Invite | Supabase Auth | Supabase dashboard template |
The two Supabase ones are rendered from templates stored in Supabase, so the
codebase can't brand them. supabase/emails/ holds what should be pasted in,
along with where each one goes and where the sender name is set — the sender
name comes from the SMTP settings, not the template, and it's the part people
actually read in an inbox.
Several of the designers shoot as well, and a day on set is a day gone. The Shoot section holds both halves of that:
Bandwidth. Mark a whole day or a slice of one. Those hours come out of the Workload grid immediately, and a strategist briefing that person for that day gets warned before they raise it — a warning, not a wall, because shoots move and a tool that refuses the brief outright just gets worked around in Slack.
The bar at the top. From 24 hours before a shoot starts until the moment it ends, everyone who raises briefs sees a line naming who's out and when. A notification is a moment; this is a state, and by the Tuesday it matters the notification is four hundred rows down a bell nobody opens.
Call sheets. Locations, scripts by day, crew and reporting times, actors, and which meals are provided. Plus the run-up checklist — the same three phases every time, ticked live on the day. It saves as you type.
The sheet is Neuroid stationery: the mark, the brand rule, the shoot's name in the display face and DM Sans throughout, on one A4 page with none of the app around it. Screen and paper are the same artifact deliberately — two renderings of one document is two things to keep in step, and the one nobody looks at is the one that goes to the crew.
Send emails the whole sheet to everyone at Neuroid plus any crew with an address against their name — the freelancers and client-side people who don't have an account. It lists them by name before anything goes, because this is the one action here that leaves the building and can't be taken back. The email carries the whole sheet rather than a link to something half of them can't sign into, and who it actually reached is kept on the shoot so nobody sends it three times on the morning of.
A brief raised after midday can't be given the same day's due date. The designer's day is planned in the morning, so a same-day deadline set at 3pm is a promise the floor can't keep. The date picker won't offer today, and the database refuses it as well. Both judge it on the studio's clock rather than the viewer's, so the form and the database always agree.
It is also, occasionally, wrong — a client escalates, an ad set is live on a claim legal wants down. Until there was a way through, the only way through was to lie about the due date, which is worse than the rule it dodges: the number stops meaning anything for everybody.
So a strategist can tick this can't wait, which unlocks today and asks for a sentence saying why. What happens next is the whole design:
An admin's own escalation is approved as they raise it. Making the person who carries the studio wait for a second signature on their own call is ceremony, not control.
A decision, once made, stands — a declined rush can't be quietly flipped later by whoever wanted it. And because the reason and the decision are both kept on the ticket, it stays possible to look back at how often the rule got broken and on whose say-so. That's the number that matters: an exception nobody counts becomes the process.
| View | What it's for |
|---|---|
| List (default) | Grouped table with coloured status cells, edit status and assignee inline |
| Board | Kanban across all nine statuses, drag to move |
| Today | Who has picked up what today, grouped by designer — the answer to "what are you working on?" without asking |
Filters (search, status, brand, format, designer, who raised it, and a date period over raised/due/approved) live in the URL, so a filtered screen can be pasted to a colleague and open the same way. Save any combination as a named view, private or shared with the team.