1
0

chore: Remove obsolete planning and documentation files.

This commit is contained in:
2026-03-11 14:28:37 +01:00
parent c9515a822d
commit ccfa7e1b9d
4 changed files with 0 additions and 798 deletions

View File

@@ -1,81 +0,0 @@
# Command Ideas for AnchorCli
## Session & Help
### `/help`
Show available commands, version info, and tool capabilities. Combines `/help`, `/version`, `/about`, and `/tools`.
### `/clear`
Clear the terminal screen and optionally reset conversation with `/clear --reset`.
### `/history`
Show the current chat history. Use `/history <n>` to show last N messages.
## Navigation
### `/cd [path]`
Change directory. With no argument, shows current working directory (combines `/cwd`, `/pwd`, `/cd`).
### `/ls`
List files in current directory (alias for ListDir tool).
## Configuration
### `/config`
Show or modify settings. Subcommands:
- `/config model <name>` - Change AI model
- `/config endpoint <url>` - Change API endpoint
- `/config debug <on|off>` - Toggle debug mode
## Conversation Management
### `/save [filename]`
Save current conversation to a file (JSON or markdown format).
### `/load <filename>`
Load a previous conversation from a file.
### `/export <filename>`
Export chat history in a specific format (JSON, markdown, plain text).
## Advanced Features
### `/undo`
Undo the last file edit (requires edit history tracking).
### `/diff [file]`
Show differences between current and original file state. With no argument, shows all pending changes.
### `/search <pattern>`
Quick file/content search across the project.
### `/stats`
Show session statistics (files edited, tokens used, commands run, estimated costs).
### `/macro <name> [commands...]`
Create and execute multi-step command sequences.
### `/alias <name> <command>`
Create custom command shortcuts.
## Safety & Integration
### `--dry-run` / Read-only Mode
Run Anchor without mutating any files. Shows what *would* happen (edits, deletes, renames) without applying changes. Perfect for reviewing AI suggestions before committing.
### Git Integration
Seamless version control integration:
- Auto-create a branch per session (`anchor session --git-branch`)
- Auto-commit after successful edits with descriptive messages
- Show git diff before/after operations
- Revert to pre-session state if something goes wrong
### Mutation Rate Limits
Prevent runaway AI from trashing a project:
- Configurable max file edits per conversation turn
- Hard cap on delete/rename operations without confirmation
- Cooldown period after N rapid mutations
- Warning when approaching limits
### File Type Restrictions
Config to block edits on sensitive patterns (`*.config`, `*.sql`, `*.production.*`, etc.). Requires explicit override flag.

View File

