first commit

This commit is contained in:
Tomas Dvorak
2026-02-22 10:42:17 +01:00
commit 55885a0e8f
239 changed files with 103690 additions and 0 deletions
+33
View File
@@ -0,0 +1,33 @@
# Devour
Devour is a context ingestion and management system for AI that scrapes, indexes, and serves documentation from multiple sources.
## Installation
```bash
go install github.com/yourorg/devour/cmd/devour@latest
```
## Quick Start
```bash
# Initialize
devour init
# Scrape documentation
devour scrape https://docs.example.com
# Query indexed docs
devour query "authentication flow"
# Start MCP server
devour serve
```
## Documentation
See [README.md](README.md) for full documentation.
## License
MIT License
+38
View File
@@ -0,0 +1,38 @@
# Build stage
FROM golang:1.22-alpine AS builder
WORKDIR /app
# Install dependencies
RUN apk add --no-cache git
# Copy go mod files
COPY go.mod go.sum ./
RUN go mod download
# Copy source code
COPY . .
# Build binary
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o devour ./cmd/devour
# Runtime stage
FROM alpine:3.19
WORKDIR /app
# Install ca-certificates for HTTPS
RUN apk --no-cache add ca-certificates tzdata
# Copy binary from builder
COPY --from=builder /app/devour /app/devour
# Create data directories
RUN mkdir -p /app/devour_data/docs /app/devour_data/index /app/devour_data/metadata
# Set environment
ENV DEVOUR_DATA_DIR=/app/devour_data
# Entry point
ENTRYPOINT ["/app/devour"]
CMD ["--help"]
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Devour Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+84
View File
@@ -0,0 +1,84 @@
.PHONY: build test lint clean install run docker
# Binary name
BINARY=devour
MAIN_PATH=./cmd/devour
# Go parameters
GOCMD=go
GOBUILD=$(GOCMD) build
GOCLEAN=$(GOCMD) clean
GOTEST=$(GOCMD) test
GOGET=$(GOCMD) get
GOMOD=$(GOCMD) mod
# Build flags
VERSION=$(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
BUILD_TIME=$(shell date -u '+%Y-%m-%d_%H:%M:%S')
LDFLAGS=-ldflags "-s -w -X main.Version=$(VERSION) -X main.BuildTime=$(BUILD_TIME)"
# Default target
all: clean build
## build: Build the binary
build:
$(GOBUILD) $(LDFLAGS) -o $(BINARY) $(MAIN_PATH)
## build-prod: Build optimized production binary
build-prod:
CGO_ENABLED=0 $(GOBUILD) $(LDFLAGS) -o $(BINARY) $(MAIN_PATH)
## test: Run tests
test:
$(GOTEST) -v ./...
## test-coverage: Run tests with coverage
test-coverage:
$(GOTEST) -coverprofile=coverage.out ./...
$(GOCMD) tool cover -html=coverage.out -o coverage.html
## lint: Run linter
lint:
@which golangci-lint > /dev/null || go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
golangci-lint run ./...
## fmt: Format code
fmt:
$(GOCMD) fmt ./...
## clean: Clean build artifacts
clean:
$(GOCLEAN)
rm -f $(BINARY)
rm -f coverage.out coverage.html
## install: Install binary to GOPATH/bin
install:
$(GOCMD) install $(MAIN_PATH)
## run: Run the application
run:
$(GOCMD) run $(MAIN_PATH)
## deps: Download dependencies
deps:
$(GOMOD) download
$(GOMOD) tidy
## docker: Build Docker image
docker:
docker build -t devour:latest .
## docker-run: Run Docker container
docker-run:
docker run -it --rm \
-e OPENAI_API_KEY=$(OPENAI_API_KEY) \
-v $(PWD)/devour_data:/app/devour_data \
devour:latest
## help: Show this help
help:
@echo 'Usage: make [target]'
@echo ''
@echo 'Targets:'
@sed -n 's/^##//p' $(MAKEFILE_LIST) | column -t -s ':'
+564
View File
@@ -0,0 +1,564 @@
<p align="center">
<img src="devour_logo.svg" alt="Devour Logo" width="300">
</p>
<h1 align="center">Devour</h1>
<p align="center">
<strong>Context Ingestion & Management for AI</strong>
</p>
<p align="center">
<a href="#features">Features</a> •
<a href="#installation">Installation</a> •
<a href="#quick-start">Quick Start</a> •
<a href="#architecture">Architecture</a> •
<a href="#cli-reference">CLI Reference</a> •
<a href="#configuration">Configuration</a>
</p>
---
## What is Devour?
Devour is a **context ingestion and management system** designed to feed structured, relevant context to AI models for generating accurate, fully working code.
It scrapes, indexes, and serves documentation from multiple sources:
- GitHub repositories
- OpenAPI/Swagger specifications
- Markdown/HTML documentation sites
- JSON/YAML schemas
- Local project files
### Two Modes of Operation
| Mode | Description | Use Case |
|------|-------------|----------|
| **Local** | Runs as an OpenCode skill on your machine | Single developer, offline work |
| **Remote** | MCP server hosted on your infrastructure | Teams, multi-project support |
---
## Features
### 🕷️ Multi-Source Scraping
- **GitHub** - Clone and parse repos, extract README, docs, code structure
- **OpenAPI** - Parse Swagger specs into structured endpoints
- **Web Docs** - Crawl documentation sites with Colly
- **Local Files** - Index your project's docs folder
### 🧠 Intelligent Indexing
- Vector embeddings via OpenAI (text-embedding-3-small/large)
- Semantic similarity search for context retrieval
- Metadata tracking (source, timestamp, file type)
### 🔄 Automatic Updates
- Configurable scheduler (default: every 3 days)
- Content hash comparison for change detection
- Automatic re-indexing on updates
### 🔌 MCP Integration
- Exposes context via Model Context Protocol
- **Local mode**: stdio transport (OpenCode skill)
- **Remote mode**: HTTP/SSE transport (MCP server)
### 💾 Flexible Storage
```
devour_data/
├── docs/ # Raw scraped documents
├── index/ # Vector embeddings
└── metadata/ # Source tracking & timestamps
```
### 📊 Quality Scorecard
![Quality Scorecard](scorecard.png)
Devour includes a built-in code quality analysis system that generates a comprehensive scorecard for your project.
```bash
# Run quality analysis
devour quality scan
# Generate a visual scorecard badge
devour quality scan --badge-path scorecard.png
```
**Features:**
- Multi-language support (Go, Python, JavaScript, etc.)
- Severity-based scoring (T1-T4 tiers)
- Technical debt tracking
- Automated code review integration
---
## Installation
### Prerequisites
- Go 1.22+
- OpenAI API key (for embeddings)
### From Source
```bash
# Clone the repository
git clone https://github.com/yourorg/devour.git
cd devour
# Install dependencies
go mod download
# Build
go build -o devour ./cmd/devour
# Install globally
go install ./cmd/devour
```
### Quick Install
```bash
go install github.com/yourorg/devour/cmd/devour@latest
```
---
## Quick Start
### 1. Initialize a Project
```bash
# Create devour config in current directory
devour init
# Or specify a path
devour init ./my-project
```
### 2. Get Documentation (NEW!)
```bash
# Quick access to popular language/framework docs
devour get go http # Go HTTP package
devour get python asyncio # Python asyncio module
devour get react hooks # React Hooks documentation
devour get docker compose # Docker Compose docs
devour get rust tokio # Rust Tokio crate
devour get spring boot # Spring Boot framework
# Enhanced markdown output
devour get go http --format markdown
```
**Supported Languages:**
- `go`, `golang` - Go packages (pkg.go.dev)
- `rust` - Rust crates (docs.rs)
- `python`, `py` - Python modules (docs.python.org)
- `java` - Java packages (docs.oracle.com)
- `spring` - Spring Boot (docs.spring.io)
- `typescript`, `ts` - TypeScript (typescriptlang.org)
- `react` - React (react.dev)
- `vue` - Vue.js (vuejs.org)
- `nuxt` - Nuxt (nuxt.com)
- `docker` - Docker (docs.docker.com)
- `cloudflare`, `cf` - Cloudflare (developers.cloudflare.com)
- `astro` - Astro (docs.astro.build)
### 3. Scrape Documentation
```bash
# Scrape from a URL
devour scrape https://docs.example.com
# Scrape a GitHub repo
devour scrape https://github.com/org/repo
# Scrape local docs
devour scrape ./docs
# Multiple sources
devour scrape --sources sources.yaml
```
### 4. Query Context
```bash
# Search indexed docs
devour query "How do I authenticate with the API?"
# With options
devour query "authentication" --limit 5 --format json
```
### 5. Start the Server
```bash
# Local MCP server (stdio transport)
devour serve
# Remote MCP server (HTTP)
devour serve --remote --port 8080
```
### 6. Check Status
```bash
devour status
```
---
## Enhanced Features
### 🎯 Simplified Language Interface
The new `devour get` command provides instant access to documentation for popular languages and frameworks without needing to remember full URLs:
```bash
# Instead of: devour scrape https://pkg.go.dev/net/http
devour get go http
# Instead of: devour scrape https://react.dev/reference/react/hooks
devour get react hooks
# Instead of: devour scrape https://docs.docker.com/compose
devour get docker compose
```
### 📝 Rich Markdown Output
Enable enhanced markdown formatting for beautiful, structured documentation:
```bash
devour get go http --format markdown
```
**Features:**
- 📋 Document metadata tables
- 📑 Auto-generated table of contents
- 🎨 Enhanced typography with emoji indicators
- 🔗 Automatic link conversion
- 📚 Structured content sections
- 🏷️ Source attribution and timestamps
### 🧠 Smart Content Enhancement
The markdown formatter automatically:
- Converts plain URLs to clickable links
- Adds visual indicators for examples, notes, and warnings
- Fixes code block formatting
- Generates proper heading structure
- Creates document metadata tables
---
## Architecture
```
┌─────────────────────────────────────────────────────────────────┐
│ Devour System │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌──────────┐ ┌───────────┐ ┌──────────┐ │
│ │ Scraper │───▶│ Indexer │───▶│ Storage │───▶│ Server │ │
│ └─────────┘ └──────────┘ └───────────┘ └──────────┘ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ ┌─────────┐ ┌──────────┐ ┌───────────┐ ┌──────────┐ │
│ │ GitHub │ │ OpenAI │ │ Vector DB │ │ MCP │ │
│ │ Web │ │ Embeds │ │ (chromem) │ │ Protocol │ │
│ │ Local │ │ │ │ │ │ │ │
│ └─────────┘ └──────────┘ └───────────┘ └──────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Scheduler │ │
│ │ (Auto-update every 3 days, configurable) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### Data Flow
```
User Query → Devour Server → Embedding Generation → Vector Search
AI Response ← Context Chunks ← Top-K Relevant Docs ←───┘
```
---
## CLI Reference
### Commands
| Command | Description |
|---------|-------------|
| `devour init [path]` | Initialize Devour for a project |
| `devour get <language> <keyword>` | **NEW** Quick docs fetch for popular languages |
| `devour scrape <source>` | Scrape docs from URL, repo, or path |
| `devour serve` | Start MCP server (local or remote) |
| `devour query <text>` | Search indexed documentation |
| `devour status` | Show index stats and last update |
| `devour sync` | Fetch updates from all sources |
| `devour push <path>` | Push docs to remote MCP server |
### Flags
```bash
# Global flags
--config, -c Config file path (default: ./devour.yaml)
--verbose, -v Enable verbose logging
--quiet, -q Suppress non-error output
# scrape flags
--sources, -s YAML file with source definitions
--format, -f Output format: json, markdown (default: json)
--concurrency Parallel scraping workers (default: 10)
# serve flags
--remote Run as remote HTTP server
--port, -p HTTP port (default: 8080)
--host HTTP host (default: localhost)
# query flags
--limit, -l Max results (default: 5)
--format, -f Output: json, text, markdown
--threshold Similarity threshold (default: 0.7)
```
---
## Configuration
### devour.yaml
```yaml
# Devour Configuration
# Storage paths
storage:
docs_dir: ./devour_data/docs
index_dir: ./devour_data/index
metadata_dir: ./devour_data/metadata
# Embedding settings
embeddings:
provider: openai # openai, local
model: text-embedding-3-small
api_key: ${OPENAI_API_KEY} # Env var reference
# Vector database
vector_db:
type: chromem # chromem, weaviate, faiss
persist: true
# Scraping settings
scraper:
user_agent: "Devour/1.0"
timeout: 30s
retry_count: 3
concurrency: 10
rate_limit: 500ms
# Scheduler
scheduler:
enabled: true
interval: 72h # Every 3 days
check_method: hash # hash, timestamp
# Server settings
server:
mode: local # local, remote
port: 8080
host: localhost
# Sources (for sync)
sources:
- name: project-docs
type: url
url: https://docs.example.com
include: ["**/*.md", "**/*.html"]
exclude: ["**/api/**"]
- name: api-spec
type: openapi
url: https://api.example.com/openapi.json
- name: github-repo
type: github
repo: org/repo
branch: main
paths: ["docs/", "README.md"]
```
---
## API Reference
### MCP Tools (when running as server)
#### `devour_query`
Search indexed documentation for relevant context.
```json
{
"query": "How do I authenticate?",
"limit": 5,
"threshold": 0.7
}
```
#### `devour_add`
Add documents to the index.
```json
{
"documents": [
{
"content": "Document text...",
"metadata": {
"source": "https://...",
"type": "markdown"
}
}
]
}
```
#### `devour_status`
Get indexing status and statistics.
### REST API (remote mode)
```
GET /health # Health check
GET /status # Index statistics
POST /query # Search documents
POST /documents # Add documents
GET /documents # List documents
DELETE /documents/:id # Delete document
POST /sync # Trigger sync
```
---
## Integration Examples
### With OpenCode (Local Mode)
Add to your OpenCode skills:
```yaml
# ~/.opencode/skills.yaml
skills:
- name: devour
path: /path/to/devour
commands:
- devour serve
```
Then in OpenCode:
```
/devour query "authentication flow"
```
### With AI Applications
```go
import "github.com/yourorg/devour/pkg/client"
func main() {
client := client.New("http://localhost:8080")
results, err := client.Query(ctx, "How do I use the API?", 5)
if err != nil {
log.Fatal(err)
}
for _, r := range results {
fmt.Printf("Score: %.2f - %s\n", r.Score, r.Content[:100])
}
}
```
---
## Development
### Project Structure
```
devour/
├── cmd/devour/ # CLI entrypoint
│ └── main.go
├── internal/
│ ├── scraper/ # Scraping logic
│ ├── indexer/ # Embedding generation
│ ├── server/ # MCP server
│ ├── scheduler/ # Background updates
│ └── ai/ # AI integrations
├── pkg/
│ ├── client/ # Go client library
│ └── types/ # Shared types
├── devour_data/ # Default data directory
├── go.mod
├── Makefile
└── README.md
```
### Building
```bash
# Development build
go build -o devour ./cmd/devour
# Production build
CGO_ENABLED=0 go build -ldflags="-s -w" -o devour ./cmd/devour
# Run tests
go test ./...
# Run with coverage
go test -cover ./...
```
### Makefile Targets
```bash
make build # Build binary
make test # Run tests
make lint # Run linter
make docker # Build Docker image
make install # Install locally
```
---
## Roadmap
- [ ] Local LLM support (Ollama, LocalAI)
- [ ] Multi-tenant support for remote mode
- [ ] Web UI for document management
- [ ] Git-based versioning for docs
- [ ] Plugin system for custom scrapers
- [ ] Reranking with cross-encoders
---
## Contributing
Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details.
---
## License
MIT License - see [LICENSE](LICENSE) for details.
---
<p align="center">
<sub>Built with ❤️ for better AI context</sub>
</p>
+559
View File
@@ -0,0 +1,559 @@
---
name: devour
description: >
Context ingestion and management system for AI. Scrapes, indexes, and serves
documentation from GitHub repos, OpenAPI specs, web docs, and local files.
Provides semantic search via vector embeddings to feed relevant context to
AI models. Runs in local mode (stdio) or remote mode (HTTP MCP server).
Supports automatic updates via configurable scheduler. Integrates with
OpenAI for embeddings and LLM context injection. Triggers on: "devour",
"scrape docs", "index documentation", "context for AI", "vector search docs",
"semantic search", "ingest documentation", "documentation to AI".
allowed-tools:
- Read
- Write
- Edit
- Glob
- Grep
- Bash
- WebFetch
---
# Devour — Context Ingestion Skill
Comprehensive documentation scraping, indexing, and retrieval system for
feeding structured context to AI models. Orchestrates 5 specialized modules
and supports both local (stdio) and remote (HTTP) MCP modes.
## Quick Reference
| Command | What it does |
|---------|-------------|
| `/devour init [path]` | Initialize Devour for a project |
| `/devour get <language> <keyword>` | **NEW** Quick docs fetch for popular languages |
| `/devour scrape <source>` | Scrape docs from URL, GitHub, or local path |
| `/devour serve` | Start MCP server (local or remote) |
| `/devour query <text>` | Search indexed documentation |
| `/devour status` | Show index stats and health |
| `/devour sync` | Fetch updates from all configured sources |
| `/devour push <path>` | Push docs to remote MCP server |
| `/devour sources` | Manage documentation sources |
## Orchestration Logic
When the user invokes `/devour get <language> <keyword>`:
1. **Map language to base URL**:
- `go http``https://pkg.go.dev/http`
- `python asyncio``https://docs.python.org/3/library/asyncio.html`
- `react hooks``https://react.dev/reference/react/hooks`
- `docker compose``https://docs.docker.com/compose`
2. **Auto-detect source type** based on language:
- Go → `godocs` parser
- Python → `pythondocs` parser
- React → `reactdocs` parser
- Docker → `dockerdocs` parser
3. **Execute enhanced scrape** with pre-configured parameters:
- Automatic language-specific parsing
- Enhanced markdown formatting (if requested)
- Metadata extraction and enrichment
4. **Return structured documentation**:
- Rich markdown with TOC (if `--format markdown`)
- JSON with full metadata (default)
- Ready for AI context injection
When the user invokes `/devour scrape`:
1. **Detect source type** from URL/path:
- GitHub: `github.com/org/repo` → Clone, extract docs
- OpenAPI: Ends in `.json`/`.yaml` with OpenAPI spec → Parse endpoints
- Web: HTTP/HTTPS URL → Crawl with Colly
- Local: File path → Scan directory
2. **Scrape with appropriate parser**:
- Extract content (markdown, HTML, code structure)
- Clean and normalize text
- Extract metadata (title, headings, code blocks)
3. **Generate embeddings**:
- Chunk content appropriately (512-1024 tokens)
- Call OpenAI embedding API
- Store in vector database
4. **Update metadata**:
- Track source, timestamp, content hash
- Enable future update detection
When the user invokes `/devour query`:
1. Generate embedding for query text
2. Perform vector similarity search
3. Return top-K results with metadata
4. Optionally inject into AI context
## Enhanced Features
### 🎯 Language-Aware Documentation Access
The `devour get` command provides intelligent, language-specific documentation retrieval:
**Supported Languages & Mappings:**
- `go`, `golang` → Go packages (pkg.go.dev)
- `rust` → Rust crates (docs.rs)
- `python`, `py` → Python modules (docs.python.org)
- `java` → Java packages (docs.oracle.com)
- `spring` → Spring Boot (docs.spring.io)
- `typescript`, `ts` → TypeScript (typescriptlang.org)
- `react` → React (react.dev)
- `vue` → Vue.js (vuejs.org)
- `nuxt` → Nuxt (nuxt.com)
- `docker` → Docker (docs.docker.com)
- `cloudflare`, `cf` → Cloudflare (developers.cloudflare.com)
- `astro` → Astro (docs.astro.build)
**Usage Examples:**
```bash
/devour get go http # Go HTTP package docs
/devour get python asyncio # Python asyncio module
/devour get react hooks # React Hooks reference
/devour get docker compose # Docker Compose guide
/devour get rust tokio # Rust Tokio crate docs
```
### 📝 Rich Markdown Enhancement
When using `--format markdown`, Devour automatically enhances documentation:
**Auto-Generated Structure:**
- 📋 Document metadata tables (source, type, timestamp)
- 📑 Table of contents from headings
- 🎨 Visual indicators for important content
- 🔗 Automatic URL-to-link conversion
- 📚 Proper heading hierarchy
**Content Enhancement:**
- `Example:` → 💡 **Example:**
- `Note:` → 📝 **Note:**
- `Warning:` → ⚠️ **Warning:**
- `Important:` → ❗ **Important:**
- `TODO:` → 📋 **TODO:**
**Example Output Structure:**
```markdown
# Package Name
## 📋 Document Information
| Property | Value |
|----------|-------|
| **Source** | https://pkg.go.dev/http |
| **Type** | `godocs` |
| **Scraped** | 2026-02-19 12:30:00 |
## 📑 Table of Contents
- [Functions](#functions)
- [Types](#types)
- [Examples](#examples)
## 📚 Content
# Functions
💡 **Example:** Usage example here...
```
## Source Type Detection
| Pattern | Type | Parser |
|---------|------|--------|
| `github.com/*/*` | GitHub | Git clone + markdown parser |
| `*.json` + OpenAPI keys | OpenAPI | Swagger parser |
| `http://*`, `https://*` | Web | Colly crawler |
| `./path`, `/path` | Local | Directory scanner |
| `*.md`, `*.rst`, `*.txt` | File | Direct parse |
## Module Reference
### 1. Scraper Module (`internal/scraper`)
Responsible for fetching and parsing content from various sources.
**Supported sources:**
- GitHub repositories (clone, extract docs/, README.md)
- OpenAPI/Swagger specs (parse endpoints, schemas)
- HTML documentation sites (crawl, extract content)
- Markdown files (parse structure, code blocks)
- JSON/YAML configuration files
**Output format:**
```json
{
"id": "doc-uuid",
"source": "https://...",
"type": "markdown",
"title": "Document Title",
"content": "Extracted text...",
"metadata": {
"headings": ["H1", "H2"],
"code_blocks": ["go", "bash"],
"links": ["url1", "url2"]
},
"timestamp": "2025-01-15T10:00:00Z"
}
```
### 2. Indexer Module (`internal/indexer`)
Converts documents into vector embeddings for semantic search.
**Features:**
- OpenAI embedding integration (text-embedding-3-small/large)
- Intelligent chunking (512-1024 tokens, respect boundaries)
- Metadata preservation
- Batch processing for efficiency
**Chunking strategy:**
```go
type Chunk struct {
ID string
DocID string
Content string
Vector []float32
Metadata map[string]any
Position int // Position in original doc
}
```
### 3. Server Module (`internal/server`)
Exposes context via MCP protocol.
**Local mode (stdio):**
```
STDIN → JSON-RPC → Handler → Response → STDOUT
```
**Remote mode (HTTP):**
```
HTTP Request → Handler → Response → HTTP Response
```
**MCP Tools exposed:**
- `devour_query` - Semantic search
- `devour_add` - Add documents
- `devour_status` - Get stats
- `devour_sync` - Trigger update
**MCP Resources:**
- `devour://documents` - All indexed docs
- `devour://sources` - Configured sources
- `devour://stats` - Index statistics
### 4. Scheduler Module (`internal/scheduler`)
Manages automatic updates from configured sources.
**Default schedule:** Every 72 hours (3 days)
**Change detection methods:**
- Content hash comparison (default)
- Last-Modified timestamp
- ETag header
- Git commit hash (for repos)
**Configuration:**
```yaml
scheduler:
enabled: true
interval: 72h
check_method: hash
retry_count: 3
retry_delay: 1h
```
### 5. AI Module (`internal/ai`)
Handles AI integrations for embeddings and context injection.
**Supported providers:**
- OpenAI (primary)
- Ollama (local, planned)
- Custom endpoints
**Context injection format:**
```go
type Context struct {
Query string
Results []SearchResult
SystemPrompt string
}
func (c *Context) ToPrompt() string {
// Format for LLM consumption
}
```
## Configuration Schema
### devour.yaml
```yaml
# Core configuration
version: 1
# Storage paths
storage:
docs_dir: ./devour_data/docs
index_dir: ./devour_data/index
metadata_dir: ./devour_data/metadata
# Embedding configuration
embeddings:
provider: openai
model: text-embedding-3-small
dimensions: 1536
api_key: ${OPENAI_API_KEY}
batch_size: 100
# Vector database
vector_db:
type: chromem # chromem, weaviate, faiss
persist: true
similarity_metric: cosine
# Scraping configuration
scraper:
user_agent: "Devour/1.0 (+https://github.com/yourorg/devour)"
timeout: 30s
retry_count: 3
retry_delay: 5s
concurrency: 10
rate_limit: 500ms
max_depth: 3
cache_dir: ./devour_data/cache
# Scheduler configuration
scheduler:
enabled: true
interval: 72h
check_method: hash
on_startup: false
# Server configuration
server:
mode: local # local, remote
transport: stdio # stdio, http
host: localhost
port: 8080
cors:
enabled: false
origins: []
# Source definitions
sources:
- name: example-docs
type: url
url: https://docs.example.com
include:
- "**/*.md"
- "**/*.html"
exclude:
- "**/api/**"
- "**/legacy/**"
schedule: 24h # Override global schedule
- name: api-spec
type: openapi
url: https://api.example.com/openapi.json
schedule: 168h # Weekly
- name: internal-repo
type: github
repo: myorg/myrepo
branch: main
paths:
- docs/
- README.md
auth_token: ${GITHUB_TOKEN}
```
## Environment Variables
| Variable | Description | Default |
|----------|-------------|---------|
| `OPENAI_API_KEY` | OpenAI API key | Required |
| `DEVOUR_CONFIG` | Config file path | `./devour.yaml` |
| `DEVOUR_DATA_DIR` | Data directory | `./devour_data` |
| `GITHUB_TOKEN` | GitHub auth token | Optional |
| `DEVOUR_LOG_LEVEL` | Log level (debug, info, warn, error) | `info` |
| `DEVOUR_PORT` | Server port | `8080` |
## Quality Gates
Built-in validation rules:
- ⚠️ **WARNING** if document count < 10 (may be incomplete scrape)
- ⚠️ **WARNING** if average chunk size < 100 tokens (over-fragmented)
- 🛑 **HARD STOP** if embedding API fails (cannot index without vectors)
- 🛑 **HARD STOP** if storage is not writable (cannot persist)
## Output Formats
### Query Results (JSON)
```json
{
"query": "authentication",
"results": [
{
"id": "chunk-uuid",
"document_id": "doc-uuid",
"content": "Relevant text excerpt...",
"score": 0.89,
"source": "https://docs.example.com/auth",
"metadata": {
"title": "Authentication Guide",
"section": "Getting Started"
}
}
],
"total": 15,
"took_ms": 45
}
```
### Status Output
```
Devour Status
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Index Health: ✅ Healthy
Documents: 1,247 indexed
Chunks: 8,392 total
Vector Dimension: 1536
Last Updated: 2025-01-15 10:30:00
Storage Used: 124 MB
Sources (3):
✅ example-docs (234 docs, synced 2h ago)
✅ api-spec (12 docs, synced 1d ago)
⚠️ internal-repo (pending first sync)
Next Scheduled Sync: 2025-01-18 10:30:00
```
## Integration Patterns
### With OpenCode
```yaml
# In OpenCode session
> /devour init
> /devour scrape https://docs.myframework.com
> /devour serve
# In another terminal or session
> /devour query "how to handle authentication"
# Returns relevant context for AI
```
### With AI Assistant
```go
// AI assistant queries Devour automatically
func getRelevantContext(query string) string {
resp, _ := http.Post("http://localhost:8080/query",
"application/json",
bytes.NewReader([]byte(`{"query":"`+query+`"}`)))
var result QueryResponse
json.NewDecoder(resp.Body).Decode(&result)
// Inject into prompt
return formatContextForAI(result.Results)
}
```
### As MCP Tool
```json
// AI calls via MCP
{
"method": "tools/call",
"params": {
"name": "devour_query",
"arguments": {
"query": "API rate limiting",
"limit": 5
}
}
}
```
## Sub-Skills
This skill can delegate to specialized modules:
1. **devour-scrape** — Scraping operations
2. **devour-index** — Indexing and embeddings
3. **devour-query** — Search and retrieval
4. **devour-sync** — Synchronization tasks
5. **devour-serve** — Server management
## Error Handling
| Error | Cause | Resolution |
|-------|-------|------------|
| `E001` | OpenAI API error | Check API key, rate limits |
| `E002` | Source unreachable | Verify URL, check network |
| `E003` | Storage write failure | Check permissions, disk space |
| `E004` | Invalid source type | Use supported: url, github, openapi, local |
| `E005` | Index corruption | Rebuild index with `devour sync --rebuild` |
## Performance Tuning
### Scraping
```yaml
scraper:
concurrency: 20 # Parallel workers
rate_limit: 200ms # Between requests
timeout: 60s # Per request
```
### Indexing
```yaml
embeddings:
batch_size: 200 # API batch size
vector_db:
index_type: hnsw # Fast similarity search
m: 16 # HNSW connectivity
```
### Querying
```yaml
query:
ef_search: 64 # HNSW search depth
limit: 10 # Default result count
```
## Troubleshooting
### Common Issues
**Slow queries:**
- Increase `ef_search` for better recall
- Use smaller `limit` values
- Consider index type (HNSW vs Flat)
**API rate limits:**
- Reduce `batch_size`
- Add delays between batches
- Use caching
**Memory usage:**
- Reduce `concurrency`
- Process in smaller batches
- Use disk-backed storage
---
*Devour: Feed your AI the context it craves.*
+328
View File
@@ -0,0 +1,328 @@
mode: atomic
github.com/yourorg/devour/cmd/devour/main.go:7.13,9.2 1 0
github.com/yourorg/devour/cmd/realtest/main.go:11.13,38.30 7 0
github.com/yourorg/devour/cmd/realtest/main.go:38.30,42.15 3 0
github.com/yourorg/devour/cmd/realtest/main.go:42.15,44.12 2 0
github.com/yourorg/devour/cmd/realtest/main.go:47.3,56.17 4 0
github.com/yourorg/devour/cmd/realtest/main.go:56.17,58.12 2 0
github.com/yourorg/devour/cmd/realtest/main.go:61.3,63.20 2 0
github.com/yourorg/devour/cmd/realtest/main.go:63.20,68.32 5 0
github.com/yourorg/devour/cmd/realtest/main.go:68.32,70.5 1 0
github.com/yourorg/devour/cmd/realtest/main.go:70.10,72.5 1 0
github.com/yourorg/devour/cmd/realtest/main.go:75.3,76.17 2 0
github.com/yourorg/devour/cmd/realtest/main.go:76.17,78.4 1 0
github.com/yourorg/devour/cmd/realtest/main.go:78.9,80.4 1 0
github.com/yourorg/devour/cmd/realtest/main.go:82.3,82.16 1 0
github.com/yourorg/devour/cmd/realtest/main.go:85.2,108.30 5 0
github.com/yourorg/devour/cmd/realtest/main.go:108.30,110.15 2 0
github.com/yourorg/devour/cmd/realtest/main.go:110.15,112.4 1 0
github.com/yourorg/devour/cmd/realtest/main.go:112.9,114.4 1 0
github.com/yourorg/devour/cmd/realtest/main.go:117.2,118.38 2 0
github.com/yourorg/devour/cmd/demo.go:26.13,28.2 1 0
github.com/yourorg/devour/cmd/demo.go:30.55,37.51 5 0
github.com/yourorg/devour/cmd/demo.go:37.51,39.3 1 0
github.com/yourorg/devour/cmd/demo.go:41.2,51.16 7 0
github.com/yourorg/devour/cmd/demo.go:51.16,53.3 1 0
github.com/yourorg/devour/cmd/demo.go:55.2,60.16 4 0
github.com/yourorg/devour/cmd/demo.go:60.16,62.3 1 0
github.com/yourorg/devour/cmd/demo.go:64.2,68.29 3 0
github.com/yourorg/devour/cmd/demo.go:68.29,69.20 1 0
github.com/yourorg/devour/cmd/demo.go:69.20,71.18 2 0
github.com/yourorg/devour/cmd/demo.go:71.18,73.5 1 0
github.com/yourorg/devour/cmd/demo.go:76.2,83.29 5 0
github.com/yourorg/devour/cmd/demo.go:83.29,84.58 1 0
github.com/yourorg/devour/cmd/demo.go:84.58,86.9 2 0
github.com/yourorg/devour/cmd/demo.go:90.2,90.21 1 0
github.com/yourorg/devour/cmd/demo.go:90.21,92.17 2 0
github.com/yourorg/devour/cmd/demo.go:92.17,97.31 4 0
github.com/yourorg/devour/cmd/demo.go:97.31,98.16 1 0
github.com/yourorg/devour/cmd/demo.go:98.16,100.11 2 0
github.com/yourorg/devour/cmd/demo.go:102.5,102.32 1 0
github.com/yourorg/devour/cmd/demo.go:107.2,124.12 17 0
github.com/yourorg/devour/cmd/get.go:40.13,45.2 3 0
github.com/yourorg/devour/cmd/get.go:47.54,53.16 4 0
github.com/yourorg/devour/cmd/get.go:53.16,55.3 1 0
github.com/yourorg/devour/cmd/get.go:58.2,70.44 8 0
github.com/yourorg/devour/cmd/get.go:73.64,74.18 1 0
github.com/yourorg/devour/cmd/get.go:75.22,76.60 1 0
github.com/yourorg/devour/cmd/get.go:77.14,78.77 1 0
github.com/yourorg/devour/cmd/get.go:79.22,80.51 1 0
github.com/yourorg/devour/cmd/get.go:80.51,82.4 1 0
github.com/yourorg/devour/cmd/get.go:83.3,83.80 1 0
github.com/yourorg/devour/cmd/get.go:84.14,85.88 1 0
github.com/yourorg/devour/cmd/get.go:86.16,87.111 1 0
github.com/yourorg/devour/cmd/get.go:88.26,89.91 1 0
github.com/yourorg/devour/cmd/get.go:90.15,91.75 1 0
github.com/yourorg/devour/cmd/get.go:92.13,93.70 1 0
github.com/yourorg/devour/cmd/get.go:94.14,95.69 1 0
github.com/yourorg/devour/cmd/get.go:96.16,97.65 1 0
github.com/yourorg/devour/cmd/get.go:98.26,99.75 1 0
github.com/yourorg/devour/cmd/get.go:100.15,101.76 1 0
github.com/yourorg/devour/cmd/get.go:102.10,103.172 1 0
github.com/yourorg/devour/cmd/get.go:107.48,108.18 1 0
github.com/yourorg/devour/cmd/get.go:109.22,110.18 1 0
github.com/yourorg/devour/cmd/get.go:111.14,112.20 1 0
github.com/yourorg/devour/cmd/get.go:113.22,114.22 1 0
github.com/yourorg/devour/cmd/get.go:115.14,116.20 1 0
github.com/yourorg/devour/cmd/get.go:117.16,118.22 1 0
github.com/yourorg/devour/cmd/get.go:119.26,120.18 1 0
github.com/yourorg/devour/cmd/get.go:121.15,122.21 1 0
github.com/yourorg/devour/cmd/get.go:123.13,124.19 1 0
github.com/yourorg/devour/cmd/get.go:125.14,126.20 1 0
github.com/yourorg/devour/cmd/get.go:127.16,128.22 1 0
github.com/yourorg/devour/cmd/get.go:129.26,130.26 1 0
github.com/yourorg/devour/cmd/get.go:131.15,132.21 1 0
github.com/yourorg/devour/cmd/get.go:133.10,134.15 1 0
github.com/yourorg/devour/cmd/init.go:31.13,34.2 2 0
github.com/yourorg/devour/cmd/init.go:36.55,39.19 2 0
github.com/yourorg/devour/cmd/init.go:39.19,41.3 1 0
github.com/yourorg/devour/cmd/init.go:44.2,44.53 1 0
github.com/yourorg/devour/cmd/init.go:44.53,46.3 1 0
github.com/yourorg/devour/cmd/init.go:48.2,51.61 2 0
github.com/yourorg/devour/cmd/init.go:51.61,53.3 1 0
github.com/yourorg/devour/cmd/init.go:56.2,57.71 2 0
github.com/yourorg/devour/cmd/init.go:57.71,59.3 1 0
github.com/yourorg/devour/cmd/init.go:62.2,70.27 3 0
github.com/yourorg/devour/cmd/init.go:70.27,71.48 1 0
github.com/yourorg/devour/cmd/init.go:71.48,73.4 1 0
github.com/yourorg/devour/cmd/init.go:76.2,83.12 7 0
github.com/yourorg/devour/cmd/init.go:86.48,88.12 2 0
github.com/yourorg/devour/cmd/init.go:88.12,90.3 1 0
github.com/yourorg/devour/cmd/init.go:92.2,143.9 1 0
github.com/yourorg/devour/cmd/languages.go:21.13,23.2 1 0
github.com/yourorg/devour/cmd/languages.go:25.60,97.33 5 0
github.com/yourorg/devour/cmd/languages.go:97.33,101.41 4 0
github.com/yourorg/devour/cmd/languages.go:101.41,103.4 1 0
github.com/yourorg/devour/cmd/languages.go:104.3,104.16 1 0
github.com/yourorg/devour/cmd/languages.go:107.2,117.12 10 0
github.com/yourorg/devour/cmd/push.go:32.13,35.2 2 0
github.com/yourorg/devour/cmd/push.go:37.55,40.22 2 0
github.com/yourorg/devour/cmd/push.go:40.22,43.3 1 0
github.com/yourorg/devour/cmd/push.go:45.2,47.23 3 0
github.com/yourorg/devour/cmd/push.go:47.23,49.3 1 0
github.com/yourorg/devour/cmd/push.go:57.2,61.12 4 0
github.com/yourorg/devour/cmd/quality.go:136.13,171.2 22 0
github.com/yourorg/devour/cmd/quality.go:173.62,175.19 2 0
github.com/yourorg/devour/cmd/quality.go:175.19,177.3 1 0
github.com/yourorg/devour/cmd/quality.go:179.2,179.49 1 0
github.com/yourorg/devour/cmd/quality.go:179.49,181.3 1 0
github.com/yourorg/devour/cmd/quality.go:183.2,200.16 6 0
github.com/yourorg/devour/cmd/quality.go:200.16,203.3 2 0
github.com/yourorg/devour/cmd/quality.go:205.2,206.8 2 0
github.com/yourorg/devour/cmd/quality.go:206.8,208.59 2 0
github.com/yourorg/devour/cmd/quality.go:208.59,210.4 1 0
github.com/yourorg/devour/cmd/quality.go:211.8,215.3 3 0
github.com/yourorg/devour/cmd/quality.go:217.2,219.16 3 0
github.com/yourorg/devour/cmd/quality.go:219.16,221.3 1 0
github.com/yourorg/devour/cmd/quality.go:223.2,223.48 1 0
github.com/yourorg/devour/cmd/quality.go:226.64,234.54 5 0
github.com/yourorg/devour/cmd/quality.go:234.54,239.55 2 0
github.com/yourorg/devour/cmd/quality.go:239.55,242.4 2 0
github.com/yourorg/devour/cmd/quality.go:245.2,245.24 1 0
github.com/yourorg/devour/cmd/quality.go:245.24,248.3 2 0
github.com/yourorg/devour/cmd/quality.go:251.2,255.23 3 0
github.com/yourorg/devour/cmd/quality.go:256.14,257.54 1 0
github.com/yourorg/devour/cmd/quality.go:258.10,260.13 2 0
github.com/yourorg/devour/cmd/quality.go:264.62,271.54 4 0
github.com/yourorg/devour/cmd/quality.go:271.54,275.55 2 0
github.com/yourorg/devour/cmd/quality.go:275.55,277.4 1 0
github.com/yourorg/devour/cmd/quality.go:280.2,280.24 1 0
github.com/yourorg/devour/cmd/quality.go:280.24,283.3 2 0
github.com/yourorg/devour/cmd/quality.go:286.2,289.17 3 0
github.com/yourorg/devour/cmd/quality.go:289.17,292.3 2 0
github.com/yourorg/devour/cmd/quality.go:295.2,295.14 1 0
github.com/yourorg/devour/cmd/quality.go:295.14,296.33 1 0
github.com/yourorg/devour/cmd/quality.go:296.33,298.37 1 0
github.com/yourorg/devour/cmd/quality.go:298.37,299.78 1 0
github.com/yourorg/devour/cmd/quality.go:299.78,301.11 2 0
github.com/yourorg/devour/cmd/quality.go:304.4,304.49 1 0
github.com/yourorg/devour/cmd/quality.go:304.49,307.5 2 0
github.com/yourorg/devour/cmd/quality.go:312.2,320.13 8 0
github.com/yourorg/devour/cmd/quality.go:320.13,323.24 3 0
github.com/yourorg/devour/cmd/quality.go:324.27,325.90 1 0
github.com/yourorg/devour/cmd/quality.go:326.27,327.94 1 0
github.com/yourorg/devour/cmd/quality.go:328.27,329.97 1 0
github.com/yourorg/devour/cmd/quality.go:330.27,331.93 1 0
github.com/yourorg/devour/cmd/quality.go:334.3,334.104 1 0
github.com/yourorg/devour/cmd/quality.go:337.2,337.12 1 0
github.com/yourorg/devour/cmd/quality.go:340.65,341.19 1 0
github.com/yourorg/devour/cmd/quality.go:341.19,343.3 1 0
github.com/yourorg/devour/cmd/quality.go:345.2,356.28 4 0
github.com/yourorg/devour/cmd/quality.go:356.28,358.3 1 0
github.com/yourorg/devour/cmd/quality.go:360.2,360.23 1 0
github.com/yourorg/devour/cmd/quality.go:360.23,362.3 1 0
github.com/yourorg/devour/cmd/quality.go:365.2,370.54 4 0
github.com/yourorg/devour/cmd/quality.go:370.54,374.59 2 0
github.com/yourorg/devour/cmd/quality.go:374.59,376.4 1 0
github.com/yourorg/devour/cmd/quality.go:380.2,381.35 2 0
github.com/yourorg/devour/cmd/quality.go:381.35,382.23 1 0
github.com/yourorg/devour/cmd/quality.go:382.23,385.31 3 0
github.com/yourorg/devour/cmd/quality.go:385.31,387.5 1 0
github.com/yourorg/devour/cmd/quality.go:388.4,389.20 2 0
github.com/yourorg/devour/cmd/quality.go:389.20,391.5 1 0
github.com/yourorg/devour/cmd/quality.go:392.4,393.9 2 0
github.com/yourorg/devour/cmd/quality.go:397.2,397.12 1 0
github.com/yourorg/devour/cmd/quality.go:397.12,399.3 1 0
github.com/yourorg/devour/cmd/quality.go:402.2,402.51 1 0
github.com/yourorg/devour/cmd/quality.go:402.51,404.3 1 0
github.com/yourorg/devour/cmd/quality.go:406.2,415.16 3 0
github.com/yourorg/devour/cmd/quality.go:415.16,417.3 1 0
github.com/yourorg/devour/cmd/quality.go:419.2,419.61 1 0
github.com/yourorg/devour/cmd/quality.go:419.61,421.3 1 0
github.com/yourorg/devour/cmd/quality.go:423.2,424.23 2 0
github.com/yourorg/devour/cmd/quality.go:424.23,426.3 1 0
github.com/yourorg/devour/cmd/quality.go:428.2,428.12 1 0
github.com/yourorg/devour/cmd/quality.go:431.72,434.51 2 0
github.com/yourorg/devour/cmd/quality.go:434.51,436.3 1 0
github.com/yourorg/devour/cmd/quality.go:438.2,448.16 4 0
github.com/yourorg/devour/cmd/quality.go:448.16,450.3 1 0
github.com/yourorg/devour/cmd/quality.go:452.2,452.61 1 0
github.com/yourorg/devour/cmd/quality.go:452.61,454.3 1 0
github.com/yourorg/devour/cmd/quality.go:457.2,457.16 1 0
github.com/yourorg/devour/cmd/quality.go:458.14,459.51 1 0
github.com/yourorg/devour/cmd/quality.go:460.10,461.38 1 0
github.com/yourorg/devour/cmd/quality.go:465.61,473.31 7 0
github.com/yourorg/devour/cmd/quality.go:473.31,476.3 2 0
github.com/yourorg/devour/cmd/quality.go:478.2,479.42 2 0
github.com/yourorg/devour/cmd/quality.go:479.42,481.3 1 0
github.com/yourorg/devour/cmd/quality.go:483.2,491.33 3 0
github.com/yourorg/devour/cmd/quality.go:491.33,493.25 2 0
github.com/yourorg/devour/cmd/quality.go:493.25,494.12 1 0
github.com/yourorg/devour/cmd/quality.go:497.3,498.36 2 0
github.com/yourorg/devour/cmd/quality.go:498.36,501.4 1 0
github.com/yourorg/devour/cmd/quality.go:502.3,502.16 1 0
github.com/yourorg/devour/cmd/quality.go:505.2,508.12 3 0
github.com/yourorg/devour/cmd/quality.go:511.61,516.54 4 0
github.com/yourorg/devour/cmd/quality.go:516.54,520.55 2 0
github.com/yourorg/devour/cmd/quality.go:520.55,522.4 1 0
github.com/yourorg/devour/cmd/quality.go:525.2,525.24 1 0
github.com/yourorg/devour/cmd/quality.go:525.24,528.3 2 0
github.com/yourorg/devour/cmd/quality.go:530.2,539.12 5 0
github.com/yourorg/devour/cmd/quality.go:539.12,540.36 1 0
github.com/yourorg/devour/cmd/quality.go:540.36,541.86 1 0
github.com/yourorg/devour/cmd/quality.go:541.86,542.13 1 0
github.com/yourorg/devour/cmd/quality.go:545.4,545.42 1 0
github.com/yourorg/devour/cmd/quality.go:545.42,546.30 1 0
github.com/yourorg/devour/cmd/quality.go:546.30,548.20 2 0
github.com/yourorg/devour/cmd/quality.go:548.20,550.15 2 0
github.com/yourorg/devour/cmd/quality.go:552.6,552.24 1 0
github.com/yourorg/devour/cmd/quality.go:552.24,555.7 2 0
github.com/yourorg/devour/cmd/quality.go:556.6,556.11 1 0
github.com/yourorg/devour/cmd/quality.go:560.8,561.20 1 0
github.com/yourorg/devour/cmd/quality.go:561.20,563.4 1 0
github.com/yourorg/devour/cmd/quality.go:565.3,567.27 3 0
github.com/yourorg/devour/cmd/quality.go:567.27,568.34 1 0
github.com/yourorg/devour/cmd/quality.go:568.34,570.10 2 0
github.com/yourorg/devour/cmd/quality.go:574.3,574.20 1 0
github.com/yourorg/devour/cmd/quality.go:574.20,576.4 1 0
github.com/yourorg/devour/cmd/quality.go:578.3,578.41 1 0
github.com/yourorg/devour/cmd/quality.go:578.41,579.29 1 0
github.com/yourorg/devour/cmd/quality.go:579.29,581.19 2 0
github.com/yourorg/devour/cmd/quality.go:581.19,583.6 1 0
github.com/yourorg/devour/cmd/quality.go:584.5,586.10 3 0
github.com/yourorg/devour/cmd/quality.go:591.2,591.15 1 0
github.com/yourorg/devour/cmd/quality.go:591.15,593.3 1 0
github.com/yourorg/devour/cmd/quality.go:593.8,595.3 1 0
github.com/yourorg/devour/cmd/quality.go:597.2,597.21 1 0
github.com/yourorg/devour/cmd/quality.go:597.21,599.28 2 0
github.com/yourorg/devour/cmd/quality.go:599.28,601.4 1 0
github.com/yourorg/devour/cmd/quality.go:604.2,604.12 1 0
github.com/yourorg/devour/cmd/quality.go:607.64,610.19 2 0
github.com/yourorg/devour/cmd/quality.go:610.19,612.3 1 0
github.com/yourorg/devour/cmd/quality.go:614.2,614.24 1 0
github.com/yourorg/devour/cmd/quality.go:614.24,616.3 1 0
github.com/yourorg/devour/cmd/quality.go:618.2,618.103 1 0
github.com/yourorg/devour/cmd/quality.go:621.48,627.54 4 0
github.com/yourorg/devour/cmd/quality.go:627.54,632.55 2 0
github.com/yourorg/devour/cmd/quality.go:632.55,635.4 2 0
github.com/yourorg/devour/cmd/quality.go:638.2,643.16 5 0
github.com/yourorg/devour/cmd/quality.go:643.16,645.3 1 0
github.com/yourorg/devour/cmd/quality.go:647.2,648.51 2 0
github.com/yourorg/devour/cmd/quality.go:648.51,650.3 1 0
github.com/yourorg/devour/cmd/quality.go:652.2,656.12 4 0
github.com/yourorg/devour/cmd/quality.go:659.67,665.16 4 0
github.com/yourorg/devour/cmd/quality.go:665.16,667.3 1 0
github.com/yourorg/devour/cmd/quality.go:669.2,672.56 2 0
github.com/yourorg/devour/cmd/quality.go:672.56,674.3 1 0
github.com/yourorg/devour/cmd/quality.go:674.8,676.64 2 0
github.com/yourorg/devour/cmd/quality.go:676.64,678.4 1 0
github.com/yourorg/devour/cmd/quality.go:679.3,679.30 1 0
github.com/yourorg/devour/cmd/quality.go:682.2,682.77 1 0
github.com/yourorg/devour/cmd/quality.go:682.77,684.3 1 0
github.com/yourorg/devour/cmd/quality.go:686.2,688.12 2 0
github.com/yourorg/devour/cmd/query.go:31.13,35.2 3 0
github.com/yourorg/devour/cmd/query.go:37.56,39.19 2 0
github.com/yourorg/devour/cmd/query.go:39.19,41.3 1 0
github.com/yourorg/devour/cmd/query.go:43.2,59.12 9 0
github.com/yourorg/devour/cmd/root.go:29.16,30.42 1 0
github.com/yourorg/devour/cmd/root.go:30.42,33.3 2 0
github.com/yourorg/devour/cmd/root.go:36.13,52.2 13 0
github.com/yourorg/devour/cmd/root.go:54.19,55.19 1 0
github.com/yourorg/devour/cmd/root.go:55.19,57.3 1 0
github.com/yourorg/devour/cmd/root.go:57.8,62.3 4 0
github.com/yourorg/devour/cmd/root.go:64.2,67.56 3 0
github.com/yourorg/devour/cmd/root.go:67.56,69.3 1 0
github.com/yourorg/devour/cmd/scrape.go:57.13,63.2 5 0
github.com/yourorg/devour/cmd/scrape.go:65.57,66.25 1 0
github.com/yourorg/devour/cmd/scrape.go:66.25,68.3 1 0
github.com/yourorg/devour/cmd/scrape.go:70.2,70.20 1 0
github.com/yourorg/devour/cmd/scrape.go:70.20,72.3 1 0
github.com/yourorg/devour/cmd/scrape.go:74.2,85.22 4 0
github.com/yourorg/devour/cmd/scrape.go:85.22,87.3 1 0
github.com/yourorg/devour/cmd/scrape.go:89.2,95.14 6 0
github.com/yourorg/devour/cmd/scrape.go:95.14,97.3 1 0
github.com/yourorg/devour/cmd/scrape.go:99.2,109.16 5 0
github.com/yourorg/devour/cmd/scrape.go:109.16,111.3 1 0
github.com/yourorg/devour/cmd/scrape.go:113.2,115.24 2 0
github.com/yourorg/devour/cmd/scrape.go:115.24,117.3 1 0
github.com/yourorg/devour/cmd/scrape.go:119.2,119.56 1 0
github.com/yourorg/devour/cmd/scrape.go:119.56,121.3 1 0
github.com/yourorg/devour/cmd/scrape.go:123.2,123.27 1 0
github.com/yourorg/devour/cmd/scrape.go:123.27,127.33 3 0
github.com/yourorg/devour/cmd/scrape.go:127.33,145.4 4 0
github.com/yourorg/devour/cmd/scrape.go:145.9,148.18 3 0
github.com/yourorg/devour/cmd/scrape.go:148.18,150.5 1 0
github.com/yourorg/devour/cmd/scrape.go:153.3,154.63 2 0
github.com/yourorg/devour/cmd/scrape.go:154.63,156.4 1 0
github.com/yourorg/devour/cmd/scrape.go:158.3,158.53 1 0
github.com/yourorg/devour/cmd/scrape.go:161.2,165.12 4 0
github.com/yourorg/devour/cmd/scrape.go:168.48,170.2 1 0
github.com/yourorg/devour/cmd/scrape.go:172.60,174.16 2 0
github.com/yourorg/devour/cmd/scrape.go:174.16,176.3 1 0
github.com/yourorg/devour/cmd/scrape.go:178.2,181.9 3 0
github.com/yourorg/devour/cmd/scrape.go:182.69,183.34 1 0
github.com/yourorg/devour/cmd/scrape.go:184.56,185.36 1 0
github.com/yourorg/devour/cmd/scrape.go:186.33,187.38 1 0
github.com/yourorg/devour/cmd/scrape.go:188.33,189.36 1 0
github.com/yourorg/devour/cmd/scrape.go:190.32,191.38 1 0
github.com/yourorg/devour/cmd/scrape.go:192.72,193.34 1 0
github.com/yourorg/devour/cmd/scrape.go:194.27,195.37 1 0
github.com/yourorg/devour/cmd/scrape.go:196.27,197.35 1 0
github.com/yourorg/devour/cmd/scrape.go:198.26,199.36 1 0
github.com/yourorg/devour/cmd/scrape.go:200.73,201.38 1 0
github.com/yourorg/devour/cmd/scrape.go:201.38,203.4 1 0
github.com/yourorg/devour/cmd/scrape.go:204.3,204.38 1 0
github.com/yourorg/devour/cmd/scrape.go:205.43,206.42 1 0
github.com/yourorg/devour/cmd/scrape.go:207.34,208.37 1 0
github.com/yourorg/devour/cmd/scrape.go:209.28,210.34 1 0
github.com/yourorg/devour/cmd/scrape.go:211.10,212.31 1 0
github.com/yourorg/devour/cmd/scrape.go:216.43,218.16 2 0
github.com/yourorg/devour/cmd/scrape.go:218.16,220.3 1 0
github.com/yourorg/devour/cmd/scrape.go:222.2,223.20 2 0
github.com/yourorg/devour/cmd/scrape.go:223.20,225.3 1 0
github.com/yourorg/devour/cmd/scrape.go:227.2,227.15 1 0
github.com/yourorg/devour/cmd/scrape.go:230.43,237.20 6 0
github.com/yourorg/devour/cmd/scrape.go:237.20,239.3 1 0
github.com/yourorg/devour/cmd/scrape.go:241.2,241.13 1 0
github.com/yourorg/devour/cmd/serve.go:33.13,37.2 3 0
github.com/yourorg/devour/cmd/serve.go:39.56,40.17 1 0
github.com/yourorg/devour/cmd/serve.go:40.17,48.3 5 0
github.com/yourorg/devour/cmd/serve.go:50.2,60.53 3 0
github.com/yourorg/devour/cmd/status.go:24.57,54.2 16 0
github.com/yourorg/devour/cmd/sync.go:30.13,34.2 3 0
github.com/yourorg/devour/cmd/sync.go:36.55,37.17 1 0
github.com/yourorg/devour/cmd/sync.go:37.17,39.3 1 0
github.com/yourorg/devour/cmd/sync.go:39.8,41.3 1 0
github.com/yourorg/devour/cmd/sync.go:43.2,43.22 1 0
github.com/yourorg/devour/cmd/sync.go:43.22,45.3 1 0
github.com/yourorg/devour/cmd/sync.go:57.2,61.12 4 0
+125
View File
@@ -0,0 +1,125 @@
package cmd
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/spf13/cobra"
)
var demoCmd = &cobra.Command{
Use: "demo",
Short: "Run a demonstration of Devour features",
Long: `Demonstrates Devour's capabilities by scraping sample documentation
and showing the enhanced markdown output.
This command will:
1. Scrape Go's HTTP package documentation
2. Generate enhanced markdown output
3. Display a preview of the results
4. Show statistics about the operation`,
RunE: runDemo,
}
func init() {
rootCmd.AddCommand(demoCmd)
}
func runDemo(cmd *cobra.Command, args []string) error {
fmt.Println("🎬 Devour Feature Demonstration")
fmt.Println("═══════════════════════════════════════════════════════════════")
fmt.Println()
// Create demo directory
demoDir := "devour_demo"
if err := os.MkdirAll(demoDir, 0755); err != nil {
return fmt.Errorf("failed to create demo directory: %w", err)
}
fmt.Println("📍 Step 1: Scraping Go HTTP package documentation...")
fmt.Println(" Command: devour get go http --format markdown --output devour_demo")
fmt.Println()
// Set demo output directory and format
scrapeOutput = demoDir
scrapeFormat = "markdown"
// Use the get command logic to fetch Go HTTP docs
err := runGet(cmd, []string{"go", "http"})
if err != nil {
return fmt.Errorf("demo scrape failed: %w", err)
}
fmt.Println()
fmt.Println("📊 Step 2: Analyzing results...")
// Count files in demo directory
files, err := os.ReadDir(demoDir)
if err != nil {
return fmt.Errorf("failed to read demo directory: %w", err)
}
fmt.Printf(" ✓ Generated %d markdown files\n", len(files))
// Show file sizes
var totalSize int64
for _, file := range files {
if !file.IsDir() {
info, err := file.Info()
if err == nil {
totalSize += info.Size()
}
}
}
fmt.Printf(" ✓ Total size: %d bytes\n", totalSize)
fmt.Println()
fmt.Println("📖 Step 3: Preview of enhanced markdown output...")
// Find and show preview of first markdown file
var firstFile string
for _, file := range files {
if !file.IsDir() && filepath.Ext(file.Name()) == ".md" {
firstFile = filepath.Join(demoDir, file.Name())
break
}
}
if firstFile != "" {
content, err := os.ReadFile(firstFile)
if err == nil {
// Show first 20 lines
lines := strings.Split(string(content), "\n")
fmt.Printf(" From file: %s\n", filepath.Base(firstFile))
fmt.Println(" ─────────────────────────────────────────")
for i, line := range lines {
if i >= 20 {
fmt.Printf(" ... (%d more lines)\n", len(lines)-20)
break
}
fmt.Printf(" %s\n", line)
}
}
}
fmt.Println()
fmt.Println("✨ Demo Complete!")
fmt.Println()
fmt.Println("🚀 What you can do next:")
fmt.Printf(" • View all files: ls -la %s/\n", demoDir)
fmt.Printf(" • Open in editor: code %s/\n", demoDir)
fmt.Println(" • Try other languages: devour get python asyncio")
fmt.Println(" • Use enhanced format: devour get react hooks --format markdown")
fmt.Println(" • See all languages: devour languages")
fmt.Println()
fmt.Println("💡 Pro Tip: The enhanced markdown includes:")
fmt.Println(" • 📋 Document metadata tables")
fmt.Println(" • 📑 Auto-generated table of contents")
fmt.Println(" • 🎨 Visual indicators for important content")
fmt.Println(" • 🔗 Automatic link conversion")
fmt.Println(" • 📚 Structured content sections")
return nil
}
+9
View File
@@ -0,0 +1,9 @@
package main
import (
"github.com/yourorg/devour/cmd"
)
func main() {
cmd.Execute()
}
+136
View File
@@ -0,0 +1,136 @@
package cmd
import (
"fmt"
"strings"
"github.com/spf13/cobra"
)
var getCmd = &cobra.Command{
Use: "get <language> <keyword>",
Short: "Get documentation for a language/framework",
Long: `Quickly fetch documentation for popular languages and frameworks.
This command automatically maps language+keyword combinations to their official documentation sites.
Supported languages:
go, golang - Go documentation (pkg.go.dev)
rust - Rust documentation (docs.rs)
python, py - Python documentation (docs.python.org)
java - Java documentation (docs.oracle.com)
spring - Spring Boot documentation (docs.spring.io)
typescript, ts - TypeScript documentation (typescriptlang.org)
react - React documentation (react.dev)
vue - Vue.js documentation (vuejs.org)
nuxt - Nuxt documentation (nuxt.com)
docker - Docker documentation (docs.docker.com)
cloudflare, cf - Cloudflare documentation (developers.cloudflare.com)
astro - Astro documentation (docs.astro.build)
Examples:
devour get go http # Go HTTP package documentation
devour get python asyncio # Python asyncio module
devour get react hooks # React Hooks documentation
devour get docker compose # Docker Compose docs
devour get rust tokio # Rust Tokio crate`,
Args: cobra.ExactArgs(2),
RunE: runGet,
}
func init() {
// Add flags that can override defaults
getCmd.Flags().StringVarP(&scrapeFormat, "format", "f", "json", "output format (json, markdown)")
getCmd.Flags().StringVarP(&scrapeOutput, "output", "o", "", "output directory (default: devour_data/docs)")
getCmd.Flags().IntVar(&scrapeConcurrency, "concurrency", 10, "parallel scraping workers")
}
func runGet(cmd *cobra.Command, args []string) error {
language := strings.ToLower(args[0])
keyword := strings.ToLower(args[1])
// Map language to base URL and construct full URL
url, err := constructDocURL(language, keyword)
if err != nil {
return err
}
// Set the scrape type based on language
sourceType := mapLanguageToType(language)
// Reuse the existing scrape logic with pre-determined values
scrapeType = string(sourceType)
sourceURL := url
fmt.Printf("Getting docs for: %s %s\n", language, keyword)
fmt.Printf("URL: %s\n", sourceURL)
fmt.Printf("Type: %s\n", sourceType)
fmt.Println()
// Call the existing scrape logic
return runScrape(cmd, []string{sourceURL})
}
func constructDocURL(language, keyword string) (string, error) {
switch language {
case "go", "golang":
return fmt.Sprintf("https://pkg.go.dev/%s", keyword), nil
case "rust":
return fmt.Sprintf("https://docs.rs/%s/latest/%s/", keyword, keyword), nil
case "python", "py":
if keyword == "stdlib" || keyword == "standard" {
return "https://docs.python.org/3/library/", nil
}
return fmt.Sprintf("https://docs.python.org/3/library/%s.html", keyword), nil
case "java":
return fmt.Sprintf("https://docs.oracle.com/javase/8/docs/api/%s.html", keyword), nil
case "spring":
return fmt.Sprintf("https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#%s", keyword), nil
case "typescript", "ts":
return fmt.Sprintf("https://www.typescriptlang.org/docs/handbook/%s.html", keyword), nil
case "react":
return fmt.Sprintf("https://react.dev/reference/react/%s", keyword), nil
case "vue":
return fmt.Sprintf("https://vuejs.org/guide/%s.html", keyword), nil
case "nuxt":
return fmt.Sprintf("https://nuxt.com/docs/guide/%s", keyword), nil
case "docker":
return fmt.Sprintf("https://docs.docker.com/%s", keyword), nil
case "cloudflare", "cf":
return fmt.Sprintf("https://developers.cloudflare.com/%s", keyword), nil
case "astro":
return fmt.Sprintf("https://docs.astro.build/en/guides/%s", keyword), nil
default:
return "", fmt.Errorf("unsupported language: %s. Supported languages: go, rust, python, java, spring, typescript, react, vue, nuxt, docker, cloudflare, astro", language)
}
}
func mapLanguageToType(language string) string {
switch language {
case "go", "golang":
return "godocs"
case "rust":
return "rustdocs"
case "python", "py":
return "pythondocs"
case "java":
return "javadocs"
case "spring":
return "springdocs"
case "typescript", "ts":
return "tsdocs"
case "react":
return "reactdocs"
case "vue":
return "vuedocs"
case "nuxt":
return "nuxtdocs"
case "docker":
return "dockerdocs"
case "cloudflare", "cf":
return "cloudflaredocs"
case "astro":
return "astrodocs"
default:
return "web"
}
}
+144
View File
@@ -0,0 +1,144 @@
package cmd
import (
"fmt"
"os"
"path/filepath"
"github.com/spf13/cobra"
)
var initCmd = &cobra.Command{
Use: "init [path]",
Short: "Initialize Devour for a project",
Long: `Initialize Devour configuration for a project.
Creates a devour.yaml configuration file and the necessary directory
structure for storing documents and indexes.
Examples:
devour init # Initialize in current directory
devour init ./my-project # Initialize in specific directory
devour init --remote # Initialize with remote mode config`,
RunE: runInit,
}
var (
initRemote bool
initForce bool
)
func init() {
initCmd.Flags().BoolVar(&initRemote, "remote", false, "configure for remote mode")
initCmd.Flags().BoolVarP(&initForce, "force", "f", false, "overwrite existing config")
}
func runInit(cmd *cobra.Command, args []string) error {
// Determine target directory
targetDir := "."
if len(args) > 0 {
targetDir = args[0]
}
// Create directory if it doesn't exist
if err := os.MkdirAll(targetDir, 0755); err != nil {
return fmt.Errorf("failed to create directory: %w", err)
}
configPath := filepath.Join(targetDir, "devour.yaml")
// Check if config already exists
if _, err := os.Stat(configPath); err == nil && !initForce {
return fmt.Errorf("config file already exists at %s (use --force to overwrite)", configPath)
}
// Create default config
config := generateDefaultConfig(initRemote)
if err := os.WriteFile(configPath, []byte(config), 0644); err != nil {
return fmt.Errorf("failed to write config: %w", err)
}
// Create data directories
dataDir := filepath.Join(targetDir, "devour_data")
dirs := []string{
filepath.Join(dataDir, "docs"),
filepath.Join(dataDir, "index"),
filepath.Join(dataDir, "metadata"),
filepath.Join(dataDir, "cache"),
}
for _, dir := range dirs {
if err := os.MkdirAll(dir, 0755); err != nil {
return fmt.Errorf("failed to create directory %s: %w", dir, err)
}
}
fmt.Printf("✓ Created config at %s\n", configPath)
fmt.Printf("✓ Created data directories at %s\n", dataDir)
fmt.Println("\nNext steps:")
fmt.Println(" 1. Set OPENAI_API_KEY environment variable")
fmt.Println(" 2. Add sources to devour.yaml")
fmt.Println(" 3. Run 'devour scrape <source>' to index documentation")
return nil
}
func generateDefaultConfig(remote bool) string {
mode := "local"
if remote {
mode = "remote"
}
return fmt.Sprintf(`# Devour Configuration
version: 1
# Storage paths
storage:
docs_dir: ./devour_data/docs
index_dir: ./devour_data/index
metadata_dir: ./devour_data/metadata
# Embedding settings
embeddings:
provider: openai
model: text-embedding-3-small
dimensions: 1536
api_key: ${OPENAI_API_KEY}
batch_size: 100
# Vector database
vector_db:
type: chromem
persist: true
similarity_metric: cosine
# Scraping settings
scraper:
user_agent: "Devour/1.0"
timeout: 30s
retry_count: 3
concurrency: 10
rate_limit: 500ms
max_depth: 3
cache_dir: ./devour_data/cache
# Scheduler
scheduler:
enabled: true
interval: 72h
check_method: hash
# Server settings
server:
mode: %s
port: 8080
host: localhost
# Sources (add your own)
sources: []
# - name: example-docs
# type: url
# url: https://docs.example.com
# include: ["**/*.md", "**/*.html"]
`, mode)
}
+118
View File
@@ -0,0 +1,118 @@
package cmd
import (
"fmt"
"strings"
"github.com/spf13/cobra"
)
var languagesCmd = &cobra.Command{
Use: "languages",
Short: "Show supported languages and their mappings",
Long: `Display all supported languages for the 'devour get' command
along with their base URLs and examples.
This helps you discover what documentation sources are available
and how to reference them quickly.`,
RunE: runLanguages,
}
func init() {
rootCmd.AddCommand(languagesCmd)
}
func runLanguages(cmd *cobra.Command, args []string) error {
fmt.Println("🌐 Devour Supported Languages")
fmt.Println("═══════════════════════════════════════════════════════════════")
fmt.Println()
languages := []struct {
langs []string
url string
examples []string
}{
{
langs: []string{"go", "golang"},
url: "https://pkg.go.dev/{package}",
examples: []string{"devour get go http", "devour get go fmt", "devour get golang json"},
},
{
langs: []string{"rust"},
url: "https://docs.rs/{crate}/latest/{crate}/",
examples: []string{"devour get rust tokio", "devour get rust serde", "devour get rust clap"},
},
{
langs: []string{"python", "py"},
url: "https://docs.python.org/3/library/{module}.html",
examples: []string{"devour get python asyncio", "devour get py requests", "devour get python stdlib"},
},
{
langs: []string{"java"},
url: "https://docs.oracle.com/javase/8/docs/api/{package}.html",
examples: []string{"devour get java string", "devour get java arraylist"},
},
{
langs: []string{"spring"},
url: "https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#{section}",
examples: []string{"devour get spring boot", "devour get spring testing"},
},
{
langs: []string{"typescript", "ts"},
url: "https://www.typescriptlang.org/docs/handbook/{topic}.html",
examples: []string{"devour get typescript interfaces", "devour get ts decorators"},
},
{
langs: []string{"react"},
url: "https://react.dev/reference/react/{feature}",
examples: []string{"devour get react hooks", "devour get react components", "devour get react state"},
},
{
langs: []string{"vue"},
url: "https://vuejs.org/guide/{topic}.html",
examples: []string{"devour get vue components", "devour get vue reactivity"},
},
{
langs: []string{"nuxt"},
url: "https://nuxt.com/docs/guide/{topic}",
examples: []string{"devour get nuxt routing", "devour get nuxt middleware"},
},
{
langs: []string{"docker"},
url: "https://docs.docker.com/{topic}",
examples: []string{"devour get docker compose", "devour get docker build", "devour get docker networking"},
},
{
langs: []string{"cloudflare", "cf"},
url: "https://developers.cloudflare.com/{topic}",
examples: []string{"devour get cloudflare workers", "devour get cf pages", "devour get cloudflare dns"},
},
{
langs: []string{"astro"},
url: "https://docs.astro.build/en/guides/{topic}",
examples: []string{"devour get astro routing", "devour get astro components"},
},
}
for _, lang := range languages {
fmt.Printf("🔷 %s\n", strings.Join(lang.langs, ", "))
fmt.Printf(" URL: %s\n", lang.url)
fmt.Printf(" Examples:\n")
for _, example := range lang.examples {
fmt.Printf(" • %s\n", example)
}
fmt.Println()
}
fmt.Println("💡 Pro Tips:")
fmt.Println(" • Use 'devour get <language> help' for language-specific help")
fmt.Println(" • Add --format markdown for enhanced documentation")
fmt.Println(" • Most languages support common aliases (e.g., py → python)")
fmt.Println()
fmt.Println("🚀 Quick Start:")
fmt.Println(" devour get go http --format markdown")
fmt.Println(" devour get python asyncio")
fmt.Println(" devour get react hooks")
return nil
}
+62
View File
@@ -0,0 +1,62 @@
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var pushCmd = &cobra.Command{
Use: "push <path>",
Short: "Push documents to remote MCP server",
Long: `Push local documents to a remote Devour MCP server.
Useful for:
- Syncing local documentation to a shared server
- Backing up indexed content
- Contributing to a team knowledge base
Examples:
devour push ./docs
devour push ./docs --server http://devour.company.com
devour push ./docs --server http://localhost:8080 --project my-project`,
Args: cobra.ExactArgs(1),
RunE: runPush,
}
var (
pushServer string
pushProject string
)
func init() {
pushCmd.Flags().StringVar(&pushServer, "server", "", "remote Devour server URL")
pushCmd.Flags().StringVarP(&pushProject, "project", "p", "", "project name on remote server")
}
func runPush(cmd *cobra.Command, args []string) error {
path := args[0]
if pushServer == "" {
// Try to get from config
pushServer = "http://localhost:8080"
}
fmt.Printf("📤 Pushing to: %s\n", pushServer)
fmt.Printf(" Path: %s\n", path)
if pushProject != "" {
fmt.Printf(" Project: %s\n", pushProject)
}
// TODO: Implement actual push logic
// 1. Scan path for documents
// 2. Connect to remote server
// 3. Upload documents
// 4. Wait for indexing confirmation
fmt.Println()
fmt.Println("⚠️ Push functionality not yet implemented")
fmt.Println(" Remote server support coming soon")
return nil
}
+736
View File
@@ -0,0 +1,736 @@
package cmd
import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"time"
"github.com/spf13/cobra"
"github.com/yourorg/devour/internal/quality"
"github.com/yourorg/devour/internal/quality/detectors"
"github.com/yourorg/devour/internal/quality/plugins"
"github.com/yourorg/devour/internal/quality/plugins/go/fixers"
"github.com/yourorg/devour/internal/quality/review"
"github.com/yourorg/devour/internal/quality/scorecard"
_ "github.com/yourorg/devour/internal/quality/plugins/go"
)
// qualityCmd represents the quality command
var qualityCmd = &cobra.Command{
Use: "quality",
Short: "Code quality analysis and technical debt tracking",
Long: `Analyze code quality issues like complexity, duplication, naming inconsistencies,
and more. Supports multiple languages including Go, Python, TypeScript, Java, and Rust.
Examples:
devour quality scan ./src # Scan current directory
devour quality scan --lang go ./src # Scan with explicit language
devour quality status # Show current status
devour quality next # Show next priority issue
devour quality resolve fixed 123 --note "Refactored" # Mark issue as fixed`,
}
// scanCmd represents the scan subcommand
var scanCmd = &cobra.Command{
Use: "scan [path]",
Short: "Run code quality analysis",
Long: `Scan the given path for code quality issues. Automatically detects language
unless specified with --lang flag.
The scan will detect:
- Complexity issues (nested loops, excessive function calls)
- Code duplication and near-duplicates
- Naming inconsistencies across directories
- Large files and god classes
- Orphaned code and unused exports`,
RunE: runQualityScan,
}
// qualityStatusCmd represents the quality status subcommand
var qualityStatusCmd = &cobra.Command{
Use: "status",
Short: "Show code quality status and scorecard",
Long: `Display the current code quality status including:
- Overall health score and grade
- Findings broken down by type and severity
- Progress metrics and next steps`,
RunE: runQualityStatus,
}
// nextCmd represents the next subcommand
var nextCmd = &cobra.Command{
Use: "next",
Short: "Show the next highest priority issue to fix",
Long: `Display the next highest priority finding based on severity and score.
This helps you focus on the most impactful improvements first.`,
RunE: runQualityNext,
}
// resolveCmd represents the resolve subcommand
var resolveCmd = &cobra.Command{
Use: "resolve <status> <id>",
Short: "Mark a finding as resolved",
Long: `Mark a finding with a specific status:
- fixed: Issue has been resolved
- wontfix: Issue won't be fixed (valid reason required)
- false_positive: Finding is incorrect
- ignore: Temporarily ignore the finding
Examples:
devour quality resolve fixed abc123 --note "Refactored complex function"
devour quality resolve wontfix def456 --note "Legacy code, planned replacement"`,
RunE: runQualityResolve,
}
// Quality flags
var (
qualityPath string
qualityLanguage string
qualityExclude []string
qualityThreshold int
qualityMinLOC int
qualityTargetScore int
qualityFormat string
qualityResetSubjective bool
qualityNoBadge bool
qualityBadgePath string
)
var explain bool
var tier int
var resolveNote string
var attest string
var qualityUseAST bool
var statusNarrative bool
var fixDryRun bool
var fixAll bool
var reviewPrepare bool
var reviewImport string
var fixCmd = &cobra.Command{
Use: "fix [id]",
Short: "Auto-fix code quality issues",
Long: `Automatically fix T1 (auto-fixable) issues.
Examples:
devour quality fix unused_import::file.go::fmt # Fix specific issue
devour quality fix --all --dry-run # Preview all fixes
devour quality fix --all # Fix all auto-fixable issues`,
RunE: runQualityFix,
}
var reviewCmd = &cobra.Command{
Use: "review",
Short: "Generate or import AI review packets",
Long: `Generate a review packet for AI analysis, or import responses.
Examples:
devour quality review --prepare # Generate review packet
devour quality review --import responses.json # Import AI responses`,
RunE: runQualityReview,
}
func init() {
rootCmd.AddCommand(qualityCmd)
qualityCmd.AddCommand(scanCmd, qualityStatusCmd, nextCmd, resolveCmd, fixCmd, reviewCmd)
// Scan flags
scanCmd.Flags().StringVar(&qualityPath, "path", ".", "Path to scan")
scanCmd.Flags().StringVar(&qualityLanguage, "lang", "", "Language (auto-detected if not specified)")
scanCmd.Flags().StringSliceVar(&qualityExclude, "exclude", []string{}, "Exclude patterns")
scanCmd.Flags().IntVar(&qualityThreshold, "threshold", 15, "Minimum score to flag an issue")
scanCmd.Flags().IntVar(&qualityMinLOC, "min-loc", 50, "Minimum lines of code to analyze")
scanCmd.Flags().IntVar(&qualityTargetScore, "target-score", 95, "Target health score")
scanCmd.Flags().StringVar(&qualityFormat, "format", "text", "Output format (text, json)")
scanCmd.Flags().BoolVar(&qualityResetSubjective, "reset-subjective", false, "Reset subjective baseline")
scanCmd.Flags().BoolVar(&qualityNoBadge, "no-badge", false, "Skip badge generation")
scanCmd.Flags().StringVar(&qualityBadgePath, "badge-path", "scorecard.png", "Badge output path")
// Status flags
qualityStatusCmd.Flags().StringVar(&qualityFormat, "format", "text", "Output format (text, json)")
qualityStatusCmd.Flags().BoolVar(&statusNarrative, "narrative", false, "Include narrative analysis")
// Next flags
nextCmd.Flags().BoolVar(&explain, "explain", false, "Show detailed explanation")
nextCmd.Flags().IntVar(&tier, "tier", 0, "Filter by severity tier (1-4)")
// Resolve flags
resolveCmd.Flags().StringVar(&resolveNote, "note", "", "Note explaining the resolution (required)")
resolveCmd.Flags().StringVar(&attest, "attest", "", "Attestation of improvement")
// Fix flags
fixCmd.Flags().BoolVar(&fixDryRun, "dry-run", false, "Show what would be fixed without making changes")
fixCmd.Flags().BoolVar(&fixAll, "all", false, "Fix all auto-fixable issues")
// Review flags
reviewCmd.Flags().BoolVar(&reviewPrepare, "prepare", false, "Generate review packet for AI analysis")
reviewCmd.Flags().StringVar(&reviewImport, "import", "", "Import review responses from file")
}
func runQualityScan(cmd *cobra.Command, args []string) error {
path := qualityPath
if len(args) > 0 {
path = args[0]
}
if _, err := os.Stat(path); os.IsNotExist(err) {
return fmt.Errorf("path does not exist: %s", path)
}
config := &quality.Config{
Path: path,
Language: qualityLanguage,
Exclude: qualityExclude,
Threshold: qualityThreshold,
MinLOC: qualityMinLOC,
TargetScore: qualityTargetScore,
ResetSubjective: qualityResetSubjective,
NoBadge: qualityNoBadge,
BadgePath: qualityBadgePath,
}
scanner := quality.NewScanner(config)
finder := quality.NewDefaultFileFinder()
scanner.SetFileFinder(finder)
lang := qualityLanguage
if lang == "" {
lang = quality.DetectLanguage(path)
fmt.Printf("Auto-detected language: %s\n", lang)
}
plugin, ok := plugins.Get(lang)
if ok {
fmt.Printf("Using %s plugin with AST analysis\n", lang)
for _, detector := range plugin.CreateDetectors(finder) {
scanner.RegisterDetector(detector)
}
} else {
scanner.RegisterDetector(detectors.NewComplexityDetector(finder))
scanner.RegisterDetector(detectors.NewDuplicationDetector(finder))
scanner.RegisterDetector(detectors.NewNamingDetector(finder))
}
ctx := context.Background()
result, err := scanner.Scan(ctx)
if err != nil {
return fmt.Errorf("scan failed: %w", err)
}
return outputScanResult(result, qualityFormat)
}
func runQualityStatus(cmd *cobra.Command, args []string) error {
// Load previous scan results
dataDir := filepath.Join(".", "devour_data", "quality")
statusFile := filepath.Join(dataDir, "status.json")
var findings []quality.Finding
var lastScan time.Time
if data, err := os.ReadFile(statusFile); err == nil {
var status struct {
Findings []quality.Finding `json:"findings"`
Timestamp time.Time `json:"timestamp"`
}
if err := json.Unmarshal(data, &status); err == nil {
findings = status.Findings
lastScan = status.Timestamp
}
}
if len(findings) == 0 {
fmt.Println("No previous scan results found. Run 'devour quality scan' first.")
return nil
}
// Generate scorecard
scorer := quality.NewScorer(qualityTargetScore)
scorecard := scorer.GenerateScorecard(findings, lastScan)
// Output based on format
switch qualityFormat {
case "json":
return json.NewEncoder(os.Stdout).Encode(scorecard)
default:
fmt.Println(scorer.FormatScorecard(scorecard))
return nil
}
}
func runQualityNext(cmd *cobra.Command, args []string) error {
// Load previous scan results
dataDir := filepath.Join(".", "devour_data", "quality")
statusFile := filepath.Join(dataDir, "status.json")
var findings []quality.Finding
if data, err := os.ReadFile(statusFile); err == nil {
var status struct {
Findings []quality.Finding `json:"findings"`
}
if err := json.Unmarshal(data, &status); err == nil {
findings = status.Findings
}
}
if len(findings) == 0 {
fmt.Println("No findings found. Run 'devour quality scan' first.")
return nil
}
// Get next priority finding
scorer := quality.NewScorer(qualityTargetScore)
next := scorer.GetNextPriority(findings)
if next == nil {
fmt.Println("🎉 No open issues to fix!")
return nil
}
// Filter by tier if specified
if tier > 0 {
if int(next.Severity) != tier {
// Find next in specified tier
for _, finding := range findings {
if finding.Status == quality.StatusOpen && int(finding.Severity) == tier {
next = &finding
break
}
}
if next == nil || int(next.Severity) != tier {
fmt.Printf("No open issues found in tier %d.\n", tier)
return nil
}
}
}
// Display finding
fmt.Printf("Next Priority Issue (T%d)\n", int(next.Severity))
fmt.Println("=======================================")
fmt.Printf("File: %s:%d\n", next.File, next.Line)
fmt.Printf("Title: %s\n", next.Title)
fmt.Printf("Score: %d\n", next.Score)
fmt.Printf("ID: %s\n", next.ID)
fmt.Printf("\nDescription:\n%s\n", next.Description)
if explain {
fmt.Printf("\nExplanation:\n")
fmt.Printf("This is a T%d severity issue. ", int(next.Severity))
switch next.Severity {
case quality.SeverityT1:
fmt.Println("T1 issues are typically auto-fixable like unused imports or debug logs.")
case quality.SeverityT2:
fmt.Println("T2 issues require quick manual fixes like unused variables or dead exports.")
case quality.SeverityT3:
fmt.Println("T3 issues need judgment calls like near-duplicates or single-use abstractions.")
case quality.SeverityT4:
fmt.Println("T4 issues require major refactoring like god components or mixed concerns.")
}
fmt.Printf("\nTo fix: devour quality resolve fixed %s --note \"Describe what you fixed\"\n", next.ID)
}
return nil
}
func runQualityResolve(cmd *cobra.Command, args []string) error {
if len(args) < 2 {
return fmt.Errorf("usage: devour quality resolve <status> <id>")
}
status := quality.Status(args[0])
id := args[1]
// Validate status
validStatuses := map[quality.Status]bool{
quality.StatusFixed: true,
quality.StatusWontfix: true,
quality.StatusFalsePositive: true,
quality.StatusIgnored: true,
}
if !validStatuses[status] {
return fmt.Errorf("invalid status: %s", status)
}
if resolveNote == "" {
return fmt.Errorf("--note is required when resolving findings")
}
// Load current findings
dataDir := filepath.Join(".", "devour_data", "quality")
statusFile := filepath.Join(dataDir, "status.json")
var findings []quality.Finding
if data, err := os.ReadFile(statusFile); err == nil {
var statusData struct {
Findings []quality.Finding `json:"findings"`
}
if err := json.Unmarshal(data, &statusData); err == nil {
findings = statusData.Findings
}
}
// Find and update the finding
found := false
for i, finding := range findings {
if finding.ID == id {
findings[i].Status = status
findings[i].UpdatedAt = time.Now()
if finding.Metadata == nil {
findings[i].Metadata = make(map[string]string)
}
findings[i].Metadata["resolution_note"] = resolveNote
if attest != "" {
findings[i].Metadata["attestation"] = attest
}
found = true
break
}
}
if !found {
return fmt.Errorf("finding not found: %s", id)
}
// Save updated findings
if err := os.MkdirAll(dataDir, 0755); err != nil {
return fmt.Errorf("failed to create data directory: %w", err)
}
statusData := struct {
Findings []quality.Finding `json:"findings"`
Timestamp time.Time `json:"timestamp"`
}{
Findings: findings,
Timestamp: time.Now(),
}
data, err := json.MarshalIndent(statusData, "", " ")
if err != nil {
return fmt.Errorf("failed to marshal status: %w", err)
}
if err := os.WriteFile(statusFile, data, 0644); err != nil {
return fmt.Errorf("failed to save status: %w", err)
}
fmt.Printf("Resolved: %s as %s\n", id, status)
if resolveNote != "" {
fmt.Printf("Note: %s\n", resolveNote)
}
return nil
}
func outputScanResult(result *quality.ScanResult, format string) error {
// Save results to data directory
dataDir := filepath.Join(".", "devour_data", "quality")
if err := os.MkdirAll(dataDir, 0755); err != nil {
return fmt.Errorf("failed to create data directory: %w", err)
}
statusFile := filepath.Join(dataDir, "status.json")
statusData := struct {
Findings []quality.Finding `json:"findings"`
Timestamp time.Time `json:"timestamp"`
}{
Findings: result.Findings,
Timestamp: result.Timestamp,
}
data, err := json.MarshalIndent(statusData, "", " ")
if err != nil {
return fmt.Errorf("failed to marshal results: %w", err)
}
if err := os.WriteFile(statusFile, data, 0644); err != nil {
return fmt.Errorf("failed to save results: %w", err)
}
// Generate scorecard badge if not disabled
if !qualityNoBadge && qualityBadgePath != "" {
if err := generateScorecardBadge(result, qualityBadgePath); err != nil {
fmt.Fprintf(os.Stderr, "Warning: failed to generate scorecard badge: %v\n", err)
} else {
fmt.Printf("Scorecard badge generated: %s\n", qualityBadgePath)
}
}
// Output based on format
switch format {
case "json":
return json.NewEncoder(os.Stdout).Encode(result)
default:
return formatScanResultText(result)
}
}
func generateScorecardBadge(result *quality.ScanResult, outputPath string) error {
// Calculate grade from score
scorer := quality.NewScorer(qualityTargetScore)
grade := scorer.GetHealthGrade(result.StrictScore)
// Group findings by type and tier
findByType := make(map[string]int)
findByTier := make(map[string]int)
for _, f := range result.Findings {
findByType[f.Type]++
tierName := fmt.Sprintf("T%d", int(f.Severity))
findByTier[tierName]++
}
// Get project name from current directory
projectName := "devour"
if dir, err := os.Getwd(); err == nil {
projectName = filepath.Base(dir)
}
// Prepare scorecard data
scoreData := &scorecard.ScorecardData{
ProjectName: projectName,
Version: "", // Could be extracted from version info
OverallScore: float64(result.Score),
StrictScore: float64(result.StrictScore),
Grade: grade,
FindingsTotal: len(result.Findings),
FindingsOpen: len(result.Findings), // All findings are open initially
LastScan: result.Timestamp,
FindByType: findByType,
FindByTier: findByTier,
}
return scorecard.Generate(scoreData, outputPath)
}
func formatScanResultText(result *quality.ScanResult) error {
fmt.Println("Code Quality Scan Results")
fmt.Println("=======================================")
fmt.Printf("Files checked: %d\n", result.FilesChecked)
fmt.Printf("Duration: %s\n", result.Duration)
fmt.Printf("Score: %d (strict: %d)\n", result.Score, result.StrictScore)
fmt.Printf("Findings: %d\n\n", len(result.Findings))
if len(result.Findings) == 0 {
fmt.Println("No code quality issues found.")
return nil
}
bySeverity := make(map[quality.Severity][]quality.Finding)
for _, finding := range result.Findings {
bySeverity[finding.Severity] = append(bySeverity[finding.Severity], finding)
}
tiers := []quality.Severity{quality.SeverityT4, quality.SeverityT3, quality.SeverityT2, quality.SeverityT1}
tierNames := map[quality.Severity]string{
quality.SeverityT1: "T1 (Auto-fixable)",
quality.SeverityT2: "T2 (Quick manual)",
quality.SeverityT3: "T3 (Needs judgment)",
quality.SeverityT4: "T4 (Major refactor)",
}
for _, severity := range tiers {
findings := bySeverity[severity]
if len(findings) == 0 {
continue
}
fmt.Printf("[%s] %d issues\n", tierNames[severity], len(findings))
for _, finding := range findings {
fmt.Printf(" - %s:%d - %s (score: %d)\n",
filepath.Base(finding.File), finding.Line, finding.Title, finding.Score)
}
fmt.Println()
}
fmt.Println("Run 'devour quality next' to see the highest priority issue to fix.")
fmt.Println("Run 'devour quality status' for detailed scorecard.")
return nil
}
func runQualityFix(cmd *cobra.Command, args []string) error {
dataDir := filepath.Join(".", "devour_data", "quality")
statusFile := filepath.Join(dataDir, "status.json")
var findings []quality.Finding
if data, err := os.ReadFile(statusFile); err == nil {
var status struct {
Findings []quality.Finding `json:"findings"`
}
if err := json.Unmarshal(data, &status); err == nil {
findings = status.Findings
}
}
if len(findings) == 0 {
fmt.Println("No findings found. Run 'devour quality scan' first.")
return nil
}
availableFixers := []plugins.Fixer{
fixers.NewUnusedImportFixer(),
fixers.NewFormattingFixer(),
}
ctx := context.Background()
fixed := 0
var errors []string
if fixAll {
for _, finding := range findings {
if finding.Status != quality.StatusOpen || finding.Severity != quality.SeverityT1 {
continue
}
for _, fixer := range availableFixers {
if fixer.CanFix(finding) {
result, err := fixer.Fix(ctx, finding, fixDryRun)
if err != nil {
errors = append(errors, fmt.Sprintf("%s: %v", finding.ID, err))
continue
}
if result.Success {
fixed++
fmt.Printf("[OK] %s\n", result.Message)
}
break
}
}
}
} else {
if len(args) < 1 {
return fmt.Errorf("specify a finding ID or use --all")
}
targetID := args[0]
var target *quality.Finding
for i := range findings {
if findings[i].ID == targetID {
target = &findings[i]
break
}
}
if target == nil {
return fmt.Errorf("finding not found: %s", targetID)
}
for _, fixer := range availableFixers {
if fixer.CanFix(*target) {
result, err := fixer.Fix(ctx, *target, fixDryRun)
if err != nil {
return fmt.Errorf("fix failed: %w", err)
}
fmt.Printf("[OK] %s\n", result.Message)
fixed = 1
break
}
}
}
if fixDryRun {
fmt.Printf("\nDry run complete. %d issues would be fixed.\n", fixed)
} else {
fmt.Printf("\nFixed %d issues.\n", fixed)
}
if len(errors) > 0 {
fmt.Printf("\nErrors:\n")
for _, e := range errors {
fmt.Printf(" • %s\n", e)
}
}
return nil
}
func runQualityReview(cmd *cobra.Command, args []string) error {
dataDir := filepath.Join(".", "devour_data")
if reviewPrepare {
return prepareReviewPacket(dataDir)
}
if reviewImport != "" {
return importReviewResponses(dataDir, reviewImport)
}
return fmt.Errorf("use --prepare to generate a review packet or --import <file> to import responses")
}
func prepareReviewPacket(dataDir string) error {
statusFile := filepath.Join(dataDir, "quality", "status.json")
var findings []quality.Finding
var lastScan time.Time
if data, err := os.ReadFile(statusFile); err == nil {
var status struct {
Findings []quality.Finding `json:"findings"`
Timestamp time.Time `json:"timestamp"`
}
if err := json.Unmarshal(data, &status); err == nil {
findings = status.Findings
lastScan = status.Timestamp
}
}
scorer := quality.NewScorer(qualityTargetScore)
scorecard := scorer.GenerateScorecard(findings, lastScan)
gen := review.NewPacketGenerator(dataDir)
packet, err := gen.Generate(findings, scorecard, "go")
if err != nil {
return fmt.Errorf("failed to generate review packet: %w", err)
}
filename := fmt.Sprintf("review-%s.json", time.Now().Format("20060102-150405"))
if err := gen.Save(packet, filename); err != nil {
return fmt.Errorf("failed to save review packet: %w", err)
}
fmt.Printf("Review packet generated: %s/review/%s\n", dataDir, filename)
fmt.Printf("Findings to review: %d\n", len(packet.Findings))
fmt.Printf("Questions: %d\n", len(packet.Questions))
return nil
}
func importReviewResponses(dataDir string, filename string) error {
gen := review.NewPacketGenerator(dataDir)
responses := make(map[string]string)
data, err := os.ReadFile(filename)
if err != nil {
return fmt.Errorf("failed to read responses file: %w", err)
}
var respData struct {
Responses map[string]string `json:"responses"`
}
if err := json.Unmarshal(data, &respData); err == nil {
responses = respData.Responses
} else {
var simpleResponses map[string]string
if err := json.Unmarshal(data, &simpleResponses); err != nil {
return fmt.Errorf("failed to parse responses: %w", err)
}
responses = simpleResponses
}
if err := gen.ImportReview(filepath.Base(filename), responses); err != nil {
return fmt.Errorf("failed to import responses: %w", err)
}
fmt.Printf("Imported %d review responses\n", len(responses))
return nil
}
+60
View File
@@ -0,0 +1,60 @@
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var queryCmd = &cobra.Command{
Use: "query <text>",
Short: "Search indexed documentation",
Long: `Search indexed documentation using semantic similarity.
Returns the most relevant document chunks based on vector similarity
to the query text.
Examples:
devour query "how to authenticate"
devour query "API rate limiting" --limit 10
devour query "deployment" --format json`,
Args: cobra.MinimumNArgs(1),
RunE: runQuery,
}
var (
queryLimit int
queryFormat string
queryThreshold float64
)
func init() {
queryCmd.Flags().IntVarP(&queryLimit, "limit", "l", 5, "maximum number of results")
queryCmd.Flags().StringVarP(&queryFormat, "format", "f", "text", "output format (text, json, markdown)")
queryCmd.Flags().Float64Var(&queryThreshold, "threshold", 0.7, "similarity threshold (0-1)")
}
func runQuery(cmd *cobra.Command, args []string) error {
query := args[0]
if len(args) > 1 {
query = fmt.Sprintf("%s", args)
}
fmt.Printf("Searching: %q\n", query)
fmt.Printf(" Limit: %d\n", queryLimit)
fmt.Printf(" Threshold: %.2f\n", queryThreshold)
fmt.Println()
// TODO: Implement actual query logic
// 1. Generate embedding for query
// 2. Search vector database
// 3. Format and return results
// Placeholder results
fmt.Println("Results:")
fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
fmt.Println("⚠️ Query functionality not yet implemented")
fmt.Println(" Index some documents first with 'devour scrape'")
return nil
}
+119
View File
@@ -0,0 +1,119 @@
package main
import (
"context"
"fmt"
"time"
"github.com/yourorg/devour/internal/scraper"
)
func main() {
fmt.Println("=== Devour Real HTTP Scraping Test ===")
fmt.Println()
config := &scraper.Config{
UserAgent: "Devour/1.0 (Documentation Scraper)",
Timeout: 30 * time.Second,
RetryCount: 3,
RetryDelay: 1 * time.Second,
Concurrency: 10,
}
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
sources := []struct {
name string
st scraper.SourceType
url string
}{
{"Go stdlib net/http", scraper.SourceTypeGoDocs, "https://pkg.go.dev/net/http"},
{"Spring AI MCP", scraper.SourceTypeSpringDocs, "https://docs.spring.io/spring-ai/reference/api/mcp/mcp-overview.html"},
{"React Hooks", scraper.SourceTypeReactDocs, "https://react.dev/reference/react"},
{"Vue Composition API", scraper.SourceTypeVueDocs, "https://vuejs.org/api/"},
{"Cloudflare Docs", scraper.SourceTypeCloudflareDocs, "https://developers.cloudflare.com/"},
}
for _, src := range sources {
fmt.Printf("=== Testing: %s ===\n", src.name)
s := scraper.NewScraper(src.st, config)
if s == nil {
fmt.Printf(" ✗ Scraper not available for type: %s\n\n", src.st)
continue
}
source := &scraper.Source{
Name: src.name,
Type: src.st,
URL: src.url,
}
fmt.Printf(" Fetching: %s\n", src.url)
docs, err := s.Scrape(ctx, source)
if err != nil {
fmt.Printf(" ✗ Error: %v\n\n", err)
continue
}
fmt.Printf(" ✓ Scraped %d documents\n", len(docs))
if len(docs) > 0 {
first := docs[0]
fmt.Printf(" First document:\n")
fmt.Printf(" Title: %s\n", first.Title)
fmt.Printf(" Type: %s\n", first.Type)
if len(first.Content) > 100 {
fmt.Printf(" Content preview: %s...\n", first.Content[:100])
} else {
fmt.Printf(" Content: %s\n", first.Content)
}
}
changed, hash, err := s.DetectChanges(ctx, source, "")
if err != nil {
fmt.Printf(" ✗ Change detection error: %v\n", err)
} else {
fmt.Printf(" ✓ Change detection: changed=%v, hash=%s\n", changed, hash[:16]+"...")
}
fmt.Println()
}
fmt.Println("=== All Source Types ===")
fmt.Println()
fmt.Println("Available scrapers:")
allTypes := []scraper.SourceType{
scraper.SourceTypeWeb,
scraper.SourceTypeGitHub,
scraper.SourceTypeOpenAPI,
scraper.SourceTypeLocal,
scraper.SourceTypeGoDocs,
scraper.SourceTypeRustDocs,
scraper.SourceTypePythonDocs,
scraper.SourceTypeJavaDocs,
scraper.SourceTypeSpringDocs,
scraper.SourceTypeTSDocs,
scraper.SourceTypeReactDocs,
scraper.SourceTypeVueDocs,
scraper.SourceTypeNuxtDocs,
scraper.SourceTypeMCPDocs,
scraper.SourceTypeDockerDocs,
scraper.SourceTypeCloudflareDocs,
scraper.SourceTypeAstroDocs,
}
for _, st := range allTypes {
s := scraper.NewScraper(st, config)
if s != nil {
fmt.Printf(" ✓ %s\n", st)
} else {
fmt.Printf(" ✗ %s (not implemented)\n", st)
}
}
fmt.Println()
fmt.Println("=== Test Complete ===")
}
+92
View File
@@ -0,0 +1,92 @@
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/yourorg/devour/internal/ui"
)
var (
cfgFile string
verbose bool
showLogo bool
)
var rootCmd = &cobra.Command{
Use: "devour",
Short: "Context ingestion and management for AI",
Long: ` ___
.-' '-.
/ O O \
| _ |
\ '-' /
'-.___.-'
Documentation Devourer
Devour scrapes, indexes, and serves documentation from multiple sources
(GitHub repos, OpenAPI specs, web docs, local files) to feed structured
context to AI models for generating accurate, fully working code.
Runs in two modes:
- Local mode: OpenCode skill running entirely on your machine
- Remote mode: MCP server for multi-user/team access`,
Version: "1.0.0",
}
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func init() {
cobra.OnInitialize(initConfig)
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file (default is ./devour.yaml)")
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "enable verbose logging")
rootCmd.PersistentFlags().BoolVarP(&showLogo, "logo", "l", false, "show the Devour character logo")
rootCmd.AddCommand(initCmd)
rootCmd.AddCommand(scrapeCmd)
rootCmd.AddCommand(getCmd)
rootCmd.AddCommand(languagesCmd)
rootCmd.AddCommand(demoCmd)
rootCmd.AddCommand(serveCmd)
rootCmd.AddCommand(queryCmd)
rootCmd.AddCommand(statusCmd)
rootCmd.AddCommand(syncCmd)
rootCmd.AddCommand(pushCmd)
rootCmd.AddCommand(logoCmd)
}
// logoCmd displays the Devour character
var logoCmd = &cobra.Command{
Use: "logo",
Short: "Display the Devour character logo",
Long: `Display the ASCII art Devour character mascot.`,
Run: func(cmd *cobra.Command, args []string) {
ui.PrintCharacter()
},
}
func initConfig() {
if cfgFile != "" {
viper.SetConfigFile(cfgFile)
} else {
viper.AddConfigPath(".")
viper.AddConfigPath("$HOME/.devour")
viper.SetConfigType("yaml")
viper.SetConfigName("devour")
}
viper.AutomaticEnv()
viper.SetEnvPrefix("DEVOUR")
if err := viper.ReadInConfig(); err == nil && verbose {
fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
}
}
+242
View File
@@ -0,0 +1,242 @@
package cmd
import (
"context"
"encoding/json"
"fmt"
"net/url"
"os"
"path/filepath"
"strings"
"time"
"github.com/spf13/cobra"
"github.com/yourorg/devour/internal/markdown"
"github.com/yourorg/devour/internal/scraper"
)
var scrapeCmd = &cobra.Command{
Use: "scrape <source>",
Short: "Scrape documentation from a source",
Long: `Scrape and index documentation from various sources.
Supported source types:
- godocs: Go packages (pkg.go.dev)
- rustdocs: Rust crates (docs.rs)
- pythondocs: Python modules (docs.python.org)
- javadocs: Java packages (docs.oracle.com)
- springdocs: Spring Boot (docs.spring.io)
- tsdocs: TypeScript (typescriptlang.org)
- reactdocs: React (react.dev)
- vuedocs: Vue.js (vuejs.org)
- nuxtdocs: Nuxt (nuxt.com)
- mcpdocs: MCP servers (hub.docker.com/mcp)
- dockerdocs: Docker (docs.docker.com)
- cloudflaredocs: Cloudflare (developers.cloudflare.com)
- astrodocs: Astro (docs.astro.build)
- url: Generic web pages
- github: GitHub repositories
Examples:
devour scrape https://pkg.go.dev/net/http --type godocs
devour scrape https://react.dev/reference/react --type reactdocs
devour scrape https://developers.cloudflare.com/ --type cloudflaredocs
devour scrape --sources sources.yaml`,
Args: cobra.MaximumNArgs(1),
RunE: runScrape,
}
var (
scrapeFormat string
scrapeSources string
scrapeOutput string
scrapeConcurrency int
scrapeType string
)
func init() {
scrapeCmd.Flags().StringVarP(&scrapeFormat, "format", "f", "json", "output format (json, markdown)")
scrapeCmd.Flags().StringVarP(&scrapeSources, "sources", "s", "", "YAML file with source definitions")
scrapeCmd.Flags().StringVarP(&scrapeOutput, "output", "o", "", "output directory (default: devour_data/docs)")
scrapeCmd.Flags().IntVar(&scrapeConcurrency, "concurrency", 10, "parallel scraping workers")
scrapeCmd.Flags().StringVarP(&scrapeType, "type", "t", "", "source type (auto-detected if not specified)")
}
func runScrape(cmd *cobra.Command, args []string) error {
if scrapeSources != "" {
return scrapeFromConfig(scrapeSources)
}
if len(args) == 0 {
return fmt.Errorf("source argument required when not using --sources flag")
}
sourceURL := args[0]
config := &scraper.Config{
UserAgent: "Devour/1.0 (Documentation Scraper)",
Timeout: 30 * time.Second,
RetryCount: 3,
RetryDelay: 1 * time.Second,
Concurrency: scrapeConcurrency,
}
sourceType := scraper.SourceType(scrapeType)
if sourceType == "" {
sourceType = detectSourceType(sourceURL)
}
fmt.Printf("Scraping: %s\n", sourceURL)
fmt.Printf(" Type: %s\n", sourceType)
fmt.Printf(" Concurrency: %d\n", scrapeConcurrency)
fmt.Println()
s := scraper.NewScraper(sourceType, config)
if s == nil {
return fmt.Errorf("unsupported source type: %s", sourceType)
}
source := &scraper.Source{
Name: extractName(sourceURL),
Type: sourceType,
URL: sourceURL,
}
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
docs, err := s.Scrape(ctx, source)
if err != nil {
return fmt.Errorf("scraping failed: %w", err)
}
fmt.Printf("✓ Scraped %d documents\n\n", len(docs))
if scrapeOutput == "" {
scrapeOutput = "devour_data/docs"
}
if err := os.MkdirAll(scrapeOutput, 0755); err != nil {
return fmt.Errorf("failed to create output directory: %w", err)
}
for i, doc := range docs {
var filename string
var content []byte
if scrapeFormat == "markdown" {
filename = fmt.Sprintf("%s_%d.md", sanitizeFilename(doc.Title), i)
// Create enhanced markdown document
markdownDoc := &markdown.Document{
ID: doc.ID,
Source: doc.Source,
Type: string(doc.Type),
Title: doc.Title,
Content: doc.Content,
URL: doc.URL,
Metadata: doc.Metadata,
Hash: doc.Hash,
Timestamp: doc.Timestamp,
}
formatter := markdown.NewFormatter()
content = []byte(formatter.FormatWithTOC(markdownDoc))
} else {
filename = fmt.Sprintf("%s_%d.json", sanitizeFilename(doc.Title), i)
content, err = json.MarshalIndent(doc, "", " ")
if err != nil {
return fmt.Errorf("failed to marshal document: %w", err)
}
}
filePath := filepath.Join(scrapeOutput, filename)
if err := os.WriteFile(filePath, content, 0644); err != nil {
return fmt.Errorf("failed to write document: %w", err)
}
fmt.Printf(" 📄 %s (%s)\n", filename, doc.Type)
}
fmt.Printf("\n✓ Scraping complete!\n")
fmt.Printf(" Output: %s\n", scrapeOutput)
fmt.Println(" Run 'devour status' to see indexed documents")
return nil
}
func scrapeFromConfig(configPath string) error {
return fmt.Errorf("scraping from config file not yet implemented")
}
func detectSourceType(sourceURL string) scraper.SourceType {
u, err := url.Parse(sourceURL)
if err != nil {
return scraper.SourceTypeWeb
}
host := u.Host
path := u.Path
switch {
case host == "pkg.go.dev" || strings.HasSuffix(host, "pkg.go.dev"):
return scraper.SourceTypeGoDocs
case host == "docs.rs" || host == "doc.rust-lang.org":
return scraper.SourceTypeRustDocs
case host == "docs.python.org":
return scraper.SourceTypePythonDocs
case host == "docs.oracle.com":
return scraper.SourceTypeJavaDocs
case host == "docs.spring.io":
return scraper.SourceTypeSpringDocs
case host == "www.typescriptlang.org" || host == "typescriptlang.org":
return scraper.SourceTypeTSDocs
case host == "react.dev":
return scraper.SourceTypeReactDocs
case host == "vuejs.org":
return scraper.SourceTypeVueDocs
case host == "nuxt.com":
return scraper.SourceTypeNuxtDocs
case strings.Contains(host, "docker.com") || host == "docs.docker.com":
if strings.Contains(path, "/mcp/") {
return scraper.SourceTypeMCPDocs
}
return scraper.SourceTypeDockerDocs
case host == "developers.cloudflare.com":
return scraper.SourceTypeCloudflareDocs
case host == "docs.astro.build":
return scraper.SourceTypeAstroDocs
case host == "github.com":
return scraper.SourceTypeGitHub
default:
return scraper.SourceTypeWeb
}
}
func extractName(sourceURL string) string {
u, err := url.Parse(sourceURL)
if err != nil {
return "unknown"
}
parts := strings.Split(strings.Trim(u.Path, "/"), "/")
if len(parts) > 0 {
return parts[len(parts)-1]
}
return u.Host
}
func sanitizeFilename(name string) string {
name = strings.ToLower(name)
name = strings.ReplaceAll(name, " ", "_")
name = strings.ReplaceAll(name, "/", "_")
name = strings.ReplaceAll(name, ":", "_")
name = strings.ReplaceAll(name, ".", "_")
if len(name) > 50 {
name = name[:50]
}
return name
}
+61
View File
@@ -0,0 +1,61 @@
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var serveCmd = &cobra.Command{
Use: "serve",
Short: "Start the MCP server",
Long: `Start the Devour MCP server.
In local mode (default), the server communicates via stdio, making it
suitable for use as an OpenCode skill.
In remote mode (--remote flag), the server listens on HTTP and exposes
a REST API for multi-user access.
Examples:
devour serve # Local mode (stdio)
devour serve --remote # Remote mode on default port
devour serve --remote --port 3000`,
RunE: runServe,
}
var (
serveRemote bool
servePort int
serveHost string
)
func init() {
serveCmd.Flags().BoolVar(&serveRemote, "remote", false, "run as remote HTTP server")
serveCmd.Flags().IntVarP(&servePort, "port", "p", 8080, "HTTP port (remote mode only)")
serveCmd.Flags().StringVar(&serveHost, "host", "localhost", "HTTP host (remote mode only)")
}
func runServe(cmd *cobra.Command, args []string) error {
if serveRemote {
fmt.Printf("🚀 Starting Devour server in remote mode\n")
fmt.Printf(" Host: %s\n", serveHost)
fmt.Printf(" Port: %d\n", servePort)
fmt.Printf(" URL: http://%s:%d\n", serveHost, servePort)
// TODO: Start HTTP MCP server
return fmt.Errorf("remote mode not yet implemented")
}
fmt.Println("🚀 Starting Devour server in local mode (stdio)")
fmt.Println(" Communicating via JSON-RPC over stdin/stdout")
// TODO: Start stdio MCP server
// Should handle JSON-RPC messages for:
// - devour_query
// - devour_add
// - devour_status
// - devour_sync
return fmt.Errorf("local mode not yet implemented")
}
+61
View File
@@ -0,0 +1,61 @@
package cmd
import (
"fmt"
"time"
"github.com/spf13/cobra"
"github.com/yourorg/devour/internal/ui"
)
var statusCmd = &cobra.Command{
Use: "status",
Short: "Show index status and statistics",
Long: `Display the current status of the Devour index.
Shows:
- Index health
- Document count
- Last update time
- Source status
- Storage usage`,
RunE: runStatus,
}
func runStatus(cmd *cobra.Command, args []string) error {
// Print the small character mascot
ui.PrintCharacterSmall()
fmt.Println()
ui.PrintHeader("Devour Status")
// TODO: Implement actual status check
// Check:
// - Index existence and health
// - Document count
// - Vector count
// - Last sync time
// - Source status
// Placeholder status
ui.PrintKeyValue("Index Health", "⚠️ Not initialized")
ui.PrintKeyValue("Documents", "0 indexed")
ui.PrintKeyValue("Chunks", "0 total")
ui.PrintKeyValue("Vector Dimension", "1536")
ui.PrintKeyValue("Last Updated", "Never")
ui.PrintKeyValue("Storage Used", "0 MB")
fmt.Println()
ui.PrintSection("Sources")
ui.PrintInfo(" None configured")
fmt.Println()
ui.PrintSection("Next Steps")
fmt.Println(" 1. Run 'devour init' to initialize")
fmt.Println(" 2. Run 'devour scrape <source>' to index documents")
// Show when check happened
fmt.Printf("\nStatus as of: %s\n", time.Now().Format(time.RFC3339))
return nil
}
+62
View File
@@ -0,0 +1,62 @@
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var syncCmd = &cobra.Command{
Use: "sync",
Short: "Synchronize with configured sources",
Long: `Fetch updates from all configured sources.
Checks each source for changes (using hash or timestamp comparison)
and updates the index accordingly.
Examples:
devour sync # Sync all sources
devour sync --source my-docs # Sync specific source
devour sync --rebuild # Full rebuild`,
RunE: runSync,
}
var (
syncSource string
syncRebuild bool
syncForce bool
)
func init() {
syncCmd.Flags().StringVarP(&syncSource, "source", "s", "", "sync specific source only")
syncCmd.Flags().BoolVar(&syncRebuild, "rebuild", false, "rebuild entire index")
syncCmd.Flags().BoolVarP(&syncForce, "force", "f", false, "force sync even if no changes detected")
}
func runSync(cmd *cobra.Command, args []string) error {
if syncRebuild {
fmt.Println("🔄 Rebuilding index from all sources...")
} else {
fmt.Println("🔄 Syncing with configured sources...")
}
if syncSource != "" {
fmt.Printf(" Source: %s\n", syncSource)
}
// TODO: Implement actual sync logic
// 1. Load sources from config
// 2. For each source:
// a. Check for changes (hash/timestamp)
// b. If changes detected or --force:
// - Scrape updated content
// - Re-generate embeddings
// - Update index
// 3. Update metadata
fmt.Println()
fmt.Println("⚠️ Sync functionality not yet implemented")
fmt.Println(" Configure sources in devour.yaml first")
return nil
}
+3989
View File
File diff suppressed because it is too large Load Diff
Submodule desloppify/desloppify added at ad2f456292
Executable
BIN
View File
Binary file not shown.
+88
View File
@@ -0,0 +1,88 @@
# Devour Example Configuration
# Copy this file to devour.yaml and customize for your needs
version: 1
# Storage paths
storage:
docs_dir: ./devour_data/docs
index_dir: ./devour_data/index
metadata_dir: ./devour_data/metadata
# Embedding settings
embeddings:
provider: openai # openai, mock
model: text-embedding-3-small
dimensions: 1536
api_key: ${OPENAI_API_KEY} # Use environment variable
batch_size: 100
# Vector database
vector_db:
type: memory # memory, chromem
persist: true
similarity_metric: cosine
# Scraping settings
scraper:
user_agent: "Devour/1.0 (+https://github.com/yourorg/devour)"
timeout: 30s
retry_count: 3
retry_delay: 5s
concurrency: 10
rate_limit: 500ms
max_depth: 3
cache_dir: ./devour_data/cache
# Scheduler
scheduler:
enabled: true
interval: 72h # Every 3 days
check_method: hash # hash, timestamp
on_startup: false
# Server settings
server:
mode: local # local, remote
port: 8080
host: localhost
# Example sources
sources:
# Web documentation
- name: example-docs
type: url
url: https://docs.example.com
include:
- ".*\\.md"
- ".*\\.html"
exclude:
- ".*/api/.*"
- ".*/legacy/.*"
schedule: 24h
# OpenAPI specification
- name: api-spec
type: openapi
url: https://api.example.com/openapi.json
schedule: 168h # Weekly
# GitHub repository
- name: github-repo
type: github
repo: org/repository
branch: main
include:
- "docs/.*"
- "README.md"
exclude:
- "docs/internal/.*"
# auth_token: ${GITHUB_TOKEN} # Optional for private repos
# Local files
- name: local-docs
type: local
path: ./docs
include:
- ".*\\.md"
- ".*\\.txt"
+51
View File
@@ -0,0 +1,51 @@
# Devour Configuration
version: 1
# Storage paths
storage:
docs_dir: ./devour_data/docs
index_dir: ./devour_data/index
metadata_dir: ./devour_data/metadata
# Embedding settings
embeddings:
provider: openai
model: text-embedding-3-small
dimensions: 1536
api_key: ${OPENAI_API_KEY}
batch_size: 100
# Vector database
vector_db:
type: chromem
persist: true
similarity_metric: cosine
# Scraping settings
scraper:
user_agent: "Devour/1.0"
timeout: 30s
retry_count: 3
concurrency: 10
rate_limit: 500ms
max_depth: 3
cache_dir: ./devour_data/cache
# Scheduler
scheduler:
enabled: true
interval: 72h
check_method: hash
# Server settings
server:
mode: local
port: 8080
host: localhost
# Sources (add your own)
sources: []
# - name: example-docs
# type: url
# url: https://docs.example.com
# include: ["**/*.md", "**/*.html"]
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 59 KiB

