Files
coder/scripts/clidocgen/main.go
T
Kyle Carberry edee917d88 feat: add experimental agents support (#22290)
feat: add AI chat system with agent tools and chat UI

Introduce the chatd subsystem and Agents UI for AI-powered chat
within Coder workspaces.

- Add chatd package with chat loop, message compaction, prompt
  management, and LLM provider integration (OpenAI, Anthropic)
- Add agent tools: create workspace, list/read templates, read/write/
  edit files, execute commands
- Add chat API endpoints with streaming, message editing, and
  durable reconnection
- Add database schema and migrations for chats, chat messages, chat
  providers, and chat model configs
- Add RBAC policies and dbauthz enforcement for chat resources
- Add Agents UI pages with conversation timeline, queued messages
  list, diff viewer, and model configuration panel
- Add comprehensive test coverage including coderd integration tests,
  chatd unit tests, and Storybook stories
- Gate feature behind experiments flag

---------

Co-authored-by: Cian Johnston <cian@coder.com>
Co-authored-by: Danielle Maywood <danielle@themaywoods.com>
Co-authored-by: Jeremy Ruppel <jeremy@coder.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 16:50:56 +00:00

196 lines
4.2 KiB
Go

package main
import (
"encoding/json"
"os"
"path/filepath"
"sort"
"strings"
"github.com/coder/coder/v2/enterprise/cli"
"github.com/coder/flog"
"github.com/coder/serpent"
)
// route is an individual page object in the docs manifest.json.
type route struct {
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
Path string `json:"path,omitempty"`
IconPath string `json:"icon_path,omitempty"`
State []string `json:"state,omitempty"`
Children []route `json:"children,omitempty"`
}
// manifest describes the entire documentation index.
type manifest struct {
Versions []string `json:"versions,omitempty"`
Routes []route `json:"routes,omitempty"`
}
func prepareEnv() {
// Unset CODER_ environment variables
for _, env := range os.Environ() {
if strings.HasPrefix(env, "CODER_") {
split := strings.SplitN(env, "=", 2)
if err := os.Unsetenv(split[0]); err != nil {
panic(err)
}
}
}
// Override default OS values to ensure the same generated results.
err := os.Setenv("CLIDOCGEN_CACHE_DIRECTORY", "~/.cache")
if err != nil {
panic(err)
}
err = os.Setenv("CLIDOCGEN_CONFIG_DIRECTORY", "~/.config/coderv2")
if err != nil {
panic(err)
}
err = os.Setenv("TMPDIR", "/tmp")
if err != nil {
panic(err)
}
}
func deleteEmptyDirs(dir string) error {
return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
return nil
}
ents, err := os.ReadDir(path)
if err != nil {
return err
}
if len(ents) == 0 {
flog.Infof("deleting empty dir\t %v", path)
err = os.Remove(path)
if err != nil {
return err
}
}
return nil
})
}
func main() {
prepareEnv()
workdir, err := os.Getwd()
if err != nil {
flog.Fatalf("getwd: %v", err)
}
root := (&cli.RootCmd{})
// wroteMap indexes file paths to commands.
wroteMap := make(map[string]*serpent.Command)
var (
docsDir = filepath.Join(workdir, "docs")
cliMarkdownDir = filepath.Join(docsDir, "reference/cli")
)
cmd, err := root.Command(root.EnterpriseSubcommands())
if err != nil {
flog.Fatalf("creating command: %v", err)
}
err = genTree(
cliMarkdownDir,
cmd,
wroteMap,
)
if err != nil {
flog.Fatalf("generating markdowns: %v", err)
}
// Delete old files
err = filepath.Walk(cliMarkdownDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
_, ok := wroteMap[path]
if !ok {
flog.Infof("deleting old doc\t %v", path)
if err := os.Remove(path); err != nil {
return err
}
}
return nil
})
if err != nil {
flog.Fatalf("deleting old docs: %v", err)
}
err = deleteEmptyDirs(cliMarkdownDir)
if err != nil {
flog.Fatalf("deleting empty dirs: %v", err)
}
// Update manifest
manifestPath := filepath.Join(docsDir, "manifest.json")
manifestByt, err := os.ReadFile(manifestPath)
if err != nil {
flog.Fatalf("reading manifest: %v", err)
}
var manifest manifest
err = json.Unmarshal(manifestByt, &manifest)
if err != nil {
flog.Fatalf("unmarshalling manifest: %v", err)
}
var found bool
for i := range manifest.Routes {
rt := &manifest.Routes[i]
if rt.Title != "Reference" {
continue
}
for j := range rt.Children {
child := &rt.Children[j]
if child.Title != "Command Line" {
continue
}
child.Children = nil
found = true
for path, cmd := range wroteMap {
relPath, err := filepath.Rel(docsDir, path)
if err != nil {
flog.Fatalf("getting relative path: %v", err)
}
child.Children = append(child.Children, route{
Title: fullName(cmd),
Description: cmd.Short,
Path: relPath,
})
}
// Sort children by title because wroteMap iteration is
// non-deterministic.
sort.Slice(child.Children, func(i, j int) bool {
return child.Children[i].Title < child.Children[j].Title
})
}
}
if !found {
flog.Fatalf("could not find Command Line route in manifest")
}
manifestByt, err = json.MarshalIndent(manifest, "", " ")
if err != nil {
flog.Fatalf("marshaling manifest: %v", err)
}
err = os.WriteFile(manifestPath, manifestByt, 0o600)
if err != nil {
flog.Fatalf("writing manifest: %v", err)
}
}