@@ -1,90 +0,0 @@
# Improvements for AnchorCli
This document contains criticisms and suggestions for improving the AnchorCli project.
## Architecture
1. **Program.cs is too large (433 lines)** - Split into smaller classes: ChatSession, ReplLoop, ResponseStreamer
2. **No dependency injection** - Use Microsoft.Extensions.DependencyInjection for testability
3. **Static tool classes with global Log delegates** - Convert to instance classes with injected ILogger
## Testing
4. **No unit tests** - Add xUnit project, test HashlineEncoder/Validator, tools, and ContextCompactor
5. **No integration tests** - Use Spectre.Console.Testing for TUI workflows
6. **No CI/CD** - Add GitHub Actions for test runs on push/PR
## Documentation
7. **Missing XML docs** - Add summary docs to public APIs
8. **Incomplete README** - Add contributing, development, troubleshooting sections
9. **No CHANGELOG.md** - Track releases and changes
## Security & Safety
10. **Command execution unsandboxed** - Add allowlist/denylist, time limits, output size limits
11. **No mutation rate limiting** - Track edits per turn, add configurable limits
12. **API key in plain text** - Use OS keychain or env var, set restrictive file permissions
## Performance
13. **No file read caching** - Cache file content per-turn with invalidation on write
14. **Regex not static** - Make compiled regexes static readonly
## User Experience
15. **No undo** - Store edit history, add /undo command
16. **No session persistence** - Add /save and /load commands
17. **Limited error recovery** - Better error messages, /debug mode
## Developer Experience
18. **No .editorconfig** - Add code style enforcement
19. **No solution file** - Create AnchorCli.sln
20. **Hardcoded model list** - Fetch from OpenRouter API dynamically
21. **No version info** - Add <Version> to .csproj, display in /help
## Code Quality
22. **Inconsistent error handling** - Standardize on error strings, avoid empty catch blocks
23. **Magic numbers** - Extract to named constants (150_000, 300, KeepRecentTurns=2)
24. **Commented-out debug code** - Remove or use #if DEBUG
25. **Weak hash algorithm** - Adler-8 XOR only has 256 values; consider 4-char hex
## Build & Dependencies
26. **No LICENSE file** - Add MIT LICENSE file
## Priority
### High
- [ ] Add unit tests
- [ ] Implement undo functionality
- [ ] Add mutation rate limiting
- [x] Refactor Program.cs
- [x] Add LICENSE file
### Medium
- [ ] Session persistence
- [ ] XML documentation
- [ ] Error handling consistency
- [x] .editorconfig
- [ ] Dynamic model list
### Low
- [ ] CHANGELOG.md
- [ ] CI/CD pipeline
- [ ] Stronger hash algorithm
- [ ] Code coverage reporting
## Quick Wins (<1 hour each)
- [x] Add <Version> to .csproj
- [x] Create LICENSE file
- [x] Add .editorconfig
- [x] Remove commented code
- [x] Extract magic numbers to constants
- [x] Add XML docs to Hashline classes
- [x] Make regexes static readonly
*Prioritize based on goals: safety, testability, or user experience.*

View File

