docs: rewrite README with "Why spec-forge?" section and fix unclosed code fence#17
Conversation
…ence fix Co-authored-by: spencercjh <29922079+spencercjh@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR rewrites the README to add a "Why spec-forge?" section that explains the concrete pain points solved by the tool (fragmented gRPC tooling, swaggo annotation burden for Gin, undocumented CloudWeGo tools, go-zero goctl bugs), updates the one-liner, reorganizes existing content for better flow, and fixes an unclosed code fence bug.
Changes:
- Added a new "Why spec-forge?" section with per-framework pain-point explanations, an LLM comparison, and an AI Agent angle
- Reorganized sections (Supported Frameworks table moved before Framework-Specific Usage, Configuration/LLM Providers moved after framework details), expanded Quick Start with examples for all four frameworks, and added callout blockquotes in Gin and gRPC sections
- Fixed a missing closing
```fence in the "Publishing to ReadMe.com" section
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
- Change google/gnostic status from 'abandoned' to 'inactive' - Update Hertz/Kitex docs with hertz-contrib/swagger-generate references - Remove unconfirmed go-zero bug mentions - Split framework details into separate docs to reduce README size - Add docs/spring-boot.md, gin.md, go-zero.md, grpc-protoc.md, hertz.md, kitex.md Signed-off-by: spencercjh <spencercjh@gmail.com>
Review Summary by QodoRewrite README with framework pain points and extract framework docs
WalkthroughsDescription• Rewrote README with "Why spec-forge?" section explaining pain points in OpenAPI generation across frameworks • Added comprehensive framework-specific documentation files (Gin, gRPC, go-zero, Spring Boot, Hertz, Kitex) • Moved framework details to separate docs to reduce README size and improve maintainability • Enhanced Quick Start section with examples for all four supported frameworks • Added "Zero annotations for Gin" feature highlight and improved positioning against competing tools • Fixed unclosed code fence in Publishing to ReadMe.com section Diagramflowchart LR
A["README.md<br/>Original"] -->|"Add Why section<br/>& framework callouts"| B["README.md<br/>Refactored"]
A -->|"Extract framework<br/>details"| C["docs/gin.md"]
A -->|"Extract framework<br/>details"| D["docs/grpc-protoc.md"]
A -->|"Extract framework<br/>details"| E["docs/go-zero.md"]
A -->|"Extract framework<br/>details"| F["docs/spring-boot.md"]
A -->|"Extract framework<br/>details"| G["docs/hertz.md"]
A -->|"Extract framework<br/>details"| H["docs/kitex.md"]
B -->|"Link to"| C
B -->|"Link to"| D
B -->|"Link to"| E
B -->|"Link to"| F
File Changes1. README.md
|
Code Review by Qodo
1.
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Fix incorrect 'goctl-swagger' to 'goctl' - Fix incorrect 'goctl swagger' to 'goctl api swagger' to match implementation Signed-off-by: spencercjh <spencercjh@gmail.com>
Clarify that spec-forge prefers ./mvnw / ./gradlew over system binaries, including support for parent directory wrappers in multi-module projects. Signed-off-by: spencercjh <spencercjh@gmail.com>
Use OPENAI_API_KEY instead of LLM_API_KEY when --provider openai is specified, to match the CLI's provider-specific environment variable requirements. Signed-off-by: spencercjh <spencercjh@gmail.com>
Remove invalid --enrich, --provider, and --model flags from generate command examples. The generate command only supports --language for enrichment configuration; enrichment is triggered via .spec-forge.yaml or automatically when configured. --provider and --model only exist on the enrich subcommand. Signed-off-by: spencercjh <spencercjh@gmail.com>
…code fence (#17) * Initial plan * docs: rewrite README with Why section, framework callouts, and code fence fix Co-authored-by: spencercjh <29922079+spencercjh@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * docs: refine README and add framework-specific documentation - Change google/gnostic status from 'abandoned' to 'inactive' - Update Hertz/Kitex docs with hertz-contrib/swagger-generate references - Remove unconfirmed go-zero bug mentions - Split framework details into separate docs to reduce README size - Add docs/spring-boot.md, gin.md, go-zero.md, grpc-protoc.md, hertz.md, kitex.md Signed-off-by: spencercjh <spencercjh@gmail.com> * docs: fix go-zero tooling/command names - Fix incorrect 'goctl-swagger' to 'goctl' - Fix incorrect 'goctl swagger' to 'goctl api swagger' to match implementation Signed-off-by: spencercjh <spencercjh@gmail.com> * docs: add wrapper priority note to spring-boot.md Clarify that spec-forge prefers ./mvnw / ./gradlew over system binaries, including support for parent directory wrappers in multi-module projects. Signed-off-by: spencercjh <spencercjh@gmail.com> * docs: fix OpenAI API key environment variable in examples Use OPENAI_API_KEY instead of LLM_API_KEY when --provider openai is specified, to match the CLI's provider-specific environment variable requirements. Signed-off-by: spencercjh <spencercjh@gmail.com> * docs: fix generate command enrichment examples Remove invalid --enrich, --provider, and --model flags from generate command examples. The generate command only supports --language for enrichment configuration; enrichment is triggered via .spec-forge.yaml or automatically when configured. --provider and --model only exist on the enrich subcommand. Signed-off-by: spencercjh <spencercjh@gmail.com> --------- Signed-off-by: spencercjh <spencercjh@gmail.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: spencercjh <29922079+spencercjh@users.noreply.github.com> Co-authored-by: Spencer Cai <spencercjh@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Review feedback addressed: - CustomProvider: use p.name instead of hardcoded CustomProviderName in error messages - StreamWriter: optimize bytes.Contains to bytes.IndexByte for hot path - E2E tests: handle enrich.enabled=false config, pass --custom-api-key-env for custom provider - Design doc: fix StreamWriter file path, update Stream to *bool type, correct prefix casing Fixes review comments #11, #12, #13, #16, #17 on PR #57 Signed-off-by: caijiahao <caijh@inesa.com> Signed-off-by: spencercjh <spencercjh@gmail.com>
* docs: add Phase 4.1 Streaming design document Design P4.1: Real-time streaming support for LLM enrichment Key features: - Option pattern for Provider interface (WithStreamingFunc) - StreamWriter for thread-safe output with batch prefix - Concurrent processing with mutex-protected output - CLI flag --no-stream to disable streaming (enabled by default) Signed-off-by: spencercjh <spencercjh@gmail.com> * docs: add P4.1 Streaming implementation plan 8 tasks with TDD approach: 1. Provider interface refactoring (Option pattern) 2. StreamWriter implementation 3. Provider streaming implementation 4. BatchProcessor streaming integration 5. Enricher streaming support 6. CLI integration (--no-stream flag) 7. Integration testing 8. Final verification Signed-off-by: spencercjh <spencercjh@gmail.com> * feat(provider): add Option pattern for streaming support - Add GenerateOptions, Option, WithStreamingFunc, applyOptions - Update Provider interface to accept ...Option - Update all provider implementations with new signature (streaming logic in next commit) Signed-off-by: spencercjh <spencercjh@gmail.com> * feat(processor): add StreamWriter for thread-safe streaming output - StreamWriter with mutex-protected writes - WriteWithPrefix adds batch type prefix - Tested for concurrent access safety Signed-off-by: spencercjh <spencercjh@gmail.com> * feat(provider): add streaming support to all providers - Replace GenerateFromSinglePrompt with GenerateContent - Pass StreamingFunc to langchaingo via llms.WithStreamingFunc - Return content from response.Choices for non-streaming callers Signed-off-by: spencercjh <spencercjh@gmail.com> * feat(processor): integrate streaming into BatchProcessor - Add StreamWriter field and WithStreamWriter option - Pass streaming callback to provider with batch type prefix - Update NewBatchProcessor to accept functional options Task 4 of P4.1 streaming implementation Signed-off-by: spencercjh <spencercjh@gmail.com> * feat(enricher): add Stream option to EnrichOptions - Add Stream (bool) and Writer (io.Writer) to EnrichOptions - Create StreamWriter when streaming is enabled - Default: streaming enabled to os.Stdout Signed-off-by: spencercjh <spencercjh@gmail.com> * feat(cli): add --no-stream flag to enrich command - Streaming enabled by default - Use --no-stream to disable and wait for complete response Signed-off-by: spencercjh <spencercjh@gmail.com> * test(enricher): add streaming integration test - Test that streaming callback is invoked - Test that StreamWriter output contains batch prefix [api] Signed-off-by: spencercjh <spencercjh@gmail.com> * style(enricher): fix whitespace alignment Signed-off-by: spencercjh <spencercjh@gmail.com> * fix(batch): update streaming function to ignore context parameter Signed-off-by: spencercjh <spencercjh@gmail.com> * fix(processor): add nil check to NewStreamWriter Prevent runtime panic by validating writer parameter is not nil. Add test to verify panic behavior with nil writer. Signed-off-by: spencercjh <spencercjh@gmail.com> * test(enricher): add test for Stream: false path Add TestEnricher_WithStreamingDisabled to verify that no streaming callback is passed to the provider when Stream option is disabled. Signed-off-by: spencercjh <spencercjh@gmail.com> * test(processor): add streaming callback verification in batch tests Add TestBatchProcessor_ProcessBatch_WithStreaming to verify that streaming callback is properly invoked when StreamWriter is configured. Signed-off-by: spencercjh <spencercjh@gmail.com> * feat(processor): add chunk buffering to StreamWriter - Add buffer accumulation for streaming chunks - Auto-flush on newline, buffer threshold, or prefix change - Add WithFlushThreshold option for configurable buffering - Add Flush method for explicit buffer clearing - Update enricher to flush StreamWriter after processing - Add tests for buffering behavior Signed-off-by: spencercjh <spencercjh@gmail.com> * feat(processor): add streaming debug logging and metrics - Add StreamWriterMetrics struct to track streaming statistics - Add GetMetrics() method for retrieving metrics - Add WithDebug option for enabling debug logging - Add WithFlushThreshold option for configurable buffering - Track total chunks, bytes, flushes, and unique prefixes - Debug logging shows chunk details and flush events Signed-off-by: spencercjh <spencercjh@gmail.com> * style: fix formatting issues from golangci-lint Signed-off-by: spencercjh <spencercjh@gmail.com> * docs: clarify make verify usage in CLAUDE.md Explain that make verify checks for uncommitted changes and should only be used after committing. Recommend individual commands (make fmt, lint, test) before committing to avoid false failures from git diff check. Signed-off-by: spencercjh <spencercjh@gmail.com> * test(e2e): add conditional enrich E2E tests with streaming verification - Add skipIfNoConfig helper to skip tests without valid E2E config - Check for .spec-forge.e2e.local.yaml or .spec-forge.local.yaml - Verify API key environment variable is set before running - Test streaming output with prefix verification ([api], [schema], [param]) - Test --no-stream flag disables streaming prefixes - Test local config file loading mechanism - Add .spec-forge.e2e.example.yaml as configuration template - Update .gitignore to exclude E2E local configs Signed-off-by: spencercjh <spencercjh@gmail.com> * test(e2e): add conditional enrich E2E tests with streaming verification - Add loadE2EConfig helper to load config from integration-tests/.spec-forge.e2e.local.yaml - Skip tests gracefully if no valid config found (file missing or API key env not set) - Add TestE2E_Enrich_NoStreamFlag to verify --no-stream disables streaming prefixes - Add TestE2E_Enrich_WithStreaming to test real LLM enrichment with streaming output - Add TestE2E_Enrich_WithLocalConfig to test local config file loading - Add .spec-forge.e2e.example.yaml as configuration template - Update .gitignore to exclude integration-tests/.spec-forge.e2e.local.yaml - Document E2E enrich test setup in integration-tests/README.md Signed-off-by: spencercjh <spencercjh@gmail.com> * test(e2e): fix enrich E2E tests for streaming verification - Remove streaming prefix check in stdout (StreamWriter writes to os.Stdout directly, not Cobra's buffer) - Remove TestE2E_Enrich_WithLocalConfig (tests Viper config loading, not enrich logic) - Verify enrichment by checking the output spec file contains descriptions - Update README to document only the two streaming-related tests - All tests pass: - TestE2E_Enrich_Help: PASS - TestE2E_Enrich_MissingArgs: PASS - TestE2E_Enrich_NonExistentFile: PASS - TestE2E_Enrich_NoStreamFlag: SKIP (requires config) - TestE2E_Enrich_WithStreaming: PASS (with config) / SKIP (without config) Signed-off-by: spencercjh <spencercjh@gmail.com> * fix(provider): satisfy goconst in anthropic and custom providers Agent-Logs-Url: https://github.com/spencercjh/spec-forge/sessions/be20b0d1-0dc2-45c4-adc4-b2bec21a9a95 Co-authored-by: spencercjh <29922079+spencercjh@users.noreply.github.com> * fix(review): address PR review feedback for P4.1 streaming - Change EnrichOptions.Stream to *bool (tri-state) to avoid backwards compatibility issues with zero value of bool (false). Now nil means use default (true), explicit false/true overrides. - Add error return when LLM providers return empty choices to prevent silent enrichment failures (OpenAI, Anthropic, Ollama, Custom). - Fix comment in batch.go to reflect lowercase prefix values. - Remove stale TestE2E_Enrich_WithLocalConfig entry from README table. - Make E2E test assertion language-agnostic (check for non-empty descriptions instead of specific Chinese text). Signed-off-by: spencercjh <spencercjh@gmail.com> * fix(review): address additional PR review feedback for P4.1 streaming Review feedback addressed: - CustomProvider: use p.name instead of hardcoded CustomProviderName in error messages - StreamWriter: optimize bytes.Contains to bytes.IndexByte for hot path - E2E tests: handle enrich.enabled=false config, pass --custom-api-key-env for custom provider - Design doc: fix StreamWriter file path, update Stream to *bool type, correct prefix casing Fixes review comments #11, #12, #13, #16, #17 on PR #57 Signed-off-by: caijiahao <caijh@inesa.com> Signed-off-by: spencercjh <spencercjh@gmail.com> * fix(review): address third round of Copilot review feedback for P4.1 Review feedback addressed: - factory.go: use AnthropicProviderName and CustomProviderName constants instead of string literals - design doc: update EnrichOptions summary to reflect actual API (Stream *bool, Writer io.Writer) - design doc: fix data flow example to use lowercase prefix [api] instead of [API] - stream_writer.go: validate WithFlushThreshold to use DefaultFlushThreshold for zero/negative values - e2e_enrich_test.go: pass --timeout flag when configured in E2E config Fixes review comments #21-26 on PR #57 Signed-off-by: caijiahao <caijh@inesa.com> Signed-off-by: spencercjh <spencercjh@gmail.com> * fix(review): address fourth round of Copilot review feedback Review feedback addressed: - e2e_enrich_test.go: change Enabled to *bool to distinguish between "not set" (nil, run tests) and "explicitly false" (skip tests) - README.md: update skip message path to match actual output - batch.go: add Flush() call after each LLM call to ensure buffered streaming output is visible for short responses Note: #30 (streaming to os.Stdout) is intentional design for real-time terminal feedback and not changed. Fixes review comments #27-29 on PR #57 Signed-off-by: caijiahao <caijh@inesa.com> Signed-off-by: spencercjh <spencercjh@gmail.com> * fix(review): address fifth round of Copilot review feedback Review feedback addressed: - e2e_enrich_test.go: match API key env var logic to CLI behavior (OPENAI_API_KEY, ANTHROPIC_API_KEY, or LLM_API_KEY for custom) - e2e_enrich_test.go: use YAML parsing for provider-agnostic assertions instead of language-specific substring matching - design doc: update output example to show actual raw JSON streaming behavior instead of hypothetical human-readable text Note: #32 (streaming writes to os.Stdout) is intentional design for real-time terminal feedback - already addressed in #30 response. Fixes review comments #31, #33, #34 on PR #57 Signed-off-by: caijiahao <caijh@inesa.com> Signed-off-by: spencercjh <spencercjh@gmail.com> * fix(enricher,stream_writer): improve error handling and metrics updates Signed-off-by: spencercjh <spencercjh@gmail.com> * feat(batch): add streaming support and improve processing logic Signed-off-by: spencercjh <spencercjh@gmail.com> * fix(review): address Copilot review feedback - streaming, concurrency, docs - Remove redundant atomic ops in StreamWriter, use mutex-only sync - Handle short writes in flushLocked() - Change final Flush error to warning (streaming is ancillary) - Streaming mode processes batches sequentially for readable output - --no-stream enables concurrent processing (--concurrency applies) - Update flag descriptions, config comments, design doc, CLAUDE.md Signed-off-by: spencercjh <spencercjh@gmail.com> --------- Signed-off-by: spencercjh <spencercjh@gmail.com> Signed-off-by: caijiahao <caijh@inesa.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: spencercjh <29922079+spencercjh@users.noreply.github.com>
The README read like an API reference with no explanation of why spec-forge exists — the real pain points (abandoned gRPC tooling, swaggo annotation burden, go-zero bugs) were invisible to new visitors.
Changes
New "Why spec-forge?" section
google/gnostic(abandoned),grpc-gateway(Swagger 2.0 only), andbuf(no official OpenAPI docs) — explains whyprotoc-gen-connect-openapiwas chosenswaggo/swagannotation problem (compiler-unvalidated, manual sync) vs. spec-forge's zero-annotation AST approachgoctl swaggerbugs are patched internallyUpdated one-liner
Structural improvements
Bug fix
```fence in the### Publishing to ReadMe.comsection that left the code block unclosed before## LicenseOriginal prompt
Background
The current README is functional but too neutral — it reads like an API reference rather than a compelling pitch. Many developers question the value of spec-forge, but the project actually solves very real, concrete pain points that are not communicated anywhere in the current README.
After a deep discussion about the project's positioning, the following key insights emerged that should be reflected in the README:
Core Pain Points spec-forge Solves (that are NOT in the current README)
1. gRPC/Protobuf ecosystem is fragmented and poorly documented:
google/gnostic'sprotoc-gen-openapiis abandoned/unmaintained — yet still referenced by most tutorialsgrpc-gateway'sprotoc-gen-openapiv2only outputs Swagger 2.0, not OpenAPI 3.xbufhas no official documentation for OpenAPI generation — developers rely entirely on third-party blogs2. Gin requires verbose godoc annotations (swaggo pain):
swaggo/swag) requires writing// @Summary,// @Param,// @Successetc. for every handler3. Hertz and Kitex (CloudWeGo ecosystem) have hidden, undocumented tooling:
4. go-zero's
goctl swaggerhas known bugs:5. AI-enriched specs are fundamentally different from "AI-generated" specs:
6. The AI Agent era makes high-quality OpenAPI specs increasingly critical:
What Needs to Change in README.md
Please rewrite the
README.mdwith the following structure. Preserve all existing technical content (configuration, commands, framework-specific usage, etc.) — only add and reorganize, don't remove working documentation.New README Structure:
Title + Badges (keep as-is)
One-liner — upgrade from the current neutral description to something that leads with the problem:
NEW: "Why spec-forge?" section — this is the most important addition. Must include:
A clear statement that generating OpenAPI from backend code is harder than it should be, with a concrete breakdown by ecosystem:
google/gnosticis abandoned,grpc-gatewayonly outputs Swagger 2.0,bufhas no official OpenAPI docs (only community blogs), leaving developers lostswaggorequires hundreds of verbose// @swaggerannotations that the compiler never validates — spec-forge uses AST analysis requiring zero annotationsgoctl swaggerhas known bugs that spec-forge patches internallyA "vs AI-generated docs" explanation:
A mention of the AI Agent angle:
Features (keep existing 4 bullets, consider adding one about "zero annotation" for Gin)
How It Works (keep existing mermaid diagram + step descriptions)
Installation (keep as-is)
Quick Start (keep as-is, but make the examples span more frameworks — not just Spring Boot)
Supported Frameworks (keep existing table)
Framework-Specific Usage — keep all existing content, but add a clear callout for Gin explaining the "no annotations needed" benefit at the top of...
This pull request was created from Copilot chat.
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.