astrabot 0.1.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.
- package/README.md +411 -0
- package/ai/ai.config.ts +27 -0
- package/ai/auto-retry.ts +117 -0
- package/ai/config-loader.ts +132 -0
- package/ai/index.ts +4 -0
- package/ai/retry-prompt.ts +30 -0
- package/bin/astra +2 -0
- package/core/retry/error-classifier.ts +208 -0
- package/core/retry/index.ts +29 -0
- package/core/retry/retry-config.ts +142 -0
- package/core/retry/retry-engine.ts +215 -0
- package/game/index.html +573 -0
- package/game/neon-breaker.html +1037 -0
- package/index.ts +140 -0
- package/modes/agent/action-tracker.ts +47 -0
- package/modes/agent/agent-tools.ts +338 -0
- package/modes/agent/approval.ts +184 -0
- package/modes/agent/diff-view.ts +34 -0
- package/modes/agent/orchestrator.ts +234 -0
- package/modes/agent/tool-executor.ts +993 -0
- package/modes/agent/types.ts +68 -0
- package/modes/ask/orchestrator.ts +230 -0
- package/modes/auto.ts +88 -0
- package/modes/cli.ts +43 -0
- package/modes/multi/agent-pool-manager.ts +337 -0
- package/modes/multi/examples.ts +441 -0
- package/modes/multi/message-broker.ts +179 -0
- package/modes/multi/multi-agent-orchestrator.ts +891 -0
- package/modes/multi/orchestrator.ts +414 -0
- package/modes/multi/types.ts +245 -0
- package/modes/multi/workflow-builder.ts +569 -0
- package/modes/plan/orchestrator.ts +198 -0
- package/modes/plan/planner.ts +121 -0
- package/modes/plan/selection.ts +43 -0
- package/modes/plan/types.ts +13 -0
- package/modes/plan/web-tools.ts +132 -0
- package/modes/setup.ts +210 -0
- package/package.json +62 -0
- package/session/index.ts +45 -0
- package/session/session-context.ts +188 -0
- package/session/session-manager.ts +374 -0
- package/session/session-tools.ts +109 -0
- package/session/store.ts +278 -0
- package/tsconfig.json +30 -0
- package/tui/spinner.ts +182 -0
- package/tui/terminal-md.ts +17 -0
- package/tui/wakeup.ts +231 -0
package/README.md
ADDED
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
# Astra
|
|
2
|
+
|
|
3
|
+
**AI-native development companion — agentic coding in your terminal.**
|
|
4
|
+
|
|
5
|
+
Astra gives a Large Language Model full programmatic access to your filesystem, shell, and the web — all gated behind a staging-first approval pipeline that keeps you in control. Built on [Bun](https://bun.sh), powered by [OpenRouter](https://openrouter.ai), and driven by the [Vercel AI SDK](https://sdk.vercel.ai).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
10
|
+
|
|
11
|
+
- [Features](#features)
|
|
12
|
+
- [Quick Start](#quick-start)
|
|
13
|
+
- [Commands](#commands)
|
|
14
|
+
- [Interaction Modes](#interaction-modes)
|
|
15
|
+
- [Auto Mode](#auto-mode)
|
|
16
|
+
- [Agent Mode](#agent-mode)
|
|
17
|
+
- [Ask Mode](#ask-mode)
|
|
18
|
+
- [Plan Mode](#plan-mode)
|
|
19
|
+
- [Multi-Agent Mode](#multi-agent-mode)
|
|
20
|
+
- [Architecture](#architecture)
|
|
21
|
+
- [Core Components](#core-components)
|
|
22
|
+
- [Tool System](#tool-system)
|
|
23
|
+
- [Staging & Approval Pipeline](#staging--approval-pipeline)
|
|
24
|
+
- [Action Tracking](#action-tracking)
|
|
25
|
+
- [Session Management](#session-management)
|
|
26
|
+
- [Auto-Retry Engine](#auto-retry-engine)
|
|
27
|
+
- [Environment Variables](#environment-variables)
|
|
28
|
+
- [Project Structure](#project-structure)
|
|
29
|
+
- [Dependencies](#dependencies)
|
|
30
|
+
- [Roadmap](#roadmap)
|
|
31
|
+
- [License](#license)
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Features
|
|
36
|
+
|
|
37
|
+
- **Five interaction modes** — Auto, Agent, Ask, Plan, and Multi-Agent, each tailored to a different development workflow
|
|
38
|
+
- **Auto-router** — classifies user intent and routes to the correct mode automatically
|
|
39
|
+
- **Full filesystem access** — read, create, modify, and delete files and directories through an AI agent, all via an in-memory staging overlay
|
|
40
|
+
- **Shell execution** — queue arbitrary shell commands for agent-driven workflows (sync and background)
|
|
41
|
+
- **Git integration** — `git status`, `git diff`, and `git log` tools for repository awareness
|
|
42
|
+
- **Project-aware tooling** — run tests, linting, formatting, and framework detection by reading `package.json`
|
|
43
|
+
- **Web research** — built-in web search (via DuckDuckGo or Firecrawl), URL crawling, and HTTP fetching
|
|
44
|
+
- **Staging-first mutations** — no file is ever written or deleted without explicit user approval; all changes are staged in memory and presented for review before apply
|
|
45
|
+
- **Per-file diff review** — granular approval flow with unified diffs so you can inspect exactly what changed
|
|
46
|
+
- **Skill system** — discover and load `SKILL.md` files from Cursor and Claude skill directories, plus custom directories via `SKILLS_DIRS`
|
|
47
|
+
- **Session management** — sessions are persisted to disk with context summaries, enabling resumption after interruption
|
|
48
|
+
- **Auto-retry engine** — exponential backoff with jitter for resilient AI provider calls
|
|
49
|
+
- **Configurable safety** — exclude patterns, file size limits, and per-tool permission toggles per agent role
|
|
50
|
+
- **Rich terminal UI** — interactive prompts via `@clack/prompts`, markdown rendering, ASCII banner on startup, animated spinners, and colored logging
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Quick Start
|
|
55
|
+
|
|
56
|
+
### Prerequisites
|
|
57
|
+
|
|
58
|
+
| Requirement | Version | Purpose |
|
|
59
|
+
|-------------|---------|---------|
|
|
60
|
+
| [Bun](https://bun.sh) | >=1.0.0 | Runtime and package manager |
|
|
61
|
+
| [OpenRouter](https://openrouter.ai) API key | — | LLM provider access (required) |
|
|
62
|
+
| [Firecrawl](https://www.firecrawl.dev/) API key | — | Web search and crawling (optional) |
|
|
63
|
+
|
|
64
|
+
### Installation
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
git clone <repository-url>
|
|
68
|
+
cd astra
|
|
69
|
+
bun install
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Configuration
|
|
73
|
+
|
|
74
|
+
Run the interactive setup wizard:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
bun run index.ts setup
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
This creates `~/.astra/.env` with your API keys and preferred model. Alternatively, create the file manually:
|
|
81
|
+
|
|
82
|
+
```env
|
|
83
|
+
OPENROUTER_API_KEY=sk-or-...
|
|
84
|
+
OPENROUTER_DEFAULT_MODEL=anthropic/claude-sonnet-4.5
|
|
85
|
+
FIRECRAWL_API_KEY=fc-... # optional
|
|
86
|
+
SKILLS_DIRS=/path/to/skills # optional
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Launch
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
bun run index.ts wakeup
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Commands
|
|
98
|
+
|
|
99
|
+
| Command | Description |
|
|
100
|
+
|---------|-------------|
|
|
101
|
+
| `astra wakeup` | Display the banner and pick an interaction mode |
|
|
102
|
+
| `astra setup` | Interactive configuration wizard for `~/.astra/.env` |
|
|
103
|
+
| `astra play` | Launch the arcade mini-game (Snake or Neon Breaker) |
|
|
104
|
+
| `astra reset` | Purge all stored configurations, sessions, and credentials |
|
|
105
|
+
| `astra --version` | Print the current version |
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Interaction Modes
|
|
110
|
+
|
|
111
|
+
### Auto Mode
|
|
112
|
+
|
|
113
|
+
Auto mode classifies your natural-language request and routes it to the correct downstream mode (Agent, Ask, Plan, or Multi-Agent) automatically.
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
You: "fix the bug in store.ts"
|
|
117
|
+
→ Router classifies as "agent" → executes Agent Mode
|
|
118
|
+
|
|
119
|
+
You: "explain how this app works"
|
|
120
|
+
→ Router classifies as "ask" → executes Ask Mode
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**How it works:**
|
|
124
|
+
|
|
125
|
+
1. You type any request
|
|
126
|
+
2. An LLM classifies the intent into one of four categories
|
|
127
|
+
3. The session is logged with the routing decision
|
|
128
|
+
4. The selected mode executes with your original prompt
|
|
129
|
+
|
|
130
|
+
### Agent Mode
|
|
131
|
+
|
|
132
|
+
The primary autonomous coding mode. You describe a goal, and the agent iteratively uses its toolset — reading files, writing code, running shell commands — to accomplish it.
|
|
133
|
+
|
|
134
|
+
**Flow:**
|
|
135
|
+
|
|
136
|
+
1. You provide a natural-language goal
|
|
137
|
+
2. The agent enters a tool loop (up to 50 steps), calling tools to explore and modify the codebase
|
|
138
|
+
3. Each tool call is logged and displayed in real time
|
|
139
|
+
4. After the agent finishes, the **approval flow** presents all staged changes grouped by file
|
|
140
|
+
5. You can **accept all**, **review individually** (with diffs), or **cancel**
|
|
141
|
+
6. Approved changes are applied to disk; rejected changes are discarded
|
|
142
|
+
|
|
143
|
+
**Available tools (35+):**
|
|
144
|
+
|
|
145
|
+
| Category | Tools |
|
|
146
|
+
|----------|-------|
|
|
147
|
+
| Filesystem | `read_file`, `create_file`, `modify_file`, `delete_file`, `create_folder`, `list_files`, `search_files`, `read_multiple_files`, `replace_in_file`, `append_to_file`, `insert_at_line` |
|
|
148
|
+
| Shell | `run_command`, `run_background_command`, `execute_shell` |
|
|
149
|
+
| Git | `git_status`, `git_diff`, `git_log` |
|
|
150
|
+
| Project | `run_tests`, `run_test_file`, `lint_project`, `format_project`, `detect_framework`, `read_package_json`, `analyze_codebase` |
|
|
151
|
+
| Search | `grep` |
|
|
152
|
+
| Web | `web_search`, `fetch_url`, `web_crawl` (requires Firecrawl) |
|
|
153
|
+
| Skills | `list_skills`, `read_skill` |
|
|
154
|
+
| Session | `session_status`, `session_history` |
|
|
155
|
+
| Planning | `create_plan`, `get_plan`, `show_pending_changes`, `discard_changes` |
|
|
156
|
+
|
|
157
|
+
### Ask Mode
|
|
158
|
+
|
|
159
|
+
A **read-only** Q&A interface. The agent can explore your codebase and the web to answer questions, but cannot modify files (with the optional exception of saving the response).
|
|
160
|
+
|
|
161
|
+
**Flow:**
|
|
162
|
+
|
|
163
|
+
1. You ask a question
|
|
164
|
+
2. The agent uses read-only tools to formulate an answer
|
|
165
|
+
3. The answer is rendered as markdown in the terminal
|
|
166
|
+
4. You're given the option to save the Q&A pair as a `.md` file
|
|
167
|
+
|
|
168
|
+
**Available tools:** All read-only tools from Agent mode (filesystem read, search, codebase analysis, git, web, skills, session). All mutation tools are stripped.
|
|
169
|
+
|
|
170
|
+
### Plan Mode
|
|
171
|
+
|
|
172
|
+
Breaks a high-level goal into a structured, executable plan. The agent researches your codebase, generates a step-by-step plan, and you select which steps to execute.
|
|
173
|
+
|
|
174
|
+
**Flow:**
|
|
175
|
+
|
|
176
|
+
1. You describe a high-level goal
|
|
177
|
+
2. The agent researches the codebase and generates a structured plan (1–20 steps) with complexity ratings (`low`, `medium`, `high`)
|
|
178
|
+
3. The plan is displayed with a research summary and numbered steps
|
|
179
|
+
4. You select which steps to execute (all selected by default)
|
|
180
|
+
5. Each selected step runs as an independent agent loop (up to 50 steps per step)
|
|
181
|
+
6. All mutations across all steps are collected and presented in a single approval flow
|
|
182
|
+
|
|
183
|
+
### Multi-Agent Mode
|
|
184
|
+
|
|
185
|
+
Coordinates multiple AI agents working together on complex workflows.
|
|
186
|
+
|
|
187
|
+
**Workflow selection:**
|
|
188
|
+
|
|
189
|
+
- **Use predefined template** — choose from 6 templates:
|
|
190
|
+
|
|
191
|
+
| Template | Agents | Strategy |
|
|
192
|
+
|----------|--------|----------|
|
|
193
|
+
| Code Review | Researcher → Implementer → Reviewer | Sequential |
|
|
194
|
+
| Feature Development | Coordinator → Backend Dev + Frontend Dev → QA | Hierarchical |
|
|
195
|
+
| Bug Fix | Debug Agent → Fix Agent → Test Agent | Sequential |
|
|
196
|
+
| Collaborative Research | Researcher 1 + Researcher 2 + Researcher 3 | Parallel |
|
|
197
|
+
| Security Audit | Scanner → Analyzer → Reporter | Sequential |
|
|
198
|
+
| Full-Stack Feature | Database + API + UI (parallel) | Hierarchical |
|
|
199
|
+
|
|
200
|
+
- **AI-smart build** — the LLM analyzes your goal and automatically designs a custom agent topology with the optimal strategy and role assignments
|
|
201
|
+
|
|
202
|
+
**Orchestration strategies:**
|
|
203
|
+
|
|
204
|
+
| Strategy | Behavior |
|
|
205
|
+
|----------|----------|
|
|
206
|
+
| **Sequential** | Agents run one after another; each agent's output is visible to subsequent agents |
|
|
207
|
+
| **Parallel** | Agents run concurrently in batches (default 3 at a time) |
|
|
208
|
+
| **Hierarchical** | Coordinator runs first (planning), then specialists execute |
|
|
209
|
+
| **Collaborative** | Agents take turns; outputs are broadcast via a message broker |
|
|
210
|
+
|
|
211
|
+
**Agent roles and permissions:**
|
|
212
|
+
|
|
213
|
+
| Role | Permissions | Default Max Steps | Tools |
|
|
214
|
+
|------|------------|-------------------|-------|
|
|
215
|
+
| Researcher | Read-only | 30 | 16 |
|
|
216
|
+
| Implementer | Full read/write/execute | 50 | 26 |
|
|
217
|
+
| Reviewer | Read + execute (no write) | 25 | 15 |
|
|
218
|
+
| Coordinator | Read-only + planning | 20 | 8 |
|
|
219
|
+
| Custom | Based on selected tools | 30 | Variable |
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Architecture
|
|
224
|
+
|
|
225
|
+
### Core Components
|
|
226
|
+
|
|
227
|
+
| File | Responsibility |
|
|
228
|
+
|------|----------------|
|
|
229
|
+
| `index.ts` | Entry point; registers commands via Commander |
|
|
230
|
+
| `tui/wakeup.ts` | Banner rendering and top-level mode selection |
|
|
231
|
+
| `tui/terminal-md.ts` | Markdown-to-terminal rendering via `marked` + `marked-terminal` |
|
|
232
|
+
| `tui/spinner.ts` | Animated spinner with elapsed time display |
|
|
233
|
+
| `modes/cli.ts` | CLI mode loop (Auto / Agent / Plan / Ask / Multi-Agent) |
|
|
234
|
+
| `modes/auto.ts` | Auto-router: LLM-based intent classification |
|
|
235
|
+
| `modes/setup.ts` | Interactive configuration wizard |
|
|
236
|
+
| `ai/ai.config.ts` | OpenRouter provider initialization |
|
|
237
|
+
| `ai/config-loader.ts` | Manages `~/.astra/.env` file |
|
|
238
|
+
| `ai/auto-retry.ts` | Automatic retry with exponential backoff |
|
|
239
|
+
| `ai/retry-prompt.ts` | Manual retry prompt fallback |
|
|
240
|
+
|
|
241
|
+
### Tool System
|
|
242
|
+
|
|
243
|
+
The tool system has two layers:
|
|
244
|
+
|
|
245
|
+
1. **`ToolExecutor`** (`modes/agent/tool-executor.ts`) — The core execution engine. All filesystem operations, shell commands, and skill lookups are implemented here. Mutations are staged in an in-memory overlay and never touch disk until explicitly approved.
|
|
246
|
+
|
|
247
|
+
2. **`createAgentTools()`** (`modes/agent/agent-tools.ts`) — Wraps every `ToolExecutor` method as a Vercel AI SDK `tool()` with a Zod input schema, making them available to the LLM agent.
|
|
248
|
+
|
|
249
|
+
Additional tool sets:
|
|
250
|
+
- **`createWebTools()`** (`modes/plan/web-tools.ts`) — Firecrawl-based web search, crawl, and fetch tools
|
|
251
|
+
- **`createSessionTools()`** (`session/session-tools.ts`) — `session_status` and `session_history` tools injected into every agent
|
|
252
|
+
|
|
253
|
+
### Staging & Approval Pipeline
|
|
254
|
+
|
|
255
|
+
This is the safety backbone of Astra. No mutation ever touches disk without explicit consent.
|
|
256
|
+
|
|
257
|
+
**Phase 1 — Staging:** When the agent calls a mutation tool, the `ToolExecutor` validates path safety (must be within workspace root, not excluded), records the operation in an in-memory overlay, and logs it to the `ActionTracker` with status `"pending"`.
|
|
258
|
+
|
|
259
|
+
**Phase 2 — Approval:** After the agent completes, all pending mutations are grouped by file path and presented to the user with three options:
|
|
260
|
+
- **Approve and apply all** — marks every pending mutation as approved
|
|
261
|
+
- **Review one by one** — iterates through each group, showing a unified diff and prompting for accept/reject
|
|
262
|
+
- **Cancel** — rejects all pending mutations
|
|
263
|
+
|
|
264
|
+
**Phase 3 — Application:** Approved actions are replayed against the real filesystem: folders are created, files are written or deleted, and shell commands are spawned.
|
|
265
|
+
|
|
266
|
+
### Action Tracking
|
|
267
|
+
|
|
268
|
+
The `ActionTracker` maintains an append-only log of every action the agent takes. Each entry includes a unique ID, timestamp, action type, file path, before/after content snapshots, and status (`pending`, `executed`, `approved`, `rejected`). This log powers the approval flow, enables auditability, and supports future undo/redo features.
|
|
269
|
+
|
|
270
|
+
### Session Management
|
|
271
|
+
|
|
272
|
+
Sessions are stored in `~/.astra/sessions/index.json` with atomic writes (temp file + rename). Each session records the workspace path, mode, status, LLM-generated summary, touched files, and action counts. Sessions support:
|
|
273
|
+
|
|
274
|
+
- **Auto-resume** — interrupted sessions are detected on wakeup and offered for resumption
|
|
275
|
+
- **Context injection** — on resume, the previous session's summary is injected into the agent's instructions
|
|
276
|
+
- **Session tools** — agents can call `session_status` and `session_history` to recall previous work
|
|
277
|
+
|
|
278
|
+
### Auto-Retry Engine
|
|
279
|
+
|
|
280
|
+
Built-in retry logic with exponential backoff, jitter, and error classification. Four presets are available:
|
|
281
|
+
|
|
282
|
+
| Preset | Max Retries | Base Delay | Max Delay | Use Case |
|
|
283
|
+
|--------|-------------|------------|-----------|----------|
|
|
284
|
+
| `aiCall` | 3 | 1s | 30s | AI provider API calls |
|
|
285
|
+
| `toolExecution` | 2 | 500ms | 5s | Tool execution |
|
|
286
|
+
| `network` | 5 | 2s | 60s | Network operations |
|
|
287
|
+
| `critical` | 5 | 1s | 60s | Critical operations |
|
|
288
|
+
|
|
289
|
+
Errors are classified by category (rate limit, network, timeout, server, etc.) with per-category retryability and suggested delay overrides.
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Environment Variables
|
|
294
|
+
|
|
295
|
+
| Variable | Required | Description |
|
|
296
|
+
|----------|----------|-------------|
|
|
297
|
+
| `OPENROUTER_API_KEY` | Yes | OpenRouter API key for LLM access |
|
|
298
|
+
| `OPENROUTER_DEFAULT_MODEL` | Yes | Model identifier (e.g., `anthropic/claude-sonnet-4.5`) |
|
|
299
|
+
| `FIRECRAWL_API_KEY` | No | Enables `web_search`, `web_crawl`, and `fetch_url` via Firecrawl SDK |
|
|
300
|
+
| `SKILLS_DIRS` | No | Semicolon-separated paths to additional skill directories |
|
|
301
|
+
|
|
302
|
+
Config file location: `~/.astra/.env`
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## Project Structure
|
|
307
|
+
|
|
308
|
+
```
|
|
309
|
+
astra/
|
|
310
|
+
├── index.ts # CLI entry point (Commander)
|
|
311
|
+
├── package.json # Dependencies, scripts, bin config
|
|
312
|
+
├── tsconfig.json # TypeScript config (strict, Bun types)
|
|
313
|
+
│
|
|
314
|
+
├── ai/ # AI provider configuration
|
|
315
|
+
│ ├── index.ts # Re-exports getAgentModel
|
|
316
|
+
│ ├── ai.config.ts # OpenRouter provider setup
|
|
317
|
+
│ ├── config-loader.ts # ~/.astra/.env management
|
|
318
|
+
│ ├── auto-retry.ts # Automatic retry integration
|
|
319
|
+
│ └── retry-prompt.ts # Manual retry prompt fallback
|
|
320
|
+
│
|
|
321
|
+
├── tui/ # Terminal UI
|
|
322
|
+
│ ├── terminal-md.ts # Markdown → terminal rendering
|
|
323
|
+
│ ├── spinner.ts # Animated spinner with elapsed time
|
|
324
|
+
│ └── wakeup.ts # Banner + top-level mode selection
|
|
325
|
+
│
|
|
326
|
+
├── modes/ # Interaction modes
|
|
327
|
+
│ ├── cli.ts # CLI mode loop
|
|
328
|
+
│ ├── auto.ts # Auto-router (intent classification)
|
|
329
|
+
│ ├── setup.ts # Configuration wizard
|
|
330
|
+
│ │
|
|
331
|
+
│ ├── agent/ # Agent mode
|
|
332
|
+
│ │ ├── types.ts # Type definitions + default config
|
|
333
|
+
│ │ ├── action-tracker.ts # Append-only action log
|
|
334
|
+
│ │ ├── tool-executor.ts # Core execution engine + staging overlay
|
|
335
|
+
│ │ ├── agent-tools.ts # Vercel AI SDK tool definitions
|
|
336
|
+
│ │ ├── diff-view.ts # Unified diff generation
|
|
337
|
+
│ │ ├── approval.ts # Interactive approval flow
|
|
338
|
+
│ │ └── orchestrator.ts # Agent loop + approval + apply
|
|
339
|
+
│ │
|
|
340
|
+
│ ├── ask/ # Ask mode (read-only Q&A)
|
|
341
|
+
│ │ └── orchestrator.ts
|
|
342
|
+
│ │
|
|
343
|
+
│ ├── plan/ # Plan mode
|
|
344
|
+
│ │ ├── types.ts # Plan and PlanStep interfaces
|
|
345
|
+
│ │ ├── planner.ts # LLM-driven plan generation
|
|
346
|
+
│ │ ├── selection.ts # Interactive step picker
|
|
347
|
+
│ │ ├── web-tools.ts # Firecrawl web tools
|
|
348
|
+
│ │ └── orchestrator.ts # Plan → select → execute → approve
|
|
349
|
+
│ │
|
|
350
|
+
│ └── multi/ # Multi-agent mode
|
|
351
|
+
│ ├── types.ts # Full type system
|
|
352
|
+
│ ├── agent-pool-manager.ts # Agent registration and tracking
|
|
353
|
+
│ ├── message-broker.ts # Pub-sub communication channel
|
|
354
|
+
│ ├── multi-agent-orchestrator.ts # Strategy dispatch engine
|
|
355
|
+
│ ├── workflow-builder.ts # Fluent API + predefined templates
|
|
356
|
+
│ ├── examples.ts # Example workflow configurations
|
|
357
|
+
│ └── orchestrator.ts # Multi-agent approval flow
|
|
358
|
+
│
|
|
359
|
+
├── session/ # Session persistence
|
|
360
|
+
│ ├── index.ts # Public API re-exports
|
|
361
|
+
│ ├── store.ts # JSON file store (atomic writes)
|
|
362
|
+
│ ├── session-manager.ts # Begin/end/resume/summarise
|
|
363
|
+
│ ├── session-context.ts # Context capture and summary
|
|
364
|
+
│ └── session-tools.ts # session_status + session_history tools
|
|
365
|
+
│
|
|
366
|
+
├── core/ # Core utilities
|
|
367
|
+
│ └── retry/ # Retry engine
|
|
368
|
+
│ ├── index.ts # Public API re-exports
|
|
369
|
+
│ ├── retry-config.ts # Configuration and presets
|
|
370
|
+
│ ├── retry-engine.ts # Execution with backoff + jitter
|
|
371
|
+
│ └── error-classifier.ts # Error categorisation
|
|
372
|
+
│
|
|
373
|
+
└── game/ # Standalone arcade games
|
|
374
|
+
├── index.html # Snake (HTML5 Canvas)
|
|
375
|
+
└── neon-breaker.html # Brick Breaker
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
## Dependencies
|
|
381
|
+
|
|
382
|
+
| Package | Purpose |
|
|
383
|
+
|---------|---------|
|
|
384
|
+
| `@openrouter/ai-sdk-provider` | OpenRouter as LLM provider for Vercel AI SDK |
|
|
385
|
+
| `@clack/prompts` | Interactive terminal prompts |
|
|
386
|
+
| `ai` | Vercel AI SDK (ToolLoopAgent, generateText, stepCountIs) |
|
|
387
|
+
| `@mendable/firecrawl-js` | Web search, crawling, and scraping |
|
|
388
|
+
| `commander` | CLI argument parsing |
|
|
389
|
+
| `chalk` | Terminal string styling |
|
|
390
|
+
| `figlet` | ASCII art banner generation |
|
|
391
|
+
| `marked` + `marked-terminal` | Markdown parsing and terminal rendering |
|
|
392
|
+
| `diff` | Unified diff generation |
|
|
393
|
+
| `dotenv` | `.env` file loading |
|
|
394
|
+
| `zod` | Schema validation |
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
## Roadmap
|
|
399
|
+
|
|
400
|
+
- [ ] **Telegram mode** — stub present in wakeup menu
|
|
401
|
+
- [ ] **Undo/redo support** — via action log replay
|
|
402
|
+
- [ ] **Streaming token output** — for real-time agent response display
|
|
403
|
+
- [ ] **Configurable tool allowlists per mode** — currently hardcoded per mode
|
|
404
|
+
- [ ] **Multi-model support with per-mode model selection** — partially implemented in multi-agent
|
|
405
|
+
- [ ] **Persistent action history across sessions** — sessions store summaries but not full action logs
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
## License
|
|
410
|
+
|
|
411
|
+
MIT
|
package/ai/ai.config.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { createOpenRouter } from "@openrouter/ai-sdk-provider";
|
|
2
|
+
import { getEnv,getConfigPath } from "./config-loader";
|
|
3
|
+
|
|
4
|
+
export function getAgentModel() {
|
|
5
|
+
const apiKey = getEnv("OPENROUTER_API_KEY");
|
|
6
|
+
const modelId = getEnv("OPENROUTER_DEFAULT_MODEL");
|
|
7
|
+
|
|
8
|
+
if (!apiKey) {
|
|
9
|
+
throw new Error(
|
|
10
|
+
`OPENROUTER_API_KEY is not set.` +
|
|
11
|
+
`\n Run "astra setup" to configure your keys, or set the env var.` +
|
|
12
|
+
`\n Config file: ${getConfigPath()}`
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (!modelId) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
`OPENROUTER_DEFAULT_MODEL is not set.` +
|
|
19
|
+
`\n Run "astra setup" to configure your keys, or set the env var.` +
|
|
20
|
+
`\n Config file: ${getConfigPath()}`
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const provider = createOpenRouter({ apiKey });
|
|
25
|
+
|
|
26
|
+
return provider(modelId);
|
|
27
|
+
}
|
package/ai/auto-retry.ts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Automatic Retry Module for AI Calls
|
|
3
|
+
*
|
|
4
|
+
* Provides seamless integration of retry logic with the existing
|
|
5
|
+
* AI provider calls, replacing the manual retry prompt with
|
|
6
|
+
* automatic retry capability.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import chalk from 'chalk';
|
|
10
|
+
import { withRetry, RetryPresets } from '../core/retry';
|
|
11
|
+
import type { RetryConfig } from '../core/retry';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Configuration for AI call retries
|
|
15
|
+
*/
|
|
16
|
+
export interface AiRetryConfig {
|
|
17
|
+
/** Enable automatic retry (default: true) */
|
|
18
|
+
enabled: boolean;
|
|
19
|
+
/** Retry configuration */
|
|
20
|
+
retryConfig: Partial<RetryConfig>;
|
|
21
|
+
/** Show retry progress to user */
|
|
22
|
+
showProgress: boolean;
|
|
23
|
+
/** Ask user before retrying (fallback to manual mode) */
|
|
24
|
+
askBeforeRetry: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Default AI retry configuration
|
|
29
|
+
*/
|
|
30
|
+
export const DEFAULT_AI_RETRY_CONFIG: AiRetryConfig = {
|
|
31
|
+
enabled: true,
|
|
32
|
+
retryConfig: RetryPresets.aiCall,
|
|
33
|
+
showProgress: true,
|
|
34
|
+
askBeforeRetry: false,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Execute an AI call with automatic retry
|
|
39
|
+
*/
|
|
40
|
+
export async function withAiRetry<T>(
|
|
41
|
+
operation: () => Promise<T>,
|
|
42
|
+
context: string,
|
|
43
|
+
config: Partial<AiRetryConfig> = {},
|
|
44
|
+
): Promise<T> {
|
|
45
|
+
const fullConfig = {
|
|
46
|
+
...DEFAULT_AI_RETRY_CONFIG,
|
|
47
|
+
...config,
|
|
48
|
+
retryConfig: {
|
|
49
|
+
...DEFAULT_AI_RETRY_CONFIG.retryConfig,
|
|
50
|
+
...config.retryConfig,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
if (!fullConfig.enabled) {
|
|
55
|
+
return operation();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const { showProgress } = fullConfig;
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
const { result, stats } = await withRetry(
|
|
62
|
+
operation,
|
|
63
|
+
{
|
|
64
|
+
...fullConfig.retryConfig,
|
|
65
|
+
onRetry: (attempt, error, delayMs) => {
|
|
66
|
+
if (showProgress) {
|
|
67
|
+
console.log(
|
|
68
|
+
chalk.yellow(`\n ⚠ Retry ${attempt}/${fullConfig.retryConfig.maxRetries} after ${error.category} error`)
|
|
69
|
+
);
|
|
70
|
+
console.log(chalk.dim(` Waiting ${Math.round(delayMs / 1000)}s before retry...`));
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
onExhausted: (error, totalAttempts) => {
|
|
74
|
+
if (showProgress) {
|
|
75
|
+
console.log(
|
|
76
|
+
chalk.red(`\n ✗ All ${totalAttempts} attempts failed (${error.category})`)
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
if (showProgress && stats.totalRetries > 0) {
|
|
84
|
+
console.log(
|
|
85
|
+
chalk.green(` ✓ Succeeded after ${stats.totalAttempts} attempt(s)`)
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return result;
|
|
90
|
+
} catch (error) {
|
|
91
|
+
// If automatic retry is enabled but failed, optionally fall back to manual retry
|
|
92
|
+
if (fullConfig.askBeforeRetry) {
|
|
93
|
+
const { promptToRetryAiCall } = await import('./retry-prompt');
|
|
94
|
+
const shouldRetry = await promptToRetryAiCall(context, error);
|
|
95
|
+
if (shouldRetry) {
|
|
96
|
+
return withAiRetry(operation, context, {
|
|
97
|
+
...config,
|
|
98
|
+
askBeforeRetry: false, // Prevent infinite loop
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Create a retryable version of an AI call function
|
|
108
|
+
*/
|
|
109
|
+
export function createRetryableAiCall<TArgs extends unknown[], TReturn>(
|
|
110
|
+
fn: (...args: TArgs) => Promise<TReturn>,
|
|
111
|
+
context: string,
|
|
112
|
+
config: Partial<AiRetryConfig> = {},
|
|
113
|
+
): (...args: TArgs) => Promise<TReturn> {
|
|
114
|
+
return async (...args: TArgs): Promise<TReturn> => {
|
|
115
|
+
return withAiRetry(() => fn(...args), context, config);
|
|
116
|
+
};
|
|
117
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
import dotenv from "dotenv";
|
|
5
|
+
|
|
6
|
+
const CONFIG_DIR = path.join(homedir(), ".astra");
|
|
7
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, ".env");
|
|
8
|
+
|
|
9
|
+
let loaded = false;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Ensure the ~/.astra/.env file is loaded into process.env.
|
|
13
|
+
* Only runs once; subsequent calls are a no-op.
|
|
14
|
+
*/
|
|
15
|
+
function ensureConfigLoaded(): void {
|
|
16
|
+
if (loaded) return;
|
|
17
|
+
|
|
18
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
19
|
+
dotenv.config({ path: CONFIG_FILE, override: false });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
loaded = true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Resolve an environment variable, checking process.env first,
|
|
27
|
+
* then the config file (which is already merged into process.env).
|
|
28
|
+
*/
|
|
29
|
+
export function getEnv(key: string): string | undefined {
|
|
30
|
+
ensureConfigLoaded();
|
|
31
|
+
return process.env[key];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Return the path to the config file.
|
|
36
|
+
*/
|
|
37
|
+
export function getConfigPath(): string {
|
|
38
|
+
return CONFIG_FILE;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Return the path to the config directory.
|
|
43
|
+
*/
|
|
44
|
+
export function getConfigDir(): string {
|
|
45
|
+
return CONFIG_DIR;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Write a key=value pair into the config file, creating the directory
|
|
50
|
+
* if it doesn't exist. Overwrites existing values for the same key.
|
|
51
|
+
*/
|
|
52
|
+
export function saveConfig(entries: Record<string, string>): void {
|
|
53
|
+
// Read existing content so we can merge
|
|
54
|
+
let existing = "";
|
|
55
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
56
|
+
existing = fs.readFileSync(CONFIG_FILE, "utf8");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const lines = existing.split("\n");
|
|
60
|
+
const keys = new Set(Object.keys(entries));
|
|
61
|
+
|
|
62
|
+
// Update existing lines that match a key
|
|
63
|
+
const updated: string[] = [];
|
|
64
|
+
for (const line of lines) {
|
|
65
|
+
const trimmed = line.trim();
|
|
66
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
67
|
+
updated.push(line);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const eqIdx = trimmed.indexOf("=");
|
|
71
|
+
if (eqIdx === -1) {
|
|
72
|
+
updated.push(line);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
76
|
+
if (keys.has(key)) {
|
|
77
|
+
updated.push(`${key}=${entries[key]}`);
|
|
78
|
+
keys.delete(key);
|
|
79
|
+
} else {
|
|
80
|
+
updated.push(line);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Append any keys that weren't already present
|
|
85
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
86
|
+
if (value) {
|
|
87
|
+
updated.push(`${key}=${value}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Ensure directory exists
|
|
92
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
93
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
fs.writeFileSync(CONFIG_FILE, updated.join("\n") + "\n", "utf8");
|
|
97
|
+
|
|
98
|
+
// Also inject into current process so the user doesn't have to restart
|
|
99
|
+
ensureConfigLoaded();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ─── Retry Configuration ───────────────────────────────────────────────────
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Get retry configuration from environment
|
|
106
|
+
*/
|
|
107
|
+
export function getRetryConfig(): {
|
|
108
|
+
enabled: boolean;
|
|
109
|
+
maxRetries: number;
|
|
110
|
+
showProgress: boolean;
|
|
111
|
+
} {
|
|
112
|
+
return {
|
|
113
|
+
enabled: getEnv("ASTRA_AGENT_RETRY_ENABLED") !== "false",
|
|
114
|
+
maxRetries: parseInt(getEnv("ASTRA_AGENT_RETRY_MAX") || "3", 10),
|
|
115
|
+
showProgress: getEnv("ASTRA_AGENT_RETRY_PROGRESS") !== "false",
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Get multi-agent retry configuration
|
|
121
|
+
*/
|
|
122
|
+
export function getMultiRetryConfig(): {
|
|
123
|
+
enabled: boolean;
|
|
124
|
+
maxRetries: number;
|
|
125
|
+
backoffMultiplier: number;
|
|
126
|
+
} {
|
|
127
|
+
return {
|
|
128
|
+
enabled: getEnv("ASTRA_MULTI_RETRY_ENABLED") !== "false",
|
|
129
|
+
maxRetries: parseInt(getEnv("ASTRA_MULTI_RETRY_MAX") || "2", 10),
|
|
130
|
+
backoffMultiplier: parseInt(getEnv("ASTRA_MULTI_RETRY_BACKOFF") || "2", 10),
|
|
131
|
+
};
|
|
132
|
+
}
|
package/ai/index.ts
ADDED