-
Notifications
You must be signed in to change notification settings - Fork 0
feat: add LLM-friendly commands (env info, project doctor, project explain, project scaffold) #39
Description
Summary
DevForge currently manages repositories, project lifecycle, and Docker — but lacks capabilities that help LLMs and developers quickly understand, diagnose, and bootstrap projects. This issue proposes four new commands that make DevForge a tool LLMs can invoke to reason about codebases.
This was identified by comparing DevForge with Lino, a .NET-specific scaffolding CLI. While Lino's code generation features are too framework-specific to port, the gap in project introspection and structured output for machine consumption is clear. All four commands support a --json flag for LLM-consumable structured output.
Motivation
- LLM-friendliness: An LLM working on a codebase can run
dev project explain . --jsonto instantly understand architecture, entry points, dependencies, and patterns — without reading dozens of files. - Developer onboarding:
dev project doctoranddev project explainreduce the time to understand a new project from minutes to seconds. - Project bootstrapping:
dev project scaffoldbrings the existing Claude Code scaffolding skills into the CLI, making them available to any LLM or automation. - Environment debugging:
dev env info --jsongives an LLM (or developer) a complete picture of installed tools and auth status in one command.
Implementation Order and Dependencies
Feature 1: dev env info (simplest, introduces --json output pattern)
│
v
Feature 2: dev project doctor (reuses JSON pattern, introduces ProjectChecker interface)
│
v
Feature 3: dev project explain (reuses ProjectChecker, adds architecture analysis)
│
v
Feature 4: dev project scaffold (most complex, uses embedded templates)
Features 1 and 2 are mostly independent and could be implemented in parallel. Feature 3 reuses ProjectChecker from Feature 2. Feature 4 is fully independent but benefits from patterns established by the first three.
Feature 1: dev env info
Purpose: Dump all installed tooling versions, Git provider auth status, and shell info in one structured output. An LLM debugging "why won't this build" can run this first.
Usage
dev env info # human-readable output
dev env info --json # structured JSON for LLM consumptionExample JSON Output
{
"devforge_version": "0.5.0",
"tools": [
{"name": "go", "version": "1.23.0", "installed": true},
{"name": "node", "version": "20.11.0", "installed": true},
{"name": "python3", "version": "3.12.1", "installed": true},
{"name": "java", "version": "", "installed": false},
{"name": "docker", "version": "24.0.7", "installed": true},
{"name": "git", "version": "2.43.0", "installed": true},
{"name": "make", "version": "4.4.1", "installed": true},
{"name": "terraform", "version": "", "installed": false}
],
"auth": [
{"provider": "github", "env_var": "GH_TOKEN", "set": true},
{"provider": "azuredevops", "env_var": "AZURE_DEVOPS_EXT_PAT", "set": false},
{"provider": "gitlab", "env_var": "GITLAB_TOKEN", "set": false}
],
"shell": "/bin/zsh"
}New Files
| File | Purpose |
|---|---|
internal/env/runner.go |
ToolRunner interface + DefaultToolRunner (wraps exec.Command for version checks) |
internal/env/info.go |
RunInfo function, InfoConfig, EnvReport, ToolStatus, AuthStatus types |
internal/env/info_test.go |
BDD parallel tests |
internal/testutil/doubles/tool_runner_stub.go |
Test double for ToolRunner |
Key Interfaces
// ToolRunner abstracts external tool version detection for testability.
type ToolRunner interface {
Version(tool string, args ...string) (string, error)
EnvVar(name string) string
}Design Details
- Mapper from tool name to version-check arguments (no switch/case):
var toolVersionArgs = map[string][]string{ "go": {"version"}, "node": {"--version"}, "python3": {"--version"}, "java": {"-version"}, "docker": {"--version"}, "git": {"--version"}, "make": {"--version"}, "terraform": {"version"}, }
- Auth token check uses env vars:
GH_TOKEN,AZURE_DEVOPS_EXT_PAT,GITLAB_TOKEN - CLI wiring: new
envCmdcommand group inmain.gowithnewEnvInfoCmd()factory
Feature 2: dev project doctor [path]
Purpose: Run project health diagnostics with structured pass/warn/fail results. An LLM can parse the output to identify and fix issues automatically.
Usage
dev project doctor . # human-readable with status symbols
dev project doctor . --json # structured JSONExample JSON Output
{
"language": "go",
"checks": [
{"category": "sdk", "name": "SDK installed", "status": "pass", "detail": "go 1.23.0"},
{"category": "tools", "name": "make", "status": "pass"},
{"category": "tools", "name": "docker", "status": "pass"},
{"category": "tools", "name": "git", "status": "pass"},
{"category": "makefile", "name": "target: lint", "status": "pass"},
{"category": "makefile", "name": "target: test", "status": "pass"},
{"category": "makefile", "name": "target: sast", "status": "warn", "detail": "target not found in Makefile"},
{"category": "docs", "name": "README.md", "status": "pass"},
{"category": "docs", "name": "CHANGELOG.md", "status": "pass"},
{"category": "docs", "name": "CONTRIBUTING.md", "status": "fail", "detail": "file not found"},
{"category": "ci", "name": "CI/CD config", "status": "pass", "detail": ".github/workflows/"}
],
"summary": {"pass": 9, "warn": 1, "fail": 1}
}New Files
| File | Purpose |
|---|---|
internal/project/checker.go |
ProjectChecker interface + DefaultProjectChecker |
internal/project/doctor.go |
RunDoctor function, DoctorConfig, CheckResult, DoctorReport types |
internal/project/doctor_test.go |
BDD parallel tests |
internal/testutil/doubles/project_checker_stub.go |
Test double for ProjectChecker |
Key Interfaces
// ProjectChecker abstracts filesystem checks for testability.
type ProjectChecker interface {
FileExists(path string) bool
ReadFile(path string) (string, error)
ToolExists(name string) bool
}Health Checks (mapper pattern — slice of check functions, no switch/case)
| Check | Category | Logic |
|---|---|---|
| SDK installed | sdk |
info.CurrentVersion != "" |
| Required tools | tools |
checker.ToolExists("make"), "docker", "git" |
| Makefile targets | makefile |
Read Makefile, regex for lint:, test:, sast:, build: |
| Documentation | docs |
checker.FileExists("README.md"), CHANGELOG.md, CONTRIBUTING.md |
| CI/CD config | ci |
Check .github/workflows/ or .gitlab-ci.yml or azure-pipelines.yml |
Feature 3: dev project explain [path]
Purpose: Machine-readable project analysis. This is the highest-value LLM feature — an LLM runs this to instantly understand a codebase's architecture, entry points, dependencies, and patterns before making changes.
Usage
dev project explain . # human-readable sections
dev project explain . --json # structured JSON for LLM consumptionExample JSON Output
{
"language": "go",
"sdk_version": "1.23.0",
"required_version": "1.23",
"dependencies": [
{"name": "github.com/spf13/cobra", "version": "v1.8.0", "source_file": "go.mod"},
{"name": "github.com/sirupsen/logrus", "version": "v1.9.3", "source_file": "go.mod"},
{"name": "github.com/rios0rios0/langforge", "version": "v0.3.0", "source_file": "go.mod"},
{"name": "github.com/rios0rios0/gitforge", "version": "v0.2.0", "source_file": "go.mod"}
],
"structure": {
"layers": [
{"name": "cmd", "path": "cmd/devforge", "type": "cmd"},
{"name": "repo", "path": "internal/repo", "type": "internal"},
{"name": "project", "path": "internal/project", "type": "internal"},
{"name": "docker", "path": "internal/docker", "type": "internal"},
{"name": "testutil", "path": "internal/testutil", "type": "test"}
],
"entry_points": ["cmd/devforge/main.go"],
"patterns": ["Clean Architecture", "Dependency Injection", "Mapper Pattern"],
"file_tree": [
{"path": "cmd/devforge", "is_dir": true, "layer": "cmd"},
{"path": "internal/repo", "is_dir": true, "layer": "internal"},
{"path": "internal/project", "is_dir": true, "layer": "internal"},
{"path": "internal/docker", "is_dir": true, "layer": "internal"},
{"path": "internal/testutil/doubles", "is_dir": true, "layer": "test"},
{"path": "internal/testutil/builders", "is_dir": true, "layer": "test"}
]
}
}New Files
| File | Purpose |
|---|---|
internal/project/analyzer.go |
ProjectAnalyzer interface + DefaultProjectAnalyzer |
internal/project/explain.go |
RunExplain function, ExplainConfig, ExplainReport, DependencyInfo types |
internal/project/explain_test.go |
BDD parallel tests |
internal/project/analyzer_test.go |
Tests for analysis logic |
internal/testutil/doubles/project_analyzer_stub.go |
Test double for ProjectAnalyzer |
Key Interfaces
// ProjectAnalyzer abstracts project structure analysis for testability.
type ProjectAnalyzer interface {
Analyze(repoPath string) (*ProjectStructure, error)
}Analysis Logic
- Layer detection (mapper):
{"domain": "domain", "infrastructure": "infrastructure", "cmd": "cmd", "internal": "internal", "test": "test", "pkg": "pkg", "src": "src", "lib": "lib"} - Pattern detection: Clean Architecture =
domain/+infrastructure/present; DDD =entities/+commands/inside domain; CQRS = separate command/query directories - Entry points (mapper per language):
{"go": ["main.go"], "node": ["index.ts", "index.js"], "python": ["main.py", "__main__.py", "app.py"]} - Dependencies: parse
go.modrequire block,package.jsondependencies,pyproject.tomldependencies - File tree: walk directory (max depth 3), annotate each dir with its detected layer
Feature 4: dev project scaffold [language] [path]
Purpose: Language-agnostic project scaffolding following Clean Architecture. Brings the existing Claude Code scaffolding skills into the CLI so any LLM or automation can bootstrap projects.
Usage
dev project scaffold go ./my-project --name my-project --module github.com/org/my-project
dev project scaffold python ./my-api --name my-api
dev project scaffold node ./my-app --name my-appSupported Languages
| Language | Aliases | Generated Structure |
|---|---|---|
| Go | go |
cmd/<name>/main.go, internal/domain/{entities,commands,repositories}/, internal/infrastructure/{controllers,repositories}/, Makefile, go.mod |
| Python | python |
src/<name>/domain/{entities,commands,repositories}/, src/<name>/infrastructure/{controllers,repositories}/, tests/, pyproject.toml, Makefile |
| Node/TS | node, ts, typescript |
src/domain/{entities,commands,repositories}/, src/infrastructure/{controllers,repositories}/, tests/, package.json, tsconfig.json, Makefile |
All languages also get: README.md, CHANGELOG.md, CONTRIBUTING.md, .gitignore, .editorconfig
New Files
| File | Purpose |
|---|---|
internal/project/scaffold.go |
RunScaffold function, ScaffoldConfig type |
internal/project/templates.go |
TemplateRenderer interface + DefaultTemplateRenderer using embed.FS |
internal/project/templates/go/** |
Go project template files |
internal/project/templates/python/** |
Python project template files |
internal/project/templates/node/** |
Node/TypeScript project template files |
internal/project/templates/common/** |
Shared templates (README, CHANGELOG, etc.) |
internal/project/scaffold_test.go |
BDD parallel tests |
internal/testutil/doubles/template_renderer_stub.go |
Test double for TemplateRenderer |
Key Interfaces
type TemplateRenderer interface {
Render(templatePath string, data TemplateData) (string, error)
ListTemplates(language string) ([]string, error)
}Design Details
- Language dispatch via mapper (no switch/case):
{"go": "go", "python": "python", "node": "node", "ts": "node", "typescript": "node"} - Embedded templates via
//go:embed templates/*+ Gotext/template - Generates Clean Architecture structure (domain/infrastructure split)
- Target dir must be empty or non-existent;
--forceflag to override - CLI flags:
--name(project name),--module(module path, e.g., Go module or npm scope)
Files Modified
| File | Change |
|---|---|
cmd/devforge/main.go |
Add envCmd group + 4 new command factory functions (newEnvInfoCmd, newProjectDoctorCmd, newProjectExplainCmd, newProjectScaffoldCmd) |
CHANGELOG.md |
Add 4 entries under [Unreleased] > Added |
CLAUDE.md |
Update Usage and Architecture sections |
README.md |
Add new commands to usage section |
Design Constraints
These constraints come from the existing codebase patterns:
- Interface-driven testability: all external operations behind interfaces, stubs in
internal/testutil/doubles/using the fluent builder pattern (NewStub().WithError(err)) - No switch/case: all dispatch uses mapper pattern (maps of string → value/function)
- BDD tests:
// given,// when,// thenblocks, parallel viat.Parallel()+t.Run() - Output convention: status/log to
cfg.Output(stderr) vialogf(), machine output to stdout - Config structs: each operation gets a config struct with injected dependencies (see
project.Configpattern)
Verification Checklist
After each feature:
-
make build— binary compiles -
make test— all tests pass (new + existing) -
make lint— no lint errors - Manual smoke test of the new command
-
--jsonoutput is valid JSON (parseable byjq)
Final integration:
-
dev env info --json | jq .works -
dev project doctor . --json | jq .works -
dev project explain . --json | jq .works -
dev project scaffold go /tmp/test-project --name test-project --module github.com/test/test-projectcreates expected files - All documentation updated (CHANGELOG, CLAUDE.md, README)