// Package coding provides a ready-to-use coding agent built on top of agentcore's // ReAct agent, middleware stack, and profile system. // // It is the agentcore equivalent of deepagents-code — a production-grade coding // assistant with file operations, shell security, Git safety, and optional sub-agents. // // Quick start: // // agent := coding.New(&coding.Config{ // Model: myModel, // }) // runner := core.NewTypedRunner(core.RunnerConfig[*schema.Message]{Agent: agent}) // iter := runner.Run(ctx, []*schema.Message{schema.UserMessage("fix this bug")}) // // With the profile system: // // coding.RegisterHarnessProfile() // agent, _ := profile.NewAgent(ctx, &profile.AgentConfig{ // ModelSpec: "anthropic:claude-sonnet-4-6", // HarnessProfileName: "coding-agent", // }) package coding import ( "context" "fmt" "os" "os/exec" "strings" "ragflow/internal/harness/core" "ragflow/internal/harness/core/middlewares/filesystem" "ragflow/internal/harness/core/middlewares/subagent" "ragflow/internal/harness/core/profile" "ragflow/internal/harness/core/schema" ) // Config configures the coding agent. type Config struct { // Name of the agent. Default: "coding_agent". Name string // Model is the chat model. Required (unless using profile system). Model core.Model[*schema.Message] // Tools are additional tools to register beyond the built-in coding tools. Tools []core.Tool // Instruction overrides the default coding system prompt. Instruction string // MaxIterations limits the ReAct loop. Default: 30. MaxIterations int // EnableShell when true adds shell execution capability via a local backend. // Default: false (read-only file operations). EnableShell bool // ShellBackend is the filesystem backend to use for shell execution. // When nil and EnableShell is true, a default local shell backend is created. ShellBackend filesystem.Backend // ShellAllowList configures which shell commands are allowed. // When nil, the default allow-list is used (if EnableShell is true). // When EnableShell is false, this field is ignored. ShellAllowList *ShellAllowListConfig // FilesystemBackend is the filesystem backend for file operations. // When nil, an InMemoryBackend is used (read-only from agent perspective). // Use a LocalFilesystemBackend for real file system access. FilesystemBackend filesystem.Backend // SubAgentSpecs declares sub-agents for task delegation. SubAgentSpecs []subagent.SubAgentSpec // SubAgentConfig configures the SubAgentMiddleware (recursion depth, events). SubAgentConfig *subagent.Config // RegisterHarness when true also registers the "coding-agent" harness profile. // Default: false. RegisterHarness bool } // DefaultConfig returns a Config with sensible defaults. func DefaultConfig() *Config { return &Config{ Name: "coding_agent", MaxIterations: 30, EnableShell: false, } } // New creates a fully-configured coding ReActAgent. // // The agent includes: // - Coding-optimized system prompt (with Git safety rules) // - Filesystem middleware (read/write/edit/ls/glob/grep) // - Shell allow-list middleware (when EnableShell is true) // - SubAgentMiddleware (when SubAgentSpecs is non-empty) // - Optional "coding-agent" harness profile registration func New(cfg *Config) *core.ReActAgent[*schema.Message] { if cfg == nil { cfg = DefaultConfig() } if cfg.MaxIterations <= 0 { cfg.MaxIterations = 30 } if cfg.Name == "" { cfg.Name = "coding_agent" } instruction := cfg.Instruction if instruction == "" { instruction = systemPrompt } // Build middleware stack. var middlewares []core.ReActMiddleware // 1. Shell allow-list middleware (applied BEFORE filesystem to intercept execute calls). if cfg.EnableShell { shellCfg := cfg.ShellAllowList if shellCfg == nil { shellCfg = &ShellAllowListConfig{ AllowedCommands: DefaultShellAllowList(), BlockedCommands: DefaultBlockedCommands(), } } middlewares = append(middlewares, NewShellAllowList(shellCfg)) } // 2. Filesystem middleware (provides read/write/edit/ls/glob/grep/execute). fsCfg := &filesystem.Config{ Backend: cfg.FilesystemBackend, } if cfg.EnableShell && cfg.ShellBackend != nil { fsCfg.Backend = cfg.ShellBackend } else if cfg.EnableShell && cfg.FilesystemBackend == nil { // Create default local shell backend. fsCfg.Backend = &localShellBackend{} } middlewares = append(middlewares, filesystem.New(fsCfg)) // 3. SubAgentMiddleware (when sub-agents are declared). if len(cfg.SubAgentSpecs) > 0 { saCfg := cfg.SubAgentConfig if saCfg == nil { saCfg = &subagent.Config{MaxDepth: 5} } saMW := subagent.New(cfg.SubAgentSpecs, saCfg) middlewares = append(middlewares, saMW) // Build react config — Init() prepares the sub-agent for middleware // inheritance, while ToolContributor handles automatic tool injection. reactCfg := &core.ReActConfig[*schema.Message]{ Model: cfg.Model, Instruction: instruction, MaxIterations: cfg.MaxIterations, Middlewares: middlewares, Tools: cfg.Tools, } saMW.Init(context.Background(), reactCfg) return core.NewReActAgent(reactCfg) } // Build react config without sub-agents. reactCfg := &core.ReActConfig[*schema.Message]{ Model: cfg.Model, Instruction: instruction, MaxIterations: cfg.MaxIterations, Middlewares: middlewares, Tools: cfg.Tools, } if cfg.EnableShell || cfg.FilesystemBackend != nil { // Must have at least one tool for ReAct loop. if len(reactCfg.Tools) == 0 { // Filesystem middleware adds tools via BeforeAgent, but we need // at least one tool to trigger the ReAct loop. reactCfg.Tools = append(reactCfg.Tools, &execTool{}) } } return core.NewReActAgent(reactCfg) } // ---- Local shell backend ---- // localShellBackend implements filesystem.Backend with local shell execution. type localShellBackend struct{} func (b *localShellBackend) Read(path string) (string, error) { data, err := os.ReadFile(path) if err != nil { return "", err } return string(data), nil } func (b *localShellBackend) Write(path, content string) error { return os.WriteFile(path, []byte(content), 0644) } func (b *localShellBackend) Edit(path, old, new string) error { data, err := os.ReadFile(path) if err != nil { return err } content := string(data) if !strings.Contains(content, old) { return fmt.Errorf("edit_file: string %q not found in %s", old, path) } content = strings.Replace(content, old, new, 1) return os.WriteFile(path, []byte(content), 0644) } func (b *localShellBackend) Ls(path string) ([]string, error) { entries, err := os.ReadDir(path) if err != nil { return nil, err } names := make([]string, 0, len(entries)) for _, e := range entries { names = append(names, e.Name()) } return names, nil } func (b *localShellBackend) Glob(pattern string) ([]string, error) { // Simple glob via the shell. cmd := exec.Command("sh", "-c", fmt.Sprintf("ls -d %s 2>/dev/null", pattern)) out, err := cmd.Output() if err != nil { return nil, nil } lines := strings.TrimSpace(string(out)) if lines == "" { return nil, nil } return strings.Split(lines, "\n"), nil } func (b *localShellBackend) Grep(pattern, path string) (string, error) { cmd := exec.Command("grep", "-rn", pattern, path) out, err := cmd.Output() if err != nil { // grep returns exit code 1 when no matches. return "", nil } return string(out), nil } func (b *localShellBackend) Execute(command string) (string, error) { cmd := exec.Command("sh", "-c", command) out, err := cmd.CombinedOutput() if err != nil { return string(out), fmt.Errorf("execute: %w\n%s", err, string(out)) } return string(out), nil } // ---- Dummy tool to bootstrap ReAct loop ---- type execTool struct{} func (t *execTool) Name() string { return "_bootstrap_tool" } func (t *execTool) Description() string { return "Internal bootstrap tool" } func (t *execTool) Invoke(ctx context.Context, args string, opts ...core.ToolOption) (string, error) { return "", nil } func (t *execTool) Stream(ctx context.Context, args string, opts ...core.ToolOption) (*schema.StreamReader[string], error) { return schema.StreamReaderFromArray([]string{""}), nil } // ---- Harness profile registration ---- // HarnessProfile returns a pre-configured HarnessProfile for coding agents. // It registers the standard coding agent middleware stack. func HarnessProfile() *profile.HarnessProfile { return &profile.HarnessProfile{ Name: "coding-agent", BaseSystemPrompt: strPtr(systemPrompt), MaxIterations: 30, RecursionDepth: 5, } } // RegisterHarnessProfile registers the "coding-agent" harness profile globally. // After calling this, users can create coding agents via profile.NewAgent: // // agent, _ := profile.NewAgent(ctx, &profile.AgentConfig{ // ModelSpec: "anthropic:claude-sonnet-4-6", // HarnessProfileName: "coding-agent", // }) func RegisterHarnessProfile() { if profile.LookupHarness("coding-agent") != nil { return // already registered } profile.RegisterHarness(HarnessProfile()) } func strPtr(s string) *string { return &s }