@@ -1,293 +0,0 @@
# Provider Support Plan
## Current Problems
1. **OpenRouter Hardcoded**: Endpoint, headers, and pricing API calls are hardcoded to OpenRouter
2. **Config Ineffective**: SetupTui allows "custom endpoint" but Program.cs ignores it
3. **Token Count**: Token usage tracking only works with OpenRouter response headers
4. **Pricing Only for One Provider**: Models list shows pricing, but only when using OpenRouter
---
## Goals
1. Make the system **endpoint-agnostic**
2. Support pricing/token tracking for **multiple providers**
3. Keep **OpenRouter as the default** (familiar)
4. Allow users to configure any OpenAI-compatible endpoint
5. Show pricing/token info **only when available** for each provider
---
## Provider Categories
### Tier 1: Native Support (Built-in)
- OpenRouter (default)
- Ollama (local, no auth)
- Groq (high-speed inference)
- Anthropic (native or via API)
- OpenAI (official api)
### Tier 2: Config-Based Support
- Cerebras
- DeepSeek
- Any OpenAI-compatible endpoint that supports custom headers
### Tier 3: Manual Configuration Required
- Self-hosted endpoints
- Corporate proxies
- Custom middleware layers
---
```csharp
// Example: Provider interface
class PricingProvider
{
// Get pricing info from provider's API
async Task<List<ModelPricing>> GetModelsAsync(string apiKey);
// Get tokens from response
async Task<TokenUsage> GetTokensFromResponseAsync(HttpResponseMessage response);
// Add provider-specific headers if needed
void AddHeaders(HttpRequestMessage request, string apiKey);
}
```
**Supported Implementations:**
- `OpenRouterProvider` (uses `/api/v1/models` + `x-total-tokens`)
- `GroqProvider` (uses Groq's pricing API + response headers)
- `OllamaProvider` (free tier, no pricing lookup, basic token counting)
- `OpenAIProvider` (uses OpenAI's model list + token counting)
- `GenericProvider` (fallback for any OpenAI-compatible endpoint)
**Configuration:**
Store provider selection in `anchor.config.json`:
```json
{
"apiKey": "your-key",
"model": "qwen3.5-27b",
"endpoint": "https://openrouter.ai/api/v1",
"provider": "openrouter"
}
```
Auto-detect provider from endpoint URL if not specified.
---
## Pricing System
### Current State
- Uses OpenRouter's `/api/v1/models` endpoint
- Displays pricing in a table during startup
- Only works when using OpenRouter
### Improved Behavior
**When endpoint matches known provider:**
1. Fetch pricing from that provider's API
2. Display pricing in the startup table
3. Show per-prompt costs in chat output
**When endpoint is generic/unsupported:**
1. Skip API call (no pricing lookup)
2. Display `---` or `$` placeholders
3. Optional: Show "Pricing not available" note
**User Feedback:**
- Show clear messaging: "Pricing data loaded from OpenRouter"
- Show: "Pricing not available for this endpoint" (for unsupported)
- Don't break chat functionality if pricing fails
### Pricing Data Format
Store in `ModelPricing` class:
```csharp
class ModelPricing
{
string ModelId;
decimal InputPricePerMTokens;
decimal OutputPricePerMTokens;
double? CacheCreationPricePerMTokens; // if supported
}
```
---
## Token Tracking System
### Current State
- Uses `x-total-tokens` from OpenRouter headers
- Only works with OpenRouter responses
### Multi-Provider Strategy
**OpenRouter:**
- Use `x-total-tokens` header
- Use `x-response-timing` for latency tracking
**Groq:**
- Use `x-groq-tokens` header
- Use `x-groq-response-time` for latency
**OpenAI:**
- Use `x-ai-response-tokens` header (if available)
- Fall back to response body if needed
**Ollama:**
- No official token counting
- Use output length as proxy estimate
- Optional: Show message token estimates
**Generic/Fallback:**
- Parse `total_tokens` from response JSON
- Fall back to character count estimates
- Show placeholder when unavailable
### Integration Points
**During Chat Session:**
1. After each response, extract tokens from response headers
2. Store in `ChatSession.TokensUsed` object
3. Display in status bar: `Tokens: 128/2048 • Cost: $0.002`
**At Session End:**
1. Show summary: `Total tokens: 1,024 | Total cost: $0.015`
2. Write to session log or history file
---
## Implementation Roadmap
### Phase 1: Conditional Pricing (Current Issues First)
- [ ] Check if endpoint is OpenRouter before fetching pricing
- [ ] Skip pricing API call for non-OpenRouter endpoints
- [ ] Show placeholder message if pricing not available
- [ ] **Time estimate:** 2 hours
### Phase 2: Provider Configuration
- [ ] Add `provider` field to `AnchorConfig` model
- [ ] Update `SetupTui` to ask "Which provider?" (openrouter, ollama, groq, etc.)
- [ ] Auto-detect provider from endpoint URL (smart default)
- [ ] Write provider to config file on setup
- [ ] **Time estimate:** 3 hours
### Phase 3: Provider Abstraction
- [ ] Create `IPricingProvider` interface
- [ ] Move existing `PricingProvider` to `OpenRouterProvider`
- [ ] Create `GenericPricingProvider` for fallback
- [ ] Add provider factory: `ProviderFactory.Create(providerName)`
- [ ] **Time estimate:** 5 hours
### Phase 4: Token Tracking Enhancement
- [ ] Create `ITokenTracker` interface
- [ ] Implement token extraction for multiple providers
- [ ] Display token usage in status bar
- [ ] Add per-prompt cost calculation
- [ ] **Time estimate:** 6 hours
### Phase 5: Second Provider Implementation
- [ ] Implement `GroqProvider` (similar to OpenRouter)
- [ ] Test with Groq API
- [ ] Update documentation
- [ ] **Time estimate:** 4 hours
### Phase 6: Future-Proofing (Optional)
- [ ] Add plugin system for custom providers
- [ ] Allow users to define custom pricing rules
- [ ] Support OpenRouter-compatible custom endpoints
- [ ] **Time estimate:** 8+ hours
---
## User Configuration Guide
### Automatic Setup
Run `/setup` in the chat or `anchor setup` in CLI:
```
Which provider are you using?
1) OpenRouter (qwen models)
2) Groq (qwen/gemma models)
3) Ollama (local models)
4) OpenAI (gpt models)
5) Custom endpoint
```
### Manual Configuration
Edit `anchor.config.json` directly:
```json
{
"apiKey": "your-api-key",
"model": "qwen3.5-27b",
"endpoint": "https://api.groq.com/openai/v1",
"provider": "groq" // optional, auto-detected if missing
}
```
### Environment Variables
For custom setup:
```
ANCHOR_ENDPOINT=https://api.groq.com/openai/v1
ANCHOR_PROVIDER=groq
ANCHOR_API_KEY=...
ANCHOR_MODEL=qwen3.5-27b
```
---
## Known Limitations
### Tier 1 Providers (Full Support)
**✓ OpenRouter**
- Pricing: ✓ (native API)
- Tokens: ✓ (response headers)
- Cost tracking: ✓
**✓ Groq** (after Phase 4)
- Pricing: ✓ (will add)
- Tokens: ✓ (response headers)
- Cost tracking: ✓
### Tier 2 Providers (Partial Support)
**○ Ollama**
- Pricing: ○ (free, no lookup needed)
- Tokens: ○ (estimated from output)
- Cost tracking: ○ (placeholder)
**○ OpenAI**
- Pricing: ○ (manual pricing display)
- Tokens: ○ (header extraction)
- Cost tracking: ○ (config-based)
### Tier 3 Providers (Basic Support)
**□ Custom Endpoints**
- Pricing: □ (manual only)
- Tokens: □ (fallback parsing)
- Cost tracking: □ (user-defined)
---
## Future Enhancements
1. **Pricing Database**: Maintain own pricing database (like OpenRouter's)
2. **Cost Estimator**: Predict costs before sending message
3. **Usage Alerts**: Warn user when approaching budget limits
4. **Multi-Model Support**: Compare costs between different providers
5. **Plugin System**: Allow community to add new providers
---
## Success Criteria
- ✅ Users can choose from 3+ providers in setup
- ✅ Pricing displays only for supported endpoints
- ✅ Token tracking works for all Tier 1 providers
- ✅ No breaking changes to existing OpenRouter users
- ✅ Clear documentation on what each provider supports
- ✅ Graceful degradation for unsupported features
---
*Last Updated: 2025-12-23*

View File

@@ -1,334 +0,0 @@
# Sandbox Implementation Plan for AnchorCli
## Overview
By default, all file and directory operations are restricted to the current working directory (CWD).
Users can bypass this restriction with the `--no-sandbox` flag.
## Usage
```bash
# Default: sandbox enabled (operations limited to CWD)
anchor
# Disable sandbox (allow operations anywhere)
anchor --no-sandbox
```
## Architecture
The implementation leverages the existing `ResolvePath()` methods in `FileTools` and `DirTools`.
Since tools are static classes without dependency injection, we use a static `SandboxContext` class.
---
## Implementation Steps
### Step 1: Create `SandboxContext.cs`
Create a new file `Core/SandboxContext.cs`:
```csharp
using System;
namespace AnchorCli;
/// <summary>
/// Static context holding sandbox configuration.
/// Checked by ResolvePath() to validate paths are within working directory.
/// </summary>
internal static class SandboxContext
{
private static string? _workingDirectory;
private static bool _enabled = true;
public static bool Enabled
{
get => _enabled;
set => _enabled = value;
}
public static string WorkingDirectory
{
get => _workingDirectory ?? Environment.CurrentDirectory;
set => _workingDirectory = value;
}
/// <summary>
/// Validates that a resolved path is within the working directory (if sandbox is enabled).
/// Returns the resolved path if valid, or null if outside sandbox (no exception thrown).
/// When null is returned, the calling tool should return an error message to the agent.
/// </summary>
public static string? ValidatePath(string resolvedPath)
{
if (!_enabled)
return resolvedPath;
var workDir = WorkingDirectory;
// Normalize paths for comparison
var normalizedPath = Path.GetFullPath(resolvedPath).TrimEnd(Path.DirectorySeparatorChar);
var normalizedWorkDir = Path.GetFullPath(workDir).TrimEnd(Path.DirectorySeparatorChar);
// Check if path starts with working directory
if (!normalizedPath.StartsWith(normalizedWorkDir, StringComparison.OrdinalIgnoreCase))
{
// Return null to signal violation - caller handles error messaging
return null;
}
return resolvedPath;
}
public static void Initialize(bool sandboxEnabled)
{
_enabled = sandboxEnabled;
_workingDirectory = Environment.CurrentDirectory;
}
}
```
---
### Step 2: Modify `Program.cs`
Add argument parsing and initialize the sandbox context:
**After line 15** (after the `setup` subcommand check), add:
```csharp
// ── Parse sandbox flag ──────────────────────────────────────────────────
bool sandboxEnabled = !args.Contains("--no-sandbox");
SandboxContext.Initialize(sandboxEnabled);
if (!sandboxEnabled)
{
AnsiConsole.MarkupLine("[dim grey]Sandbox disabled (--no-sandbox)[/]");
}
```
---
### Step 3: Update `FileTools.ResolvePath()`
**Replace lines 322-323** with:
internal static string? ResolvePath(string path, out string? errorMessage)
{
errorMessage = null;
var resolved = Path.IsPathRooted(path)
? path
: Path.GetFullPath(path, Environment.CurrentDirectory);
var validated = SandboxContext.ValidatePath(resolved);
if (validated == null)
{
errorMessage = $"Sandbox violation: Path '{path}' is outside working directory '{SandboxContext.WorkingDirectory}'. Use --no-sandbox to disable restrictions.";
return null;
}
return validated;
}
---
### Step 4: Update `DirTools.ResolvePath()`
**Replace lines 84-85** with:
```csharp
internal static string? ResolvePath(string path, out string? errorMessage)
{
errorMessage = null;
var resolved = Path.IsPathRooted(path)
? path
: Path.GetFullPath(path, Environment.CurrentDirectory);
var validated = SandboxContext.ValidatePath(resolved);
if (validated == null)
{
errorMessage = $"Sandbox violation: Path '{path}' is outside working directory '{SandboxContext.WorkingDirectory}'. Use --no-sandbox to disable restrictions.";
return null;
}
return validated;
}
---
### Step 5: Update Tool Descriptions (Optional but Recommended)
Update the `[Description]` attributes to mention sandbox behavior:
**FileTools.cs - ReadFile** (line 23):
```csharp
[Description("Read a file. Max 200 lines per call. Returns lines with line:hash| anchors. Sandbox: restricted to working directory unless --no-sandbox is used. IMPORTANT: Call GrepFile first...")]
```
**DirTools.cs - CreateDir** (line 63):
```csharp
[Description("Create a new directory. Creates parent directories if they don't exist. Sandbox: restricted to working directory unless --no-sandbox is used. Returns OK on success...")]
```
Repeat for other tools as needed.
---
## How Tools Handle Sandbox Violations
Each tool that uses `ResolvePath()` must check for `null` return and handle it gracefully:
### FileTools Pattern
```csharp
// Before (old code):
var resolvedPath = ResolvePath(path);
var content = File.ReadAllText(resolvedPath);
// After (new code):
var resolvedPath = ResolvePath(path, out var errorMessage);
if (resolvedPath == null)
return $"ERROR: {errorMessage}";
var content = File.ReadAllText(resolvedPath);
```
### DirTools Pattern
```csharp
// Before (old code):
var resolvedPath = ResolvePath(path);
Directory.CreateDirectory(resolvedPath);
// After (new code):
var resolvedPath = ResolvePath(path, out var errorMessage);
if (resolvedPath == null)
return $"ERROR: {errorMessage}";
Directory.CreateDirectory(resolvedPath);
return "OK";
```
### EditTools
No changes needed - it already calls `FileTools.ResolvePath()`, so the sandbox check happens there.
### Tools That Don't Use ResolvePath
- `ListDir` with no path argument (uses current directory)
- `GetFileInfo` - needs to be updated to use `ResolvePath()`
- `FindFiles` - needs to be updated to validate the search path
---
---
## Error Handling - No Crashes
When a sandbox violation occurs, the program **does not crash**. Instead:
1. `ResolvePath()` returns `null` and sets `errorMessage`
2. The tool returns the error message to the agent
3. The agent sees the error and can continue the conversation
4. The user sees a clear error message in the chat
**Example tool implementation pattern:**
```csharp
public static async Task<string> ReadFile(string path, int startLine, int endLine)
{
var resolvedPath = ResolvePath(path, out var errorMessage);
if (resolvedPath == null)
return $"ERROR: {errorMessage}"; // Return error, don't throw
// ... rest of the tool logic
}
```
**What the agent sees:**
```
Tool result: ERROR: Sandbox violation: Path '/home/tomi/.ssh' is outside working directory '/home/tomi/dev/anchor'. Use --no-sandbox to disable restrictions.
```
**What the user sees in chat:**
> The agent tried to read `/home/tomi/.ssh` but was blocked by the sandbox. The agent can now adjust its approach or ask you to run with `--no-sandbox`.
---
## Edge Cases Handled
| Case | Behavior |
|------|----------|
| **Symlinks inside CWD pointing outside** | Follows symlink (user-created link = intentional) |
| **Path traversal (`../..`)** | Blocked if result is outside CWD |
| **Absolute paths** | Validated against CWD |
| **Network paths** | Blocked (not under CWD) |
| **Case sensitivity** | Uses `OrdinalIgnoreCase` for cross-platform compatibility |
---
## Security Notes
⚠️ **The sandbox is a safety feature, not a security boundary.**
- It prevents **accidental** modifications to system files
- It does **not** protect against malicious intent
- `CommandTool.ExecuteCommand()` can still run arbitrary shell commands
- A determined user can always use `--no-sandbox`
For true isolation, run anchor in a container or VM.
---
## Testing Checklist
- [ ] `ReadFile` on file inside CWD → **Success**
- [ ] `ReadFile` on file outside CWD → **Sandbox violation error**
- [ ] `ReadFile` with `../` traversal outside CWD → **Sandbox violation error**
- [ ] `CreateDir` outside CWD → **Sandbox violation error**
- [ ] `anchor --no-sandbox` then read `/etc/passwd`**Success**
- [ ] Symlink inside CWD pointing to `/etc/passwd`**Success** (user-created link)
- [ ] Case variations on Windows (`C:\Users` vs `c:\users`) → **Success**
---
## Migration Guide
### Existing Workflows
If you have scripts or workflows that rely on accessing files outside the project:
```bash
# Update your scripts to use --no-sandbox
anchor --no-sandbox
```
### CI/CD Integration
For CI environments where sandbox may not be needed:
```yaml
# GitHub Actions example
- name: Run anchor
run: anchor --no-sandbox
```
---
## Files Modified
| File | Changes |
|------|---------|
| `Core/SandboxContext.cs` | **New file** - Static sandbox state and validation |
| `Program.cs` | Add `--no-sandbox` parsing, call `SandboxContext.Initialize()` |
| `Tools/FileTools.cs` | Update `ResolvePath()` signature to return `string?` with `out errorMessage`; update all tool methods to check for null |
| `Tools/DirTools.cs` | Update `ResolvePath()` signature to return `string?` with `out errorMessage`; update all tool methods to check for null |
| `Tools/EditTools.cs` | No changes (uses `FileTools.ResolvePath()`, sandbox check happens there) |
| `Tools/CommandTool.cs` | **Not sandboxed** - shell commands can access any path (documented limitation) |
---
## Future Enhancements
- **Allowlist**: Let users specify additional safe directories via config
- **Per-tool sandbox**: Some tools (e.g., `GrepRecursive`) could have different rules
- **Audit mode**: Log all file operations for review
- **Interactive prompt**: Ask for confirmation before violating sandbox instead of hard fail