fix: convert antfarm from broken submodule to regular directory

Fixes Gitea 500 error caused by invalid submodule reference.
Converted antfarm from pseudo-submodule (missing .gitmodules) to
regular directory with all source files.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Echo
2026-02-11 16:03:37 +00:00
parent 43f441c8ae
commit dc64d18224
102 changed files with 9049 additions and 1 deletions

View File

@@ -0,0 +1,48 @@
import { readFileSync, existsSync } from 'fs';
import { resolve, dirname } from 'path';
import { fileURLToPath } from 'url';
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
const __dirname = dirname(fileURLToPath(import.meta.url));
const landingDir = resolve(__dirname, '..');
describe('Landing page', () => {
it('index.html exists', () => {
assert.ok(existsSync(resolve(landingDir, 'index.html')));
});
it('style.css exists', () => {
assert.ok(existsSync(resolve(landingDir, 'style.css')));
});
it('index.html contains required sections', () => {
const html = readFileSync(resolve(landingDir, 'index.html'), 'utf-8');
assert.ok(html.includes('id="features"'), 'missing features section');
assert.ok(html.includes('id="quickstart"'), 'missing quickstart section');
assert.ok(html.includes('id="commands"'), 'missing commands section');
assert.ok(html.includes('<title>'), 'missing title');
assert.ok(html.includes('meta name="viewport"'), 'missing viewport meta');
assert.ok(html.includes('meta name="description"'), 'missing description meta');
});
it('index.html references style.css', () => {
const html = readFileSync(resolve(landingDir, 'index.html'), 'utf-8');
assert.ok(html.includes('style.css'));
});
it('style.css contains essential rules', () => {
const css = readFileSync(resolve(landingDir, 'style.css'), 'utf-8');
assert.ok(css.includes('.hero'), 'missing hero styles');
assert.ok(css.includes('.feature-grid'), 'missing feature grid');
assert.ok(css.includes('@media'), 'missing responsive styles');
});
it('all internal links have valid targets', () => {
const html = readFileSync(resolve(landingDir, 'index.html'), 'utf-8');
const anchors = [...html.matchAll(/href="#([^"]+)"/g)].map(m => m[1]);
for (const id of anchors) {
assert.ok(html.includes(`id="${id}"`), `missing target for #${id}`);
}
});
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 MiB

View File

@@ -0,0 +1,278 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Antfarm Dashboard</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Geist+Mono&display=swap" rel="stylesheet">
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:'Inter',-apple-system,BlinkMacSystemFont,sans-serif;background:#FAF8F5;color:#3A3226;min-height:100vh}
header{background:#6B7F3B;border-bottom:2px solid #5a6b32;padding:12px 24px;display:flex;align-items:center;gap:16px;flex-wrap:wrap}
header h1{font-family:'Inter',sans-serif;font-size:22px;font-weight:600;color:#fff;letter-spacing:0}
header h1 span{color:#D4E8A0}
select{background:#5a6b32;color:#fff;border:1px solid #4a5a28;border-radius:6px;padding:6px 12px;font-size:14px;cursor:pointer}
select:focus{outline:none;border-color:#8ECFC0}
.board{display:flex;gap:16px;padding:24px;overflow-x:auto;min-height:calc(100vh - 65px)}
.column{min-width:220px;flex:1;background:#fff;border:none;border-radius:8px;display:flex;flex-direction:column;box-shadow:0 2px 8px rgba(58,50,38,.1)}
.column-header{padding:12px 16px;border-bottom:1px solid #eee;font-size:13px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:#6B7F3B;background:#f5f0e8;border-radius:8px 8px 0 0}
.column-header .count{background:#6B7F3B;color:#fff;border-radius:10px;padding:1px 8px;font-size:11px;margin-left:8px}
.cards{padding:8px;flex:1;display:flex;flex-direction:column;gap:8px;overflow-y:auto}
.card{background:#FAF8F5;border:1px solid #D4C4A0;border-radius:6px;padding:12px;cursor:pointer;transition:border-color .15s,box-shadow .15s}
.card:hover{border-color:#E8845C;box-shadow:0 2px 8px rgba(232,132,92,.15)}
.card.done{border-left:3px solid #6B7F3B}
.card.failed{border-left:3px solid #E8845C}
.card-title{font-size:13px;font-weight:500;color:#3A3226;margin-bottom:6px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.card-meta{font-size:11px;color:#8b8072;display:flex;justify-content:space-between;align-items:center}
.badge{font-size:10px;font-weight:600;padding:2px 6px;border-radius:4px;text-transform:uppercase}
.badge-running{background:#8ECFC033;color:#3a9e8a}
.badge-done{background:#6B7F3B22;color:#6B7F3B}
.badge-failed{background:#E8845C22;color:#d4603a}
.badge-pending{background:#D4C4A044;color:#8b8072}
.empty{color:#8b8072;font-size:12px;text-align:center;padding:24px 8px}
.refresh-note{color:rgba(255,255,255,.6);font-size:11px;margin-left:auto}
.overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(58,50,38,.5);z-index:100;display:flex;align-items:center;justify-content:center;opacity:1;pointer-events:auto}
.panel{background:#fff;border:1px solid #D4C4A0;border-radius:12px;width:90%;max-width:640px;max-height:85vh;overflow-y:auto;padding:24px;position:relative;box-shadow:0 8px 32px rgba(58,50,38,.15)}
.panel-close{position:absolute;top:12px;right:16px;background:none;border:none;color:#8b8072;font-size:20px;cursor:pointer;padding:4px 8px;border-radius:4px}
.panel-close:hover{color:#3A3226;background:#f5f0e8}
.panel h2{font-size:16px;font-weight:600;color:#3A3226;margin-bottom:4px;padding-right:40px}
.panel-task{font-size:13px;color:#8b8072;margin-bottom:16px;line-height:1.5}
.panel-meta{display:flex;gap:12px;margin-bottom:20px;font-size:12px;color:#8b8072;flex-wrap:wrap}
.panel-meta span{display:flex;align-items:center;gap:4px}
.steps-list{display:flex;flex-direction:column;gap:8px;margin-bottom:24px}
.step-row{display:flex;align-items:center;gap:12px;padding:10px 12px;background:#FAF8F5;border:1px solid #D4C4A0;border-radius:6px}
.step-icon{width:24px;height:24px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:12px;flex-shrink:0}
.step-icon.done{background:#6B7F3B22;color:#6B7F3B}
.step-icon.running{background:#8ECFC033;color:#3a9e8a}
.step-icon.pending{background:#D4C4A044;color:#8b8072}
.step-name{font-size:13px;font-weight:500;color:#3A3226;flex:1}
.step-agent{font-size:11px;color:#8b8072;font-family:'Geist Mono',monospace}
.step-status{font-size:11px;text-transform:uppercase;font-weight:600}
.stories-section{border-top:1px solid #D4C4A0;padding-top:20px}
.stories-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}
.stories-header h3{font-size:15px;font-weight:600;color:#3A3226}
.stories-header span{font-size:13px;font-weight:600;color:#6B7F3B}
.progress-bar{background:#D4C4A044;border-radius:4px;height:8px;margin-bottom:16px;overflow:hidden}
.progress-fill{background:#6B7F3B;height:100%;border-radius:4px}
.story-row{display:flex;align-items:center;gap:8px;padding:10px 12px;background:#FAF8F5;border:1px solid #D4C4A0;border-radius:6px;margin-bottom:6px}
.story-name{font-size:13px;font-weight:500;color:#3A3226;flex:1}
</style>
</head>
<body>
<header>
<h1><span>antfarm</span> dashboard</h1>
<select><option>feature-dev</option></select>
<span class="refresh-note">Auto-refresh: 30s</span>
</header>
<div class="board">
<div class="column">
<div class="column-header">plan<span class="count">3</span></div>
<div class="cards">
<div class="card done"><div class="card-title">Add user authentication with OAuth</div><div class="card-meta"><span class="badge badge-done">done</span><span>Feb 9, 10:02 AM</span></div></div>
<div class="card done"><div class="card-title">Refactor payment processing pipeline</div><div class="card-meta"><span class="badge badge-done">done</span><span>Feb 9, 9:45 AM</span></div></div>
<div class="card done"><div class="card-title">Add webhook retry with exponential backoff</div><div class="card-meta"><span class="badge badge-done">done</span><span>Feb 9, 8:30 AM</span></div></div>
</div>
</div>
<div class="column">
<div class="column-header">setup<span class="count">2</span></div>
<div class="cards">
<div class="card done"><div class="card-title">Add user authentication with OAuth</div><div class="card-meta"><span class="badge badge-done">done</span><span>Feb 9, 10:14 AM</span></div></div>
<div class="card done"><div class="card-title">Refactor payment processing pipeline</div><div class="card-meta"><span class="badge badge-done">done</span><span>Feb 9, 9:58 AM</span></div></div>
</div>
</div>
<div class="column">
<div class="column-header">implement<span class="count">3</span></div>
<div class="cards">
<div class="card"><div class="card-title">Add user authentication with OAuth</div><div class="card-meta"><span class="badge badge-running">running</span><span>Stories: 5/7</span></div></div>
<div class="card"><div class="card-title">Refactor payment processing pipeline</div><div class="card-meta"><span class="badge badge-running">running</span><span>Stories: 2/5</span></div></div>
<div class="card"><div class="card-title">Add webhook retry with exponential backoff</div><div class="card-meta"><span class="badge badge-pending">pending</span><span>Feb 9, 8:31 AM</span></div></div>
</div>
</div>
<div class="column">
<div class="column-header">verify<span class="count">2</span></div>
<div class="cards">
<div class="card"><div class="card-title">Migrate database to connection pooling</div><div class="card-meta"><span class="badge badge-running">running</span><span>Feb 9, 9:22 AM</span></div></div>
<div class="card"><div class="card-title">Add rate limiting to public API endpoints</div><div class="card-meta"><span class="badge badge-pending">pending</span><span>Feb 9, 9:10 AM</span></div></div>
</div>
</div>
<div class="column">
<div class="column-header">test<span class="count">2</span></div>
<div class="cards">
<div class="card"><div class="card-title">Add real-time notifications via WebSockets</div><div class="card-meta"><span class="badge badge-running">running</span><span>Feb 9, 8:55 AM</span></div></div>
<div class="card failed"><div class="card-title">Implement team invitation flow</div><div class="card-meta"><span class="badge badge-failed">failed</span><span>Retry 2/3</span></div></div>
</div>
</div>
<div class="column">
<div class="column-header">PR<span class="count">2</span></div>
<div class="cards">
<div class="card"><div class="card-title">Add CSV export for billing reports</div><div class="card-meta"><span class="badge badge-running">running</span><span>Feb 9, 8:40 AM</span></div></div>
<div class="card done"><div class="card-title">Add audit logging for admin actions</div><div class="card-meta"><span class="badge badge-done">done</span><span>Feb 9, 7:15 AM</span></div></div>
</div>
</div>
<div class="column">
<div class="column-header">review<span class="count">3</span></div>
<div class="cards">
<div class="card"><div class="card-title">Add RBAC with role hierarchy</div><div class="card-meta"><span class="badge badge-running">running</span><span>Feb 9, 7:50 AM</span></div></div>
<div class="card done"><div class="card-title">Implement SSO with SAML 2.0</div><div class="card-meta"><span class="badge badge-done">done</span><span>Feb 9, 6:30 AM</span></div></div>
<div class="card done"><div class="card-title">Add dark mode support</div><div class="card-meta"><span class="badge badge-done">done</span><span>Feb 8, 11:45 PM</span></div></div>
</div>
</div>
</div>
<!-- Detail panel overlay -->
<div class="overlay">
<div class="panel">
<button class="panel-close"></button>
<h2>feature-dev</h2>
<div class="panel-task">Add user authentication with OAuth</div>
<div class="panel-meta">
<span><span class="badge badge-running">running</span></span>
<span>Created: Feb 9, 9:58 AM</span>
<span>Updated: Feb 9, 10:41 AM</span>
</div>
<div class="steps-list">
<div class="step-row">
<div class="step-icon done"></div>
<div class="step-name">plan</div>
<div class="step-agent">planner</div>
<div class="step-status"><span class="badge badge-done">done</span></div>
</div>
<div class="step-row">
<div class="step-icon done"></div>
<div class="step-name">setup</div>
<div class="step-agent">setup</div>
<div class="step-status"><span class="badge badge-done">done</span></div>
</div>
<div class="step-row">
<div class="step-icon running"></div>
<div class="step-name">implement</div>
<div class="step-agent">developer</div>
<div class="step-status"><span class="badge badge-running">running</span></div>
</div>
<div class="step-row">
<div class="step-icon pending"></div>
<div class="step-name">verify</div>
<div class="step-agent">verifier</div>
<div class="step-status"><span class="badge badge-pending">pending</span></div>
</div>
<div class="step-row">
<div class="step-icon pending"></div>
<div class="step-name">test</div>
<div class="step-agent">tester</div>
<div class="step-status"><span class="badge badge-pending">pending</span></div>
</div>
<div class="step-row">
<div class="step-icon pending"></div>
<div class="step-name">pr</div>
<div class="step-agent">developer</div>
<div class="step-status"><span class="badge badge-pending">pending</span></div>
</div>
<div class="step-row">
<div class="step-icon pending"></div>
<div class="step-name">review</div>
<div class="step-agent">reviewer</div>
<div class="step-status"><span class="badge badge-pending">pending</span></div>
</div>
</div>
<div class="stories-section">
<div class="stories-header">
<h3>Stories</h3>
<span>6 / 12 done</span>
</div>
<div class="progress-bar"><div class="progress-fill" style="width:50%"></div></div>
<div class="story-row">
<div class="step-icon done"></div>
<div class="story-name">S-1: Create OAuth provider configuration module</div>
<div class="step-status"><span class="badge badge-done">done</span></div>
</div>
<div class="story-row">
<div class="step-icon done"></div>
<div class="story-name">S-2: Implement Google OAuth callback handler</div>
<div class="step-status"><span class="badge badge-done">done</span></div>
</div>
<div class="story-row">
<div class="step-icon done"></div>
<div class="story-name">S-3: Implement GitHub OAuth callback handler</div>
<div class="step-status"><span class="badge badge-done">done</span></div>
</div>
<div class="story-row" style="flex-direction:column;align-items:stretch;gap:0;padding:0;overflow:hidden">
<div style="display:flex;align-items:center;gap:8px;padding:10px 12px;cursor:pointer">
<div class="step-icon done"></div>
<div class="story-name" style="flex:1">S-4: Add JWT token generation and validation</div>
<div class="step-status"><span class="badge badge-done">done</span></div>
<span style="color:#8b8072;font-size:10px;display:inline-block;transform:rotate(90deg)"></span>
</div>
<div style="padding:0 12px 12px 44px;font-size:12px;color:#5a5045;line-height:1.6">
<div style="margin-bottom:8px">Generate signed JWT tokens on login, validate on every protected request. Support configurable expiry and issuer claims.</div>
<div style="margin-bottom:8px">
<div style="font-weight:600;font-size:11px;text-transform:uppercase;letter-spacing:.3px;color:#6B7F3B;margin-bottom:4px">Acceptance Criteria</div>
<label style="display:flex;align-items:flex-start;gap:6px;margin-bottom:3px;cursor:default"><span style="color:#6B7F3B;flex-shrink:0"></span><span>JWT signed with RS256 using configurable secret</span></label>
<label style="display:flex;align-items:flex-start;gap:6px;margin-bottom:3px;cursor:default"><span style="color:#6B7F3B;flex-shrink:0"></span><span>Token includes user ID, email, roles, exp, iat, iss claims</span></label>
<label style="display:flex;align-items:flex-start;gap:6px;margin-bottom:3px;cursor:default"><span style="color:#6B7F3B;flex-shrink:0"></span><span>Validation middleware rejects expired and malformed tokens</span></label>
<label style="display:flex;align-items:flex-start;gap:6px;margin-bottom:3px;cursor:default"><span style="color:#6B7F3B;flex-shrink:0"></span><span>Refresh token rotation implemented with reuse detection</span></label>
<label style="display:flex;align-items:flex-start;gap:6px;margin-bottom:3px;cursor:default"><span style="color:#6B7F3B;flex-shrink:0"></span><span>Unit tests cover generation, validation, expiry, and invalid signatures</span></label>
</div>
</div>
</div>
<div class="story-row">
<div class="step-icon done"></div>
<div class="story-name">S-5: Create session middleware with refresh tokens</div>
<div class="step-status"><span class="badge badge-done">done</span></div>
</div>
<div class="story-row">
<div class="step-icon done"></div>
<div class="story-name">S-6: Build user profile merge for linked accounts</div>
<div class="step-status"><span class="badge badge-done">done</span></div>
</div>
<div class="story-row">
<div class="step-icon running"></div>
<div class="story-name">S-7: Add CSRF protection to auth endpoints</div>
<div class="step-status"><span class="badge badge-running">running</span></div>
</div>
<div class="story-row">
<div class="step-icon pending"></div>
<div class="story-name">S-8: Implement account lockout after failed attempts</div>
<div class="step-status"><span class="badge badge-pending">pending</span></div>
</div>
<div class="story-row">
<div class="step-icon pending"></div>
<div class="story-name">S-9: Add OAuth scope permission UI</div>
<div class="step-status"><span class="badge badge-pending">pending</span></div>
</div>
<div class="story-row">
<div class="step-icon pending"></div>
<div class="story-name">S-10: Create auth error handling and user feedback</div>
<div class="step-status"><span class="badge badge-pending">pending</span></div>
</div>
<div class="story-row">
<div class="step-icon pending"></div>
<div class="story-name">S-11: Add logout with token revocation</div>
<div class="step-status"><span class="badge badge-pending">pending</span></div>
</div>
<div class="story-row">
<div class="step-icon pending"></div>
<div class="story-name">S-12: Write auth integration tests</div>
<div class="step-status"><span class="badge badge-pending">pending</span></div>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,165 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Antfarm Dashboard</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Geist+Mono&display=swap" rel="stylesheet">
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:'Inter',-apple-system,BlinkMacSystemFont,sans-serif;background:#FAF8F5;color:#3A3226;min-height:100vh}
header{background:#6B7F3B;border-bottom:2px solid #5a6b32;padding:12px 24px;display:flex;align-items:center;gap:16px;flex-wrap:wrap}
header h1{font-family:'Inter',sans-serif;font-size:22px;font-weight:600;color:#fff;letter-spacing:0}
header h1 span{color:#D4E8A0}
select{background:#5a6b32;color:#fff;border:1px solid #4a5a28;border-radius:6px;padding:6px 12px;font-size:14px;cursor:pointer}
select:focus{outline:none;border-color:#8ECFC0}
.board{display:flex;gap:16px;padding:24px;overflow-x:auto;min-height:calc(100vh - 65px)}
.column{min-width:220px;flex:1;background:#fff;border:none;border-radius:8px;display:flex;flex-direction:column;box-shadow:0 2px 8px rgba(58,50,38,.1)}
.column-header{padding:12px 16px;border-bottom:1px solid #eee;font-size:13px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:#6B7F3B;background:#f5f0e8;border-radius:8px 8px 0 0}
.column-header .count{background:#6B7F3B;color:#fff;border-radius:10px;padding:1px 8px;font-size:11px;margin-left:8px}
.cards{padding:8px;flex:1;display:flex;flex-direction:column;gap:8px;overflow-y:auto}
.card{background:#FAF8F5;border:1px solid #D4C4A0;border-radius:6px;padding:12px;cursor:pointer;transition:border-color .15s,box-shadow .15s}
.card:hover{border-color:#E8845C;box-shadow:0 2px 8px rgba(232,132,92,.15)}
.card.done{border-left:3px solid #6B7F3B}
.card.failed{border-left:3px solid #E8845C}
.card-title{font-size:13px;font-weight:500;color:#3A3226;margin-bottom:6px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.card-meta{font-size:11px;color:#8b8072;display:flex;justify-content:space-between;align-items:center}
.badge{font-size:10px;font-weight:600;padding:2px 6px;border-radius:4px;text-transform:uppercase}
.badge-running{background:#8ECFC033;color:#3a9e8a}
.badge-done{background:#6B7F3B22;color:#6B7F3B}
.badge-failed{background:#E8845C22;color:#d4603a}
.badge-pending{background:#D4C4A044;color:#8b8072}
.empty{color:#8b8072;font-size:12px;text-align:center;padding:24px 8px}
.refresh-note{color:rgba(255,255,255,.6);font-size:11px;margin-left:auto}
</style>
</head>
<body>
<header>
<h1><span>antfarm</span> dashboard</h1>
<select><option>feature-dev</option></select>
<span class="refresh-note">Auto-refresh: 30s</span>
</header>
<div class="board">
<!-- plan column -->
<div class="column">
<div class="column-header">plan<span class="count">3</span></div>
<div class="cards">
<div class="card done">
<div class="card-title">Add user authentication with OAuth</div>
<div class="card-meta"><span class="badge badge-done">done</span><span>Feb 9, 10:02 AM</span></div>
</div>
<div class="card done">
<div class="card-title">Refactor payment processing pipeline</div>
<div class="card-meta"><span class="badge badge-done">done</span><span>Feb 9, 9:45 AM</span></div>
</div>
<div class="card done">
<div class="card-title">Add webhook retry with exponential backoff</div>
<div class="card-meta"><span class="badge badge-done">done</span><span>Feb 9, 8:30 AM</span></div>
</div>
</div>
</div>
<!-- setup column -->
<div class="column">
<div class="column-header">setup<span class="count">2</span></div>
<div class="cards">
<div class="card done">
<div class="card-title">Add user authentication with OAuth</div>
<div class="card-meta"><span class="badge badge-done">done</span><span>Feb 9, 10:14 AM</span></div>
</div>
<div class="card done">
<div class="card-title">Refactor payment processing pipeline</div>
<div class="card-meta"><span class="badge badge-done">done</span><span>Feb 9, 9:58 AM</span></div>
</div>
</div>
</div>
<!-- implement column -->
<div class="column">
<div class="column-header">implement<span class="count">3</span></div>
<div class="cards">
<div class="card">
<div class="card-title">Add user authentication with OAuth</div>
<div class="card-meta"><span class="badge badge-running">running</span><span>Stories: 5/7</span></div>
</div>
<div class="card">
<div class="card-title">Refactor payment processing pipeline</div>
<div class="card-meta"><span class="badge badge-running">running</span><span>Stories: 2/5</span></div>
</div>
<div class="card">
<div class="card-title">Add webhook retry with exponential backoff</div>
<div class="card-meta"><span class="badge badge-pending">pending</span><span>Feb 9, 8:31 AM</span></div>
</div>
</div>
</div>
<!-- verify column -->
<div class="column">
<div class="column-header">verify<span class="count">2</span></div>
<div class="cards">
<div class="card">
<div class="card-title">Migrate database to connection pooling</div>
<div class="card-meta"><span class="badge badge-running">running</span><span>Feb 9, 9:22 AM</span></div>
</div>
<div class="card">
<div class="card-title">Add rate limiting to public API endpoints</div>
<div class="card-meta"><span class="badge badge-pending">pending</span><span>Feb 9, 9:10 AM</span></div>
</div>
</div>
</div>
<!-- test column -->
<div class="column">
<div class="column-header">test<span class="count">2</span></div>
<div class="cards">
<div class="card">
<div class="card-title">Add real-time notifications via WebSockets</div>
<div class="card-meta"><span class="badge badge-running">running</span><span>Feb 9, 8:55 AM</span></div>
</div>
<div class="card failed">
<div class="card-title">Implement team invitation flow</div>
<div class="card-meta"><span class="badge badge-failed">failed</span><span>Retry 2/3</span></div>
</div>
</div>
</div>
<!-- PR column -->
<div class="column">
<div class="column-header">PR<span class="count">2</span></div>
<div class="cards">
<div class="card">
<div class="card-title">Add CSV export for billing reports</div>
<div class="card-meta"><span class="badge badge-running">running</span><span>Feb 9, 8:40 AM</span></div>
</div>
<div class="card done">
<div class="card-title">Add audit logging for admin actions</div>
<div class="card-meta"><span class="badge badge-done">done</span><span>Feb 9, 7:15 AM</span></div>
</div>
</div>
</div>
<!-- review column -->
<div class="column">
<div class="column-header">review<span class="count">3</span></div>
<div class="cards">
<div class="card">
<div class="card-title">Add RBAC with role hierarchy</div>
<div class="card-meta"><span class="badge badge-running">running</span><span>Feb 9, 7:50 AM</span></div>
</div>
<div class="card done">
<div class="card-title">Implement SSO with SAML 2.0</div>
<div class="card-meta"><span class="badge badge-done">done</span><span>Feb 9, 6:30 AM</span></div>
</div>
<div class="card done">
<div class="card-title">Add dark mode support</div>
<div class="card-meta"><span class="badge badge-done">done</span><span>Feb 8, 11:45 PM</span></div>
</div>
</div>
</div>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

308
antfarm/landing/index.html Normal file
View File

@@ -0,0 +1,308 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Antfarm — Build your agent team with one command</title>
<meta name="description" content="Build your agent team in OpenClaw with one command. Deterministic multi-agent workflows defined in YAML. Zero infrastructure.">
<!-- Open Graph / Twitter Card -->
<meta property="og:type" content="website">
<meta property="og:url" content="https://antfarm.cool">
<meta property="og:title" content="Antfarm — Build your agent team with one command">
<meta property="og:description" content="Multi-agent workflows for OpenClaw. Define a team of specialized AI agents in YAML. One install. Zero infrastructure.">
<meta property="og:image" content="https://antfarm.cool/og-image.png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Antfarm — Build your agent team with one command">
<meta name="twitter:description" content="Multi-agent workflows for OpenClaw. Define a team of specialized AI agents in YAML. One install. Zero infrastructure.">
<meta name="twitter:image" content="https://antfarm.cool/og-image.png">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="topbar">
<div class="topbar-inner">
<a href="#" class="topbar-brand">
<span class="topbar-name">Antfarm</span>
</a>
<div class="topbar-links">
<a href="#workflows">Workflows</a>
<a href="#security">Security</a>
<a href="#commands">Commands</a>
<a href="https://github.com/snarktank/antfarm" class="topbar-gh">
<svg height="18" width="18" viewBox="0 0 16 16" fill="currentColor"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg>
</a>
</div>
</div>
</div>
<main class="container">
<!-- Hero -->
<section class="hero">
<div class="hero-row">
<img src="logo.jpeg" alt="Antfarm" class="hero-logo">
<h1>Build your agent team in <a href="https://docs.openclaw.ai">OpenClaw</a> with one command</h1>
</div>
<p class="hero-sub">You don't need to hire a dev team. You need to define one. Antfarm gives you a team of specialized AI agents — planner, developer, verifier, tester, reviewer — that work together in reliable, repeatable workflows. One install. Zero infrastructure.</p>
<div class="hero-actions">
<div class="install-cmd">
<code><span class="cmd-prompt">$</span> install github.com/snarktank/antfarm</code>
<button class="copy-btn" onclick="navigator.clipboard.writeText('install github.com/snarktank/antfarm').then(()=>{this.textContent='Copied!';setTimeout(()=>this.textContent='Copy',1500)})" title="Copy to clipboard">Copy</button>
</div>
<p class="install-hint">Tell your OpenClaw agent. That's it.</p>
</div>
</section>
<!-- The problem -->
<section class="section problem-section">
<h2 class="problem-text">Antfarm gives you a team of agents that specialize, verify each other, and run the same playbook every time.</h2>
</section>
<!-- Bundled workflows -->
<section id="workflows" class="section">
<h2>What you get: Agent team workflows</h2>
<div class="workflow-grid">
<div class="wf-card">
<div class="wf-header">
<h3>feature-dev</h3>
<span class="wf-badge">7 agents</span>
</div>
<p>Drop in a feature request. Get back a tested PR. The planner decomposes your task into stories. Each story gets implemented, verified, and tested in isolation. Failures retry automatically. Nothing ships without a code review.</p>
<div class="wf-pipeline">
<span>plan</span><span class="wf-arrow">&rarr;</span>
<span>setup</span><span class="wf-arrow">&rarr;</span>
<span>implement</span><span class="wf-arrow">&rarr;</span>
<span>verify</span><span class="wf-arrow">&rarr;</span>
<span>test</span><span class="wf-arrow">&rarr;</span>
<span>PR</span><span class="wf-arrow">&rarr;</span>
<span>review</span>
</div>
</div>
<div class="wf-card">
<div class="wf-header">
<h3>security-audit</h3>
<span class="wf-badge">7 agents</span>
</div>
<p>Point it at a repo. Get back a security fix PR with regression tests. Scans for vulnerabilities, ranks by severity, patches each one, re-audits after all fixes are applied.</p>
<div class="wf-pipeline">
<span>scan</span><span class="wf-arrow">&rarr;</span>
<span>prioritize</span><span class="wf-arrow">&rarr;</span>
<span>setup</span><span class="wf-arrow">&rarr;</span>
<span>fix</span><span class="wf-arrow">&rarr;</span>
<span>verify</span><span class="wf-arrow">&rarr;</span>
<span>test</span><span class="wf-arrow">&rarr;</span>
<span>PR</span>
</div>
</div>
<div class="wf-card">
<div class="wf-header">
<h3>bug-fix</h3>
<span class="wf-badge">6 agents</span>
</div>
<p>Paste a bug report. Get back a fix with a regression test. Triager reproduces it, investigator finds root cause, fixer patches, verifier confirms. Zero babysitting.</p>
<div class="wf-pipeline">
<span>triage</span><span class="wf-arrow">&rarr;</span>
<span>investigate</span><span class="wf-arrow">&rarr;</span>
<span>setup</span><span class="wf-arrow">&rarr;</span>
<span>fix</span><span class="wf-arrow">&rarr;</span>
<span>verify</span><span class="wf-arrow">&rarr;</span>
<span>PR</span>
</div>
</div>
</div>
</section>
<!-- Why it works -->
<section class="section">
<h2>Why it works</h2>
<div class="steps-grid">
<div class="step-card">
<h3>Deterministic workflows</h3>
<p>Same workflow, same steps, same order. Not "hopefully the agent remembers to test."</p>
</div>
<div class="step-card">
<h3>Agents verify each other</h3>
<p>The developer doesn't mark their own homework. A separate verifier checks every story against acceptance criteria.</p>
</div>
<div class="step-card">
<h3>Fresh context, every step</h3>
<p>Each agent gets a clean session. No context window bloat. No hallucinated state from 50 messages ago.</p>
</div>
<div class="step-card">
<h3>Retry and escalate</h3>
<p>Failed steps retry automatically. If retries exhaust, it escalates to you. Nothing fails silently.</p>
</div>
</div>
</section>
<!-- How it works -->
<section class="section">
<h2>How it works</h2>
<div class="steps-grid">
<div class="step-card">
<div class="step-num">1</div>
<h3>Define</h3>
<p>Agents and steps in YAML. Each agent gets a persona, workspace, and strict acceptance criteria. No ambiguity about who does what.</p>
</div>
<div class="step-card">
<div class="step-num">2</div>
<h3>Install</h3>
<p>One command provisions everything: agent workspaces, cron polling, subagent permissions. No Docker, no queues, no external services.</p>
</div>
<div class="step-card">
<div class="step-num">3</div>
<h3>Run</h3>
<p>Agents poll for work independently. Claim a step, do the work, pass context to the next agent. SQLite tracks state. Cron keeps it moving.</p>
</div>
</div>
</section>
<!-- Minimal by design -->
<section class="section minimal-section">
<h2>Minimal by design</h2>
<p class="section-desc">YAML + SQLite + cron. That's it. No Redis, no Kafka, no container orchestrator. Antfarm is a TypeScript CLI with zero external dependencies. It runs wherever OpenClaw runs.</p>
</section>
<!-- Ralph loop -->
<section class="section ralph-section">
<div class="ralph-row">
<img src="https://raw.githubusercontent.com/snarktank/ralph/main/ralph.webp" alt="Ralph" class="ralph-img">
<div>
<h3>Built on the Ralph loop</h3>
<p>Each agent runs in a fresh session with clean context. Memory persists through git history and progress files — the same autonomous loop pattern from <a href="https://github.com/snarktank/ralph">Ralph</a>, scaled to multi-agent workflows.</p>
</div>
</div>
</section>
<!-- Quick example -->
<section class="section">
<h2>Quick example</h2>
<div class="code-block">
<div class="code-header">
<span class="code-tab active">Terminal</span>
</div>
<pre><code><span class="c-prompt">$</span> antfarm workflow install feature-dev
<span class="c-ok"></span> Installed workflow: feature-dev
<span class="c-prompt">$</span> antfarm workflow run feature-dev <span class="c-str">"Add user authentication with OAuth"</span>
<span class="c-dim">Run: a1fdf573</span>
<span class="c-dim">Workflow: feature-dev</span>
<span class="c-dim">Status: running</span>
<span class="c-prompt">$</span> antfarm workflow status <span class="c-str">"OAuth"</span>
<span class="c-dim">Run: a1fdf573</span>
<span class="c-dim">Workflow: feature-dev</span>
<span class="c-dim">Steps:</span>
<span class="c-ok">[done ]</span> plan (planner)
<span class="c-ok">[done ]</span> setup (setup)
<span class="c-run">[running]</span> implement (developer) <span class="c-dim">Stories: 3/7 done</span>
<span class="c-pend">[pending]</span> verify (verifier)
<span class="c-pend">[pending]</span> test (tester)
<span class="c-pend">[pending]</span> pr (developer)
<span class="c-pend">[pending]</span> review (reviewer)</code></pre>
</div>
</section>
<!-- Build your own -->
<section class="section">
<h2>Build your own</h2>
<p class="section-desc">The bundled workflows are starting points. Define your own agents, steps, retry logic, and verification gates in plain YAML and Markdown. If you can write a prompt, you can build a workflow.</p>
<div class="code-block">
<div class="code-header">
<span class="code-tab active">workflow.yml</span>
</div>
<pre><code><span class="c-key">id:</span> my-workflow
<span class="c-key">name:</span> My Custom Workflow
<span class="c-key">agents:</span>
- <span class="c-key">id:</span> researcher
<span class="c-key">name:</span> Researcher
<span class="c-key">workspace:</span>
<span class="c-key">files:</span>
<span class="c-key">AGENTS.md:</span> agents/researcher/AGENTS.md
<span class="c-key">steps:</span>
- <span class="c-key">id:</span> research
<span class="c-key">agent:</span> researcher
<span class="c-key">input:</span> <span class="c-str">|</span>
<span class="c-str">Research {{task}} and report findings.</span>
<span class="c-str">Reply with STATUS: done and FINDINGS: ...</span>
<span class="c-key">expects:</span> <span class="c-str">"STATUS: done"</span></code></pre>
</div>
<p class="section-link">Full guide: <a href="https://github.com/snarktank/antfarm/blob/main/docs/creating-workflows.md">docs/creating-workflows.md</a></p>
</section>
<!-- Security -->
<section id="security" class="section">
<h2>Security</h2>
<p class="section-desc">You're installing agent teams that run code on your machine. We take that seriously.</p>
<div class="security-grid">
<div class="security-item">
<h4>Curated repo only</h4>
<p>Antfarm only installs workflows from the official <a href="https://github.com/snarktank/antfarm">snarktank/antfarm</a> repository. No arbitrary remote sources.</p>
</div>
<div class="security-item">
<h4>Reviewed for prompt injection</h4>
<p>Every workflow is reviewed for prompt injection attacks and malicious agent files before merging.</p>
</div>
<div class="security-item">
<h4>Community contributions welcome</h4>
<p>Want to add a workflow? Submit a PR. All submissions go through careful security review before they ship.</p>
</div>
<div class="security-item">
<h4>Transparent by default</h4>
<p>Every workflow is plain YAML and Markdown. You can read exactly what each agent will do before you install it.</p>
</div>
</div>
</section>
<!-- Dashboard -->
<section class="section">
<h2>Dashboard</h2>
<p class="section-desc">Monitor runs, track step progress, and view agent output in real time.</p>
<div class="dashboard-frame">
<img src="dashboard-screenshot.png" alt="Antfarm dashboard showing workflow runs and step status">
</div>
<div class="dashboard-frame" style="margin-top:16px">
<img src="dashboard-detail-screenshot.png" alt="Antfarm dashboard showing run detail with stories">
</div>
</section>
<!-- Commands -->
<section id="commands" class="section">
<h2>Commands</h2>
<div class="cmd-grid">
<div class="cmd-group">
<h4>Lifecycle</h4>
<div class="cmd-row"><code>antfarm install</code><span>Install all bundled workflows</span></div>
<div class="cmd-row"><code>antfarm uninstall</code><span>Full teardown (agents, crons, DB)</span></div>
</div>
<div class="cmd-group">
<h4>Workflows</h4>
<div class="cmd-row"><code>antfarm workflow run &lt;id&gt; &lt;task&gt;</code><span>Start a run</span></div>
<div class="cmd-row"><code>antfarm workflow status &lt;query&gt;</code><span>Check run status</span></div>
<div class="cmd-row"><code>antfarm workflow runs</code><span>List all runs</span></div>
<div class="cmd-row"><code>antfarm workflow resume &lt;run-id&gt;</code><span>Resume a failed run</span></div>
</div>
<div class="cmd-group">
<h4>Management</h4>
<div class="cmd-row"><code>antfarm workflow list</code><span>List available workflows</span></div>
<div class="cmd-row"><code>antfarm workflow install &lt;id&gt;</code><span>Install a single workflow</span></div>
<div class="cmd-row"><code>antfarm workflow uninstall &lt;id&gt;</code><span>Remove a single workflow</span></div>
<div class="cmd-row"><code>antfarm dashboard</code><span>Start the web dashboard</span></div>
<div class="cmd-row"><code>antfarm logs</code><span>View recent log entries</span></div>
</div>
</div>
</section>
</main>
<footer class="footer">
<div class="footer-inner">
<p>Part of the <a href="https://docs.openclaw.ai">OpenClaw</a> ecosystem</p>
<p>Built by <a href="https://ryancarson.com">Ryan Carson</a></p>
</div>
</footer>
</body>
</html>

BIN
antfarm/landing/logo.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

View File

@@ -0,0 +1,22 @@
# Progress Log
Run: redesign-landing-page
Task: Redesign the Antfarm landing page to utilitarian repo-style
Started: 2026-02-08 16:16 ET
## Codebase Patterns
- Tests in `landing/__tests__/landing.test.js` check for: id="features", id="quickstart", id="commands", `.hero` and `.feature-grid` in CSS, `@media` in CSS, and that all `href="#x"` have matching `id="x"`
- Tests use `node:test` + `node:assert/strict`, run with `node --test`
- Landing page is pure HTML+CSS, no JS, no build step
---
## 2026-02-08 16:16 - US-001: Rewrite landing page HTML with utilitarian repo-style layout
- Replaced marketing-style landing page with GitHub-repo-aesthetic utilitarian layout
- 7 sections: header (logo + GitHub link), description, install (id="features"), example (id="quickstart"), dashboard placeholder, commands table (id="commands"), footer
- Dark theme using GitHub-like color palette (#0d1117 bg, #58a6ff accent)
- Zero emoji anywhere
- Kept .hero class on header, .feature-grid in CSS for test compat
- Responsive with @media breakpoint at 640px
- Files changed: landing/index.html, landing/style.css
- **Learnings:** Tests check CSS for class names (.hero, .feature-grid) even if not used in HTML, so keep them in the stylesheet
---

566
antfarm/landing/style.css Normal file
View File

@@ -0,0 +1,566 @@
/* === Reset === */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap');
:root {
--bg: #FAF8F5;
--bg-card: #FFFFFF;
--bg-code: #2D2A24;
--bg-code-header: #3A3630;
--text: #3A3226;
--text-secondary: #6B6358;
--text-muted: #9B9183;
--accent: #6B7F3B;
--accent-light: #7E9544;
--coral: #E8845C;
--border: #E2D9CC;
--border-light: #EDE7DE;
--radius: 8px;
--radius-lg: 12px;
--shadow-sm: 0 1px 2px rgba(58, 50, 38, 0.06);
--shadow-md: 0 2px 8px rgba(58, 50, 38, 0.08);
}
html { scroll-behavior: smooth; }
body {
font-family: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, sans-serif;
background: var(--bg);
color: var(--text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-light); }
code {
font-family: 'IBM Plex Mono', 'SFMono-Regular', Consolas, monospace;
font-size: 0.85rem;
}
/* === Topbar === */
.topbar {
position: sticky;
top: 0;
z-index: 100;
background: rgba(250, 248, 245, 0.92);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border-bottom: 1px solid var(--border-light);
}
.topbar-inner {
max-width: 1080px;
margin: 0 auto;
padding: 12px 32px;
display: flex;
justify-content: space-between;
align-items: center;
}
.topbar-brand {
display: flex;
align-items: center;
gap: 10px;
text-decoration: none;
}
.topbar-logo {
width: 28px;
height: 28px;
border-radius: 6px;
}
.topbar-name {
font-size: 1rem;
font-weight: 600;
color: var(--text);
letter-spacing: -0.01em;
}
.topbar-links {
display: flex;
align-items: center;
gap: 24px;
}
.topbar-links a {
font-size: 0.85rem;
color: var(--text-secondary);
transition: color 0.15s;
}
.topbar-links a:hover { color: var(--text); }
.topbar-gh {
display: flex;
align-items: center;
color: var(--text-muted) !important;
}
.topbar-gh:hover { color: var(--text) !important; }
/* === Container === */
.container {
max-width: 1080px;
margin: 0 auto;
padding: 0 32px;
}
/* === Hero === */
.hero {
padding: 72px 0 56px;
border-bottom: 1px solid var(--border-light);
}
.hero-row {
display: flex;
align-items: center;
gap: 24px;
}
.hero-sub {
margin-top: 16px;
}
.hero-logo {
width: 120px;
height: 120px;
border-radius: var(--radius-lg);
flex-shrink: 0;
box-shadow: var(--shadow-md);
}
.hero h1 {
font-size: 2rem;
font-weight: 600;
line-height: 1.3;
letter-spacing: -0.025em;
max-width: 680px;
}
.hero h1 a { text-decoration: underline; text-underline-offset: 3px; }
.hero-sub {
font-size: 1.1rem;
color: var(--text-secondary);
max-width: 640px;
line-height: 1.7;
}
.hero-actions { margin-top: 32px; }
.install-cmd {
display: inline-flex;
align-items: center;
gap: 12px;
background: var(--bg-code);
border-radius: var(--radius);
padding: 12px 20px;
box-shadow: var(--shadow-md);
}
.copy-btn {
background: transparent;
border: 1px solid rgba(232, 223, 208, 0.3);
color: #E8DFD0;
font-size: 0.75rem;
padding: 4px 10px;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
white-space: nowrap;
}
.copy-btn:hover {
background: rgba(232, 223, 208, 0.1);
border-color: rgba(232, 223, 208, 0.5);
}
.install-cmd code {
color: #E8DFD0;
font-size: 0.9rem;
}
.cmd-prompt { color: var(--coral); margin-right: 8px; }
.install-hint {
margin-top: 10px;
font-size: 0.8rem;
color: var(--text-muted);
}
/* === Sections === */
.section {
padding: 56px 0;
border-bottom: 1px solid var(--border-light);
}
h2.problem-text {
font-size: 1.3rem;
font-weight: 600;
color: var(--text);
line-height: 1.5;
letter-spacing: -0.015em;
max-width: 680px;
}
.ralph-row {
display: flex;
align-items: center;
gap: 24px;
}
.ralph-img {
width: 80px;
height: 80px;
border-radius: 12px;
flex-shrink: 0;
object-fit: cover;
}
.ralph-row h3 { margin: 0 0 8px; }
.ralph-row p { margin: 0; color: var(--text-muted); font-size: 0.9rem; }
.ralph-row a { color: var(--coral); }
.section h2 {
font-size: 1.3rem;
font-weight: 600;
letter-spacing: -0.015em;
margin-bottom: 8px;
}
.section-desc {
color: var(--text-secondary);
font-size: 0.95rem;
margin-bottom: 24px;
}
.section-link {
margin-top: 16px;
font-size: 0.85rem;
color: var(--text-muted);
}
.section-link a { text-decoration: underline; text-underline-offset: 2px; }
/* === Steps grid === */
.steps-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
margin-top: 24px;
}
.step-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 24px;
box-shadow: var(--shadow-sm);
}
.step-num {
display: inline-flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
border-radius: 50%;
background: var(--accent);
color: white;
font-size: 0.8rem;
font-weight: 600;
margin-bottom: 12px;
}
.step-card h3 {
font-size: 1rem;
font-weight: 600;
margin-bottom: 6px;
}
.step-card p {
font-size: 0.875rem;
color: var(--text-secondary);
line-height: 1.6;
}
.step-card code {
background: var(--bg);
padding: 1px 5px;
border-radius: 3px;
font-size: 0.8rem;
}
/* === Code blocks === */
.code-block {
border: 1px solid #44403A;
border-radius: var(--radius-lg);
overflow: hidden;
box-shadow: var(--shadow-md);
}
.code-header {
background: var(--bg-code-header);
padding: 8px 16px;
border-bottom: 1px solid #44403A;
}
.code-tab {
font-size: 0.75rem;
color: #A89F93;
font-family: 'IBM Plex Mono', monospace;
}
.code-tab.active { color: #D4CABC; }
.code-block pre {
background: var(--bg-code);
margin: 0;
padding: 20px 24px;
overflow-x: auto;
line-height: 1.75;
}
.code-block code {
color: #E8DFD0;
font-size: 0.825rem;
background: none;
}
.c-prompt { color: var(--coral); }
.c-dim { color: #8B8278; }
.c-ok { color: #8CAA50; }
.c-run { color: #D4A843; }
.c-pend { color: #7B7368; }
.c-str { color: #C9A96E; }
.c-key { color: #8CAA50; }
.c-comment { color: #6B6358; }
/* === Workflow grid === */
.workflow-grid {
display: grid;
gap: 16px;
margin-top: 24px;
}
.wf-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 24px;
box-shadow: var(--shadow-sm);
}
.wf-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 10px;
}
.wf-header h3 {
font-family: 'IBM Plex Mono', monospace;
font-size: 0.95rem;
font-weight: 600;
color: var(--accent);
}
.wf-badge {
font-size: 0.7rem;
color: var(--text-muted);
background: var(--bg);
border: 1px solid var(--border);
border-radius: 999px;
padding: 2px 10px;
}
.wf-card > p {
font-size: 0.875rem;
color: var(--text-secondary);
line-height: 1.65;
margin-bottom: 14px;
}
.wf-pipeline {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 4px;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.75rem;
}
.wf-pipeline span {
color: var(--text-secondary);
}
.wf-arrow {
color: var(--text-muted);
margin: 0 2px;
}
/* === Security === */
.security-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
margin-top: 24px;
}
.security-item {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 24px;
box-shadow: var(--shadow-sm);
}
.security-item h4 {
font-size: 0.9rem;
font-weight: 600;
margin-bottom: 6px;
}
.security-item p {
font-size: 0.85rem;
color: var(--text-secondary);
line-height: 1.6;
}
@media (max-width: 768px) {
.security-grid { grid-template-columns: 1fr; }
}
/* === Dashboard === */
.dashboard-frame {
border: 1px solid var(--border);
border-radius: var(--radius-lg);
overflow: hidden;
box-shadow: var(--shadow-md);
background: var(--bg-card);
}
.dashboard-frame img {
width: 100%;
display: block;
}
/* === Commands === */
.cmd-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 24px;
margin-top: 24px;
}
.cmd-group h4 {
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--text-muted);
margin-bottom: 10px;
}
.cmd-row {
display: flex;
align-items: baseline;
gap: 12px;
padding: 6px 0;
border-bottom: 1px solid var(--border-light);
}
.cmd-row:last-child { border-bottom: none; }
.cmd-row code {
color: var(--coral);
white-space: nowrap;
background: none;
font-size: 0.8rem;
flex-shrink: 0;
}
.cmd-row span {
font-size: 0.8rem;
color: var(--text-muted);
}
/* === Architecture === */
.arch-block {
margin-top: 24px;
}
.arch-block pre {
background: var(--bg-code);
border-radius: var(--radius-lg);
padding: 24px 28px;
overflow-x: auto;
line-height: 1.6;
box-shadow: var(--shadow-md);
}
.arch-block code {
color: #E8DFD0;
font-size: 0.8rem;
background: none;
}
/* === Footer === */
.footer {
padding: 32px 0;
}
.footer-inner {
max-width: 1080px;
margin: 0 auto;
padding: 0 32px;
display: flex;
justify-content: space-between;
}
.footer p {
font-size: 0.8rem;
color: var(--text-muted);
}
.footer a { color: var(--text-secondary); }
.footer a:hover { color: var(--text); }
/* === Responsive === */
@media (max-width: 768px) {
.container { padding: 0 20px; }
.topbar-inner { padding: 12px 20px; }
.topbar-links a:not(.topbar-gh) { display: none; }
.hero { padding: 48px 0 40px; }
.hero-row { flex-direction: row; gap: 24px; align-items: flex-start; }
.hero-logo { width: 100px; height: 100px; }
.hero h1 { font-size: 1.5rem; }
.hero-sub { font-size: 1rem; }
.steps-grid { grid-template-columns: 1fr; }
.section { padding: 40px 0; }
.cmd-grid { grid-template-columns: 1fr; }
.cmd-row { flex-direction: column; gap: 2px; }
.cmd-row code { white-space: normal; }
.wf-pipeline { font-size: 0.7rem; }
.footer-inner {
flex-direction: column;
gap: 4px;
padding: 0 20px;
}
}
@media (max-width: 480px) {
.hero-row { gap: 16px; }
.hero-logo { width: 80px; height: 80px; }
.hero h1 { font-size: 1.3rem; }
.install-cmd { padding: 10px 14px; }
.install-cmd code { font-size: 0.8rem; }
.code-block pre { padding: 14px 16px; }
.arch-block pre { padding: 16px 18px; }
}