| name | setup-instance |
| description | Use when setting up a new instance for the first time, when a user says "set me up", "get started", "configure", or "deploy" — walks through infrastructure creation, data source configuration, deployment, and visual customization interactively. |
Setup Instance — Interactive Wizard
This skill walks a new user from zero to a fully deployed, customized instance. It covers infrastructure, push notifications, data sources, deployment, prompt tuning, and visual design.
Ground Rules
- Single source of truth:
setup-run/setup-log.md (append-only). Every answer, action, and result is appended here. Never edit or delete earlier entries.
- Resumable: Every phase begins by reading
setup-run/setup-log.md. If context compacts mid-session, re-read the log to recover full state.
- Cross-platform: No Windows-only or Unix-only assumptions. Use Node/npx where possible. Quote paths. Use forward slashes.
- Interactive: Ask, confirm, then act. Never assume answers.
Phase 0: Setup Questionnaire
Goal: Gather all inputs upfront so later phases can run with minimal interruption.
- Create
setup-run/ directory at repo root.
- Create
setup-run/setup-log.md with this initial content:
# Setup Log
Started: <timestamp>
## Phase 0: Questionnaire
-
Present a welcome message:
Welcome! This wizard will set up your personal AI briefing system from scratch. I'll ask a few questions first, then handle the rest.
-
Account inventory — ask and record:
- Do you have a Cloudflare account? (yes/no)
- Are you logged into Wrangler? (
wrangler whoami — run if they say yes)
-
Tool inventory — check and record each:
- Node.js installed? (
node --version)
- Claude Code installed? (
claude --version)
- cloudflared installed? (
cloudflared --version)
- Wrangler installed? (
npx wrangler --version)
-
Data source inventory — ask:
- Which data sources do you want to connect? Options:
- Jira (project tracking)
- Fireflies (meeting transcripts)
- MS365 (email, calendar, Teams)
- Other / custom MCP server
- Record selections.
-
Domain preference — ask:
- Do you have a custom domain you want to use, or use free Cloudflare URLs?
- If custom: what domain? Which subdomains for dashboard and tunnel?
-
Resource naming — ask (or propose sensible defaults based on their
instance name / username):
D1_DATABASE_NAME (e.g. my-briefings, jdoe-briefings)
R2_BUCKET_NAME (e.g. my-exports, jdoe-exports; can be skipped if
they opt out of exports)
WORKER_NAME (e.g. my-cos-worker)
PAGES_PROJECT_NAME (e.g. my-dashboard)
TUNNEL_NAME (e.g. my-tunnel, must be unique within their Cloudflare account)
These feed into later phases — every wrangler / cloudflared command in
Phases 2+ uses the captured names. DO NOT copy the fork's template
defaults into commands verbatim; substitute the user's chosen names.
-
Summary + confirm: Present all answers in a table (including the
resource names from step 8). Ask: "Does this look right? Any changes
before we proceed?"
-
Append all answers to setup-run/setup-log.md under Phase 0 — include
an explicit ## Resource names section with each captured value so
subsequent phases can read them reliably.
Phase 1: Prerequisites Check
Goal: Install or verify every tool needed for later phases.
-
Read setup-run/setup-log.md — check Phase 0 answers for what's missing.
-
For each missing prerequisite, guide installation:
- Node.js: Direct user to https://nodejs.org or suggest
nvm/fnm.
- Claude Code:
npm install -g @anthropic-ai/claude-code
- Wrangler:
npm install -g wrangler
- cloudflared: Direct to Cloudflare's install docs for their platform.
-
If Cloudflare account = NO:
-
If Cloudflare account = YES but not logged in:
- Run
wrangler login
- Verify:
wrangler whoami
-
Re-verify all tools are available. Append results to setup-run/setup-log.md:
## Phase 1: Prerequisites
- Node.js: v<version> OK
- Claude Code: v<version> OK
- Wrangler: v<version> OK, account: <email>
- cloudflared: v<version> OK
Phase 2: Cloudflare Infrastructure
Goal: Create D1 database, configure Worker, set secrets.
-
Read setup-run/setup-log.md for current state.
-
Install project dependencies:
npm install
-
Create D1 database:
wrangler d1 create <D1_DATABASE_NAME>
Capture the database_id from output.
-
Edit server/worker/wrangler.toml:
- Set the
name field to your chosen <WORKER_NAME> (from Phase 0).
- Set the
database_name and database_id fields under [[d1_databases]] to
your <D1_DATABASE_NAME> and the captured database_id.
- R2 (optional — skip if your Cloudflare account doesn't support R2):
The
[[r2_buckets]] block is commented out by default. If you want
briefing export storage, uncomment it and set bucket_name to your
<R2_BUCKET_NAME> (from Phase 0). Verify R2 availability first:
wrangler r2 bucket list
If you get a "billing required" or "not enabled" error, leave
[[r2_buckets]] commented out — the Worker will still deploy
cleanly, exports feature just stays disabled.
-
Apply D1 migrations:
cd server/worker && wrangler d1 migrations apply <D1_DATABASE_NAME> --remote
-
Generate a random auth token:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
Save this as COS_TOKEN.
-
Set the Worker secret:
wrangler secret put COS_TOKEN
Paste the generated token when prompted.
-
Append to setup-run/setup-log.md:
## Phase 2: Cloudflare Infrastructure
- D1 database: <D1_DATABASE_NAME> (id: <database_id>)
- Migrations applied: OK
- COS_TOKEN generated: <first 8 chars>...
- Worker secret set: OK
Phase 3: Tunnel Setup
Goal: Create a cloudflared tunnel so the local API is reachable from the Worker.
-
Read setup-run/setup-log.md for domain preference from Phase 0.
-
Authenticate cloudflared:
cloudflared tunnel login
-
Create the tunnel:
cloudflared tunnel create <TUNNEL_NAME>
Capture the tunnel UUID from output.
-
If custom domain (from Phase 0):
-
If no custom domain:
-
Create server/worker/wrangler.toml from the example file:
cp server/worker/wrangler.toml.example server/worker/wrangler.toml
Then edit server/worker/wrangler.toml:
- Set
name to your Worker name
- Set
TUNNEL_URL to the resolved tunnel URL
- Set
database_id to your D1 database ID (from Phase 1)
-
Create cloudflared config file at ~/.cloudflared/config.yml:
tunnel: <tunnel-uuid>
credentials-file: <path-to-credentials-json>
ingress:
- hostname: <tunnel-hostname>
service: http://localhost:3141
- service: http_status:404
-
Append to setup-run/setup-log.md:
## Phase 3: Tunnel
- Tunnel UUID: <uuid>
- Tunnel URL: <url>
- Config written: ~/.cloudflared/config.yml
- wrangler.toml updated with TUNNEL_URL
Phase 4: Push Notifications
Goal: Configure VAPID keys so browser push works on a fresh install.
-
Read setup-run/setup-log.md for the Worker name and chosen domain.
-
Generate a VAPID keypair:
npx web-push generate-vapid-keys
Capture the publicKey and privateKey from the output.
-
Choose a VAPID subject. Use a mailto: address you control or an https:// URL for your site:
mailto:you@example.com
-
Edit server/worker/wrangler.toml and add these [vars] entries alongside TUNNEL_URL:
VAPID_PUBLIC_KEY = "<publicKey>"
VAPID_SUBJECT = "mailto:you@example.com"
-
Set the Worker secret for the private key:
cd server/worker && wrangler secret put VAPID_PRIVATE_KEY
Paste the generated private key when prompted.
-
Append to setup-run/setup-log.md:
## Phase 4: Push Notifications
- VAPID keypair generated: OK
- VAPID public key added to wrangler.toml: OK
- VAPID subject set: <mailto-or-https-subject>
- VAPID private key secret set: OK
Phase 5: MCP Server Configuration
Goal: Connect Claude Code to the user's selected data sources via MCP.
-
Read setup-run/setup-log.md for data source selections from Phase 0.
-
Read SSE endpoints from skills/setup-instance/mcp-servers.json (single
source of truth for MCP provider URLs — edit that file when a provider
rotates endpoints; don't inline URLs in this skill).
For each selected data source in Phase 0:
If the registry entry has a non-null sseUrl — hosted MCP, use mcp-remote:
claude mcp add <cliName> -- npx -y @anthropic-ai/mcp-remote@latest <sseUrl>
If the registry entry has sseUrl: null (no hosted MCP available) — follow the
manualSetup instructions on the entry. Typically this means installing a
community MCP server package locally, then registering it:
claude mcp add <cliName> -- npx -y <local-mcp-package-name>
Available entries at time of this skill version:
- Jira / Confluence (Atlassian):
atlassian → hosted https://mcp.atlassian.com/v1/sse
- Fireflies:
fireflies → hosted https://mcp.fireflies.ai/sse
- MS365:
ms365 → no hosted MCP available — community local MCP server required (see registry manualSetup field)
Verify first: before running claude mcp add <cliName>, run
claude mcp list and skip the add if the server is already present
(rerunning mcp add on an existing name errors out).
-
Each mcp add command will trigger an OAuth flow in the browser. Guide the user:
A browser window will open for authentication. Log in and authorize access, then return here.
-
Verify all servers are connected:
claude mcp list
-
Append to setup-run/setup-log.md:
## Phase 5: MCP Servers
- Configured: <list of server names>
- Verification: all servers responding
Phase 6: Environment & Deploy
Goal: Write env config, deploy Worker and frontend.
-
Read setup-run/setup-log.md for COS_TOKEN and tunnel URL.
-
Write .env at repo root:
COS_TOKEN=<token-from-phase-2>
COS_WORKER_URL=https://placeholder.workers.dev
-
Deploy the Worker:
cd server/worker && wrangler deploy
Capture the deployed Worker URL from output.
-
Update .env with the actual Worker URL:
COS_WORKER_URL=<deployed-worker-url>
-
Build the frontend:
cd app && npm install && VITE_API_URL=<worker-url> npm run build
On Windows (PowerShell), use:
cd app && npm install && $env:VITE_API_URL="<worker-url>"; npm run build
-
Deploy the frontend:
cd app && wrangler pages deploy dist --project-name <PAGES_PROJECT_NAME>
Capture the Pages URL from output.
-
Append to setup-run/setup-log.md:
## Phase 6: Deployment
- Worker URL: <worker-url>
- Frontend URL: <pages-url>
- .env written: OK
Phase 7: Validation
Goal: Verify the full stack works end-to-end.
-
Read setup-run/setup-log.md for all URLs.
-
Test Worker health:
curl -s <worker-url>/health
Expect a 200 response.
-
Start the local API server and tunnel:
- Start local API:
npx tsx server/local/server.ts
- Start tunnel:
cloudflared tunnel run <TUNNEL_NAME>
- (Guide user to run these in separate terminals, or use the start script if available.)
-
Run a test briefing:
npx tsx agent/cli.ts work --new-session
-
Verify the briefing synced to D1:
curl -s -H "Authorization: Bearer <COS_TOKEN>" <worker-url>/briefings | node -e "process.stdin.on('data',d=>console.log(JSON.parse(d).length,'briefings found'))"
-
Present results to user:
Your system is live! Here are your URLs:
-
Append to setup-run/setup-log.md:
## Phase 7: Validation
- Worker health: OK
- Test briefing generated: OK
- Briefing synced to D1: OK
- All URLs verified
Phase 8: Prompt Customization
Goal: Tailor briefing prompts to the user's work and interests. All customizations are written to the gitignored local/ directory — never modify tracked prompt files.
-
Read setup-run/setup-log.md for data source selections.
-
Create local/ and local/briefings/ directories if they don't exist:
mkdir -p local/briefings
-
Persona — ask about the user's identity and role:
- "Tell me about yourself — your role, your team, what you're responsible for."
- "What context should the AI have about you to make briefings useful?"
- Based on the conversation, write
local/persona.md — a natural-language description of who the user is, their role, responsibilities, and preferences.
- If
local/persona.md already exists: Read it, show the user what's there, and ask: "Want to update this, or keep it as-is?"
-
Work briefing focus — ask:
- Which Jira projects should be included? (project keys)
- What meeting patterns matter most? (standups, 1:1s, planning, all)
- Any email patterns to watch? (specific senders, subjects, labels)
- What should the briefing prioritize? (blockers, deadlines, decisions, status changes)
- Write the focus instructions to
local/briefings/work-focus.md.
- If file already exists: Show current content, ask before overwriting.
-
News/field briefing focus — ask:
- What topics to monitor? (AI/ML, industry-specific, competitors)
- Specific companies or products to track?
- Preferred sources? (arxiv, HN, specific blogs, newsletters)
- How technical should field briefings be? (executive summary vs deep dive)
- Write the focus instructions to
local/briefings/news-focus.md.
- If file already exists: Show current content, ask before overwriting.
-
Community briefing feeds (optional — only if the user wants a
community digest):
Community briefings pull items from RSS/GraphQL feeds the user configures.
They ship with NO default feeds — each instance selects its own from the
suggestion catalog at agent/feeds/template-sources.ts or defines custom.
Ask: "Do you want a Community briefing? It digests items from RSS / GraphQL
feeds you configure. Common use cases include tracking research papers,
industry newsletters, or niche community forums."
If no: skip this step. (The Community tab still appears in the UI
because the type is registered, but the briefing will run with zero
items until feeds are configured.)
If yes:
-
Open agent/feeds/template-sources.ts and walk through the suggestion
categories (finance, science, biotech, geopolitics, rationalism/AI,
custom RSS). Ask which categories the user wants.
-
For each selected category, show the example URL(s) from the catalog
and ask: "Use this source, pick a different URL, or skip?"
-
The user can also define fully custom feeds at this step — ask for:
id (short unique slug)
name (human-readable)
kind ("rss" or "graphql")
url (RSS URL) OR GraphQL endpoint + query
-
Write the selected feeds to local/feeds.json as an array matching the
FeedSource[] discriminated-union shape in agent/feeds/types.ts:
- RSS feeds use
feedUrl (NOT url)
- GraphQL feeds use
graphqlUrl + baseUrl + optional limit
Example covering both kinds:
[
{
"id": "nature-news",
"name": "Nature News",
"kind": "rss",
"feedUrl": "https://www.nature.com/nature.rss"
},
{
"id": "custom-feed-1",
"name": "My team's RSS",
"kind": "rss",
"feedUrl": "https://example.com/feed.xml"
},
{
"id": "example-graphql",
"name": "Example GraphQL Feed",
"kind": "graphql",
"graphqlUrl": "https://api.example.com/graphql",
"baseUrl": "https://example.com",
"limit": 20
}
]
-
If local/feeds.json already exists: show current entries, ask
whether to add/replace/keep.
-
Optional: write local/briefings/community-focus.md with any
per-topic guidance ("prioritize engineering papers", "skip op-eds",
etc.) — the community prompt consumes this via readLocalOverride.
-
General focus context (optional) — ask:
- "Is there anything that should apply to ALL briefing types? Company context, current priorities, things to always watch for?"
- If yes: write to
local/briefing-focus.md.
- If no: skip (no file created).
-
Run a test briefing to show the output:
npx tsx agent/cli.ts work --new-session
-
Ask: "How does this look? Want to adjust anything?"
-
Iterate if needed. Edit the relevant local/ file and re-run test briefings.
-
Append to setup-run/setup-log.md:
## Phase 8: Prompt Customization
- Persona written: local/persona.md
- Work focus written: local/briefings/work-focus.md
- News focus written: local/briefings/news-focus.md
- Community feeds: local/feeds.json (or "skipped")
- Community focus: local/briefings/community-focus.md (or "skipped")
- General focus: local/briefing-focus.md (or "skipped")
- Test briefing approved: yes/no
Important: All prompt customizations go in local/. NEVER modify agent/prompts/components.ts, agent/briefings/work/config.ts, or agent/briefings/news/config.ts directly. These tracked files contain defaults that are used when no local override exists.
Phase 9: Visual Customization
Goal: Let the user choose palette, layout, and icons for their dashboard. Design iteration happens inline — preview HTMLs are generated and opened in the browser. All customizations are written to the gitignored local/ directory.
-
Read setup-run/setup-log.md for current state.
-
Ask: "Want to customize the visual design, or use the defaults?"
- If defaults: skip to step 15.
-
Create setup-run/previews/round-1/ directory.
-
Palette options: Generate 3 self-contained HTML files using skills/setup-instance/templates/palette-preview.html as the template:
setup-run/previews/round-1/palette-a.html
setup-run/previews/round-1/palette-b.html
setup-run/previews/round-1/palette-c.html
Each HTML file:
- Is fully self-contained (Tailwind CDN, inline styles)
- Uses
skills/setup-instance/templates/mock-briefing.json for sample data
- Renders a realistic dashboard preview with a distinct color palette
- Can be opened directly in any browser
-
Tell the user:
I've generated 3 palette previews. Open these files in your browser:
Which direction do you prefer? (A, B, C, or describe what you want instead)
-
Based on feedback:
- If they pick one: proceed with that palette.
- If they want changes: create
setup-run/previews/round-2/ with refined options. Repeat until satisfied.
-
Layout variants: Once palette is chosen, generate 2-3 layout option HTMLs in a new round directory:
- Vary card arrangement, header style, information density.
- Same palette, different layout structures.
- User picks or requests changes.
-
Icon options: Once layout is chosen, generate icon preview using skills/setup-instance/templates/icon-canvas.html:
- Show 3-4 icon/favicon options rendered on a canvas.
- User picks one.
-
Write the chosen palette to local/theme.css as CSS @theme overrides. Only include tokens that differ from the base Warm Stone palette in app/src/index.css:
@theme {
--color-background: #f5f3ef;
--color-accent: #2563eb;
}
- If
local/theme.css already exists: Show current content, ask before overwriting.
-
Write PWA identity to local/pwa.json — only include fields that differ from defaults:
{
"name": "My Briefings",
"short_name": "Briefings",
"theme_color": "#2563eb"
}
- If
local/pwa.json already exists: Show current content, ask before overwriting.
-
Export the chosen icon as favicon and PWA icons. Save source files to local/icons/ and copy the built PNGs to app/public/. Note: icon changes in app/public/ are tracked — this is an intentional exception to the local-only rule since PWA icons must be served from the public directory.
-
Rebuild the frontend:
cd app && npm run build
-
Redeploy:
cd app && wrangler pages deploy dist --project-name <PAGES_PROJECT_NAME>
-
Verify: git status should show NO unstaged changes from the customization. Only local/ and .env should be modified (both gitignored).
-
Append to setup-run/setup-log.md:
## Phase 9: Visual Customization
- Palette: <chosen palette name or "defaults"> → local/theme.css
- PWA identity: <chosen name or "defaults"> → local/pwa.json
- Icon: <chosen icon or "defaults">
- Rebuilt and deployed: OK
- git status: clean (no tracked file changes)
Important: All visual customizations go in local/. NEVER modify app/src/index.css or app/layers/pwa/manifest.ts directly. These tracked files contain defaults that are used when no local override exists.
Phase 10: Completion
Goal: Summarize everything and leave the user with a clear picture of their system.
-
Read setup-run/setup-log.md for all details.
-
Save final config to setup-run/final/config-summary.json:
{
"timestamp": "<ISO timestamp>",
"infrastructure": {
"d1_database": "<D1_DATABASE_NAME>",
"d1_database_id": "<id>",
"worker_url": "<url>",
"frontend_url": "<url>",
"tunnel_uuid": "<uuid>",
"tunnel_url": "<url>"
},
"data_sources": ["<list of configured MCP servers>"],
"prompts": {
"work_focus": "<summary>",
"field_focus": "<summary>",
"voice": "<tone>"
},
"design": {
"palette": "<name or defaults>",
"layout": "<name or defaults>",
"icon": "<name or defaults>"
}
}
-
Present the final summary:
Setup complete! Here's your system:
URLs
Data Sources:
Briefing Focus: <work + field summaries>
Design: <palette + layout + icon>
Next steps:
- Run
npx tsx agent/cli.ts work --new-session for a work briefing
- Run
npx tsx agent/cli.ts news --new-session for a news briefing
- Visit your dashboard to see results
- Set up cron jobs for automated briefing generation
-
Append completion to setup-run/setup-log.md:
## Phase 10: Completion
- Config summary saved: setup-run/final/config-summary.json
- Completed: <timestamp>