@@ -0,0 +1,33 @@
# Docker Compose - Ask me about Docker
## 📋 Document Information
| Property | Value |
|----------|-------|
| **Source** | https://docs.docker.com/compose |
| **Type** | `docker-section` |
| **Scraped** | 2026-02-19 12:18:51 |
| **Page_title** | Docker Compose |
| **Doc_url** | https://docs.docker.com/compose |
| **Doc_type** | docker-section |
## 📑 Table of Contents
- [Ask me about Docker](#ask-me-about-docker)
## 📚 Content
# Ask me about Docker
Get instant answers to your Docker questions. I can help with
commands, concepts, troubleshooting, and best practices.
Try asking:
How do Docker Hardened Images work?
What is MCP Toolkit?
How do I create an org?
---
*Document scraped by [Devour](https://github.com/yourorg/devour) on 2026-02-19 12:18:51*
*Source: [https://docs.docker.com/compose](https://docs.docker.com/compose)*
@@ -0,0 +1,24 @@
# Docker Compose - Browse common FAQs
## 📋 Document Information
| Property | Value |
|----------|-------|
| **Source** | https://docs.docker.com/compose |
| **Type** | `docker-section` |
| **Scraped** | 2026-02-19 12:18:51 |
| **Doc_url** | https://docs.docker.com/compose |
| **Doc_type** | docker-section |
| **Page_title** | Docker Compose |
## 📑 Table of Contents
- [Browse common FAQs](#browse-common-faqs)
## 📚 Content
# Browse common FAQs
---
*Document scraped by [Devour](https://github.com/yourorg/devour) on 2026-02-19 12:18:51*
*Source: [https://docs.docker.com/compose](https://docs.docker.com/compose)*
@@ -0,0 +1,33 @@
# Docker Compose - Docker Compose
## 📋 Document Information
| Property | Value |
|----------|-------|
| **Source** | https://docs.docker.com/compose |
| **Type** | `docker-section` |
| **Scraped** | 2026-02-19 12:18:51 |
| **Doc_type** | docker-section |
| **Page_title** | Docker Compose |
| **Doc_url** | https://docs.docker.com/compose |
## 📑 Table of Contents
- [Docker Compose](#docker-compose)
## 📚 Content
# Docker Compose
Copy as Markdown
Open Markdown
Ask Docs AI
Claude
Open in Claudefunction getCurrentPlaintextUrl(){const e=window.location.href.split("#")[0].replace(/\/$/,"");return`${e}.md`}function copyMarkdown(){fetch(getCurrentPlaintextUrl()).then(e=>e.text()).then(e=>{navigator.clipboard.writeText(e).then(()=>{const e=document.querySelector('[data-heap-id="copy-markdown-button"]');if(!e)return;const t=e.querySelectorAll(".icon-svg"),n=t[0],s=t[1];n.classList.add("hidden"),s.classList.remove("hidden"),setTimeout(()=>{n.classList.remove("hidden"),s.classList.add("hidden")},2e3)})}).catch(e=>{console.error("Error copying markdown:",e)})}function viewPlainText(){window.open(getCurrentPlaintextUrl(),"_blank")}function openInDocsAI(){const e=document.querySelector(".open-kapa-widget");e?e.click():alert("Couldn't find Docs AI.")}function openInClaude(){const e=getCurrentPlaintextUrl(),t=`Read ${e} so I can ask questions about it.`,n=encodeURIComponent(t),s=`[https://claude.ai/new?q=${n}`;window.open(s,"_blank")](https://claude.ai/new?q=${n}`;window.open(s,"_blank"))}
---
*Document scraped by [Devour](https://github.com/yourorg/devour) on 2026-02-19 12:18:51*
*Source: [https://docs.docker.com/compose](https://docs.docker.com/compose)*
@@ -0,0 +1,24 @@
# Docker Compose - Explore the Compose file reference
## 📋 Document Information
| Property | Value |
|----------|-------|
| **Source** | https://docs.docker.com/compose |
| **Type** | `docker-section` |
| **Scraped** | 2026-02-19 12:18:51 |
| **Page_title** | Docker Compose |
| **Doc_url** | https://docs.docker.com/compose |
| **Doc_type** | docker-section |
## 📑 Table of Contents
- [Explore the Compose file reference](#explore-the-compose-file-reference)
## 📚 Content
# Explore the Compose file reference
---
*Document scraped by [Devour](https://github.com/yourorg/devour) on 2026-02-19 12:18:51*
*Source: [https://docs.docker.com/compose](https://docs.docker.com/compose)*
@@ -0,0 +1,24 @@
# Docker Compose - How Compose works
## 📋 Document Information
| Property | Value |
|----------|-------|
| **Source** | https://docs.docker.com/compose |
| **Type** | `docker-section` |
| **Scraped** | 2026-02-19 12:18:51 |
| **Page_title** | Docker Compose |
| **Doc_url** | https://docs.docker.com/compose |
| **Doc_type** | docker-section |
## 📑 Table of Contents
- [How Compose works](#how-compose-works)
## 📚 Content
# How Compose works
---
*Document scraped by [Devour](https://github.com/yourorg/devour) on 2026-02-19 12:18:51*
*Source: [https://docs.docker.com/compose](https://docs.docker.com/compose)*
@@ -0,0 +1,24 @@
# Docker Compose - Install Compose
## 📋 Document Information
| Property | Value |
|----------|-------|
| **Source** | https://docs.docker.com/compose |
| **Type** | `docker-section` |
| **Scraped** | 2026-02-19 12:18:51 |
| **Page_title** | Docker Compose |
| **Doc_url** | https://docs.docker.com/compose |
| **Doc_type** | docker-section |
## 📑 Table of Contents
- [Install Compose](#install-compose)
## 📚 Content
# Install Compose
---
*Document scraped by [Devour](https://github.com/yourorg/devour) on 2026-02-19 12:18:51*
*Source: [https://docs.docker.com/compose](https://docs.docker.com/compose)*
@@ -0,0 +1,24 @@
# Docker Compose - Quickstart
## 📋 Document Information
| Property | Value |
|----------|-------|
| **Source** | https://docs.docker.com/compose |
| **Type** | `docker-section` |
| **Scraped** | 2026-02-19 12:18:51 |
| **Doc_url** | https://docs.docker.com/compose |
| **Doc_type** | docker-section |
| **Page_title** | Docker Compose |
## 📑 Table of Contents
- [Quickstart](#quickstart)
## 📚 Content
# Quickstart
---
*Document scraped by [Devour](https://github.com/yourorg/devour) on 2026-02-19 12:18:51*
*Source: [https://docs.docker.com/compose](https://docs.docker.com/compose)*
@@ -0,0 +1,24 @@
# Docker Compose - Use Compose Bridge
## 📋 Document Information
| Property | Value |
|----------|-------|
| **Source** | https://docs.docker.com/compose |
| **Type** | `docker-section` |
| **Scraped** | 2026-02-19 12:18:51 |
| **Page_title** | Docker Compose |
| **Doc_url** | https://docs.docker.com/compose |
| **Doc_type** | docker-section |
## 📑 Table of Contents
- [Use Compose Bridge](#use-compose-bridge)
## 📚 Content
# Use Compose Bridge
---
*Document scraped by [Devour](https://github.com/yourorg/devour) on 2026-02-19 12:18:51*
*Source: [https://docs.docker.com/compose](https://docs.docker.com/compose)*
@@ -0,0 +1,24 @@
# Docker Compose - View the release notes
## 📋 Document Information
| Property | Value |
|----------|-------|
| **Source** | https://docs.docker.com/compose |
| **Type** | `docker-section` |
| **Scraped** | 2026-02-19 12:18:51 |
| **Doc_type** | docker-section |
| **Page_title** | Docker Compose |
| **Doc_url** | https://docs.docker.com/compose |
## 📑 Table of Contents
- [View the release notes](#view-the-release-notes)
## 📚 Content
# View the release notes
---
*Document scraped by [Devour](https://github.com/yourorg/devour) on 2026-02-19 12:18:51*
*Source: [https://docs.docker.com/compose](https://docs.docker.com/compose)*
@@ -0,0 +1,24 @@
# Docker Compose - Why use Compose?
## 📋 Document Information
| Property | Value |
|----------|-------|
| **Source** | https://docs.docker.com/compose |
| **Type** | `docker-section` |
| **Scraped** | 2026-02-19 12:18:51 |
| **Page_title** | Docker Compose |
| **Doc_url** | https://docs.docker.com/compose |
| **Doc_type** | docker-section |
## 📑 Table of Contents
- [Why use Compose?](#why-use-compose)
## 📚 Content
# Why use Compose?
---
*Document scraped by [Devour](https://github.com/yourorg/devour) on 2026-02-19 12:18:51*
*Source: [https://docs.docker.com/compose](https://docs.docker.com/compose)*
+26
View File
@@ -0,0 +1,26 @@
# Docker Compose
## 📋 Document Information
| Property | Value |
|----------|-------|
| **Source** | https://docs.docker.com/compose |
| **Type** | `docker-docs` |
| **Scraped** | 2026-02-19 12:18:51 |
| **Title** | Docker Compose |
| **Doc_url** | https://docs.docker.com/compose |
| **Doc_type** | docker-docs |
## 📑 Table of Contents
- [Docker Compose](#docker-compose)
## 📚 Content
# Docker Compose
Learn how to use Docker Compose to define and run multi-container applications with this detailed introduction to the tool.
---
*Document scraped by [Devour](https://github.com/yourorg/devour) on 2026-02-19 12:18:51*
*Source: [https://docs.docker.com/compose](https://docs.docker.com/compose)*
File diff suppressed because one or more lines are too long
@@ -0,0 +1,17 @@
{
"id": "f21ffb226ab0619b0f70d174",
"source": "http",
"type": "go-variable",
"title": "http.ErrNotSupported (var)",
"content": "# Variables\n\nErrors used by the HTTP server.\n\n```go\nvar ErrNotSupported is not available. = \u0026ProtocolError{\"feature not supported\"}\n```",
"url": "https://pkg.go.dev/http",
"metadata": {
"import_path": "Standard library/net/http",
"kind": "variable",
"name": "ErrNotSupported",
"package": "http",
"type": "is not available."
},
"hash": "b6c75246aaa9b9582da39fd4ab200ffd729e54974ff0be9edeec6ce9252b848e",
"timestamp": "2026-02-19T12:17:17.237762825+01:00"
}
@@ -0,0 +1,18 @@
{
"id": "65ee1b48edad2954c6080431",
"source": "http",
"type": "go-function",
"title": "http.func CanonicalHeaderKey ¶",
"content": "# http.func CanonicalHeaderKey ¶\n\n```\nfunc CanonicalHeaderKey(s string) string\n```\n\nCanonicalHeaderKey returns the canonical format of the\nheader key s. The canonicalization converts the first\nletter and any letter following a hyphen to upper case;\nthe rest are converted to lowercase. For example, the\ncanonical key for \"accept-encoding\" is \"Accept-Encoding\".\nIf s contains a space or invalid header field bytes, it is\nreturned without modifications.",
"url": "https://pkg.go.dev/http#func CanonicalHeaderKey ¶",
"metadata": {
"examples": "null",
"import_path": "Standard library/net/http",
"kind": "function",
"package": "http",
"signature": "func CanonicalHeaderKey(s string) string",
"symbol": "func CanonicalHeaderKey ¶"
},
"hash": "5ee382fb9f4925b7e1b28ac65e836e1527a870f5f5435af7f924377207bef78d",
"timestamp": "2026-02-19T12:17:17.237466038+01:00"
}
@@ -0,0 +1,18 @@
{
"id": "23426e9d0a96a5444a7f0951",
"source": "http",
"type": "go-function",
"title": "http.func DetectContentType ¶",
"content": "# http.func DetectContentType ¶\n\n```\nfunc DetectContentType(data []byte) string\n```\n\nDetectContentType implements the algorithm described\nat https://mimesniff.spec.whatwg.org/ to determine the\nContent-Type of the given data. It considers at most the\nfirst 512 bytes of data. DetectContentType always returns\na valid MIME type: if it cannot determine a more specific one, it\nreturns \"application/octet-stream\".",
"url": "https://pkg.go.dev/http#func DetectContentType ¶",
"metadata": {
"examples": "null",
"import_path": "Standard library/net/http",
"kind": "function",
"package": "http",
"signature": "func DetectContentType(data []byte) string",
"symbol": "func DetectContentType ¶"
},
"hash": "73dce1ebbfa64132571a04d319d15aca387bc1d958f57cabb698395253920d02",
"timestamp": "2026-02-19T12:17:17.237470527+01:00"
}
@@ -0,0 +1,18 @@
{
"id": "a6ee9f24ba2d3043479c6415",
"source": "http",
"type": "go-function",
"title": "http.func Error ¶",
"content": "# http.func Error ¶\n\n```\nfunc Error(w ResponseWriter, error string, code int)\n```\n\nError replies to the request with the specified error message and HTTP code.\nIt does not otherwise end the request; the caller should ensure no further\nwrites are done to w.\nThe error message should be plain text.",
"url": "https://pkg.go.dev/http#func Error ¶",
"metadata": {
"examples": "null",
"import_path": "Standard library/net/http",
"kind": "function",
"package": "http",
"signature": "func Error(w ResponseWriter, error string, code int)",
"symbol": "func Error ¶"
},
"hash": "53edeac9e466955b0d5e71b80793c059b7452ead3a8ca466e801530dc8f92edc",
"timestamp": "2026-02-19T12:17:17.237474404+01:00"
}
@@ -0,0 +1,18 @@
{
"id": "9542cc517b3a451a83da86a7",
"source": "http",
"type": "go-function",
"title": "http.func Handle ¶",
"content": "# http.func Handle ¶\n\n```\nfunc Handle(pattern string, handler Handler)\n```\n\nHandle registers the handler for the given pattern in DefaultServeMux.\nThe documentation for ServeMux explains how patterns are matched.",
"url": "https://pkg.go.dev/http#func Handle ¶",
"metadata": {
"examples": "null",
"import_path": "Standard library/net/http",
"kind": "function",
"package": "http",
"signature": "func Handle(pattern string, handler Handler)",
"symbol": "func Handle ¶"
},
"hash": "25d30d4ef952131e9a0e2fd270490853ffe06894dd92697aab998f852fd8a9ed",
"timestamp": "2026-02-19T12:17:17.237479644+01:00"
}
@@ -0,0 +1,18 @@
{
"id": "49614b7cafdfa80e31f4cc82",
"source": "http",
"type": "go-function",
"title": "http.func HandleFunc ¶",
"content": "# http.func HandleFunc ¶\n\n```\nfunc HandleFunc(pattern string, handler func(ResponseWriter, *Request))\n```\n\nHandleFunc registers the handler function for the given pattern in DefaultServeMux.\nThe documentation for ServeMux explains how patterns are matched.",
"url": "https://pkg.go.dev/http#func HandleFunc ¶",
"metadata": {
"examples": "null",
"import_path": "Standard library/net/http",
"kind": "function",
"package": "http",
"signature": "func HandleFunc(pattern string, handler func(ResponseWriter, *Request))",
"symbol": "func HandleFunc ¶"
},
"hash": "b4c1923d8c3ef79ca7cb366a60d2dda6078e988a3d5cc79f4bdff131546b4156",
"timestamp": "2026-02-19T12:17:17.237483942+01:00"
}
@@ -0,0 +1,18 @@
{
"id": "162c982ca1748a3233acc5d7",
"source": "http",
"type": "go-function",
"title": "http.func ListenAndServe ¶",
"content": "# http.func ListenAndServe ¶\n\n```\nfunc ListenAndServe(addr string, handler Handler) error\n```\n\nListenAndServe listens on the TCP network address addr and then calls\nServe with handler to handle requests on incoming connections.\nAccepted connections are configured to enable TCP keep-alives.",
"url": "https://pkg.go.dev/http#func ListenAndServe ¶",
"metadata": {
"examples": "null",
"import_path": "Standard library/net/http",
"kind": "function",
"package": "http",
"signature": "func ListenAndServe(addr string, handler Handler) error",
"symbol": "func ListenAndServe ¶"
},
"hash": "7295a1607be23026a4fc58ec0e58acf215c4cc9dc4ed6d7925748767f7868547",
"timestamp": "2026-02-19T12:17:17.237487799+01:00"
}
@@ -0,0 +1,18 @@
{
"id": "3da3f79b514bd00d8102a3a9",
"source": "http",
"type": "go-function",
"title": "http.func ListenAndServeTLS ¶",
"content": "# http.func ListenAndServeTLS ¶\n\n```\nfunc ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error\n```\n\nListenAndServeTLS acts identically to ListenAndServe, except that it\nexpects HTTPS connections. Additionally, files containing a certificate and\nmatching private key for the server must be provided. If the certificate\nis signed by a certificate authority, the certFile should be the concatenation\nof the server's certificate, any intermediates, and the CA's certificate.",
"url": "https://pkg.go.dev/http#func ListenAndServeTLS ¶",
"metadata": {
"examples": "null",
"import_path": "Standard library/net/http",
"kind": "function",
"package": "http",
"signature": "func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error",
"symbol": "func ListenAndServeTLS ¶"
},
"hash": "547882865ad9b060be7c1ca4c558390726d5381bd9367e4983f6f6106e142b97",
"timestamp": "2026-02-19T12:17:17.237491446+01:00"
}
@@ -0,0 +1,18 @@
{
"id": "9095e017e922d9d6ac517fa9",
"source": "http",
"type": "go-function",
"title": "http.func MaxBytesReader ¶",
"content": "# http.func MaxBytesReader ¶\n\n```\nfunc MaxBytesReader(w ResponseWriter, r io.ReadCloser, n int64) io.ReadCloser\n```\n\nMaxBytesReader is similar to io.LimitReader but is intended for\nlimiting the size of incoming request bodies. In contrast to\nio.LimitReader, MaxBytesReader's result is a ReadCloser, returns a\nnon-nil error of type *MaxBytesError for a Read beyond the limit,\nand closes the underlying reader when its Close method is called.",
"url": "https://pkg.go.dev/http#func MaxBytesReader ¶",
"metadata": {
"examples": "null",
"import_path": "Standard library/net/http",
"kind": "function",
"package": "http",
"signature": "func MaxBytesReader(w ResponseWriter, r io.ReadCloser, n int64) io.ReadCloser",
"symbol": "func MaxBytesReader ¶"
},
"hash": "3dea71e71ecd5cd1db9a835bd97e988e7e866cb4cc318eb3c80c8ec369cfb2cb",
"timestamp": "2026-02-19T12:17:17.237495694+01:00"
}
@@ -0,0 +1,18 @@
{
"id": "2b893c8d59d1f244265f1d5f",
"source": "http",
"type": "go-function",
"title": "http.func NotFound ¶",
"content": "# http.func NotFound ¶\n\n```\nfunc NotFound(w ResponseWriter, r *Request)\n```\n\nNotFound replies to the request with an HTTP 404 not found error.",
"url": "https://pkg.go.dev/http#func NotFound ¶",
"metadata": {
"examples": "null",
"import_path": "Standard library/net/http",
"kind": "function",
"package": "http",
"signature": "func NotFound(w ResponseWriter, r *Request)",
"symbol": "func NotFound ¶"
},
"hash": "5aaf8d9740b214c17db832e47f1feb9e065c62ed2ae531de3c91e0dfe8a7f4d2",
"timestamp": "2026-02-19T12:17:17.237500072+01:00"
}
@@ -0,0 +1,18 @@
{
"id": "329cdea0dfc4505ea97a4e8a",
"source": "http",
"type": "go-function",
"title": "http.func ParseHTTPVersion ¶",
"content": "# http.func ParseHTTPVersion ¶\n\n```\nfunc ParseHTTPVersion(vers string) (major, minor int, ok bool)\n```\n\nParseHTTPVersion parses an HTTP version string according to RFC 7230, section 2.6.\n\"HTTP/1.0\" returns (1, 0, true). Note that strings without\na minor version, such as \"HTTP/2\", are not valid.",
"url": "https://pkg.go.dev/http#func ParseHTTPVersion ¶",
"metadata": {
"examples": "null",
"import_path": "Standard library/net/http",
"kind": "function",
"package": "http",
"signature": "func ParseHTTPVersion(vers string) (major, minor int, ok bool)",
"symbol": "func ParseHTTPVersion ¶"
},
"hash": "a4608aaccf5ee1156607e14d7e624fcd4a7a560baedd2a8a6b5590e1029467b5",
"timestamp": "2026-02-19T12:17:17.23750434+01:00"
}
@@ -0,0 +1,18 @@
{
"id": "38c151d7359d6452a710d8b8",
"source": "http",
"type": "go-function",
"title": "http.func ParseTime ¶\n \n \n added in\n go1.1",
"content": "# http.func ParseTime ¶\n \n \n added in\n go1.1\n\n```\nfunc ParseTime(text string) (t time.Time, err error)\n```\n\nParseTime parses a time header (such as the Date: header),\ntrying each of the three formats allowed by HTTP/1.1:\nTimeFormat, time.RFC850, and time.ANSIC.",
"url": "https://pkg.go.dev/http#func ParseTime ¶\n \n \n added in\n go1.1",
"metadata": {
"examples": "null",
"import_path": "Standard library/net/http",
"kind": "function",
"package": "http",
"signature": "func ParseTime(text string) (t time.Time, err error)",
"symbol": "func ParseTime ¶\n \n \n added in\n go1.1"
},
"hash": "2ab78361d8224d68e3b0efeb5f52abf07ef22a863459b2828a59ddabe4cb250e",
"timestamp": "2026-02-19T12:17:17.237508698+01:00"
}
@@ -0,0 +1,18 @@
{
"id": "ac455995d3e7b921e08dbe30",
"source": "http",
"type": "go-function",
"title": "http.func ProxyFromEnvironment ¶",
"content": "# http.func ProxyFromEnvironment ¶\n\n```\nfunc ProxyFromEnvironment(req *Request) (*url.URL, error)\n```\n\nProxyFromEnvironment returns the URL of the proxy to use for a\ngiven request, as indicated by the environment variables\nHTTP_PROXY, HTTPS_PROXY and NO_PROXY (or the lowercase versions\nthereof). Requests use the proxy from the environment variable\nmatching their scheme, unless excluded by NO_PROXY.",
"url": "https://pkg.go.dev/http#func ProxyFromEnvironment ¶",
"metadata": {
"examples": "null",
"import_path": "Standard library/net/http",
"kind": "function",
"package": "http",
"signature": "func ProxyFromEnvironment(req *Request) (*url.URL, error)",
"symbol": "func ProxyFromEnvironment ¶"
},
"hash": "16576362fe0ebca2feba8f0a4c6966f9a2d924353bcf807bbdc16c8076b5408a",
"timestamp": "2026-02-19T12:17:17.237512145+01:00"
}
@@ -0,0 +1,18 @@
{
"id": "efb9e9afe67ca7664d403cee",
"source": "http",
"type": "go-function",
"title": "http.func ProxyURL ¶",
"content": "# http.func ProxyURL ¶\n\n```\nfunc ProxyURL(fixedURL *url.URL) func(*Request) (*url.URL, error)\n```\n\nProxyURL returns a proxy function (for use in a Transport)\nthat always returns the same URL.",
"url": "https://pkg.go.dev/http#func ProxyURL ¶",
"metadata": {
"examples": "null",
"import_path": "Standard library/net/http",
"kind": "function",
"package": "http",
"signature": "func ProxyURL(fixedURL *url.URL) func(*Request) (*url.URL, error)",
"symbol": "func ProxyURL ¶"
},
"hash": "a98c7becc36eb868a095e8642bfe08092e866156d8afe4a28a6c325ace8a1a16",
"timestamp": "2026-02-19T12:17:17.237515281+01:00"
}
@@ -0,0 +1,18 @@
{
"id": "d12dce4e35fcfdd56c96aa14",
"source": "http",
"type": "go-function",
"title": "http.func Redirect ¶",
"content": "# http.func Redirect ¶\n\n```\nfunc Redirect(w ResponseWriter, r *Request, url string, code int)\n```\n\nRedirect replies to the request with a redirect to url,\nwhich may be a path relative to the request path.\nAny non-ASCII characters in url will be percent-encoded,\nbut existing percent encodings will not be changed.",
"url": "https://pkg.go.dev/http#func Redirect ¶",
"metadata": {
"examples": "null",
"import_path": "Standard library/net/http",
"kind": "function",
"package": "http",
"signature": "func Redirect(w ResponseWriter, r *Request, url string, code int)",
"symbol": "func Redirect ¶"
},
"hash": "d548a0c7d62a3e6288eef721ea630d725f59d2050e969e314fbd28e2a5020e94",
"timestamp": "2026-02-19T12:17:17.237519509+01:00"
}
@@ -0,0 +1,18 @@
{
"id": "583357a2e03a602ec45348c2",
"source": "http",
"type": "go-function",
"title": "http.func Serve ¶",
"content": "# http.func Serve ¶\n\n```\nfunc Serve(l net.Listener, handler Handler) error\n```\n\nServe accepts incoming HTTP connections on the listener l,\ncreating a new service goroutine for each. The service goroutines\nread requests and then call handler to reply to them.",
"url": "https://pkg.go.dev/http#func Serve ¶",
"metadata": {
"examples": "null",
"import_path": "Standard library/net/http",
"kind": "function",
"package": "http",
"signature": "func Serve(l net.Listener, handler Handler) error",
"symbol": "func Serve ¶"
},
"hash": "0c54d97c723ca778078b49669b005b125519967aed99dd64f1c4ae4f46922a6a",
"timestamp": "2026-02-19T12:17:17.237526993+01:00"
}
@@ -0,0 +1,18 @@
{
"id": "45375608a6e38a1495ae3842",
"source": "http",
"type": "go-function",
"title": "http.func ServeContent ¶",
"content": "# http.func ServeContent ¶\n\n```\nfunc ServeContent(w ResponseWriter, req *Request, name string, modtime time.Time, content io.ReadSeeker)\n```\n\nServeContent replies to the request using the content in the\nprovided ReadSeeker. The main benefit of ServeContent over io.Copy\nis that it handles Range requests properly, sets the MIME type, and\nhandles If-Match, If-Unmodified-Since, If-None-Match, If-Modified-Since,\nand If-Range requests.",
"url": "https://pkg.go.dev/http#func ServeContent ¶",
"metadata": {
"examples": "null",
"import_path": "Standard library/net/http",
"kind": "function",
"package": "http",
"signature": "func ServeContent(w ResponseWriter, req *Request, name string, modtime time.Time, content io.ReadSeeker)",
"symbol": "func ServeContent ¶"
},
"hash": "03269d66f19b0fb2428038a9d4120d2d90969ab065bb31debcbb5d1e556f1cea",
"timestamp": "2026-02-19T12:17:17.23753085+01:00"
}
@@ -0,0 +1,18 @@
{
"id": "43e18a0f8502552a31de729d",
"source": "http",
"type": "go-function",
"title": "http.func ServeFile ¶",
"content": "# http.func ServeFile ¶\n\n```\nfunc ServeFile(w ResponseWriter, r *Request, name string)\n```\n\nServeFile replies to the request with the contents of the named\nfile or directory.",
"url": "https://pkg.go.dev/http#func ServeFile ¶",
"metadata": {
"examples": "null",
"import_path": "Standard library/net/http",
"kind": "function",
"package": "http",
"signature": "func ServeFile(w ResponseWriter, r *Request, name string)",
"symbol": "func ServeFile ¶"
},
"hash": "b8fc650a6847fc72d6b59d5d736c9cc70ad71effc4917928ec1d47afd2e3015f",
"timestamp": "2026-02-19T12:17:17.237534316+01:00"
}
@@ -0,0 +1,18 @@
{
"id": "15ade505ab0ce3be03d304ba",
"source": "http",
"type": "go-function",
"title": "http.func ServeFileFS ¶\n \n \n added in\n go1.22.0",
"content": "# http.func ServeFileFS ¶\n \n \n added in\n go1.22.0\n\n```\nfunc ServeFileFS(w ResponseWriter, r *Request, fsys fs.FS, name string)\n```\n\nServeFileFS replies to the request with the contents\nof the named file or directory from the file system fsys.\nThe files provided by fsys must implement io.Seeker.",
"url": "https://pkg.go.dev/http#func ServeFileFS ¶\n \n \n added in\n go1.22.0",
"metadata": {
"examples": "null",
"import_path": "Standard library/net/http",
"kind": "function",
"package": "http",
"signature": "func ServeFileFS(w ResponseWriter, r *Request, fsys fs.FS, name string)",
"symbol": "func ServeFileFS ¶\n \n \n added in\n go1.22.0"
},
"hash": "132424d7d51be2451a20fcd4c861a263431d4a6a4dc8971ce42eb5319024a780",
"timestamp": "2026-02-19T12:17:17.237537983+01:00"
}
@@ -0,0 +1,18 @@
{
"id": "c0758d9ea0206e46e9c59133",
"source": "http",
"type": "go-function",
"title": "http.func ServeTLS ¶\n \n \n added in\n go1.9",
"content": "# http.func ServeTLS ¶\n \n \n added in\n go1.9\n\n```\nfunc ServeTLS(l net.Listener, handler Handler, certFile, keyFile string) error\n```\n\nServeTLS accepts incoming HTTPS connections on the listener l,\ncreating a new service goroutine for each. The service goroutines\nread requests and then call handler to reply to them.",
"url": "https://pkg.go.dev/http#func ServeTLS ¶\n \n \n added in\n go1.9",
"metadata": {
"examples": "null",
"import_path": "Standard library/net/http",
"kind": "function",
"package": "http",
"signature": "func ServeTLS(l net.Listener, handler Handler, certFile, keyFile string) error",
"symbol": "func ServeTLS ¶\n \n \n added in\n go1.9"
},
"hash": "fe741ce59ba9dac06b0723d1f3a5584ee686272d65a619e9b17f9367511aa8b4",
"timestamp": "2026-02-19T12:17:17.237542552+01:00"
}
@@ -0,0 +1,18 @@
{
"id": "4710fd7347378202d17c3fee",
"source": "http",
"type": "go-function",
"title": "http.func SetCookie ¶",
"content": "# http.func SetCookie ¶\n\n```\nfunc SetCookie(w ResponseWriter, cookie *Cookie)\n```\n\nSetCookie adds a Set-Cookie header to the provided ResponseWriter's headers.\nThe provided cookie must have a valid Name. Invalid cookies may be\nsilently dropped.",
"url": "https://pkg.go.dev/http#func SetCookie ¶",
"metadata": {
"examples": "null",
"import_path": "Standard library/net/http",
"kind": "function",
"package": "http",
"signature": "func SetCookie(w ResponseWriter, cookie *Cookie)",
"symbol": "func SetCookie ¶"
},
"hash": "5f857191842622c283c4463e390fb208edd604e9202a7a8a80524256d0fd06d0",
"timestamp": "2026-02-19T12:17:17.237545838+01:00"
}
@@ -0,0 +1,18 @@
{
"id": "20104e1fbb1a4d9b10b41290",
"source": "http",
"type": "go-function",
"title": "http.func StatusText ¶",
"content": "# http.func StatusText ¶\n\n```\nfunc StatusText(code int) string\n```\n\nStatusText returns a text for the HTTP status code. It returns the empty\nstring if the code is unknown.",
"url": "https://pkg.go.dev/http#func StatusText ¶",
"metadata": {
"examples": "null",
"import_path": "Standard library/net/http",
"kind": "function",
"package": "http",
"signature": "func StatusText(code int) string",
"symbol": "func StatusText ¶"
},
"hash": "3454de944d115dbcd1630666faed28191fd667541f0d214a0e46f0e688d90bf1",
"timestamp": "2026-02-19T12:17:17.237549194+01:00"
}
@@ -0,0 +1,26 @@
{
"id": "1a6d3ba16db325285ee52f5f",
"source": "http",
"type": "go-constant",
"title": "http.MethodGet (const)",
"content": "# Constants\n\nCommon HTTP methods.\n\n```go\nconst (\n\tMethodGet\n\tMethodHead\n\tMethodPost\n\tMethodPut\n\tMethodPatch\n\tMethodDelete\n\tMethodConnect\n\tMethodOptions\n\tMethodTrace\n)\n```",
"url": "https://pkg.go.dev/http",
"metadata": {
"import_path": "Standard library/net/http",
"kind": "constant",
"names": [
"MethodGet",
"MethodHead",
"MethodPost",
"MethodPut",
"MethodPatch",
"MethodDelete",
"MethodConnect",
"MethodOptions",
"MethodTrace"
],
"package": "http"
},
"hash": "7fac2a470856d94db2f853265ed2ff7adc1597ef4324f2e67413a8dd1708b129",
"timestamp": "2026-02-19T12:17:17.237759058+01:00"
}
@@ -0,0 +1,20 @@
{
"id": "ec6daa6fcc79332cf26c3e5e",
"source": "http",
"type": "go-type",
"title": "http.type Client ¶",
"content": "# type http.type Client ¶\n\n```\ntype Client struct {\n\t// Transport specifies the mechanism by which individual\n\t// HTTP requests are made.\n\t// If nil, DefaultTransport is used.\n\tTransport RoundTripper\n\n\t// CheckRedirect specifies the policy for handling redirects.\n\t// If CheckRedirect is not nil, the client calls it before\n\t// following an HTTP redirect. The arguments req and via are\n\t// the upcoming request and the requests made already, oldest\n\t// first. If CheckRedirect returns an error, the Client's Get\n\t// method returns both the previous Response (with its Body\n\t// closed) and CheckRedirect's error (wrapped in a url.Error)\n\t// instead of issuing the Request req.\n\t// As a special case, if CheckRedirect returns ErrUseLastResponse,\n\t// then the most recent response is returned with its body\n\t// unclosed, along with a nil error.\n\t//\n\t// If CheckRedirect is nil, the Client uses its default policy,\n\t// which is to stop after 10 consecutive requests.\n\tCheckRedirect func(req *Request, via []*Request) error\n\n\t// Jar specifies the cookie jar.\n\t//\n\t// The Jar is used to insert relevant cookies into every\n\t// outbound Request and is updated with the cookie values\n\t// of every inbound Response. The Jar is consulted for every\n\t// redirect that the Client follows.\n\t//\n\t// If Jar is nil, cookies are only sent if they are explicitly\n\t// set on the Request.\n\tJar CookieJar\n\n\t// Timeout specifies a time limit for requests made by this\n\t// Client. The timeout includes connection time, any\n\t// redirects, and reading the response body. The timer remains\n\t// running after Get, Head, Post, or Do return and will\n\t// interrupt reading of the Response.Body.\n\t//\n\t// A Timeout of zero means no timeout.\n\t//\n\t// The Client cancels requests to the underlying Transport\n\t// as if the Request's Context ended.\n\t//\n\t// For compatibility, the Client will also use the deprecated\n\t// CancelRequest method on Transport if found. New\n\t// RoundTripper implementations should use the Request's Context\n\t// for cancellation instead of implementing CancelRequest.\n\tTimeout time.Duration\n}\n```\n\nA Client is an HTTP client. Its zero value (DefaultClient) is a\nusable client that uses DefaultTransport.",
"url": "https://pkg.go.dev/http#type Client ¶",
"metadata": {
"fields": "null",
"import_path": "Standard library/net/http",
"kind": "type",
"method_count": 0,
"package": "http",
"symbol": "type Client ¶",
"type_kind": "alias",
"underlying": "type Client struct {\n\t// Transport specifies the mechanism by which individual\n\t// HTTP requests are made.\n\t// If nil, DefaultTransport is used.\n\tTransport RoundTripper\n\n\t// CheckRedirect specifies the policy for handling redirects.\n\t// If CheckRedirect is not nil, the client calls it before\n\t// following an HTTP redirect. The arguments req and via are\n\t// the upcoming request and the requests made already, oldest\n\t// first. If CheckRedirect returns an error, the Client's Get\n\t// method returns both the previous Response (with its Body\n\t// closed) and CheckRedirect's error (wrapped in a url.Error)\n\t// instead of issuing the Request req.\n\t// As a special case, if CheckRedirect returns ErrUseLastResponse,\n\t// then the most recent response is returned with its body\n\t// unclosed, along with a nil error.\n\t//\n\t// If CheckRedirect is nil, the Client uses its default policy,\n\t// which is to stop after 10 consecutive requests.\n\tCheckRedirect func(req *Request, via []*Request) error\n\n\t// Jar specifies the cookie jar.\n\t//\n\t// The Jar is used to insert relevant cookies into every\n\t// outbound Request and is updated with the cookie values\n\t// of every inbound Response. The Jar is consulted for every\n\t// redirect that the Client follows.\n\t//\n\t// If Jar is nil, cookies are only sent if they are explicitly\n\t// set on the Request.\n\tJar CookieJar\n\n\t// Timeout specifies a time limit for requests made by this\n\t// Client. The timeout includes connection time, any\n\t// redirects, and reading the response body. The timer remains\n\t// running after Get, Head, Post, or Do return and will\n\t// interrupt reading of the Response.Body.\n\t//\n\t// A Timeout of zero means no timeout.\n\t//\n\t// The Client cancels requests to the underlying Transport\n\t// as if the Request's Context ended.\n\t//\n\t// For compatibility, the Client will also use the deprecated\n\t// CancelRequest method on Transport if found. New\n\t// RoundTripper implementations should use the Request's Context\n\t// for cancellation instead of implementing CancelRequest.\n\tTimeout time.Duration\n}"
},
"hash": "6d6d93116b24302bb67cbb484a7109bdd878d1b2b3678089bdf13515710d654b",
"timestamp": "2026-02-19T12:17:17.237577517+01:00"
}
@@ -0,0 +1,20 @@
{
"id": "5864b5986299149a4053361f",
"source": "http",
"type": "go-type",
"title": "http.type ClientConn ¶\n \n \n added in\n go1.26.0",
"content": "# type http.type ClientConn ¶\n \n \n added in\n go1.26.0\n\n```\ntype ClientConn struct {\n\t// contains filtered or unexported fields\n}\n```\n\nA ClientConn is a client connection to an HTTP server.",
"url": "https://pkg.go.dev/http#type ClientConn ¶\n \n \n added in\n go1.26.0",
"metadata": {
"fields": "null",
"import_path": "Standard library/net/http",
"kind": "type",
"method_count": 0,
"package": "http",
"symbol": "type ClientConn ¶\n \n \n added in\n go1.26.0",
"type_kind": "alias",
"underlying": "type ClientConn struct {\n\t// contains filtered or unexported fields\n}"
},
"hash": "1cca1d5c18337cbf31843d1aad74bf757c9ea8b75bce1bea430805978a69a7e4",
"timestamp": "2026-02-19T12:17:17.237581315+01:00"
}
@@ -0,0 +1,20 @@
{
"id": "83addbeae90c93c46e86c929",
"source": "http",
"type": "go-type",
"title": "http.type CloseNotifier\n deprecated\n \n \n \n \n added in\n go1.1",
"content": "# type http.type CloseNotifier\n deprecated\n \n \n \n \n added in\n go1.1\n\n```\ntype CloseNotifier interface {\n\t// CloseNotify returns a channel that receives at most a\n\t// single value (true) when the client connection has gone\n\t// away.\n\t//\n\t// CloseNotify may wait to notify until Request.Body has been\n\t// fully read.\n\t//\n\t// After the Handler has returned, there is no guarantee\n\t// that the channel receives a value.\n\t//\n\t// If the protocol is HTTP/1.1 and CloseNotify is called while\n\t// processing an idempotent request (such as GET) while\n\t// HTTP/1.1 pipelining is in use, the arrival of a subsequent\n\t// pipelined request may cause a value to be sent on the\n\t// returned channel. In practice HTTP/1.1 pipelining is not\n\t// enabled in browsers and not seen often in the wild. If this\n\t// is a problem, use HTTP/2 or only use CloseNotify on methods\n\t// such as POST.\n\tCloseNotify() \u003c-chan bool\n}\n```\n\nThe CloseNotifier interface is implemented by ResponseWriters which\nallow detecting when the underlying connection has gone away.",
"url": "https://pkg.go.dev/http#type CloseNotifier\n deprecated\n \n \n \n \n added in\n go1.1",
"metadata": {
"fields": "null",
"import_path": "Standard library/net/http",
"kind": "type",
"method_count": 0,
"package": "http",
"symbol": "type CloseNotifier\n deprecated\n \n \n \n \n added in\n go1.1",
"type_kind": "alias",
"underlying": "type CloseNotifier interface {\n\t// CloseNotify returns a channel that receives at most a\n\t// single value (true) when the client connection has gone\n\t// away.\n\t//\n\t// CloseNotify may wait to notify until Request.Body has been\n\t// fully read.\n\t//\n\t// After the Handler has returned, there is no guarantee\n\t// that the channel receives a value.\n\t//\n\t// If the protocol is HTTP/1.1 and CloseNotify is called while\n\t// processing an idempotent request (such as GET) while\n\t// HTTP/1.1 pipelining is in use, the arrival of a subsequent\n\t// pipelined request may cause a value to be sent on the\n\t// returned channel. In practice HTTP/1.1 pipelining is not\n\t// enabled in browsers and not seen often in the wild. If this\n\t// is a problem, use HTTP/2 or only use CloseNotify on methods\n\t// such as POST.\n\tCloseNotify() \u003c-chan bool\n}"
},
"hash": "e35a4cf93a2abc0be06ef805aa84a15cb4c234ab18a0b9330e16a84e3d1cf755",
"timestamp": "2026-02-19T12:17:17.237588678+01:00"
}
@@ -0,0 +1,20 @@
{
"id": "344cbf96ab225524d7377423",
"source": "http",
"type": "go-type",
"title": "http.type ConnState ¶\n \n \n added in\n go1.3",
"content": "# type http.type ConnState ¶\n \n \n added in\n go1.3\n\n```\ntype ConnState int\n```\n\nA ConnState represents the state of a client connection to a server.\nIt's used by the optional [Server.ConnState] hook.",
"url": "https://pkg.go.dev/http#type ConnState ¶\n \n \n added in\n go1.3",
"metadata": {
"fields": "null",
"import_path": "Standard library/net/http",
"kind": "type",
"method_count": 0,
"package": "http",
"symbol": "type ConnState ¶\n \n \n added in\n go1.3",
"type_kind": "alias",
"underlying": "type ConnState int"
},
"hash": "3e4d16aa1552a5a8218fc2a5460a199ef92fb68a33366e395debafc4393d9760",
"timestamp": "2026-02-19T12:17:17.237593517+01:00"
}
@@ -0,0 +1,20 @@
{
"id": "e04d37255abd69352e708f87",
"source": "http",
"type": "go-type",
"title": "http.type Cookie ¶",
"content": "# type http.type Cookie ¶\n\n```\ntype Cookie struct {\n\tName string\n\tValue string\n\tQuoted bool // indicates whether the Value was originally quoted\n\n\tPath string // optional\n\tDomain string // optional\n\tExpires time.Time // optional\n\tRawExpires string // for reading cookies only\n\n\t// MaxAge=0 means no 'Max-Age' attribute specified.\n\t// MaxAge\u003c0 means delete cookie now, equivalently 'Max-Age: 0'\n\t// MaxAge\u003e0 means Max-Age attribute present and given in seconds\n\tMaxAge int\n\tSecure bool\n\tHttpOnly bool\n\tSameSite SameSite\n\tPartitioned bool\n\tRaw string\n\tUnparsed []string // Raw text of unparsed attribute-value pairs\n}\n```\n\nA Cookie represents an HTTP cookie as sent in the Set-Cookie header of an\nHTTP response or the Cookie header of an HTTP request.",
"url": "https://pkg.go.dev/http#type Cookie ¶",
"metadata": {
"fields": "null",
"import_path": "Standard library/net/http",
"kind": "type",
"method_count": 0,
"package": "http",
"symbol": "type Cookie ¶",
"type_kind": "alias",
"underlying": "type Cookie struct {\n\tName string\n\tValue string\n\tQuoted bool // indicates whether the Value was originally quoted\n\n\tPath string // optional\n\tDomain string // optional\n\tExpires time.Time // optional\n\tRawExpires string // for reading cookies only\n\n\t// MaxAge=0 means no 'Max-Age' attribute specified.\n\t// MaxAge\u003c0 means delete cookie now, equivalently 'Max-Age: 0'\n\t// MaxAge\u003e0 means Max-Age attribute present and given in seconds\n\tMaxAge int\n\tSecure bool\n\tHttpOnly bool\n\tSameSite SameSite\n\tPartitioned bool\n\tRaw string\n\tUnparsed []string // Raw text of unparsed attribute-value pairs\n}"
},
"hash": "228c7df1786726430a79d1f72d01f8c79ce59948eb14ee3081b24e29357b55d8",
"timestamp": "2026-02-19T12:17:17.237597785+01:00"
}
@@ -0,0 +1,20 @@
{
"id": "a4d523264c15f366b4b4fee0",
"source": "http",
"type": "go-type",
"title": "http.type CookieJar ¶",
"content": "# type http.type CookieJar ¶\n\n```\ntype CookieJar interface {\n\t// SetCookies handles the receipt of the cookies in a reply for the\n\t// given URL. It may or may not choose to save the cookies, depending\n\t// on the jar's policy and implementation.\n\tSetCookies(u *url.URL, cookies []*Cookie)\n\n\t// Cookies returns the cookies to send in a request for the given URL.\n\t// It is up to the implementation to honor the standard cookie use\n\t// restrictions such as in RFC 6265.\n\tCookies(u *url.URL) []*Cookie\n}\n```\n\nA CookieJar manages storage and use of cookies in HTTP requests.",
"url": "https://pkg.go.dev/http#type CookieJar ¶",
"metadata": {
"fields": "null",
"import_path": "Standard library/net/http",
"kind": "type",
"method_count": 0,
"package": "http",
"symbol": "type CookieJar ¶",
"type_kind": "alias",
"underlying": "type CookieJar interface {\n\t// SetCookies handles the receipt of the cookies in a reply for the\n\t// given URL. It may or may not choose to save the cookies, depending\n\t// on the jar's policy and implementation.\n\tSetCookies(u *url.URL, cookies []*Cookie)\n\n\t// Cookies returns the cookies to send in a request for the given URL.\n\t// It is up to the implementation to honor the standard cookie use\n\t// restrictions such as in RFC 6265.\n\tCookies(u *url.URL) []*Cookie\n}"
},
"hash": "d4cbfb4c2754318f1f942459844795ae3ad8d44bdc7278f4bd4216fc06dbd56d",
"timestamp": "2026-02-19T12:17:17.237602414+01:00"
}
@@ -0,0 +1,20 @@
{
"id": "6ddb919fe5b492f377978d01",
"source": "http",
"type": "go-type",
"title": "http.type CrossOriginProtection ¶\n \n \n added in\n go1.25.0",
"content": "# type http.type CrossOriginProtection ¶\n \n \n added in\n go1.25.0\n\n```\ntype CrossOriginProtection struct {\n\t// contains filtered or unexported fields\n}\n```\n\nCrossOriginProtection implements protections against Cross-Site Request\nForgery (CSRF) by rejecting non-safe cross-origin browser requests.",
"url": "https://pkg.go.dev/http#type CrossOriginProtection ¶\n \n \n added in\n go1.25.0",
"metadata": {
"fields": "null",
"import_path": "Standard library/net/http",
"kind": "type",
"method_count": 0,
"package": "http",
"symbol": "type CrossOriginProtection ¶\n \n \n added in\n go1.25.0",
"type_kind": "alias",
"underlying": "type CrossOriginProtection struct {\n\t// contains filtered or unexported fields\n}"
},
"hash": "6b5d174f63b34be6d3a0f2e2c848f963aed1d30dbed6a89e38a462b966ad21b7",
"timestamp": "2026-02-19T12:17:17.237605931+01:00"
}
+20
View File
@@ -0,0 +1,20 @@
{
"id": "099998cc5e5cb8f5c2c54c91",
"source": "http",
"type": "go-type",
"title": "http.type Dir ¶",
"content": "# type http.type Dir ¶\n\n```\ntype Dir string\n```\n\nA Dir implements FileSystem using the native file system restricted to a\nspecific directory tree.",
"url": "https://pkg.go.dev/http#type Dir ¶",
"metadata": {
"fields": "null",
"import_path": "Standard library/net/http",
"kind": "type",
"method_count": 0,
"package": "http",
"symbol": "type Dir ¶",
"type_kind": "alias",
"underlying": "type Dir string"
},
"hash": "b97ba93d147e68dd3f4d62bc88eaa49a896838d1eccf5de6645f4fc11f8a7022",
"timestamp": "2026-02-19T12:17:17.237609828+01:00"
}
@@ -0,0 +1,20 @@
{
"id": "128398ed966f457505ff4428",
"source": "http",
"type": "go-type",
"title": "http.type File ¶",
"content": "# type http.type File ¶\n\n```\ntype File interface {\n\tio.Closer\n\tio.Reader\n\tio.Seeker\n\tReaddir(count int) ([]fs.FileInfo, error)\n\tStat() (fs.FileInfo, error)\n}\n```\n\nA File is returned by a FileSystem's Open method and can be\nserved by the FileServer implementation.",
"url": "https://pkg.go.dev/http#type File ¶",
"metadata": {
"fields": "null",
"import_path": "Standard library/net/http",
"kind": "type",
"method_count": 0,
"package": "http",
"symbol": "type File ¶",
"type_kind": "alias",
"underlying": "type File interface {\n\tio.Closer\n\tio.Reader\n\tio.Seeker\n\tReaddir(count int) ([]fs.FileInfo, error)\n\tStat() (fs.FileInfo, error)\n}"
},
"hash": "4a31799156fabe79a6ccc14220de64e0b633925052f63db98f0359ade1206387",
"timestamp": "2026-02-19T12:17:17.237613144+01:00"
}
@@ -0,0 +1,20 @@
{
"id": "32353559a030071b2fd63113",
"source": "http",
"type": "go-type",
"title": "http.type FileSystem ¶",
"content": "# type http.type FileSystem ¶\n\n```\ntype FileSystem interface {\n\tOpen(name string) (File, error)\n}\n```\n\nA FileSystem implements access to a collection of named files.\nThe elements in a file path are separated by slash ('/', U+002F)\ncharacters, regardless of host operating system convention.\nSee the FileServer function to convert a FileSystem to a Handler.",
"url": "https://pkg.go.dev/http#type FileSystem ¶",
"metadata": {
"fields": "null",
"import_path": "Standard library/net/http",
"kind": "type",
"method_count": 0,
"package": "http",
"symbol": "type FileSystem ¶",
"type_kind": "alias",
"underlying": "type FileSystem interface {\n\tOpen(name string) (File, error)\n}"
},
"hash": "f2e668d7fb4d6938f02ceeead81cf136f02ed65506389c1a49c10a5e04ff04a8",
"timestamp": "2026-02-19T12:17:17.237621129+01:00"
}
@@ -0,0 +1,20 @@
{
"id": "a3ac7f1a6dd81196f824401e",
"source": "http",
"type": "go-type",
"title": "http.type Flusher ¶",
"content": "# type http.type Flusher ¶\n\n```\ntype Flusher interface {\n\t// Flush sends any buffered data to the client.\n\tFlush()\n}\n```\n\nThe Flusher interface is implemented by ResponseWriters that allow\nan HTTP handler to flush buffered data to the client.",
"url": "https://pkg.go.dev/http#type Flusher ¶",
"metadata": {
"fields": "null",
"import_path": "Standard library/net/http",
"kind": "type",
"method_count": 0,
"package": "http",
"symbol": "type Flusher ¶",
"type_kind": "alias",
"underlying": "type Flusher interface {\n\t// Flush sends any buffered data to the client.\n\tFlush()\n}"
},
"hash": "c650a976ca695efb09d3172ec610beb642d47de982565a1a73a2ada2d1b6dfd4",
"timestamp": "2026-02-19T12:17:17.237624786+01:00"
}
@@ -0,0 +1,20 @@
{
"id": "d89765966a967bdb8a426e6a",
"source": "http",
"type": "go-type",
"title": "http.type Handler ¶",
"content": "# type http.type Handler ¶\n\n```\ntype Handler interface {\n\tServeHTTP(ResponseWriter, *Request)\n}\n```\n\nA Handler responds to an HTTP request.",
"url": "https://pkg.go.dev/http#type Handler ¶",
"metadata": {
"fields": "null",
"import_path": "Standard library/net/http",
"kind": "type",
"method_count": 0,
"package": "http",
"symbol": "type Handler ¶",
"type_kind": "alias",
"underlying": "type Handler interface {\n\tServeHTTP(ResponseWriter, *Request)\n}"
},
"hash": "a9eb27cce9f1cdab670795312a80fdf0afba95c15d9a4bd397b9a153a6748b8a",
"timestamp": "2026-02-19T12:17:17.237637841+01:00"
}
@@ -0,0 +1,20 @@
{
"id": "f319abdf711953b5d29d9e80",
"source": "http",
"type": "go-type",
"title": "http.type HandlerFunc ¶",
"content": "# type http.type HandlerFunc ¶\n\n```\ntype HandlerFunc func(ResponseWriter, *Request)\n```\n\nThe HandlerFunc type is an adapter to allow the use of\nordinary functions as HTTP handlers. If f is a function\nwith the appropriate signature, HandlerFunc(f) is a\nHandler that calls f.",
"url": "https://pkg.go.dev/http#type HandlerFunc ¶",
"metadata": {
"fields": "null",
"import_path": "Standard library/net/http",
"kind": "type",
"method_count": 0,
"package": "http",
"symbol": "type HandlerFunc ¶",
"type_kind": "alias",
"underlying": "type HandlerFunc func(ResponseWriter, *Request)"
},
"hash": "635c5ee91697ca7a84a6093622ca0007aa90731b717c19ac91c705e2ee525f6b",
"timestamp": "2026-02-19T12:17:17.237641217+01:00"
}
@@ -0,0 +1,20 @@
{
"id": "ac652b55c3f3f91de97a97b3",
"source": "http",
"type": "go-type",
"title": "http.type Header ¶",
"content": "# type http.type Header ¶\n\n```\ntype Header map[string][]string\n```\n\nA Header represents the key-value pairs in an HTTP header.",
"url": "https://pkg.go.dev/http#type Header ¶",
"metadata": {
"fields": "null",
"import_path": "Standard library/net/http",
"kind": "type",
"method_count": 0,
"package": "http",
"symbol": "type Header ¶",
"type_kind": "alias",
"underlying": "type Header map[string][]string"
},
"hash": "6192aa8079de858ce4a451d33162edc1bdab4259fc23be785862989f2e73b8ed",
"timestamp": "2026-02-19T12:17:17.237644613+01:00"
}
@@ -0,0 +1,20 @@
{
"id": "afaad591ba9b08106128c9cb",
"source": "http",
"type": "go-type",
"title": "http.type Hijacker ¶",
"content": "# type http.type Hijacker ¶\n\n```\ntype Hijacker interface {\n\t// Hijack lets the caller take over the connection.\n\t// After a call to Hijack the HTTP server library\n\t// will not do anything else with the connection.\n\t//\n\t// It becomes the caller's responsibility to manage\n\t// and close the connection.\n\t//\n\t// The returned net.Conn may have read or write deadlines\n\t// already set, depending on the configuration of the\n\t// Server. It is the caller's responsibility to set\n\t// or clear those deadlines as needed.\n\t//\n\t// The returned bufio.Reader may contain unprocessed buffered\n\t// data from the client.\n\t//\n\t// After a call to Hijack, the original Request.Body must not\n\t// be used. The original Request's Context remains valid and\n\t// is not canceled until the Request's ServeHTTP method\n\t// returns.\n\tHijack() (net.Conn, *bufio.ReadWriter, error)\n}\n```\n\nThe Hijacker interface is implemented by ResponseWriters that allow\nan HTTP handler to take over the connection.",
"url": "https://pkg.go.dev/http#type Hijacker ¶",
"metadata": {
"fields": "null",
"import_path": "Standard library/net/http",
"kind": "type",
"method_count": 0,
"package": "http",
"symbol": "type Hijacker ¶",
"type_kind": "alias",
"underlying": "type Hijacker interface {\n\t// Hijack lets the caller take over the connection.\n\t// After a call to Hijack the HTTP server library\n\t// will not do anything else with the connection.\n\t//\n\t// It becomes the caller's responsibility to manage\n\t// and close the connection.\n\t//\n\t// The returned net.Conn may have read or write deadlines\n\t// already set, depending on the configuration of the\n\t// Server. It is the caller's responsibility to set\n\t// or clear those deadlines as needed.\n\t//\n\t// The returned bufio.Reader may contain unprocessed buffered\n\t// data from the client.\n\t//\n\t// After a call to Hijack, the original Request.Body must not\n\t// be used. The original Request's Context remains valid and\n\t// is not canceled until the Request's ServeHTTP method\n\t// returns.\n\tHijack() (net.Conn, *bufio.ReadWriter, error)\n}"
},
"hash": "8080e50adc630207c40b37752e6f0ea39f884d92f6aea45a00a817093e594c1a",
"timestamp": "2026-02-19T12:17:17.237648881+01:00"
}
@@ -0,0 +1,20 @@
{
"id": "eaa389b5a36c8ad46f339295",
"source": "http",
"type": "go-type",
"title": "http.type HTTP2Config ¶\n \n \n added in\n go1.24.0",
"content": "# type http.type HTTP2Config ¶\n \n \n added in\n go1.24.0\n\n```\ntype HTTP2Config struct {\n\t// MaxConcurrentStreams optionally specifies the number of\n\t// concurrent streams that a client may have open at a time.\n\t// If zero, MaxConcurrentStreams defaults to at least 100.\n\t//\n\t// This parameter only applies to Servers.\n\tMaxConcurrentStreams int\n\n\t// StrictMaxConcurrentRequests controls whether an HTTP/2 server's\n\t// concurrency limit should be respected across all connections\n\t// to that server.\n\t// If true, new requests sent when a connection's concurrency limit\n\t// has been exceeded will block until an existing request completes.\n\t// If false, an additional connection will be opened if all\n\t// existing connections are at their limit.\n\t//\n\t// This parameter only applies to Transports.\n\tStrictMaxConcurrentRequests bool\n\n\t// MaxDecoderHeaderTableSize optionally specifies an upper limit for the\n\t// size of the header compression table used for decoding headers sent\n\t// by the peer.\n\t// A valid value is less than 4MiB.\n\t// If zero or invalid, a default value is used.\n\tMaxDecoderHeaderTableSize int\n\n\t// MaxEncoderHeaderTableSize optionally specifies an upper limit for the\n\t// header compression table used for sending headers to the peer.\n\t// A valid value is less than 4MiB.\n\t// If zero or invalid, a default value is used.\n\tMaxEncoderHeaderTableSize int\n\n\t// MaxReadFrameSize optionally specifies the largest frame\n\t// this endpoint is willing to read.\n\t// A valid value is between 16KiB and 16MiB, inclusive.\n\t// If zero or invalid, a default value is used.\n\tMaxReadFrameSize int\n\n\t// MaxReceiveBufferPerConnection is the maximum size of the\n\t// flow control window for data received on a connection.\n\t// A valid value is at least 64KiB and less than 4MiB.\n\t// If invalid, a default value is used.\n\tMaxReceiveBufferPerConnection int\n\n\t// MaxReceiveBufferPerStream is the maximum size of\n\t// the flow control window for data received on a stream (request).\n\t// A valid value is less than 4MiB.\n\t// If zero or invalid, a default value is used.\n\tMaxReceiveBufferPerStream int\n\n\t// SendPingTimeout is the timeout after which a health check using a ping\n\t// frame will be carried out if no frame is received on a connection.\n\t// If zero, no health check is performed.\n\tSendPingTimeout time.Duration\n\n\t// PingTimeout is the timeout after which a connection will be closed\n\t// if a response to a ping is not received.\n\t// If zero, a default of 15 seconds is used.\n\tPingTimeout time.Duration\n\n\t// WriteByteTimeout is the timeout after which a connection will be\n\t// closed if no data can be written to it. The timeout begins when data is\n\t// available to write, and is extended whenever any bytes are written.\n\tWriteByteTimeout time.Duration\n\n\t// PermitProhibitedCipherSuites, if true, permits the use of\n\t// cipher suites prohibited by the HTTP/2 spec.\n\tPermitProhibitedCipherSuites bool\n\n\t// CountError, if non-nil, is called on HTTP/2 errors.\n\t// It is intended to increment a metric for monitoring.\n\t// The errType contains only lowercase letters, digits, and underscores\n\t// (a-z, 0-9, _).\n\tCountError func(errType string)\n}\n```\n\nHTTP2Config defines HTTP/2 configuration parameters common to\nboth Transport and Server.",
"url": "https://pkg.go.dev/http#type HTTP2Config ¶\n \n \n added in\n go1.24.0",
"metadata": {
"fields": "null",
"import_path": "Standard library/net/http",
"kind": "type",
"method_count": 0,
"package": "http",
"symbol": "type HTTP2Config ¶\n \n \n added in\n go1.24.0",
"type_kind": "alias",
"underlying": "type HTTP2Config struct {\n\t// MaxConcurrentStreams optionally specifies the number of\n\t// concurrent streams that a client may have open at a time.\n\t// If zero, MaxConcurrentStreams defaults to at least 100.\n\t//\n\t// This parameter only applies to Servers.\n\tMaxConcurrentStreams int\n\n\t// StrictMaxConcurrentRequests controls whether an HTTP/2 server's\n\t// concurrency limit should be respected across all connections\n\t// to that server.\n\t// If true, new requests sent when a connection's concurrency limit\n\t// has been exceeded will block until an existing request completes.\n\t// If false, an additional connection will be opened if all\n\t// existing connections are at their limit.\n\t//\n\t// This parameter only applies to Transports.\n\tStrictMaxConcurrentRequests bool\n\n\t// MaxDecoderHeaderTableSize optionally specifies an upper limit for the\n\t// size of the header compression table used for decoding headers sent\n\t// by the peer.\n\t// A valid value is less than 4MiB.\n\t// If zero or invalid, a default value is used.\n\tMaxDecoderHeaderTableSize int\n\n\t// MaxEncoderHeaderTableSize optionally specifies an upper limit for the\n\t// header compression table used for sending headers to the peer.\n\t// A valid value is less than 4MiB.\n\t// If zero or invalid, a default value is used.\n\tMaxEncoderHeaderTableSize int\n\n\t// MaxReadFrameSize optionally specifies the largest frame\n\t// this endpoint is willing to read.\n\t// A valid value is between 16KiB and 16MiB, inclusive.\n\t// If zero or invalid, a default value is used.\n\tMaxReadFrameSize int\n\n\t// MaxReceiveBufferPerConnection is the maximum size of the\n\t// flow control window for data received on a connection.\n\t// A valid value is at least 64KiB and less than 4MiB.\n\t// If invalid, a default value is used.\n\tMaxReceiveBufferPerConnection int\n\n\t// MaxReceiveBufferPerStream is the maximum size of\n\t// the flow control window for data received on a stream (request).\n\t// A valid value is less than 4MiB.\n\t// If zero or invalid, a default value is used.\n\tMaxReceiveBufferPerStream int\n\n\t// SendPingTimeout is the timeout after which a health check using a ping\n\t// frame will be carried out if no frame is received on a connection.\n\t// If zero, no health check is performed.\n\tSendPingTimeout time.Duration\n\n\t// PingTimeout is the timeout after which a connection will be closed\n\t// if a response to a ping is not received.\n\t// If zero, a default of 15 seconds is used.\n\tPingTimeout time.Duration\n\n\t// WriteByteTimeout is the timeout after which a connection will be\n\t// closed if no data can be written to it. The timeout begins when data is\n\t// available to write, and is extended whenever any bytes are written.\n\tWriteByteTimeout time.Duration\n\n\t// PermitProhibitedCipherSuites, if true, permits the use of\n\t// cipher suites prohibited by the HTTP/2 spec.\n\tPermitProhibitedCipherSuites bool\n\n\t// CountError, if non-nil, is called on HTTP/2 errors.\n\t// It is intended to increment a metric for monitoring.\n\t// The errType contains only lowercase letters, digits, and underscores\n\t// (a-z, 0-9, _).\n\tCountError func(errType string)\n}"
},
"hash": "7864ae7f72b7b73e1b55bd24e5f5b26b51483798009abdfbc3c19a74f7314a9c",
"timestamp": "2026-02-19T12:17:17.237634665+01:00"
}
@@ -0,0 +1,20 @@
{
"id": "55f8f665d9167a08131444fb",
"source": "http",
"type": "go-type",
"title": "http.type MaxBytesError ¶\n \n \n added in\n go1.19",
"content": "# type http.type MaxBytesError ¶\n \n \n added in\n go1.19\n\n```\ntype MaxBytesError struct {\n\tLimit int64\n}\n```\n\nMaxBytesError is returned by MaxBytesReader when its read limit is exceeded.",
"url": "https://pkg.go.dev/http#type MaxBytesError ¶\n \n \n added in\n go1.19",
"metadata": {
"fields": "null",
"import_path": "Standard library/net/http",
"kind": "type",
"method_count": 0,
"package": "http",
"symbol": "type MaxBytesError ¶\n \n \n added in\n go1.19",
"type_kind": "alias",
"underlying": "type MaxBytesError struct {\n\tLimit int64\n}"
},
"hash": "94416e66fca60ad9a19c7189926173f2e83161ce2655ee1ec93a4c4fc979102f",
"timestamp": "2026-02-19T12:17:17.237652198+01:00"
}
@@ -0,0 +1,20 @@
{
"id": "1158081e3dc0c20e6046a232",
"source": "http",
"type": "go-type",
"title": "http.type ProtocolError\n deprecated",
"content": "# type http.type ProtocolError\n deprecated\n\n```\ntype ProtocolError struct {\n\tErrorString string\n}\n```\n\nProtocolError represents an HTTP protocol error.",
"url": "https://pkg.go.dev/http#type ProtocolError\n deprecated",
"metadata": {
"fields": "null",
"import_path": "Standard library/net/http",
"kind": "type",
"method_count": 0,
"package": "http",
"symbol": "type ProtocolError\n deprecated",
"type_kind": "alias",
"underlying": "type ProtocolError struct {\n\tErrorString string\n}"
},
"hash": "7f2472f84d40a62bdc48ea8aeaf1fa42f9ab98e6d5a24b01b3408c9028d66eee",
"timestamp": "2026-02-19T12:17:17.237655774+01:00"
}
@@ -0,0 +1,20 @@
{
"id": "6e0bf7294be633d4de71ad64",
"source": "http",
"type": "go-type",
"title": "http.type Protocols ¶\n \n \n added in\n go1.24.0",
"content": "# type http.type Protocols ¶\n \n \n added in\n go1.24.0\n\n```\ntype Protocols struct {\n\t// contains filtered or unexported fields\n}\n```\n\nProtocols is a set of HTTP protocols.\nThe zero value is an empty set of protocols.",
"url": "https://pkg.go.dev/http#type Protocols ¶\n \n \n added in\n go1.24.0",
"metadata": {
"fields": "null",
"import_path": "Standard library/net/http",
"kind": "type",
"method_count": 0,
"package": "http",
"symbol": "type Protocols ¶\n \n \n added in\n go1.24.0",
"type_kind": "alias",
"underlying": "type Protocols struct {\n\t// contains filtered or unexported fields\n}"
},
"hash": "76c7a0bb2c138508d8ab135980a0936c5a7e133840690bd58aef0d1f149ef99c",
"timestamp": "2026-02-19T12:17:17.237659511+01:00"
}
@@ -0,0 +1,20 @@
{
"id": "727a366f7697bb54d5a596f9",
"source": "http",
"type": "go-type",
"title": "http.type Pusher ¶\n \n \n added in\n go1.8",
"content": "# type http.type Pusher ¶\n \n \n added in\n go1.8\n\n```\ntype Pusher interface {\n\t// Push initiates an HTTP/2 server push. This constructs a synthetic\n\t// request using the given target and options, serializes that request\n\t// into a PUSH_PROMISE frame, then dispatches that request using the\n\t// server's request handler. If opts is nil, default options are used.\n\t//\n\t// The target must either be an absolute path (like \"/path\") or an absolute\n\t// URL that contains a valid host and the same scheme as the parent request.\n\t// If the target is a path, it will inherit the scheme and host of the\n\t// parent request.\n\t//\n\t// The HTTP/2 spec disallows recursive pushes and cross-authority pushes.\n\t// Push may or may not detect these invalid pushes; however, invalid\n\t// pushes will be detected and canceled by conforming clients.\n\t//\n\t// Handlers that wish to push URL X should call Push before sending any\n\t// data that may trigger a request for URL X. This avoids a race where the\n\t// client issues requests for X before receiving the PUSH_PROMISE for X.\n\t//\n\t// Push will run in a separate goroutine making the order of arrival\n\t// non-deterministic. Any required synchronization needs to be implemented\n\t// by the caller.\n\t//\n\t// Push returns ErrNotSupported if the client has disabled push or if push\n\t// is not supported on the underlying connection.\n\tPush(target string, opts *PushOptions) error\n}\n```\n\nPusher is the interface implemented by ResponseWriters that support\nHTTP/2 server push. For more background, see\nhttps://tools.ietf.org/html/rfc7540#section-8.2.",
"url": "https://pkg.go.dev/http#type Pusher ¶\n \n \n added in\n go1.8",
"metadata": {
"fields": "null",
"import_path": "Standard library/net/http",
"kind": "type",
"method_count": 0,
"package": "http",
"symbol": "type Pusher ¶\n \n \n added in\n go1.8",
"type_kind": "alias",
"underlying": "type Pusher interface {\n\t// Push initiates an HTTP/2 server push. This constructs a synthetic\n\t// request using the given target and options, serializes that request\n\t// into a PUSH_PROMISE frame, then dispatches that request using the\n\t// server's request handler. If opts is nil, default options are used.\n\t//\n\t// The target must either be an absolute path (like \"/path\") or an absolute\n\t// URL that contains a valid host and the same scheme as the parent request.\n\t// If the target is a path, it will inherit the scheme and host of the\n\t// parent request.\n\t//\n\t// The HTTP/2 spec disallows recursive pushes and cross-authority pushes.\n\t// Push may or may not detect these invalid pushes; however, invalid\n\t// pushes will be detected and canceled by conforming clients.\n\t//\n\t// Handlers that wish to push URL X should call Push before sending any\n\t// data that may trigger a request for URL X. This avoids a race where the\n\t// client issues requests for X before receiving the PUSH_PROMISE for X.\n\t//\n\t// Push will run in a separate goroutine making the order of arrival\n\t// non-deterministic. Any required synchronization needs to be implemented\n\t// by the caller.\n\t//\n\t// Push returns ErrNotSupported if the client has disabled push or if push\n\t// is not supported on the underlying connection.\n\tPush(target string, opts *PushOptions) error\n}"
},
"hash": "120d1b769c3b9398d902b069005a7e509276e0539426f49cfac1ac6429ffbe41",
"timestamp": "2026-02-19T12:17:17.237668839+01:00"
}
@@ -0,0 +1,20 @@
{
"id": "7aee1cac85f3d68e65fbf34a",
"source": "http",
"type": "go-type",
"title": "http.type PushOptions ¶\n \n \n added in\n go1.8",
"content": "# type http.type PushOptions ¶\n \n \n added in\n go1.8\n\n```\ntype PushOptions struct {\n\t// Method specifies the HTTP method for the promised request.\n\t// If set, it must be \"GET\" or \"HEAD\". Empty means \"GET\".\n\tMethod string\n\n\t// Header specifies additional promised request headers. This cannot\n\t// include HTTP/2 pseudo header fields like \":path\" and \":scheme\",\n\t// which will be added automatically.\n\tHeader Header\n}\n```\n\nPushOptions describes options for [Pusher.Push].",
"url": "https://pkg.go.dev/http#type PushOptions ¶\n \n \n added in\n go1.8",
"metadata": {
"fields": "null",
"import_path": "Standard library/net/http",
"kind": "type",
"method_count": 0,
"package": "http",
"symbol": "type PushOptions ¶\n \n \n added in\n go1.8",
"type_kind": "alias",
"underlying": "type PushOptions struct {\n\t// Method specifies the HTTP method for the promised request.\n\t// If set, it must be \"GET\" or \"HEAD\". Empty means \"GET\".\n\tMethod string\n\n\t// Header specifies additional promised request headers. This cannot\n\t// include HTTP/2 pseudo header fields like \":path\" and \":scheme\",\n\t// which will be added automatically.\n\tHeader Header\n}"
},
"hash": "3501648577d9e7e96689a73221fe9e0eda05345a0c938c3a35c3870682288759",
"timestamp": "2026-02-19T12:17:17.237663549+01:00"
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,20 @@
{
"id": "72a399cde441352e0d00dc83",
"source": "http",
"type": "go-type",
"title": "http.type Response ¶",
"content": "# type http.type Response ¶\n\n```\ntype Response struct {\n\tStatus string // e.g. \"200 OK\"\n\tStatusCode int // e.g. 200\n\tProto string // e.g. \"HTTP/1.0\"\n\tProtoMajor int // e.g. 1\n\tProtoMinor int // e.g. 0\n\n\t// Header maps header keys to values. If the response had multiple\n\t// headers with the same key, they may be concatenated, with comma\n\t// delimiters. (RFC 7230, section 3.2.2 requires that multiple headers\n\t// be semantically equivalent to a comma-delimited sequence.) When\n\t// Header values are duplicated by other fields in this struct (e.g.,\n\t// ContentLength, TransferEncoding, Trailer), the field values are\n\t// authoritative.\n\t//\n\t// Keys in the map are canonicalized (see CanonicalHeaderKey).\n\tHeader Header\n\n\t// Body represents the response body.\n\t//\n\t// The response body is streamed on demand as the Body field\n\t// is read. If the network connection fails or the server\n\t// terminates the response, Body.Read calls return an error.\n\t//\n\t// The http Client and Transport guarantee that Body is always\n\t// non-nil, even on responses without a body or responses with\n\t// a zero-length body. It is the caller's responsibility to\n\t// close Body. The default HTTP client's Transport may not\n\t// reuse HTTP/1.x \"keep-alive\" TCP connections if the Body is\n\t// not read to completion and closed.\n\t//\n\t// The Body is automatically dechunked if the server replied\n\t// with a \"chunked\" Transfer-Encoding.\n\t//\n\t// As of Go 1.12, the Body will also implement io.Writer\n\t// on a successful \"101 Switching Protocols\" response,\n\t// as used by WebSockets and HTTP/2's \"h2c\" mode.\n\tBody io.ReadCloser\n\n\t// ContentLength records the length of the associated content. The\n\t// value -1 indicates that the length is unknown. Unless Request.Method\n\t// is \"HEAD\", values \u003e= 0 indicate that the given number of bytes may\n\t// be read from Body.\n\tContentLength int64\n\n\t// Contains transfer encodings from outer-most to inner-most. Value is\n\t// nil, means that \"identity\" encoding is used.\n\tTransferEncoding []string\n\n\t// Close records whether the header directed that the connection be\n\t// closed after reading Body. The value is advice for clients: neither\n\t// ReadResponse nor Response.Write ever closes a connection.\n\tClose bool\n\n\t// Uncompressed reports whether the response was sent compressed but\n\t// was decompressed by the http package. When true, reading from\n\t// Body yields the uncompressed content instead of the compressed\n\t// content actually set from the server, ContentLength is set to -1,\n\t// and the \"Content-Length\" and \"Content-Encoding\" fields are deleted\n\t// from the responseHeader. To get the original response from\n\t// the server, set Transport.DisableCompression to true.\n\tUncompressed bool\n\n\t// Trailer maps trailer keys to values in the same\n\t// format as Header.\n\t//\n\t// The Trailer initially contains only nil values, one for\n\t// each key specified in the server's \"Trailer\" header\n\t// value. Those values are not added to Header.\n\t//\n\t// Trailer must not be accessed concurrently with Read calls\n\t// on the Body.\n\t//\n\t// After Body.Read has returned io.EOF, Trailer will contain\n\t// any trailer values sent by the server.\n\tTrailer Header\n\n\t// Request is the request that was sent to obtain this Response.\n\t// Request's Body is nil (having already been consumed).\n\t// This is only populated for Client requests.\n\tRequest *Request\n\n\t// TLS contains information about the TLS connection on which the\n\t// response was received. It is nil for unencrypted responses.\n\t// The pointer is shared between responses and should not be\n\t// modified.\n\tTLS *tls.ConnectionState\n}\n```\n\nResponse represents the response from an HTTP request.",
"url": "https://pkg.go.dev/http#type Response ¶",
"metadata": {
"fields": "null",
"import_path": "Standard library/net/http",
"kind": "type",
"method_count": 0,
"package": "http",
"symbol": "type Response ¶",
"type_kind": "alias",
"underlying": "type Response struct {\n\tStatus string // e.g. \"200 OK\"\n\tStatusCode int // e.g. 200\n\tProto string // e.g. \"HTTP/1.0\"\n\tProtoMajor int // e.g. 1\n\tProtoMinor int // e.g. 0\n\n\t// Header maps header keys to values. If the response had multiple\n\t// headers with the same key, they may be concatenated, with comma\n\t// delimiters. (RFC 7230, section 3.2.2 requires that multiple headers\n\t// be semantically equivalent to a comma-delimited sequence.) When\n\t// Header values are duplicated by other fields in this struct (e.g.,\n\t// ContentLength, TransferEncoding, Trailer), the field values are\n\t// authoritative.\n\t//\n\t// Keys in the map are canonicalized (see CanonicalHeaderKey).\n\tHeader Header\n\n\t// Body represents the response body.\n\t//\n\t// The response body is streamed on demand as the Body field\n\t// is read. If the network connection fails or the server\n\t// terminates the response, Body.Read calls return an error.\n\t//\n\t// The http Client and Transport guarantee that Body is always\n\t// non-nil, even on responses without a body or responses with\n\t// a zero-length body. It is the caller's responsibility to\n\t// close Body. The default HTTP client's Transport may not\n\t// reuse HTTP/1.x \"keep-alive\" TCP connections if the Body is\n\t// not read to completion and closed.\n\t//\n\t// The Body is automatically dechunked if the server replied\n\t// with a \"chunked\" Transfer-Encoding.\n\t//\n\t// As of Go 1.12, the Body will also implement io.Writer\n\t// on a successful \"101 Switching Protocols\" response,\n\t// as used by WebSockets and HTTP/2's \"h2c\" mode.\n\tBody io.ReadCloser\n\n\t// ContentLength records the length of the associated content. The\n\t// value -1 indicates that the length is unknown. Unless Request.Method\n\t// is \"HEAD\", values \u003e= 0 indicate that the given number of bytes may\n\t// be read from Body.\n\tContentLength int64\n\n\t// Contains transfer encodings from outer-most to inner-most. Value is\n\t// nil, means that \"identity\" encoding is used.\n\tTransferEncoding []string\n\n\t// Close records whether the header directed that the connection be\n\t// closed after reading Body. The value is advice for clients: neither\n\t// ReadResponse nor Response.Write ever closes a connection.\n\tClose bool\n\n\t// Uncompressed reports whether the response was sent compressed but\n\t// was decompressed by the http package. When true, reading from\n\t// Body yields the uncompressed content instead of the compressed\n\t// content actually set from the server, ContentLength is set to -1,\n\t// and the \"Content-Length\" and \"Content-Encoding\" fields are deleted\n\t// from the responseHeader. To get the original response from\n\t// the server, set Transport.DisableCompression to true.\n\tUncompressed bool\n\n\t// Trailer maps trailer keys to values in the same\n\t// format as Header.\n\t//\n\t// The Trailer initially contains only nil values, one for\n\t// each key specified in the server's \"Trailer\" header\n\t// value. Those values are not added to Header.\n\t//\n\t// Trailer must not be accessed concurrently with Read calls\n\t// on the Body.\n\t//\n\t// After Body.Read has returned io.EOF, Trailer will contain\n\t// any trailer values sent by the server.\n\tTrailer Header\n\n\t// Request is the request that was sent to obtain this Response.\n\t// Request's Body is nil (having already been consumed).\n\t// This is only populated for Client requests.\n\tRequest *Request\n\n\t// TLS contains information about the TLS connection on which the\n\t// response was received. It is nil for unencrypted responses.\n\t// The pointer is shared between responses and should not be\n\t// modified.\n\tTLS *tls.ConnectionState\n}"
},
"hash": "8f2874a7b61ddfea454a98432582c1140dcba6d1788289d4da600a11898054d9",
"timestamp": "2026-02-19T12:17:17.237704696+01:00"
}
@@ -0,0 +1,20 @@
{
"id": "bb46edb5d76b53fb20982084",
"source": "http",
"type": "go-type",
"title": "http.type ResponseController ¶\n \n \n added in\n go1.20",
"content": "# type http.type ResponseController ¶\n \n \n added in\n go1.20\n\n```\ntype ResponseController struct {\n\t// contains filtered or unexported fields\n}\n```\n\nA ResponseController is used by an HTTP handler to control the response.",
"url": "https://pkg.go.dev/http#type ResponseController ¶\n \n \n added in\n go1.20",
"metadata": {
"fields": "null",
"import_path": "Standard library/net/http",
"kind": "type",
"method_count": 0,
"package": "http",
"symbol": "type ResponseController ¶\n \n \n added in\n go1.20",
"type_kind": "alias",
"underlying": "type ResponseController struct {\n\t// contains filtered or unexported fields\n}"
},
"hash": "36e2020f81ee2d06574acfddd1cd69f53aeb77e53332f65f2c0b31fe5eb7cef0",
"timestamp": "2026-02-19T12:17:17.237708223+01:00"
}
@@ -0,0 +1,20 @@
{
"id": "38daa09938ee5138c4c2f46c",
"source": "http",
"type": "go-type",
"title": "http.type ResponseWriter ¶",
"content": "# type http.type ResponseWriter ¶\n\n```\ntype ResponseWriter interface {\n\t// Header returns the header map that will be sent by\n\t// [ResponseWriter.WriteHeader]. The [Header] map also is the mechanism with which\n\t// [Handler] implementations can set HTTP trailers.\n\t//\n\t// Changing the header map after a call to [ResponseWriter.WriteHeader] (or\n\t// [ResponseWriter.Write]) has no effect unless the HTTP status code was of the\n\t// 1xx class or the modified headers are trailers.\n\t//\n\t// There are two ways to set Trailers. The preferred way is to\n\t// predeclare in the headers which trailers you will later\n\t// send by setting the \"Trailer\" header to the names of the\n\t// trailer keys which will come later. In this case, those\n\t// keys of the Header map are treated as if they were\n\t// trailers. See the example. The second way, for trailer\n\t// keys not known to the [Handler] until after the first [ResponseWriter.Write],\n\t// is to prefix the [Header] map keys with the [TrailerPrefix]\n\t// constant value.\n\t//\n\t// To suppress automatic response headers (such as \"Date\"), set\n\t// their value to nil.\n\tHeader() Header\n\n\t// Write writes the data to the connection as part of an HTTP reply.\n\t//\n\t// If [ResponseWriter.WriteHeader] has not yet been called, Write calls\n\t// WriteHeader(http.StatusOK) before writing the data. If the Header\n\t// does not contain a Content-Type line, Write adds a Content-Type set\n\t// to the result of passing the initial 512 bytes of written data to\n\t// [DetectContentType]. Additionally, if the total size of all written\n\t// data is under a few KB and there are no Flush calls, the\n\t// Content-Length header is added automatically.\n\t//\n\t// Depending on the HTTP protocol version and the client, calling\n\t// Write or WriteHeader may prevent future reads on the\n\t// Request.Body. For HTTP/1.x requests, handlers should read any\n\t// needed request body data before writing the response. Once the\n\t// headers have been flushed (due to either an explicit Flusher.Flush\n\t// call or writing enough data to trigger a flush), the request body\n\t// may be unavailable. For HTTP/2 requests, the Go HTTP server permits\n\t// handlers to continue to read the request body while concurrently\n\t// writing the response. However, such behavior may not be supported\n\t// by all HTTP/2 clients. Handlers should read before writing if\n\t// possible to maximize compatibility.\n\tWrite([]byte) (int, error)\n\n\t// WriteHeader sends an HTTP response header with the provided\n\t// status code.\n\t//\n\t// If WriteHeader is not called explicitly, the first call to Write\n\t// will trigger an implicit WriteHeader(http.StatusOK).\n\t// Thus explicit calls to WriteHeader are mainly used to\n\t// send error codes or 1xx informational responses.\n\t//\n\t// The provided code must be a valid HTTP 1xx-5xx status code.\n\t// Any number of 1xx headers may be written, followed by at most\n\t// one 2xx-5xx header. 1xx headers are sent immediately, but 2xx-5xx\n\t// headers may be buffered. Use the Flusher interface to send\n\t// buffered data. The header map is cleared when 2xx-5xx headers are\n\t// sent, but not with 1xx headers.\n\t//\n\t// The server will automatically send a 100 (Continue) header\n\t// on the first read from the request body if the request has\n\t// an \"Expect: 100-continue\" header.\n\tWriteHeader(statusCode int)\n}\n```\n\nA ResponseWriter interface is used by an HTTP handler to\nconstruct an HTTP response.",
"url": "https://pkg.go.dev/http#type ResponseWriter ¶",
"metadata": {
"fields": "null",
"import_path": "Standard library/net/http",
"kind": "type",
"method_count": 0,
"package": "http",
"symbol": "type ResponseWriter ¶",
"type_kind": "alias",
"underlying": "type ResponseWriter interface {\n\t// Header returns the header map that will be sent by\n\t// [ResponseWriter.WriteHeader]. The [Header] map also is the mechanism with which\n\t// [Handler] implementations can set HTTP trailers.\n\t//\n\t// Changing the header map after a call to [ResponseWriter.WriteHeader] (or\n\t// [ResponseWriter.Write]) has no effect unless the HTTP status code was of the\n\t// 1xx class or the modified headers are trailers.\n\t//\n\t// There are two ways to set Trailers. The preferred way is to\n\t// predeclare in the headers which trailers you will later\n\t// send by setting the \"Trailer\" header to the names of the\n\t// trailer keys which will come later. In this case, those\n\t// keys of the Header map are treated as if they were\n\t// trailers. See the example. The second way, for trailer\n\t// keys not known to the [Handler] until after the first [ResponseWriter.Write],\n\t// is to prefix the [Header] map keys with the [TrailerPrefix]\n\t// constant value.\n\t//\n\t// To suppress automatic response headers (such as \"Date\"), set\n\t// their value to nil.\n\tHeader() Header\n\n\t// Write writes the data to the connection as part of an HTTP reply.\n\t//\n\t// If [ResponseWriter.WriteHeader] has not yet been called, Write calls\n\t// WriteHeader(http.StatusOK) before writing the data. If the Header\n\t// does not contain a Content-Type line, Write adds a Content-Type set\n\t// to the result of passing the initial 512 bytes of written data to\n\t// [DetectContentType]. Additionally, if the total size of all written\n\t// data is under a few KB and there are no Flush calls, the\n\t// Content-Length header is added automatically.\n\t//\n\t// Depending on the HTTP protocol version and the client, calling\n\t// Write or WriteHeader may prevent future reads on the\n\t// Request.Body. For HTTP/1.x requests, handlers should read any\n\t// needed request body data before writing the response. Once the\n\t// headers have been flushed (due to either an explicit Flusher.Flush\n\t// call or writing enough data to trigger a flush), the request body\n\t// may be unavailable. For HTTP/2 requests, the Go HTTP server permits\n\t// handlers to continue to read the request body while concurrently\n\t// writing the response. However, such behavior may not be supported\n\t// by all HTTP/2 clients. Handlers should read before writing if\n\t// possible to maximize compatibility.\n\tWrite([]byte) (int, error)\n\n\t// WriteHeader sends an HTTP response header with the provided\n\t// status code.\n\t//\n\t// If WriteHeader is not called explicitly, the first call to Write\n\t// will trigger an implicit WriteHeader(http.StatusOK).\n\t// Thus explicit calls to WriteHeader are mainly used to\n\t// send error codes or 1xx informational responses.\n\t//\n\t// The provided code must be a valid HTTP 1xx-5xx status code.\n\t// Any number of 1xx headers may be written, followed by at most\n\t// one 2xx-5xx header. 1xx headers are sent immediately, but 2xx-5xx\n\t// headers may be buffered. Use the Flusher interface to send\n\t// buffered data. The header map is cleared when 2xx-5xx headers are\n\t// sent, but not with 1xx headers.\n\t//\n\t// The server will automatically send a 100 (Continue) header\n\t// on the first read from the request body if the request has\n\t// an \"Expect: 100-continue\" header.\n\tWriteHeader(statusCode int)\n}"
},
"hash": "a25d19197b587b899ad5ce2069c49fd98afbff959dd846ad59cfa38e1f8d1cfa",
"timestamp": "2026-02-19T12:17:17.237715276+01:00"
}
@@ -0,0 +1,20 @@
{
"id": "59a67309b3f0a7768121140b",
"source": "http",
"type": "go-type",
"title": "http.type RoundTripper ¶",
"content": "# type http.type RoundTripper ¶\n\n```\ntype RoundTripper interface {\n\t// RoundTrip executes a single HTTP transaction, returning\n\t// a Response for the provided Request.\n\t//\n\t// RoundTrip should not attempt to interpret the response. In\n\t// particular, RoundTrip must return err == nil if it obtained\n\t// a response, regardless of the response's HTTP status code.\n\t// A non-nil err should be reserved for failure to obtain a\n\t// response. Similarly, RoundTrip should not attempt to\n\t// handle higher-level protocol details such as redirects,\n\t// authentication, or cookies.\n\t//\n\t// RoundTrip should not modify the request, except for\n\t// consuming and closing the Request's Body. RoundTrip may\n\t// read fields of the request in a separate goroutine. Callers\n\t// should not mutate or reuse the request until the Response's\n\t// Body has been closed.\n\t//\n\t// RoundTrip must always close the body, including on errors,\n\t// but depending on the implementation may do so in a separate\n\t// goroutine even after RoundTrip returns. This means that\n\t// callers wanting to reuse the body for subsequent requests\n\t// must arrange to wait for the Close call before doing so.\n\t//\n\t// The Request's URL and Header fields must be initialized.\n\tRoundTrip(*Request) (*Response, error)\n}\n```\n\nRoundTripper is an interface representing the ability to execute a\nsingle HTTP transaction, obtaining the Response for a given Request.",
"url": "https://pkg.go.dev/http#type RoundTripper ¶",
"metadata": {
"fields": "null",
"import_path": "Standard library/net/http",
"kind": "type",
"method_count": 0,
"package": "http",
"symbol": "type RoundTripper ¶",
"type_kind": "alias",
"underlying": "type RoundTripper interface {\n\t// RoundTrip executes a single HTTP transaction, returning\n\t// a Response for the provided Request.\n\t//\n\t// RoundTrip should not attempt to interpret the response. In\n\t// particular, RoundTrip must return err == nil if it obtained\n\t// a response, regardless of the response's HTTP status code.\n\t// A non-nil err should be reserved for failure to obtain a\n\t// response. Similarly, RoundTrip should not attempt to\n\t// handle higher-level protocol details such as redirects,\n\t// authentication, or cookies.\n\t//\n\t// RoundTrip should not modify the request, except for\n\t// consuming and closing the Request's Body. RoundTrip may\n\t// read fields of the request in a separate goroutine. Callers\n\t// should not mutate or reuse the request until the Response's\n\t// Body has been closed.\n\t//\n\t// RoundTrip must always close the body, including on errors,\n\t// but depending on the implementation may do so in a separate\n\t// goroutine even after RoundTrip returns. This means that\n\t// callers wanting to reuse the body for subsequent requests\n\t// must arrange to wait for the Close call before doing so.\n\t//\n\t// The Request's URL and Header fields must be initialized.\n\tRoundTrip(*Request) (*Response, error)\n}"
},
"hash": "d35b501612374afacff901af3acaaeebcb22464fede772a24ca87a8ed9d95b34",
"timestamp": "2026-02-19T12:17:17.237721117+01:00"
}
@@ -0,0 +1,20 @@
{
"id": "6e17e053f41676cfb2e05afe",
"source": "http",
"type": "go-type",
"title": "http.type SameSite ¶\n \n \n added in\n go1.11",
"content": "# type http.type SameSite ¶\n \n \n added in\n go1.11\n\n```\ntype SameSite int\n```\n\nSameSite allows a server to define a cookie attribute making it impossible for\nthe browser to send this cookie along with cross-site requests. The main\ngoal is to mitigate the risk of cross-origin information leakage, and provide\nsome protection against cross-site request forgery attacks.",
"url": "https://pkg.go.dev/http#type SameSite ¶\n \n \n added in\n go1.11",
"metadata": {
"fields": "null",
"import_path": "Standard library/net/http",
"kind": "type",
"method_count": 0,
"package": "http",
"symbol": "type SameSite ¶\n \n \n added in\n go1.11",
"type_kind": "alias",
"underlying": "type SameSite int"
},
"hash": "67cb7e2fd7645491b24244e3b2ac8ccc0e6f08e35f63b6144cc73bf78008940c",
"timestamp": "2026-02-19T12:17:17.237724984+01:00"
}
@@ -0,0 +1,20 @@
{
"id": "b859d57fe3d630fbffa1fc40",
"source": "http",
"type": "go-type",
"title": "http.type ServeMux ¶",
"content": "# type http.type ServeMux ¶\n\n```\ntype ServeMux struct {\n\t// contains filtered or unexported fields\n}\n```\n\nServeMux is an HTTP request multiplexer.\nIt matches the URL of each incoming request against a list of registered\npatterns and calls the handler for the pattern that\nmost closely matches the URL.",
"url": "https://pkg.go.dev/http#type ServeMux ¶",
"metadata": {
"fields": "null",
"import_path": "Standard library/net/http",
"kind": "type",
"method_count": 0,
"package": "http",
"symbol": "type ServeMux ¶",
"type_kind": "alias",
"underlying": "type ServeMux struct {\n\t// contains filtered or unexported fields\n}"
},
"hash": "4b534032974377baeec93fe79e97e8a6035764602c7d35d5eed77a666da298d9",
"timestamp": "2026-02-19T12:17:17.23772822+01:00"
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,20 @@
{
"id": "26d6a069e77bbf769284ff2e",
"source": "3",
"type": "python-module",
"title": "Python 3.14.3 documentation - Python",
"content": "# Module Python 3.14.3 documentation\n\nWelcome! This is the official documentation for Python 3.14.3.\n\nWelcome! This is the official documentation for Python 3.14.3.",
"url": "https://docs.python.org/3/",
"metadata": {
"class_count": 0,
"data_count": 0,
"doc_url": "https://docs.python.org/3/",
"exception_count": 0,
"function_count": 0,
"name": "Python 3.14.3 documentation",
"path": "Python 3.14.3 documentation",
"version": ""
},
"hash": "7d7dcfddbc2ff30f4718393a83605872b2fac9e85bbb4b44d6368dfb7bc3e69e",
"timestamp": "2026-02-19T12:16:22.536161167+01:00"
}
@@ -0,0 +1,8 @@
# React API Reference
# React API Reference
Hooks: 0, Components: 1, APIs: 0
URL: https://react.dev/reference/react/hooks
@@ -0,0 +1,8 @@
# React Server Components
# <React Server Components />
URL: https://react.dev/reference/react/hooks#React Server Components
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,322 @@
{
"generated": "2026-02-19T13:43:23.127939093+01:00",
"project_path": "devour_data",
"language": "go",
"scorecard": {
"total_score": 708,
"strict_score": 708,
"target_score": 95,
"findings_by_type": {
"complexity": 3,
"duplication": 11
},
"findings_by_tier": {
"2": 3,
"3": 11
},
"status_by_type": {
"open": 14
},
"last_scan": "2026-02-19T13:36:36.230014073+01:00"
},
"findings": [
{
"id": "complexity-demo.go-35",
"type": "complexity",
"title": "High complexity detected",
"description": "File has complexity score of 35 with signals: 55 function calls",
"file": "cmd/demo.go",
"line": 1,
"severity": 2,
"score": 35,
"status": "open",
"needs_review": false,
"context": "This function may be difficult to maintain. Consider if it can be simplified or broken down.",
"metadata": {
"loc": "126",
"signals": "55 function calls"
}
},
{
"id": "complexity-quality.go-202",
"type": "complexity",
"title": "High complexity detected",
"description": "File has complexity score of 202 with signals: 222 function calls",
"file": "cmd/quality.go",
"line": 1,
"severity": 2,
"score": 202,
"status": "open",
"needs_review": false,
"context": "This function may be difficult to maintain. Consider if it can be simplified or broken down.",
"metadata": {
"loc": "685",
"signals": "222 function calls"
}
},
{
"id": "complexity-scrape.go-51",
"type": "complexity",
"title": "High complexity detected",
"description": "File has complexity score of 51 with signals: 71 function calls",
"file": "cmd/scrape.go",
"line": 1,
"severity": 2,
"score": 51,
"status": "open",
"needs_review": false,
"context": "This function may be difficult to maintain. Consider if it can be simplified or broken down.",
"metadata": {
"loc": "243",
"signals": "71 function calls"
}
},
{
"id": "duplication-cluster-0",
"type": "duplication",
"title": "Code duplication detected",
"description": "Found 2 similar functions with 0.80 similarity",
"file": "cmd/demo.go",
"line": 26,
"severity": 3,
"score": 4,
"status": "open",
"needs_review": true,
"context": "Similar code exists elsewhere. Consider extracting common functionality.",
"metadata": {
"cluster_size": "2",
"functions": "init:26,runDemo:30",
"similarity": "0.80"
}
},
{
"id": "duplication-cluster-1",
"type": "duplication",
"title": "Code duplication detected",
"description": "Found 2 similar functions with 0.80 similarity",
"file": "cmd/get.go",
"line": 40,
"severity": 3,
"score": 4,
"status": "open",
"needs_review": true,
"context": "Similar code exists elsewhere. Consider extracting common functionality.",
"metadata": {
"cluster_size": "2",
"functions": "init:40,runGet:47",
"similarity": "0.80"
}
},
{
"id": "duplication-cluster-2",
"type": "duplication",
"title": "Code duplication detected",
"description": "Found 2 similar functions with 0.80 similarity",
"file": "cmd/init.go",
"line": 31,
"severity": 3,
"score": 4,
"status": "open",
"needs_review": true,
"context": "Similar code exists elsewhere. Consider extracting common functionality.",
"metadata": {
"cluster_size": "2",
"functions": "init:31,runInit:36",
"similarity": "0.80"
}
},
{
"id": "duplication-cluster-3",
"type": "duplication",
"title": "Code duplication detected",
"description": "Found 2 similar functions with 0.80 similarity",
"file": "cmd/languages.go",
"line": 21,
"severity": 3,
"score": 4,
"status": "open",
"needs_review": true,
"context": "Similar code exists elsewhere. Consider extracting common functionality.",
"metadata": {
"cluster_size": "2",
"functions": "init:21,runLanguages:25",
"similarity": "0.80"
}
},
{
"id": "duplication-cluster-4",
"type": "duplication",
"title": "Code duplication detected",
"description": "Found 2 similar functions with 0.80 similarity",
"file": "cmd/quality.go",
"line": 100,
"severity": 3,
"score": 4,
"status": "open",
"needs_review": true,
"context": "Similar code exists elsewhere. Consider extracting common functionality.",
"metadata": {
"cluster_size": "2",
"functions": "init:100,runQualityScan:171",
"similarity": "0.80"
}
},
{
"id": "duplication-cluster-5",
"type": "duplication",
"title": "Code duplication detected",
"description": "Found 2 similar functions with 0.80 similarity",
"file": "cmd/quality.go",
"line": 219,
"severity": 3,
"score": 4,
"status": "open",
"needs_review": true,
"context": "Similar code exists elsewhere. Consider extracting common functionality.",
"metadata": {
"cluster_size": "2",
"functions": "runQualityStatus:219,runQualityNext:257",
"similarity": "0.80"
}
},
{
"id": "duplication-cluster-6",
"type": "duplication",
"title": "Code duplication detected",
"description": "Found 2 similar functions with 0.80 similarity",
"file": "cmd/quality.go",
"line": 424,
"severity": 3,
"score": 4,
"status": "open",
"needs_review": true,
"context": "Similar code exists elsewhere. Consider extracting common functionality.",
"metadata": {
"cluster_size": "2",
"functions": "outputScanResult:424,formatScanResultText:458",
"similarity": "0.80"
}
},
{
"id": "duplication-cluster-7",
"type": "duplication",
"title": "Code duplication detected",
"description": "Found 2 similar functions with 0.80 similarity",
"file": "cmd/quality.go",
"line": 602,
"severity": 3,
"score": 4,
"status": "open",
"needs_review": true,
"context": "Similar code exists elsewhere. Consider extracting common functionality.",
"metadata": {
"cluster_size": "2",
"functions": "runQualityReview:602,prepareReviewPacket:616",
"similarity": "0.80"
}
},
{
"id": "duplication-cluster-8",
"type": "duplication",
"title": "Code duplication detected",
"description": "Found 2 similar functions with 0.80 similarity",
"file": "cmd/root.go",
"line": 29,
"severity": 3,
"score": 4,
"status": "open",
"needs_review": true,
"context": "Similar code exists elsewhere. Consider extracting common functionality.",
"metadata": {
"cluster_size": "2",
"functions": "Execute:29,init:36",
"similarity": "0.80"
}
},
{
"id": "duplication-cluster-9",
"type": "duplication",
"title": "Code duplication detected",
"description": "Found 2 similar functions with 0.80 similarity",
"file": "cmd/scrape.go",
"line": 57,
"severity": 3,
"score": 4,
"status": "open",
"needs_review": true,
"context": "Similar code exists elsewhere. Consider extracting common functionality.",
"metadata": {
"cluster_size": "2",
"functions": "init:57,runScrape:65",
"similarity": "0.80"
}
},
{
"id": "duplication-cluster-10",
"type": "duplication",
"title": "Code duplication detected",
"description": "Found 2 similar functions with 0.80 similarity",
"file": "cmd/scrape.go",
"line": 168,
"severity": 3,
"score": 4,
"status": "open",
"needs_review": true,
"context": "Similar code exists elsewhere. Consider extracting common functionality.",
"metadata": {
"cluster_size": "2",
"functions": "scrapeFromConfig:168,detectSourceType:172",
"similarity": "0.80"
}
}
],
"context": {
"total_files": 0,
"total_loc": 0,
"findings_by_dimension": {
"Code Quality": 3,
"Duplication": 11
},
"top_issues": [
"complexity: High complexity detected",
"complexity: High complexity detected",
"complexity: High complexity detected",
"duplication: Code duplication detected",
"duplication: Code duplication detected"
],
"trends": {}
},
"questions": [
{
"id": "dupe_strategy",
"category": "duplication",
"question": "How should duplicated code be consolidated?",
"options": [
"Extract to shared utility",
"Keep separate (different use cases)",
"Refactor to common interface"
]
},
{
"id": "complexity_strategy",
"category": "complexity",
"question": "What's the best approach for complex functions?",
"options": [
"Break into smaller functions",
"Introduce helper types",
"Accept current complexity"
]
},
{
"id": "priority",
"category": "planning",
"question": "Which area should be prioritized for improvement?",
"options": [
"Security issues first",
"Complexity reduction",
"Dead code cleanup",
"Architecture improvements"
]
}
]
}
File diff suppressed because it is too large Load Diff
+1153
View File
File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 74 KiB

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More