agentdev-webui 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/lib/agent-api.js +530 -0
  2. package/lib/auth.js +127 -0
  3. package/lib/config.js +53 -0
  4. package/lib/database.js +762 -0
  5. package/lib/device-flow.js +257 -0
  6. package/lib/email.js +420 -0
  7. package/lib/encryption.js +112 -0
  8. package/lib/github.js +339 -0
  9. package/lib/history.js +143 -0
  10. package/lib/pwa.js +107 -0
  11. package/lib/redis-logs.js +226 -0
  12. package/lib/routes.js +680 -0
  13. package/migrations/000_create_database.sql +33 -0
  14. package/migrations/001_create_agentdev_schema.sql +135 -0
  15. package/migrations/001_create_agentdev_schema.sql.old +100 -0
  16. package/migrations/001_create_agentdev_schema_fixed.sql +135 -0
  17. package/migrations/002_add_github_token.sql +17 -0
  18. package/migrations/003_add_agent_logs_table.sql +23 -0
  19. package/migrations/004_remove_oauth_columns.sql +11 -0
  20. package/migrations/005_add_projects.sql +44 -0
  21. package/migrations/006_project_github_token.sql +7 -0
  22. package/migrations/007_project_repositories.sql +12 -0
  23. package/migrations/008_add_notifications.sql +20 -0
  24. package/migrations/009_unified_oauth.sql +153 -0
  25. package/migrations/README.md +97 -0
  26. package/package.json +37 -0
  27. package/public/css/styles.css +1140 -0
  28. package/public/device.html +384 -0
  29. package/public/docs.html +862 -0
  30. package/public/docs.md +697 -0
  31. package/public/favicon.svg +5 -0
  32. package/public/index.html +271 -0
  33. package/public/js/app.js +2379 -0
  34. package/public/login.html +224 -0
  35. package/public/profile.html +394 -0
  36. package/public/register.html +392 -0
  37. package/public/reset-password.html +349 -0
  38. package/public/verify-email.html +177 -0
  39. package/server.js +1450 -0
