mirror of
https://github.com/Dvorinka/Devour.git
synced 2026-06-03 20:13:03 +00:00
first commit
This commit is contained in:
@@ -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
@@ -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"]
|
||||||
@@ -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.
|
||||||
@@ -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 ':'
|
||||||
@@ -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
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
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>
|
||||||
@@ -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.*
|
||||||
@@ -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
@@ -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
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/yourorg/devour/cmd"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cmd.Execute()
|
||||||
|
}
|
||||||
+136
@@ -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
@@ -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)
|
||||||
|
}
|
||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
}
|
||||||
@@ -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")
|
||||||
|
}
|
||||||
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
Submodule
+1
Submodule desloppify/desloppify added at ad2f456292
@@ -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
@@ -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)*
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
@@ -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
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
Reference in New Issue
Block a user