mirror of
https://github.com/coollabsio/coolify-cli.git
synced 2026-06-19 07:35:04 +00:00
feat(docs): generate quick llms.txt and full llms-full.txt
Refactor `coolify docs llms` to emit two AI-oriented artifacts: - `llms.txt` as a concise operating guide - `llms-full.txt` as the exhaustive command and flag catalog Update tests to cover quick/full generation, document both files in README, and adjust CI to fail when either generated file is out of date.
This commit is contained in:
@@ -49,10 +49,10 @@ jobs:
|
||||
run: go run ./coolify docs llms
|
||||
|
||||
- name: Check uncommitted changes
|
||||
run: git diff --exit-code llms.txt
|
||||
run: git diff --exit-code llms.txt llms-full.txt
|
||||
|
||||
- if: failure()
|
||||
run: echo "::error::llms.txt is out of date. Run 'go run ./coolify docs llms' and commit the changes."
|
||||
run: echo "::error::llms.txt or llms-full.txt is out of date. Run 'go run ./coolify docs llms' and commit the changes."
|
||||
|
||||
go-mod-tidy:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -72,4 +72,4 @@ jobs:
|
||||
run: git diff --exit-code
|
||||
|
||||
- if: failure()
|
||||
run: echo "::error::Check failed, please run 'go mod tidy' and commit the changes."
|
||||
run: echo "::error::Check failed, please run 'go mod tidy' and commit the changes."
|
||||
|
||||
@@ -64,6 +64,16 @@ This will install the `coolify` binary in your `$GOPATH/bin` directory (usually
|
||||
|
||||
Now you can use the CLI with the token you just added.
|
||||
|
||||
## For LLMs / AI agents
|
||||
|
||||
- Quick instructions: [`llms.txt`](./llms.txt)
|
||||
- Full command catalog: [`llms-full.txt`](./llms-full.txt)
|
||||
- Regenerate both files:
|
||||
|
||||
```bash
|
||||
go run ./coolify docs llms
|
||||
```
|
||||
|
||||
## Change default context
|
||||
You can change the default context with `coolify context use <context_name>` or `coolify context set-default <context_name>`
|
||||
## Currently Supported Commands
|
||||
|
||||
+215
-24
@@ -90,41 +90,168 @@ The markdown files will be written to the specified directory (default: ./docs).
|
||||
|
||||
var llmsCmd = &cobra.Command{
|
||||
Use: "llms",
|
||||
Short: "Generate llms.txt for AI agent command specification",
|
||||
Long: `Generate a machine-readable llms.txt file that defines all CLI commands and their parameters.
|
||||
Short: "Generate llms.txt and llms-full.txt for AI agents",
|
||||
Long: `Generate AI-friendly documentation files for the Coolify CLI.
|
||||
|
||||
This file is intended to enable AI agents to understand and interact with the CLI.
|
||||
The output file will be written to the specified path (default: ./llms.txt).`,
|
||||
This creates a concise llms.txt quick reference plus a complete llms-full.txt command catalog.
|
||||
The output files will be written to the specified paths (defaults: ./llms.txt and ./llms-full.txt).`,
|
||||
Example: ` coolify docs llms
|
||||
coolify docs llms --output=./llms.txt`,
|
||||
coolify docs llms --output=./llms.txt --full-output=./llms-full.txt`,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
outputFile, _ := cmd.Flags().GetString("output")
|
||||
fullOutputFile, _ := cmd.Flags().GetString("full-output")
|
||||
|
||||
var sb strings.Builder
|
||||
sb.WriteString(llmsIntro)
|
||||
writeLLMsAliases(&sb, rootCmd, "coolify")
|
||||
sb.WriteString(llmsBody)
|
||||
writeLLMsCommand(&sb, rootCmd, "coolify")
|
||||
|
||||
if err := os.WriteFile(outputFile, []byte(sb.String()), 0600); err != nil {
|
||||
return fmt.Errorf("failed to write llms.txt: %w", err)
|
||||
}
|
||||
|
||||
absPath, _ := filepath.Abs(outputFile)
|
||||
fmt.Printf("llms.txt generated successfully: %s\n", absPath)
|
||||
|
||||
return nil
|
||||
return writeLLMsArtifacts(outputFile, fullOutputFile)
|
||||
},
|
||||
}
|
||||
|
||||
// llmsIntro contains the static overview section prepended to the generated command reference.
|
||||
const llmsIntro = `# Coolify CLI - llms.txt
|
||||
const llmsQuickTemplate = `# Coolify CLI - llms.txt
|
||||
|
||||
> A CLI tool for interacting with the Coolify API, built with Go.
|
||||
> Quick AI/LLM instructions for the Coolify CLI.
|
||||
> Source: https://github.com/coollabsio/coolify-cli
|
||||
> API Spec: https://github.com/coollabsio/coolify/blob/v4.x/openapi.json
|
||||
|
||||
## Operating Rules
|
||||
|
||||
- Prefer ` + "`--format json`" + ` for automation and parsing.
|
||||
- Use Coolify UUIDs for resources; do not use internal numeric IDs.
|
||||
- Team commands are the exception: they use numeric team IDs.
|
||||
- Authenticate with a saved context when possible; use ` + "`--token`" + ` only for overrides.
|
||||
- Use ` + "`llms-full.txt`" + ` for the exhaustive command/flag catalog.
|
||||
|
||||
## Installation
|
||||
|
||||
` + "```bash" + `
|
||||
# Linux/macOS (recommended)
|
||||
curl -fsSL https://raw.githubusercontent.com/coollabsio/coolify-cli/main/scripts/install.sh | bash
|
||||
|
||||
# Homebrew (macOS/Linux)
|
||||
brew install coollabsio/coolify-cli/coolify-cli
|
||||
|
||||
# Windows (PowerShell)
|
||||
irm https://raw.githubusercontent.com/coollabsio/coolify-cli/main/scripts/install.ps1 | iex
|
||||
|
||||
# Go install
|
||||
go install github.com/coollabsio/coolify-cli/coolify@latest
|
||||
` + "```" + `
|
||||
|
||||
## Authentication
|
||||
|
||||
1. Get an API token from your Coolify dashboard at ` + "`/security/api-tokens`" + `
|
||||
2. For Coolify Cloud: ` + "`coolify context set-token cloud <token>`" + `
|
||||
3. For self-hosted: ` + "`coolify context add -d <context_name> <url> <token>`" + `
|
||||
4. Switch contexts with ` + "`coolify context use <context_name>`" + `
|
||||
|
||||
## Configuration
|
||||
|
||||
Config file location:
|
||||
- Linux/macOS: ` + "`~/.config/coolify/config.json`" + `
|
||||
- Windows: ` + "`%%APPDATA%%\\coolify\\config.json`" + `
|
||||
|
||||
Supports multiple contexts (instances) with ` + "`coolify context`" + ` commands.
|
||||
|
||||
## Output Formats
|
||||
|
||||
All commands support ` + "`--format`" + ` flag:
|
||||
- ` + "`table`" + ` (default) - human-readable tabular output
|
||||
- ` + "`json`" + ` - compact JSON for scripting
|
||||
- ` + "`pretty`" + ` - indented JSON for debugging
|
||||
|
||||
## Global Flags
|
||||
|
||||
- ` + "`--context <name>`" + ` - use a specific saved context
|
||||
- ` + "`--token <token>`" + ` - override token from config
|
||||
- ` + "`--format table|json|pretty`" + ` - choose output format
|
||||
- ` + "`--show-sensitive`" + ` - reveal sensitive values
|
||||
- ` + "`--debug`" + ` - enable debug output
|
||||
|
||||
## Common Workflows
|
||||
|
||||
### Contexts
|
||||
|
||||
` + "```bash" + `
|
||||
coolify context list
|
||||
coolify context verify
|
||||
coolify context version
|
||||
coolify context use prod
|
||||
` + "```" + `
|
||||
|
||||
### Inventory
|
||||
|
||||
` + "```bash" + `
|
||||
coolify server list
|
||||
coolify project list
|
||||
coolify resource list
|
||||
coolify app list
|
||||
coolify service list
|
||||
coolify database list
|
||||
` + "```" + `
|
||||
|
||||
### Applications
|
||||
|
||||
` + "```bash" + `
|
||||
coolify app get <uuid>
|
||||
coolify app start <uuid>
|
||||
coolify app stop <uuid>
|
||||
coolify app restart <uuid>
|
||||
coolify app logs <uuid> --follow
|
||||
coolify app deployments list <app-uuid>
|
||||
coolify app deployments logs <app-uuid> --follow
|
||||
` + "```" + `
|
||||
|
||||
### Environment Variables
|
||||
|
||||
` + "```bash" + `
|
||||
coolify app env list <app-uuid>
|
||||
coolify app env create <app-uuid> --key API_KEY --value secret123
|
||||
coolify app env update <app-uuid> <env-uuid-or-key> --value new-secret
|
||||
coolify app env sync <app-uuid> --file .env.production --build-time --preview
|
||||
` + "```" + `
|
||||
|
||||
### Deployments
|
||||
|
||||
` + "```bash" + `
|
||||
coolify deploy list
|
||||
coolify deploy name my-application
|
||||
coolify deploy batch api,worker,frontend --force
|
||||
coolify deploy cancel <deployment-uuid>
|
||||
` + "```" + `
|
||||
|
||||
### Databases and Services
|
||||
|
||||
` + "```bash" + `
|
||||
coolify database get <uuid>
|
||||
coolify database create postgresql --server-uuid <uuid> --project-uuid <uuid> --environment-name production
|
||||
coolify database backup list <database-uuid>
|
||||
coolify service get <uuid>
|
||||
coolify service create <type> --project-uuid <uuid> --server-uuid <uuid> --instant-deploy
|
||||
` + "```" + `
|
||||
|
||||
## Common Aliases
|
||||
|
||||
- ` + "`coolify app`" + ` | ` + "`coolify apps`" + ` | ` + "`coolify application`" + ` | ` + "`coolify applications`" + `
|
||||
- ` + "`coolify service`" + ` | ` + "`coolify services`" + ` | ` + "`coolify svc`" + `
|
||||
- ` + "`coolify database`" + ` | ` + "`coolify databases`" + ` | ` + "`coolify db`" + ` | ` + "`coolify dbs`" + `
|
||||
- ` + "`coolify teams`" + ` | ` + "`coolify team`" + `
|
||||
|
||||
## Full Reference
|
||||
|
||||
- Full command and parameter catalog: %s
|
||||
- Regenerate docs: ` + "`go run ./coolify docs llms`" + `
|
||||
`
|
||||
|
||||
const llmsFullIntro = `# Coolify CLI - llms-full.txt
|
||||
|
||||
> Full AI/LLM command catalog for the Coolify CLI.
|
||||
> Manage Coolify instances (cloud and self-hosted), servers, projects, applications, databases, services, deployments, domains, and private keys.
|
||||
> Source: https://github.com/coollabsio/coolify-cli
|
||||
> API Spec: https://github.com/coollabsio/coolify/blob/v4.x/openapi.json
|
||||
|
||||
## Companion Files
|
||||
|
||||
- Quick instructions: %s
|
||||
- Regenerate docs: ` + "`go run ./coolify docs llms`" + `
|
||||
|
||||
## Installation
|
||||
|
||||
` + "```bash" + `
|
||||
@@ -151,7 +278,7 @@ go install github.com/coollabsio/coolify-cli/coolify@latest
|
||||
|
||||
Config file location:
|
||||
- Linux/macOS: ` + "`~/.config/coolify/config.json`" + `
|
||||
- Windows: ` + "`%APPDATA%\\coolify\\config.json`" + `
|
||||
- Windows: ` + "`%%APPDATA%%\\coolify\\config.json`" + `
|
||||
|
||||
Supports multiple contexts (instances) with ` + "`coolify context`" + ` commands.
|
||||
|
||||
@@ -163,7 +290,7 @@ All commands support ` + "`--format`" + ` flag:
|
||||
- ` + "`pretty`" + ` - indented JSON for debugging
|
||||
`
|
||||
|
||||
const llmsBody = `
|
||||
const llmsFullBody = `
|
||||
|
||||
## Supported Database Types
|
||||
|
||||
@@ -254,6 +381,69 @@ coolify team members list
|
||||
|
||||
`
|
||||
|
||||
func buildQuickLLMSText(fullReferencePath string) string {
|
||||
return fmt.Sprintf(llmsQuickTemplate, fullReferencePath)
|
||||
}
|
||||
|
||||
func buildFullLLMSText(quickReferencePath string) string {
|
||||
var sb strings.Builder
|
||||
fmt.Fprintf(&sb, llmsFullIntro, quickReferencePath)
|
||||
writeLLMsAliases(&sb, rootCmd, "coolify")
|
||||
sb.WriteString(llmsFullBody)
|
||||
writeLLMsCommand(&sb, rootCmd, "coolify")
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func writeLLMsArtifacts(outputFile, fullOutputFile string) error {
|
||||
if filepath.Clean(outputFile) == filepath.Clean(fullOutputFile) {
|
||||
return fmt.Errorf("output and full-output must be different files")
|
||||
}
|
||||
|
||||
if err := ensureParentDir(outputFile); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ensureParentDir(fullOutputFile); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
quickReferencePath := llmsReferencePath(fullOutputFile, outputFile)
|
||||
fullReferencePath := llmsReferencePath(outputFile, fullOutputFile)
|
||||
|
||||
if err := os.WriteFile(outputFile, []byte(buildQuickLLMSText(fullReferencePath)), 0600); err != nil {
|
||||
return fmt.Errorf("failed to write llms.txt: %w", err)
|
||||
}
|
||||
if err := os.WriteFile(fullOutputFile, []byte(buildFullLLMSText(quickReferencePath)), 0600); err != nil {
|
||||
return fmt.Errorf("failed to write llms-full.txt: %w", err)
|
||||
}
|
||||
|
||||
absQuickPath, _ := filepath.Abs(outputFile)
|
||||
absFullPath, _ := filepath.Abs(fullOutputFile)
|
||||
fmt.Printf("llms.txt generated successfully: %s\n", absQuickPath)
|
||||
fmt.Printf("llms-full.txt generated successfully: %s\n", absFullPath)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ensureParentDir(path string) error {
|
||||
parentDir := filepath.Dir(path)
|
||||
if err := os.MkdirAll(parentDir, 0750); err != nil {
|
||||
return fmt.Errorf("failed to create output directory: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func llmsReferencePath(fromFile, toFile string) string {
|
||||
referencePath, err := filepath.Rel(filepath.Dir(fromFile), toFile)
|
||||
if err != nil {
|
||||
return filepath.ToSlash(toFile)
|
||||
}
|
||||
referencePath = filepath.ToSlash(referencePath)
|
||||
if strings.HasPrefix(referencePath, ".") || strings.HasPrefix(referencePath, "/") {
|
||||
return referencePath
|
||||
}
|
||||
return "./" + referencePath
|
||||
}
|
||||
|
||||
// writeLLMsAliases writes aliases derived from the Cobra command tree.
|
||||
func writeLLMsAliases(sb *strings.Builder, cmd *cobra.Command, parentPath string) {
|
||||
aliases := collectLLMsAliases(cmd, parentPath)
|
||||
@@ -431,6 +621,7 @@ func NewDocsCommand() *cobra.Command {
|
||||
manCmd.Flags().StringP("output-dir", "o", "./man", "Output directory for man pages")
|
||||
markdownCmd.Flags().StringP("output-dir", "o", "./docs", "Output directory for markdown files")
|
||||
llmsCmd.Flags().StringP("output", "o", "./llms.txt", "Output file path")
|
||||
llmsCmd.Flags().String("full-output", "./llms-full.txt", "Full output file path")
|
||||
|
||||
return docsCmd
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -66,3 +68,51 @@ func TestWriteLLMsAliasesUsesCommandTree(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildQuickLLMSTextIncludesCoreGuidance(t *testing.T) {
|
||||
got := buildQuickLLMSText("./llms-full.txt")
|
||||
|
||||
for _, want := range []string{
|
||||
"# Coolify CLI - llms.txt",
|
||||
"Prefer `--format json` for automation and parsing.",
|
||||
"coolify context verify",
|
||||
"coolify app logs <uuid> --follow",
|
||||
"Full command and parameter catalog: ./llms-full.txt",
|
||||
} {
|
||||
if !strings.Contains(got, want) {
|
||||
t.Fatalf("expected quick llms output to contain %q\nfull output:\n%s", want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteLLMsArtifactsWritesQuickAndFullFiles(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
quickPath := filepath.Join(tempDir, "llms.txt")
|
||||
fullPath := filepath.Join(tempDir, "nested", "llms-full.txt")
|
||||
|
||||
if err := writeLLMsArtifacts(quickPath, fullPath); err != nil {
|
||||
t.Fatalf("writeLLMsArtifacts() error = %v", err)
|
||||
}
|
||||
|
||||
quickContent, err := os.ReadFile(quickPath)
|
||||
if err != nil {
|
||||
t.Fatalf("failed reading quick file: %v", err)
|
||||
}
|
||||
fullContent, err := os.ReadFile(fullPath)
|
||||
if err != nil {
|
||||
t.Fatalf("failed reading full file: %v", err)
|
||||
}
|
||||
|
||||
for _, want := range []struct {
|
||||
content string
|
||||
substr string
|
||||
}{
|
||||
{string(quickContent), "./nested/llms-full.txt"},
|
||||
{string(fullContent), "../llms.txt"},
|
||||
{string(fullContent), "## Command Reference"},
|
||||
} {
|
||||
if !strings.Contains(want.content, want.substr) {
|
||||
t.Fatalf("expected generated content to contain %q", want.substr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2255
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user