@@ -0,0 +1,862 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <meta name="theme-color" content="#1a1a2e">
7
+ <meta name="apple-mobile-web-app-capable" content="yes">
8
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
9
+ <meta name="description" content="AgentDev client documentation">
10
+ <meta property="og:title" content="AgentDev Client Documentation">
11
+ <meta property="og:description" content="AgentDev client documentation">
12
+ <meta property="og:type" content="website">
13
+ <meta property="og:image" content="/og-image.svg">
14
+ <meta name="twitter:card" content="summary_large_image">
15
+ <meta name="twitter:title" content="AgentDev Client Documentation">
16
+ <meta name="twitter:description" content="AgentDev client documentation">
17
+ <meta name="twitter:image" content="/og-image.svg">
18
+ <title>AgentDev Client Documentation</title>
19
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg">
20
+ <link rel="apple-touch-icon" href="/icon-192.png">
21
+ <link rel="manifest" href="/manifest.json">
22
+ <style>
23
+ * { box-sizing: border-box; margin: 0; padding: 0; }
24
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #0d1117; color: #c9d1d9; min-height: 100vh; display: flex; flex-direction: column; }
25
+
26
+ /* Top bar */
27
+ .topbar { background: #161b22; border-bottom: 1px solid #30363d; padding: 12px 24px; display: flex; align-items: center; gap: 16px; position: fixed; top: 0; left: 0; right: 0; z-index: 100; height: 56px; }
28
+ .topbar-logo { color: #e94560; font-weight: bold; font-size: 18px; text-decoration: none; }
29
+ .topbar-title { color: #8b949e; font-size: 14px; }
30
+ .topbar-links { margin-left: auto; display: flex; gap: 16px; }
31
+ .topbar-links a { color: #58a6ff; text-decoration: none; font-size: 13px; }
32
+ .topbar-links a:hover { text-decoration: underline; }
33
+
34
+ /* Layout */
35
+ .layout { display: flex; margin-top: 56px; min-height: calc(100vh - 56px); }
36
+
37
+ /* Sidebar nav */
38
+ .sidebar-nav { width: 280px; background: #161b22; border-right: 1px solid #30363d; position: fixed; top: 56px; bottom: 0; left: 0; overflow-y: auto; padding: 20px 0; flex-shrink: 0; }
39
+ .sidebar-nav::-webkit-scrollbar { width: 6px; }
40
+ .sidebar-nav::-webkit-scrollbar-thumb { background: #30363d; border-radius: 3px; }
41
+ .nav-section { margin-bottom: 8px; }
42
+ .nav-section-title { padding: 8px 20px; font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; color: #8b949e; }
43
+ .nav-link { display: block; padding: 6px 20px 6px 28px; font-size: 13px; color: #c9d1d9; text-decoration: none; border-left: 2px solid transparent; transition: all 0.15s; }
44
+ .nav-link:hover { background: #1c2128; color: #f0f6fc; }
45
+ .nav-link.active { border-left-color: #e94560; color: #f0f6fc; background: #1c2128; }
46
+ .nav-link.sub { padding-left: 40px; font-size: 12px; color: #8b949e; }
47
+ .nav-link.sub:hover { color: #c9d1d9; }
48
+
49
+ /* Content area */
50
+ .content { margin-left: 280px; flex: 1; max-width: 860px; padding: 40px 48px 80px; }
51
+
52
+ /* Typography */
53
+ h1 { font-size: 32px; color: #f0f6fc; margin-bottom: 8px; font-weight: 600; }
54
+ .subtitle { font-size: 16px; color: #8b949e; margin-bottom: 32px; }
55
+ h2 { font-size: 24px; color: #f0f6fc; margin-top: 48px; margin-bottom: 16px; padding-bottom: 8px; border-bottom: 1px solid #21262d; font-weight: 600; }
56
+ h2:first-of-type { margin-top: 0; }
57
+ h3 { font-size: 18px; color: #f0f6fc; margin-top: 32px; margin-bottom: 12px; font-weight: 600; }
58
+ h4 { font-size: 15px; color: #c9d1d9; margin-top: 24px; margin-bottom: 8px; font-weight: 600; }
59
+ p { line-height: 1.7; margin-bottom: 16px; font-size: 14px; }
60
+ ul, ol { margin-bottom: 16px; padding-left: 24px; }
61
+ li { line-height: 1.7; margin-bottom: 6px; font-size: 14px; }
62
+
63
+ /* Code */
64
+ code { background: #1c2128; padding: 2px 6px; border-radius: 4px; font-family: 'SFMono-Regular', Consolas, monospace; font-size: 13px; color: #e6edf3; border: 1px solid #30363d; }
65
+ pre { background: #161b22; border: 1px solid #30363d; border-radius: 8px; padding: 16px 20px; overflow-x: auto; margin: 12px 0 20px; }
66
+ pre code { background: none; padding: 0; border: none; font-size: 13px; line-height: 1.5; }
67
+
68
+ /* Tables */
69
+ table { width: 100%; border-collapse: collapse; margin: 12px 0 20px; font-size: 13px; }
70
+ th { background: #161b22; color: #f0f6fc; text-align: left; padding: 10px 14px; border: 1px solid #30363d; font-weight: 600; }
71
+ td { padding: 10px 14px; border: 1px solid #21262d; vertical-align: top; }
72
+ tr:hover { background: #161b2244; }
73
+
74
+ /* Callouts */
75
+ .callout { padding: 12px 16px; border-radius: 6px; margin: 16px 0; font-size: 13px; line-height: 1.6; border-left: 3px solid; }
76
+ .callout-info { background: #0d419d22; border-color: #58a6ff; }
77
+ .callout-warn { background: #9e6a0322; border-color: #d29922; }
78
+ .callout-success { background: #23863622; border-color: #3fb950; }
79
+ .callout strong { display: block; margin-bottom: 4px; }
80
+ .callout-info strong { color: #58a6ff; }
81
+ .callout-warn strong { color: #d29922; }
82
+ .callout-success strong { color: #3fb950; }
83
+
84
+ /* Diagram */
85
+ .diagram { background: #161b22; border: 1px solid #30363d; border-radius: 8px; padding: 20px 24px; font-family: 'SFMono-Regular', Consolas, monospace; font-size: 13px; line-height: 1.6; margin: 16px 0 20px; overflow-x: auto; white-space: pre; color: #8b949e; }
86
+
87
+ /* Badges */
88
+ .badge { display: inline-block; padding: 2px 8px; border-radius: 12px; font-size: 11px; font-weight: 600; }
89
+ .badge-get { background: #23863622; color: #3fb950; }
90
+ .badge-post { background: #0d419d22; color: #58a6ff; }
91
+
92
+ /* Section anchor links */
93
+ .section { scroll-margin-top: 72px; }
94
+
95
+ /* Mobile */
96
+ @media (max-width: 768px) {
97
+ .sidebar-nav { display: none; }
98
+ .content { margin-left: 0; padding: 24px 16px 60px; }
99
+ .topbar { padding: 12px 16px; }
100
+ }
101
+
102
+ /* Footer */
103
+ .footer { margin-top: 64px; padding-top: 24px; border-top: 1px solid #21262d; color: #484f58; font-size: 12px; text-align: center; }
104
+ .footer a { color: #58a6ff; text-decoration: none; }
105
+ </style>
106
+ </head>
107
+ <body>
108
+
109
+ <div class="topbar">
110
+ <a href="/" class="topbar-logo"><img src="/favicon.svg" alt="" style="width:24px;height:24px;vertical-align:middle;margin-right:8px;">AgentDev</a>
111
+ <span class="topbar-title">Client Documentation</span>
112
+ <div class="topbar-links">
113
+ <a href="/">Dashboard</a>
114
+ <a href="/profile">Profile</a>
115
+ <a href="/device">Device Auth</a>
116
+ </div>
117
+ </div>
118
+
119
+ <div class="layout">
120
+ <nav class="sidebar-nav" id="sidebarNav">
121
+ <div class="nav-section">
122
+ <div class="nav-section-title">Getting Started</div>
123
+ <a href="#overview" class="nav-link">Overview</a>
124
+ <a href="#architecture" class="nav-link">Architecture</a>
125
+ <a href="#installation" class="nav-link">Installation</a>
126
+ <a href="#quickstart" class="nav-link">Quick Start</a>
127
+ </div>
128
+ <div class="nav-section">
129
+ <div class="nav-section-title">Registration</div>
130
+ <a href="#register" class="nav-link">Agent Registration</a>
131
+ <a href="#device-flow" class="nav-link">Device Flow Auth</a>
132
+ <a href="#config" class="nav-link">Configuration</a>
133
+ </div>
134
+ <div class="nav-section">
135
+ <div class="nav-section-title">CLI Commands</div>
136
+ <a href="#cmd-register" class="nav-link">agentdev register</a>
137
+ <a href="#cmd-start" class="nav-link">agentdev start</a>
138
+ <a href="#cmd-cron" class="nav-link">agentdev cron</a>
139
+ <a href="#cmd-status" class="nav-link">agentdev status</a>
140
+ <a href="#cmd-config" class="nav-link">agentdev config</a>
141
+ <a href="#cmd-ticket" class="nav-link">agentdev ticket</a>
142
+ <a href="#cmd-gh" class="nav-link">agentdev gh</a>
143
+ <a href="#cmd-openspec" class="nav-link">agentdev openspec</a>
144
+ <a href="#cmd-onboard" class="nav-link">agentdev onboard</a>
145
+ </div>
146
+ <div class="nav-section">
147
+ <div class="nav-section-title">Agent Work Cycle</div>
148
+ <a href="#work-cycle" class="nav-link">How Agents Work</a>
149
+ <a href="#heartbeat" class="nav-link">Heartbeat</a>
150
+ <a href="#ticket-execution" class="nav-link">Ticket Execution</a>
151
+ <a href="#log-streaming" class="nav-link">Log Streaming</a>
152
+ </div>
153
+ <div class="nav-section">
154
+ <div class="nav-section-title">API Reference</div>
155
+ <a href="#api-device" class="nav-link">Device Flow API</a>
156
+ <a href="#api-agent" class="nav-link">Agent API</a>
157
+ <a href="#api-github" class="nav-link">GitHub Integration</a>
158
+ </div>
159
+ <div class="nav-section">
160
+ <div class="nav-section-title">Advanced</div>
161
+ <a href="#workspace" class="nav-link">Workspace Setup</a>
162
+ <a href="#skills" class="nav-link">Claude Skills</a>
163
+ <a href="#env-vars" class="nav-link">Environment Variables</a>
164
+ <a href="#troubleshooting" class="nav-link">Troubleshooting</a>
165
+ </div>
166
+ </nav>
167
+
168
+ <main class="content">
169
+ <!-- Overview -->
170
+ <div class="section" id="overview">
171
+ <h1>AgentDev Client</h1>
172
+ <p class="subtitle">Distributed agent system for automated GitHub ticket processing with Claude CLI</p>
173
+
174
+ <p>AgentDev is a distributed agent management platform that connects local machines running Claude CLI to a central server. Agents automatically pick up GitHub tickets tagged with <code>@claude</code>, execute the work, and stream logs back to the web dashboard in real-time.</p>
175
+
176
+ <ul>
177
+ <li>Run automated agents on any machine with Claude CLI installed</li>
178
+ <li>Agents poll the server for available tickets and execute them autonomously</li>
179
+ <li>Real-time log streaming to the web dashboard via SSE</li>
180
+ <li>GitHub project board integration for ticket status tracking</li>
181
+ <li>Multi-project support with per-agent project assignment</li>
182
+ <li>Device flow authentication for secure agent registration</li>
183
+ </ul>
184
+ </div>
185
+
186
+ <!-- Architecture -->
187
+ <div class="section" id="architecture">
188
+ <h2>Architecture</h2>
189
+ <div class="diagram">
190
+ ┌──────────────────────────────────┐
191
+ │ Agent Machine (your laptop/VM) │
192
+ │ │
193
+ │ $ agentdev start │
194
+ │ ├── Polls for tickets (10s) │
195
+ │ ├── Runs Claude CLI │
196
+ │ └── Streams logs back │
197
+ │ │
198
+ │ ~/agentdev-workspace/ │
199
+ │ ├── repo-a/ │
200
+ │ ├── repo-b/ │
201
+ │ └── repo-c/ │
202
+ └──────────────┬───────────────────┘
203
+ │ HTTPS (Bearer JWT)
204
+
205
+ ┌──────────────────────────────────┐
206
+ │ AgentDev Server │
207
+ │ agentdev.datatamer.ai │
208
+ │ │
209
+ │ ├── Assign tickets to agents │
210
+ │ ├── Store OAuth tokens (enc) │
211
+ │ ├── Aggregate & broadcast logs │
212
+ │ └── GitHub project board sync │
213
+ └──────────────┬───────────────────┘
214
+
215
+
216
+ ┌──────────────────────────────────┐
217
+ │ GitHub │
218
+ │ ├── Issues (tickets) │
219
+ │ ├── Project Board (statuses) │
220
+ │ └── Repositories (code) │
221
+ └──────────────────────────────────┘</div>
222
+ </div>
223
+
224
+ <!-- Installation -->
225
+ <div class="section" id="installation">
226
+ <h2>Installation</h2>
227
+
228
+ <h3>Prerequisites</h3>
229
+ <table>
230
+ <tr><th>Requirement</th><th>Version</th><th>Check</th></tr>
231
+ <tr><td>Node.js</td><td>18+</td><td><code>node --version</code></td></tr>
232
+ <tr><td>Claude CLI</td><td>latest</td><td><code>claude --version</code></td></tr>
233
+ <tr><td>Git</td><td>any</td><td><code>git --version</code></td></tr>
234
+ </table>
235
+
236
+ <h3>Install via npm</h3>
237
+ <pre><code>npm install -g @datatamer/agentdev</code></pre>
238
+
239
+ <h3>Verify Installation</h3>
240
+ <pre><code>agentdev --version
241
+ # 1.0.0</code></pre>
242
+
243
+ <div class="callout callout-info">
244
+ <strong>Note</strong>
245
+ The GitHub CLI (<code>gh</code>) is optional. AgentDev includes its own GitHub API integration via <code>agentdev gh</code>.
246
+ </div>
247
+ </div>
248
+
249
+ <!-- Quick Start -->
250
+ <div class="section" id="quickstart">
251
+ <h2>Quick Start</h2>
252
+ <pre><code># 1. Register your agent with the server
253
+ agentdev register
254
+
255
+ # 2. Approve the device code in your browser
256
+ # Visit the URL shown and enter the code
257
+
258
+ # 3. Start processing tickets
259
+ agentdev start
260
+
261
+ # 4. Check status
262
+ agentdev status</code></pre>
263
+
264
+ <p>That's it. Your agent will poll for tickets, execute them with Claude CLI, and stream logs to the dashboard.</p>
265
+ </div>
266
+
267
+ <!-- Registration -->
268
+ <div class="section" id="register">
269
+ <h2>Agent Registration</h2>
270
+
271
+ <p>Each agent must be registered with the server before it can claim work. Registration uses the OAuth 2.0 Device Authorization Grant flow.</p>
272
+
273
+ <h3>Step 1: Run Register</h3>
274
+ <pre><code>agentdev register
275
+ # or with options:
276
+ agentdev register --url https://agentdev.datatamer.ai --name my-agent</code></pre>
277
+
278
+ <table>
279
+ <tr><th>Option</th><th>Default</th><th>Description</th></tr>
280
+ <tr><td><code>-u, --url</code></td><td><code>https://agentdev.datatamer.ai</code></td><td>Server URL</td></tr>
281
+ <tr><td><code>-n, --name</code></td><td>System hostname</td><td>Agent display name</td></tr>
282
+ </table>
283
+
284
+ <h3>Step 2: Approve in Browser</h3>
285
+ <p>The terminal will display a user code and verification URL:</p>
286
+ <pre><code>✓ Device code obtained
287
+ Code: ABCD-EFGH
288
+ Visit: https://agentdev.datatamer.ai/device
289
+ Waiting for approval...</code></pre>
290
+
291
+ <ol>
292
+ <li>Open the verification URL in your browser</li>
293
+ <li>Log in to AgentDev if not already signed in</li>
294
+ <li>Enter the user code displayed in your terminal</li>
295
+ <li>Select the project to assign the agent to</li>
296
+ <li>Click "Approve Agent"</li>
297
+ </ol>
298
+
299
+ <h3>Step 3: Confirmation</h3>
300
+ <pre><code>✓ Agent registered successfully!
301
+ Agent ID: agent-1704067200-abc123
302
+ Project: DataTamer (#1)</code></pre>
303
+
304
+ <div class="callout callout-success">
305
+ <strong>Done</strong>
306
+ Your agent token and project configuration are saved to <code>~/.config/agentdev/config.json</code>.
307
+ </div>
308
+ </div>
309
+
310
+ <!-- Device Flow -->
311
+ <div class="section" id="device-flow">
312
+ <h2>Device Flow Authentication</h2>
313
+
314
+ <p>AgentDev uses the OAuth 2.0 Device Authorization Grant (RFC 8628) for agent authentication. This is ideal for headless servers and CLI tools that can't open a browser.</p>
315
+
316
+ <h3>How It Works</h3>
317
+ <ol>
318
+ <li>Agent requests a device code from the server</li>
319
+ <li>Server returns a short user code (e.g., <code>ABCD-EFGH</code>) and verification URL</li>
320
+ <li>User visits the URL in a browser and enters the code</li>
321
+ <li>User approves and selects a project for the agent</li>
322
+ <li>Agent polls the server until the code is approved (every 5 seconds)</li>
323
+ <li>Server returns a JWT token valid for 90 days</li>
324
+ </ol>
325
+
326
+ <h3>JWT Token</h3>
327
+ <p>The token contains:</p>
328
+ <table>
329
+ <tr><th>Field</th><th>Description</th></tr>
330
+ <tr><td><code>agent_id</code></td><td>Unique agent identifier</td></tr>
331
+ <tr><td><code>user_id</code></td><td>ID of the user who approved the agent</td></tr>
332
+ <tr><td><code>project_id</code></td><td>Assigned project ID</td></tr>
333
+ <tr><td><code>type</code></td><td>Always <code>"agent"</code></td></tr>
334
+ <tr><td><code>exp</code></td><td>Expires in 90 days</td></tr>
335
+ </table>
336
+ </div>
337
+
338
+ <!-- Configuration -->
339
+ <div class="section" id="config">
340
+ <h2>Configuration</h2>
341
+
342
+ <p>Configuration is stored at <code>~/.config/agentdev/config.json</code> and managed via the <code>agentdev config</code> command or set automatically during registration.</p>
343
+
344
+ <h3>Config File</h3>
345
+ <pre><code>{
346
+ "api_url": "https://agentdev.datatamer.ai",
347
+ "agent_token": "eyJhbGciOi...",
348
+ "agent_id": "agent-1704067200-abc123",
349
+ "agent_name": "my-laptop",
350
+ "max_concurrent": 1,
351
+ "github_org": "data-tamer",
352
+ "project_id": "PVT_kwDOCJIWbs4AnuSZ",
353
+ "project_number": 1,
354
+ "status_field_id": "PVTSSF_lADOCJIWbs4AnuSZzgfaWGs",
355
+ "status_options": {
356
+ "TODO": "f75ad846",
357
+ "IN_PROGRESS": "47fc9ee4",
358
+ "TEST": "c48bc058",
359
+ "DONE": "98236657"
360
+ }
361
+ }</code></pre>
362
+
363
+ <h3>Managing Config</h3>
364
+ <pre><code># View all settings
365
+ agentdev config get
366
+
367
+ # View a specific key
368
+ agentdev config get api_url
369
+
370
+ # Update a setting
371
+ agentdev config set api_url https://agentdev.datatamer.ai</code></pre>
372
+ </div>
373
+
374
+ <!-- CLI Commands -->
375
+ <div class="section" id="cmd-register">
376
+ <h2>CLI Commands</h2>
377
+
378
+ <h3>agentdev register</h3>
379
+ <p>Register this agent with the AgentDev server using device flow authentication.</p>
380
+ <pre><code>agentdev register [--url &lt;url&gt;] [--name &lt;name&gt;]</code></pre>
381
+ <table>
382
+ <tr><th>Option</th><th>Default</th><th>Description</th></tr>
383
+ <tr><td><code>-u, --url</code></td><td><code>https://agentdev.datatamer.ai</code></td><td>Server URL</td></tr>
384
+ <tr><td><code>-n, --name</code></td><td>hostname</td><td>Agent name</td></tr>
385
+ </table>
386
+ </div>
387
+
388
+ <div class="section" id="cmd-start">
389
+ <h3>agentdev start</h3>
390
+ <p>Start the agent in daemon mode. Continuously polls for work and executes tickets.</p>
391
+ <pre><code>agentdev start [--concurrent &lt;number&gt;]</code></pre>
392
+ <table>
393
+ <tr><th>Option</th><th>Default</th><th>Description</th></tr>
394
+ <tr><td><code>-c, --concurrent</code></td><td><code>1</code></td><td>Max concurrent tickets</td></tr>
395
+ </table>
396
+ <p>Behavior:</p>
397
+ <ul>
398
+ <li>Polls for work every <strong>10 seconds</strong></li>
399
+ <li>Sends heartbeat every <strong>30 seconds</strong></li>
400
+ <li>Syncs Claude skills to <code>~/.claude/commands/</code></li>
401
+ <li>Fetches GitHub OAuth tokens from server</li>
402
+ <li>30-second backoff on errors</li>
403
+ <li>Graceful shutdown on SIGINT/SIGTERM</li>
404
+ </ul>
405
+ </div>
406
+
407
+ <div class="section" id="cmd-cron">
408
+ <h3>agentdev cron</h3>
409
+ <p>Run in batch/cron mode. Processes all available tickets in cycles with a configurable interval.</p>
410
+ <pre><code>agentdev cron [--interval &lt;minutes&gt;] [--max-tickets &lt;number&gt;]</code></pre>
411
+ <table>
412
+ <tr><th>Option</th><th>Default</th><th>Description</th></tr>
413
+ <tr><td><code>-i, --interval</code></td><td><code>5</code></td><td>Minutes between cycles</td></tr>
414
+ <tr><td><code>-m, --max-tickets</code></td><td><code>10</code></td><td>Max tickets per cycle</td></tr>
415
+ </table>
416
+ <p>Ideal for CI/CD pipelines or scheduled batch processing.</p>
417
+ </div>
418
+
419
+ <div class="section" id="cmd-status">
420
+ <h3>agentdev status</h3>
421
+ <p>Display the agent's registration status and configuration.</p>
422
+ <pre><code>agentdev status</code></pre>
423
+ <p>Shows: agent ID, name, API URL, registration status, config file location.</p>
424
+ </div>
425
+
426
+ <div class="section" id="cmd-config">
427
+ <h3>agentdev config</h3>
428
+ <p>View and modify configuration values.</p>
429
+ <pre><code># View all config
430
+ agentdev config get
431
+
432
+ # View single key
433
+ agentdev config get api_url
434
+
435
+ # Set a value
436
+ agentdev config set max_concurrent 2</code></pre>
437
+ </div>
438
+
439
+ <div class="section" id="cmd-ticket">
440
+ <h3>agentdev ticket</h3>
441
+ <p>Create and manage tickets from the command line.</p>
442
+ <pre><code># Create a ticket
443
+ agentdev ticket create -R my-repo \
444
+ --title "Fix login bug" \
445
+ --body "The login form crashes on submit" \
446
+ --claude
447
+
448
+ # Output (JSON)
449
+ agentdev ticket create -R my-repo --title "..." --json</code></pre>
450
+ <table>
451
+ <tr><th>Option</th><th>Description</th></tr>
452
+ <tr><td><code>-R &lt;repo&gt;</code></td><td>Repository name (under the org)</td></tr>
453
+ <tr><td><code>--title</code></td><td>Issue title</td></tr>
454
+ <tr><td><code>--body</code></td><td>Issue body text</td></tr>
455
+ <tr><td><code>--body-file</code></td><td>Read body from file</td></tr>
456
+ <tr><td><code>--claude / --no-claude</code></td><td>Append <code>@claude</code> tag (default: true)</td></tr>
457
+ <tr><td><code>--json</code></td><td>Output as JSON</td></tr>
458
+ </table>
459
+ <p>Creates the GitHub issue, adds it to the project board, and sets status to "Todo".</p>
460
+ </div>
461
+
462
+ <div class="section" id="cmd-gh">
463
+ <h3>agentdev gh</h3>
464
+ <p>GitHub CLI replacement with pre-configured authentication. Uses the agent's OAuth token.</p>
465
+
466
+ <h4>Issues</h4>
467
+ <pre><code># View an issue
468
+ agentdev gh issue view 42 -R owner/repo
469
+
470
+ # Create an issue
471
+ agentdev gh issue create -R owner/repo --title "Bug" --body "Details"
472
+
473
+ # Comment on an issue
474
+ agentdev gh issue comment 42 -R owner/repo --body "Fixed in PR #43"
475
+
476
+ # Reopen an issue
477
+ agentdev gh issue reopen 42 -R owner/repo</code></pre>
478
+
479
+ <h4>Pull Requests</h4>
480
+ <pre><code># Create a PR
481
+ agentdev gh pr create -R owner/repo \
482
+ --title "Fix bug" --body "Description" \
483
+ --head feature-branch --base main
484
+
485
+ # View a PR
486
+ agentdev gh pr view 43 -R owner/repo --json state,title
487
+
488
+ # Merge a PR
489
+ agentdev gh pr merge 43 -R owner/repo --squash</code></pre>
490
+
491
+ <h4>Project Board</h4>
492
+ <pre><code># Update project item status
493
+ agentdev gh project item-edit \
494
+ --id ITEM_ID \
495
+ --project-id PROJECT_ID \
496
+ --field-id FIELD_ID \
497
+ --single-select-option-id OPTION_ID</code></pre>
498
+
499
+ <h4>Raw API</h4>
500
+ <pre><code># GraphQL
501
+ agentdev gh api graphql -f query='{ viewer { login } }'
502
+
503
+ # REST
504
+ agentdev gh api rest /repos/owner/repo/issues -X GET</code></pre>
505
+ </div>
506
+
507
+ <div class="section" id="cmd-openspec">
508
+ <h3>agentdev openspec</h3>
509
+ <p>Spec-driven development commands. Wraps the <code>@fission-ai/openspec</code> CLI.</p>
510
+ <pre><code># Initialize openspec in a repo
511
+ agentdev openspec init
512
+
513
+ # List artifacts
514
+ agentdev openspec list --specs --sort recent
515
+
516
+ # Show artifact details
517
+ agentdev openspec show my-feature --json
518
+
519
+ # Validate artifacts
520
+ agentdev openspec validate --all --strict
521
+
522
+ # Get implementation instructions
523
+ agentdev openspec instructions my-artifact --change feature-x
524
+
525
+ # Archive a completed change
526
+ agentdev openspec archive my-change --yes</code></pre>
527
+ </div>
528
+
529
+ <div class="section" id="cmd-onboard">
530
+ <h3>agentdev onboard</h3>
531
+ <p>Interactive repository discovery and environment skill generation.</p>
532
+ <pre><code>agentdev onboard [--workspace &lt;path&gt;] [--no-claude] [--force]</code></pre>
533
+ <table>
534
+ <tr><th>Option</th><th>Default</th><th>Description</th></tr>
535
+ <tr><td><code>-w, --workspace</code></td><td>current dir</td><td>Workspace root to scan</td></tr>
536
+ <tr><td><code>--no-claude</code></td><td>false</td><td>Use templates instead of Claude generation</td></tr>
537
+ <tr><td><code>--force</code></td><td>false</td><td>Overwrite existing skills</td></tr>
538
+ </table>
539
+
540
+ <p>The onboard command:</p>
541
+ <ol>
542
+ <li>Scans the workspace for git repositories</li>
543
+ <li>Detects language, framework, databases, test runner, Docker setup</li>
544
+ <li>Prompts you to select repos to onboard</li>
545
+ <li>Asks for environment details (deploy method, test commands, DB connections)</li>
546
+ <li>Generates Claude skill files at <code>.claude/commands/env-&lt;name&gt;.md</code></li>
547
+ <li>Saves structured metadata to <code>.claude/repo-spec.json</code></li>
548
+ </ol>
549
+
550
+ <h4>Detected Technologies</h4>
551
+ <table>
552
+ <tr><th>Category</th><th>Detected</th></tr>
553
+ <tr><td>Languages</td><td>JavaScript, TypeScript, Python</td></tr>
554
+ <tr><td>Frameworks</td><td>Next.js, Express, Fastify, FastAPI, Django, Flask</td></tr>
555
+ <tr><td>Databases</td><td>PostgreSQL, MySQL, MongoDB, Redis, SQLite</td></tr>
556
+ <tr><td>ORMs</td><td>Prisma, SQLAlchemy, Mongoose</td></tr>
557
+ <tr><td>Test Runners</td><td>Jest, Vitest, Mocha, pytest, Playwright</td></tr>
558
+ <tr><td>Package Managers</td><td>npm, yarn, pnpm, pip, poetry, uv</td></tr>
559
+ </table>
560
+ </div>
561
+
562
+ <!-- Work Cycle -->
563
+ <div class="section" id="work-cycle">
564
+ <h2>How Agents Work</h2>
565
+
566
+ <div class="diagram">
567
+ Agent starts: agentdev start
568
+
569
+ ├── Sync Claude skills to ~/.claude/commands/
570
+ ├── Fetch OAuth tokens (GitHub) from server
571
+ ├── Start heartbeat (every 30s)
572
+
573
+
574
+ ┌─── Poll Loop (every 10s) ──────────────────────────┐
575
+ │ │
576
+ │ GET /api/agent/work │
577
+ │ ├── No ticket → print "." → wait 10s → loop │
578
+ │ └── Ticket found ↓ │
579
+ │ │
580
+ │ Execute Ticket │
581
+ │ ├── Set GH_TOKEN in env │
582
+ │ ├── Spawn: claude -p --dangerously-skip-perms │
583
+ │ │ /auto-ticket-workflow │
584
+ │ ├── Stream logs → POST /api/agent/logs (2s batch) │
585
+ │ ├── Check stop signal (every 5s) │
586
+ │ └── On exit → POST /api/agent/complete │
587
+ │ │
588
+ │ Loop back ↑ │
589
+ └──────────────────────────────────────────────────────┘</div>
590
+ </div>
591
+
592
+ <div class="section" id="heartbeat">
593
+ <h3>Heartbeat</h3>
594
+ <p>Every <strong>30 seconds</strong>, the agent sends a heartbeat to keep its status updated on the server.</p>
595
+ <pre><code>POST /api/agent/heartbeat
596
+ Authorization: Bearer &lt;token&gt;
597
+
598
+ {
599
+ "status": "idle", // or "busy"
600
+ "current_ticket": null // or ticket ID
601
+ }</code></pre>
602
+ <p>If the server doesn't receive a heartbeat for an extended period, the agent is considered offline.</p>
603
+ </div>
604
+
605
+ <div class="section" id="ticket-execution">
606
+ <h3>Ticket Execution</h3>
607
+ <p>When an agent claims a ticket, it:</p>
608
+ <ol>
609
+ <li>Sets <code>GH_TOKEN</code> environment variable from the server's OAuth tokens</li>
610
+ <li>Creates a temp file for Claude output (avoids pipe buffering)</li>
611
+ <li>Spawns Claude CLI:
612
+ <pre><code>claude -p --dangerously-skip-permissions \
613
+ --output-format stream-json --verbose</code></pre>
614
+ </li>
615
+ <li>Sends the <code>/auto-ticket-workflow</code> skill as the prompt</li>
616
+ <li>Tails the output file and parses stream-json in real-time</li>
617
+ <li>Batches parsed logs to the server every 2 seconds</li>
618
+ <li>Polls <code>/api/agent/should-stop</code> every 5 seconds (kills process if requested)</li>
619
+ <li>On process exit, reports completion via <code>/api/agent/complete</code></li>
620
+ </ol>
621
+ </div>
622
+
623
+ <div class="section" id="log-streaming">
624
+ <h3>Log Streaming</h3>
625
+ <p>Logs are batched and uploaded to the server in real-time:</p>
626
+ <ul>
627
+ <li><strong>Batch interval:</strong> every 2 seconds</li>
628
+ <li><strong>Batch size:</strong> up to 10 log entries</li>
629
+ <li><strong>Parsed content:</strong> text output, tool calls, tool results, cost info</li>
630
+ </ul>
631
+ <p>The server stores logs in PostgreSQL and broadcasts them to the web dashboard via SSE (Server-Sent Events).</p>
632
+ </div>
633
+
634
+ <!-- API Reference -->
635
+ <div class="section" id="api-device">
636
+ <h2>API Reference</h2>
637
+
638
+ <h3>Device Flow Endpoints</h3>
639
+
640
+ <h4><span class="badge badge-post">POST</span> /api/agent/device/code</h4>
641
+ <p>Request a new device code for agent registration.</p>
642
+ <pre><code>// Request
643
+ {
644
+ "agent_name": "my-agent",
645
+ "capabilities": {
646
+ "cpu_cores": 8,
647
+ "memory_gb": 16,
648
+ "platform": "linux",
649
+ "arch": "x64",
650
+ "tools": ["git", "node", "claude"]
651
+ }
652
+ }
653
+
654
+ // Response (200)
655
+ {
656
+ "device_code": "a1b2c3d4...",
657
+ "user_code": "ABCD-EFGH",
658
+ "verification_uri": "https://agentdev.datatamer.ai/device",
659
+ "expires_in": 600,
660
+ "interval": 5
661
+ }</code></pre>
662
+
663
+ <h4><span class="badge badge-post">POST</span> /api/agent/device/token</h4>
664
+ <p>Poll for token after user approves the device code.</p>
665
+ <pre><code>// Request
666
+ { "device_code": "a1b2c3d4..." }
667
+
668
+ // Response (200 - approved)
669
+ {
670
+ "access_token": "eyJhbGciOi...",
671
+ "token_type": "Bearer",
672
+ "expires_in": 7776000,
673
+ "agent_id": "agent-1704067200-abc123",
674
+ "project_id": 1
675
+ }
676
+
677
+ // Response (428 - still pending)
678
+ { "error": "authorization_pending" }</code></pre>
679
+ </div>
680
+
681
+ <div class="section" id="api-agent">
682
+ <h3>Agent API Endpoints</h3>
683
+ <p>All endpoints require <code>Authorization: Bearer &lt;token&gt;</code>.</p>
684
+
685
+ <table>
686
+ <tr><th>Method</th><th>Endpoint</th><th>Description</th></tr>
687
+ <tr><td><span class="badge badge-post">POST</span></td><td><code>/api/agent/heartbeat</code></td><td>Send agent heartbeat with status</td></tr>
688
+ <tr><td><span class="badge badge-get">GET</span></td><td><code>/api/agent/work</code></td><td>Claim the next available ticket</td></tr>
689
+ <tr><td><span class="badge badge-get">GET</span></td><td><code>/api/agent/oauth</code></td><td>Get GitHub OAuth token</td></tr>
690
+ <tr><td><span class="badge badge-post">POST</span></td><td><code>/api/agent/logs</code></td><td>Upload log entries</td></tr>
691
+ <tr><td><span class="badge badge-post">POST</span></td><td><code>/api/agent/complete</code></td><td>Mark ticket as complete/failed</td></tr>
692
+ <tr><td><span class="badge badge-get">GET</span></td><td><code>/api/agent/should-stop</code></td><td>Check for stop signal</td></tr>
693
+ <tr><td><span class="badge badge-get">GET</span></td><td><code>/api/agent/project-config</code></td><td>Get project board configuration</td></tr>
694
+ </table>
695
+
696
+ <h4><span class="badge badge-get">GET</span> /api/agent/work</h4>
697
+ <p>Claims the next available ticket for the agent's assigned project.</p>
698
+ <pre><code>// Response (ticket available)
699
+ {
700
+ "ticket": {
701
+ "id": "ticket-uuid",
702
+ "github_issue_number": 42,
703
+ "github_repo": "agentdev-webui",
704
+ "title": "Implement dark mode",
705
+ "description": "Add dark theme support"
706
+ }
707
+ }
708
+
709
+ // Response (no tickets)
710
+ { "ticket": null }</code></pre>
711
+
712
+ <h4><span class="badge badge-post">POST</span> /api/agent/logs</h4>
713
+ <p>Upload log entries. Batched by the client.</p>
714
+ <pre><code>// Request
715
+ {
716
+ "logs": ["Log line 1", "Log line 2"],
717
+ "ticket_id": "ticket-uuid"
718
+ }
719
+
720
+ // Response
721
+ { "success": true, "count": 2 }</code></pre>
722
+
723
+ <h4><span class="badge badge-post">POST</span> /api/agent/complete</h4>
724
+ <p>Report ticket completion or failure.</p>
725
+ <pre><code>// Request
726
+ {
727
+ "ticket_id": "ticket-uuid",
728
+ "success": true,
729
+ "error_message": null
730
+ }
731
+
732
+ // Response
733
+ { "success": true }</code></pre>
734
+
735
+ <h4><span class="badge badge-get">GET</span> /api/agent/should-stop?ticket_id=ID</h4>
736
+ <p>Check if the user has requested to stop this ticket's execution.</p>
737
+ <pre><code>// Response
738
+ { "should_stop": false }</code></pre>
739
+ </div>
740
+
741
+ <div class="section" id="api-github">
742
+ <h3>GitHub Integration</h3>
743
+ <p>The client includes a built-in GitHub API module (<code>agentdev gh</code>) that supports:</p>
744
+ <ul>
745
+ <li><strong>REST API</strong> &mdash; Issues, PRs, file uploads, generic REST calls</li>
746
+ <li><strong>GraphQL API</strong> &mdash; Project board operations, custom queries</li>
747
+ <li><strong>Token resolution</strong> &mdash; CLI arg &rarr; <code>GH_TOKEN</code> env &rarr; server OAuth</li>
748
+ </ul>
749
+ <p>This eliminates the dependency on the <code>gh</code> CLI while providing full GitHub API access.</p>
750
+ </div>
751
+
752
+ <!-- Advanced -->
753
+ <div class="section" id="workspace">
754
+ <h2>Workspace Setup</h2>
755
+
756
+ <h3>Default Workspace</h3>
757
+ <p>When you run <code>agentdev start</code>, the agent uses <code>~/agentdev-workspace/</code> by default.</p>
758
+
759
+ <h3>Custom Workspace</h3>
760
+ <pre><code># Set for current session
761
+ export AGENTDEV_WORKSPACE=/path/to/your/repos
762
+ agentdev start
763
+
764
+ # Set permanently
765
+ echo 'export AGENTDEV_WORKSPACE=/path/to/repos' >> ~/.bashrc</code></pre>
766
+
767
+ <div class="callout callout-info">
768
+ <strong>Tip</strong>
769
+ Using a custom workspace lets agents work with your existing cloned repos, avoiding redundant clones.
770
+ </div>
771
+ </div>
772
+
773
+ <div class="section" id="skills">
774
+ <h3>Claude Skills</h3>
775
+ <p>On startup, the agent syncs skill files from the package to <code>~/.claude/commands/</code>:</p>
776
+ <table>
777
+ <tr><th>Skill File</th><th>Purpose</th></tr>
778
+ <tr><td><code>agentdev-gh.md</code></td><td>GitHub CLI command reference for Claude</td></tr>
779
+ <tr><td><code>agentdev-openspec.md</code></td><td>OpenSpec workflow reference for Claude</td></tr>
780
+ <tr><td><code>auto-ticket.md</code></td><td>Full ticket workflow (proposal &rarr; implement &rarr; test &rarr; deploy)</td></tr>
781
+ </table>
782
+ <p>These skills are used as prompts when Claude executes tickets.</p>
783
+ </div>
784
+
785
+ <div class="section" id="env-vars">
786
+ <h3>Environment Variables</h3>
787
+ <table>
788
+ <tr><th>Variable</th><th>Description</th><th>Default</th></tr>
789
+ <tr><td><code>GH_TOKEN</code></td><td>GitHub personal access token</td><td>From server OAuth</td></tr>
790
+ <tr><td><code>AGENTDEV_WORKSPACE</code></td><td>Workspace directory path</td><td><code>~/agentdev-workspace</code></td></tr>
791
+ <tr><td><code>CLAUDE_BIN</code></td><td>Path to Claude CLI binary</td><td><code>claude</code></td></tr>
792
+ <tr><td><code>TICKET_NUMBER</code></td><td>Current ticket (set by executor)</td><td>&mdash;</td></tr>
793
+ <tr><td><code>REPO</code></td><td>Current repository (set by executor)</td><td>&mdash;</td></tr>
794
+ <tr><td><code>AGENT_ID</code></td><td>Agent identifier (set by executor)</td><td>&mdash;</td></tr>
795
+ </table>
796
+ </div>
797
+
798
+ <div class="section" id="troubleshooting">
799
+ <h3>Troubleshooting</h3>
800
+
801
+ <h4>Agent not picking up tickets</h4>
802
+ <ul>
803
+ <li>Verify the agent is running: <code>agentdev status</code></li>
804
+ <li>Check that tickets exist on the project board with status "Todo"</li>
805
+ <li>Ensure tickets have <code>@claude</code> in the description</li>
806
+ <li>Verify GitHub OAuth is configured in your <a href="/profile">Profile</a></li>
807
+ <li>Check that the agent is assigned to the correct project</li>
808
+ </ul>
809
+
810
+ <h4>Registration fails</h4>
811
+ <ul>
812
+ <li>Ensure the server URL is reachable: <code>curl https://agentdev.datatamer.ai</code></li>
813
+ <li>The device code expires after 10 minutes &mdash; re-run <code>agentdev register</code></li>
814
+ <li>Make sure you're logged into the web dashboard when approving</li>
815
+ </ul>
816
+
817
+ <h4>"claude: command not found"</h4>
818
+ <p>Install Claude CLI from <a href="https://claude.ai/download" target="_blank">claude.ai/download</a> and ensure it's in your PATH.</p>
819
+
820
+ <h4>Token expired</h4>
821
+ <p>Agent tokens expire after 90 days. Re-register:</p>
822
+ <pre><code>agentdev register</code></pre>
823
+
824
+ <h4>Check config file</h4>
825
+ <pre><code>cat ~/.config/agentdev/config.json</code></pre>
826
+ </div>
827
+
828
+ <div class="footer">
829
+ <p>AgentDev Client v1.0.0 &mdash; <a href="/">Dashboard</a> &mdash; Last updated February 2026</p>
830
+ </div>
831
+ </main>
832
+ </div>
833
+
834
+ <script>
835
+ // Highlight active nav link on scroll
836
+ const sections = document.querySelectorAll('.section');
837
+ const navLinks = document.querySelectorAll('.nav-link');
838
+
839
+ function updateActiveLink() {
840
+ let current = '';
841
+ sections.forEach(s => {
842
+ if (window.scrollY >= s.offsetTop - 80) current = s.id;
843
+ });
844
+ navLinks.forEach(l => {
845
+ l.classList.toggle('active', l.getAttribute('href') === '#' + current);
846
+ });
847
+ }
848
+
849
+ window.addEventListener('scroll', updateActiveLink);
850
+ updateActiveLink();
851
+
852
+ // Smooth scroll for nav links
853
+ navLinks.forEach(link => {
854
+ link.addEventListener('click', e => {
855
+ e.preventDefault();
856
+ const target = document.querySelector(link.getAttribute('href'));
857
+ if (target) target.scrollIntoView({ behavior: 'smooth' });
858
+ });
859
+ });
860
+ </script>
861
+ </body>
862
+ </html>