Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: ci

on:
push:
branches: [main]
pull_request:

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4
with:
version: 9

- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm

# frozen-lockfile so CI fails if pnpm-lock.yaml is stale
- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Typecheck
run: pnpm exec tsc --noEmit

# next build also runs eslint internally
- name: Build
run: pnpm build
20 changes: 17 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
# Argus
<p align="center">
<img src="./public/argus-mark.svg" width="80" alt="argus" />
</p>

A local web app that orchestrates Claude Code agents from a real UI.
<h1 align="center">argus</h1>

Workspaces, persistent agent personas, pinned skills, live output streams — all wrapping the `claude` CLI you already have. Uses your existing Max plan, no Anthropic API key required.
<p align="center">
A local web app that orchestrates Claude Code agents from a real UI.
</p>

<p align="center">
<a href="https://github.com/proofmancer/argus/blob/main/LICENSE">
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License" />
</a>
</p>

---

Workspaces, persistent agent personas, pinned skills, live output streams, all wrapping the `claude` CLI you already have. Uses your existing Max plan, no Anthropic API key required.

Named after Argus Panoptes, the many-eyed giant who watched everything.

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"db:studio": "drizzle-kit studio"
},
"pnpm": {
"onlyBuiltDependencies": ["better-sqlite3", "esbuild"]
"onlyBuiltDependencies": ["better-sqlite3", "esbuild"],
"ignoredBuiltDependencies": ["sharp", "unrs-resolver"]
},
"dependencies": {
"better-sqlite3": "^12.10.0",
Expand Down
3 changes: 0 additions & 3 deletions pnpm-workspace.yaml

This file was deleted.

8 changes: 8 additions & 0 deletions public/argus-mark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
17 changes: 5 additions & 12 deletions src/app/globals.css
Original file line number Diff line number Diff line change
@@ -1,26 +1,19 @@
@import "tailwindcss";

:root {
--background: #ffffff;
--foreground: #171717;
--background: #0a0a0a;
--foreground: #ededed;
}

@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
}

@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
}
--font-sans: var(--font-stack-sans), ui-sans-serif, system-ui, sans-serif;
--font-mono: var(--font-geist-mono), ui-monospace, SFMono-Regular, monospace;
}

body {
background: var(--background);
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
font-family: var(--font-stack-sans), ui-sans-serif, system-ui, sans-serif;
}
39 changes: 17 additions & 22 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import type { Metadata } from 'next'
import { Geist, Geist_Mono } from 'next/font/google'
import { Geist_Mono } from 'next/font/google'
import localFont from 'next/font/local'
import Link from 'next/link'
import { ArgusMark } from '@/components/ArgusMark'
import './globals.css'

const geistSans = Geist({
variable: '--font-geist-sans',
subsets: ['latin'],

// Stack Sans Notch is the brand display + UI typeface. Variable font
// covers the full 200..700 weight range from one file, so we register
// it once and let Tailwind / CSS pick the weight per element.
const stackSans = localFont({
src: './fonts/StackSansNotch-VariableFont_wght.ttf',
variable: '--font-stack-sans',
display: 'swap',
weight: '200 700',
})

const geistMono = Geist_Mono({
Expand All @@ -14,7 +22,7 @@ const geistMono = Geist_Mono({
})

export const metadata: Metadata = {
title: 'Argus · Claude Code workspace',
title: 'argus · claude code workspace',
description:
'Local desktop-class workspace for orchestrating Claude Code agents, pinning skills, and watching what they do.',
}
Expand All @@ -27,31 +35,18 @@ export default function RootLayout({
return (
<html
lang="en"
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
className={`${stackSans.variable} ${geistMono.variable} h-full antialiased`}
>
<body className="min-h-full bg-neutral-950 text-neutral-100 flex flex-col">
<header className="border-b border-neutral-800 bg-neutral-950/80 backdrop-blur">
<div className="mx-auto flex max-w-6xl items-center justify-between gap-4 px-6 py-3">
<Link
href="/"
className="flex items-center gap-2 text-sm font-semibold"
className="flex items-center gap-2.5 text-sm font-semibold lowercase"
>
<span className="inline-block h-2 w-2 rounded-full bg-amber-500" />
<span className="tracking-tight">Argus</span>
<span className="text-xs font-normal text-neutral-500">
claude code workspace
</span>
<ArgusMark className="h-5 w-5 text-amber-500" />
<span className="tracking-tight">argus</span>
</Link>
<nav className="flex items-center gap-4 text-xs text-neutral-400">
<a
href="https://github.com/anthropics/claude-code"
target="_blank"
rel="noopener noreferrer"
className="hover:text-neutral-200"
>
claude-code docs
</a>
</nav>
</div>
</header>
<main className="mx-auto flex w-full max-w-6xl flex-1 flex-col px-6 py-8">
Expand Down
29 changes: 29 additions & 0 deletions src/components/ArgusMark.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Inline SVG of the Argus mark.
*
* Inlined (not loaded as a static asset) so it inherits text color via
* `currentColor` and the parent element can size it via `width` /
* `height` className utilities. Use it anywhere a logo glyph is needed.
*/
export function ArgusMark({
className = 'h-5 w-5',
}: {
className?: string
}) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 107 107"
fill="currentColor"
className={className}
aria-hidden
>
<path d="M98.1089 98.1089H107V107H98.1089V98.1089Z" />
<path d="M80.3333 89.2241H89.224V98.1091H80.3333V89.2241Z" />
<path d="M89.2241 80.3333H98.1101V89.224H89.2241V80.3333Z" />
<path d="M49.224 89.2242C27.1307 89.2242 9.224 71.3122 9.224 49.2242C9.224 27.1362 27.1307 9.2242 49.224 9.2242C71.3174 9.2242 89.224 27.1308 89.224 49.2242V71.4482H98.109V49.2242C98.109 22.2188 76.2242 0.333496 49.2242 0.333496C22.2242 0.333496 0.333496 22.2188 0.333496 49.2242C0.333496 76.2242 22.2188 98.1152 49.2242 98.1152H71.4482V89.2292L49.224 89.2242Z" />
<path d="M49.2241 18.1094C32.0414 18.1094 18.1147 32.0361 18.1147 49.2187C18.1147 66.4067 32.0361 80.3334 49.2241 80.3334H80.3334V49.2241C80.3334 32.0361 66.4067 18.1094 49.2241 18.1094ZM71.4428 71.4427H49.2241C36.9481 71.4427 27.0001 61.4949 27.0001 49.2241C27.0001 36.9481 36.9479 27.0001 49.2241 27.0001C61.5002 27.0001 71.4481 36.9479 71.4481 49.2241V71.4427H71.4428Z" />
<path d="M62.5573 49.224C62.5573 41.8594 56.5885 35.8906 49.224 35.8906C41.8594 35.8906 35.8906 41.8594 35.8906 49.224C35.8906 56.5885 41.8594 62.5573 49.224 62.5573H62.5573V49.224Z" />
</svg>
)
}
Loading