Skip to content

GustavEkberg/abraxxas

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

220 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ἀβραξάς

Harness the powers that came before us to build what will come after.

Features

  • Mystical Board Interface - Tasks flow through six thematic columns from conception to completion
  • Autonomous Task Execution - Integration with Sprites.dev to spawn Claude Code sessions
  • AI Feedback Loop - Comment-based communication between humans and agents
  • GitHub Integration - Auto-generates feature branches and pull requests
  • Dark Occult Theme - Sleek, minimal UI with mystical aesthetics

Task Flow

Tasks progress through mystical columns:

  1. The Abyss - Backlog of tasks waiting in darkness
  2. The Altar - Tasks prepared and ready for execution
  3. The Ritual - Active execution (triggers Claude Code session)
  4. Cursed - Blocked tasks with errors
  5. The Trial - Completed tasks awaiting human review
  6. Vanquished - Successfully completed and approved

Stack

Category Technology
Framework Next.js 16 (App Router, Turbopack)
Language TypeScript 5
Functional Effect-TS
Database PostgreSQL via Drizzle ORM + @effect/sql
Auth better-auth (Email OTP, passwordless)
Email Resend
Styling Tailwind CSS 4
Telemetry Sentry + OpenTelemetry
Analytics PostHog
Testing Vitest

Getting Started

  1. Clone and rename:

    git clone <repo> my-project
    cd my-project
    rm -rf .git && git init
    git branch -M main
  2. Install dependencies:

    pnpm install
  3. Set up environment:

    cp .env.example .env.local
    cp .env.example .env.test   # For e2e tests (use a separate test database)
    File Purpose
    .env.local Development - used by Next.js, Drizzle, Vitest
    .env.test E2E tests - used by Playwright (separate test database)

    Both files are gitignored.

    Environment Variables

    Variable Required Description
    DATABASE_URL Yes PostgreSQL connection string
    DATABASE_SSL No Enable SSL for database (default: false)
    BETTER_AUTH_SECRET Yes Secret for better-auth session encryption
    NEXT_PUBLIC_PROJECT_URL Yes Public URL of the application
    APP_NAME No Application name (default: Init)
    EMAIL_SENDER Yes Email sender address for auth emails
    RESEND_API_KEY No Resend API key for sending emails
    ENCRYPTION_KEY Yes 32-byte hex key for AES-256-GCM encryption
    SPRITES_TOKEN Yes Sprites.dev API authentication token
    SPRITE_TIMEOUT_MS No Timeout for sprite operations (ms)
    OPENCODE_SETUP_REPO_URL Yes GitHub repo URL for opencode commands/skills
    AWS_ACCESS_KEY_ID No AWS credentials for S3
    AWS_SECRET_ACCESS_KEY No AWS credentials for S3
    AWS_REGION No AWS region for S3
    AWS_S3_BUCKET No S3 bucket name for file uploads
    TELEGRAM_BOT_TOKEN No Telegram bot token for notifications
    TELEGRAM_CHAT_ID No Telegram chat ID for notifications
    SENTRY_PROJECT No Sentry project slug
    SENTRY_AUTH_TOKEN No Sentry auth token for source maps
    NEXT_PUBLIC_SENTRY_DSN No Sentry DSN for error reporting
    NEXT_PUBLIC_POSTHOG_KEY No PostHog project API key for analytics

    Auto-set by Vercel: VERCEL_URL, VERCEL_BRANCH_URL

  4. Run development server:

    pnpm dev

Project Structure

lib/
├── core/                    # Core business logic (each subfolder has own errors)
│   └── post/                # Example: getPosts()
├── services/                # Infrastructure services
│   ├── auth/                # Authentication (better-auth)
│   ├── db/                  # Database (Drizzle + Effect SQL)
│   ├── email/               # Email (Resend)
│   ├── s3/                  # AWS S3 file storage
│   ├── telegram/            # Telegram notifications
│   ├── activity/            # Activity logging
│   └── telemetry/           # Error reporting & tracing
├── layers.ts                # Effect layer composition
└── next-effect/             # Next.js + Effect utilities

app/
├── (auth)/                  # Auth routes (login)
├── (dashboard)/             # Protected routes
├── api/
│   ├── auth/[...all]/       # Auth API handler
│   └── example/             # Example API route
└── page.tsx                 # Home page example

Database

Schema is defined in lib/services/db/schema.ts. Migrations are stored in lib/services/db/migrations/.

Development

Use db:push for rapid iteration - applies schema changes directly without migration files:

pnpm db:push

Production

Use db:generate to create migration files, then apply them:

pnpm db:generate  # Creates migration files from schema changes
pnpm db:push      # Applies migrations to database

Workflow

  1. Edit lib/services/db/schema.ts
  2. Run pnpm db:generate to create migration
  3. Review generated migration in lib/services/db/migrations/
  4. Run pnpm db:push to apply
  5. Commit migration files

Drizzle Studio

pnpm db:studio  # Opens GUI to browse/edit data

Patterns

Effect in Pages

async function Content() {
  await cookies()

  return await NextEffect.runPromise(
    Effect.gen(function* () {
      const posts = yield* getPosts()
      return <div>{/* render posts */}</div>
    }).pipe(
      Effect.provide(Layer.mergeAll(AppLayer)),
      Effect.scoped,
      Effect.matchEffect({
        onFailure: error =>
          Match.value(error._tag).pipe(
            Match.when('UnauthenticatedError', () => NextEffect.redirect('/login')),
            Match.orElse(() => Effect.succeed(<ErrorPage />))
          ),
        onSuccess: Effect.succeed
      })
    )
  )
}

Effect in API Routes

const handler = Effect.gen(function* () {
  const posts = yield* getPosts()
  return yield* HttpServerResponse.json({ posts })
}).pipe(
  Effect.catchAll(error =>
    Match.value(error).pipe(
      Match.tag('UnauthenticatedError', () =>
        HttpServerResponse.json({ error: 'Not authenticated' }, { status: 401 })
      ),
      Match.orElse(() =>
        HttpServerResponse.json({ error: 'Internal server error' }, { status: 500 })
      )
    )
  )
)

Creating Services

// lib/core/example/get-something.ts
export const getSomething = (id: string) =>
  Effect.gen(function* () {
    const { user } = yield* getSession()
    const db = yield* DbLive

    const result = yield* Effect.tryPromise(() =>
      db.select().from(schema.something).where(eq(schema.something.id, id))
    )

    return result
  }).pipe(Effect.withSpan('example.get-something'))

Created from

About

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors