ECC
The agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.
npx ecc-install --profile fullThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.
npx ecc-install --profile fullFair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.
npx n8nAn open-source AI agent that brings the power of Gemini directly into your terminal.
npx @google/gemini-cliSupports Mcp
Carries strong trust indicators from repository metadata
1531 GitHub stars recorded

The demo above shows a tRPC-Agent-Go service streaming agent events to an AG-UI client while the agent plans, calls tools, and updates the interface.
Get started in 3 simple steps:
# 1. Clone and setup
git clone https://github.com/trpc-group/trpc-agent-go.git
cd trpc-agent-go
# 2. Configure your LLM
export OPENAI_API_KEY="your-api-key-here"
export OPENAI_BASE_URL="your-base-url-here" # Optional
# 3. Run your first agent!
cd examples/runner
go run . -model="gpt-4o-mini" -streaming=true
What you'll see:
Try asking: "What's the current time? Then calculate 15 * 23 + 100"
package main
import (
"context"
"fmt"
"log"
"trpc.group/trpc-go/trpc-agent-go/agent/llmagent"
"trpc.group/trpc-go/trpc-agent-go/model"
"trpc.group/trpc-go/trpc-agent-go/model/openai"
"trpc.group/trpc-go/trpc-agent-go/runner"
"trpc.group/trpc-go/trpc-agent-go/tool"
"trpc.group/trpc-go/trpc-agent-go/tool/function"
)
func main() {
// Create model.
modelInstance := openai.New("deepseek-chat",
openai.WithVariant(openai.VariantDeepSeek),
)
// Create tool.
calculatorTool := function.NewFunctionTool(
calculator,
function.WithName("calculator"),
function.WithDescription("Execute addition, subtraction, multiplication, and division. "+
"Parameters: a, b are numeric values, op takes values add/sub/mul/div; "+
"returns result as the calculation result."),
)
// Enable streaming output.
genConfig := model.GenerationConfig{
Stream: true,
}
// Create Agent.
agent := llmagent.New("assistant",
llmagent.WithModel(modelInstance),
llmagent.WithTools([]tool.Tool{calculatorTool}),
llmagent.WithGenerationConfig(genConfig),
)
// Create Runner.
runner := runner.NewRunner("calculator-app", agent)
// Execute conversation.
ctx := context.Background()
events, err := runner.Run(ctx,
"user-001",
"session-001",
model.NewUserMessage("Calculate what 2+3 equals"),
)
if err != nil {
log.Fatal(err)
}
// Process event stream.
for event := range events {
if event.Object == "chat.completion.chunk" {
fmt.Print(event.Response.Choices[0].Delta.Content)
}
}
fmt.Println()
}
func calculator(ctx context.Context, req calculatorReq) (calculatorRsp, error) {
var result float64
switch req.Op {
case "add", "+":
result = req.A + req.B
case "sub", "-":
result = req.A - req.B
case "mul", "*":
result = req.A * req.B
case "div", "/":
result = req.A / req.B
default:
return calculatorRsp{}, fmt.Errorf("invalid operation: %s", req.Op)
}
return calculatorRsp{Result: result}, nil
}
type calculatorReq struct {
A float64 `json:"A" jsonschema:"description=First integer operand,required"`
B float64 `json:"B" jsonschema:"description=Second integer operand,required"`
Op string `json:"Op" jsonschema:"description=Operation type,enum=add,enum=sub,enum=mul,enum=div,required"`
}
type calculatorRsp struct {
Result float64 `json:"result"`
}
Sometimes your Agent must be created per request (for example: different
prompt, model, tools, sandbox instance). In that case, you can let Runner build
a fresh Agent for every Run(...):
r := runner.NewRunnerWithAgentFactory(
"my-app",
"assistant",
func(ctx context.Context, ro agent.RunOptions) (agent.Agent, error) {
// Use ro to build an Agent for this request.
a := llmagent.New("assistant",
llmagent.WithInstruction(ro.Instruction),
)
return a, nil
},
)
events, err := r.Run(ctx,
"user-001",
"session-001",
model.NewUserMessage("Hello"),
agent.WithInstruction("You are a helpful assistant."),
)
_ = events
_ = err
If you want to interrupt a running agent, cancel the context you passed to
Runner.Run (recommended). This stops model calls and tool calls safely and
lets the runner clean up.
Important: do not just “break” your event loop and walk away — the agent goroutine may keep running and can block on channel writes. Always cancel, then keep draining the event channel until it is closed.
Convert Ctrl+C into context cancellation:
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()
events, err := r.Run(ctx, userID, sessionID, message)
if err != nil {
return err
}
for range events {
// Drain until the runner stops (ctx canceled or run completed).
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
events, err := r.Run(ctx, userID, sessionID, message)
if err != nil {
return err
}
go func() {
time.Sleep(2 * time.Second)
cancel()
}()
for range events {
// Keep draining until the channel is closed.
}
requestID (for servers / background runs)requestID := "req-123"
events, err := r.Run(ctx, userID, sessionID, message,
agent.WithRequestID(requestID),
)
mr := r.(runner.ManagedRunner)
_ = mr.Cancel(requestID)
For more details (including detached cancellation, resume, and server cancel
routes), see docs/mkdocs/en/runner.md and docs/mkdocs/en/agui.md.
The examples directory contains runnable demos covering every major feature.
Not sure where to start? Pick a path by what you want to build:
Example: examples/llmagent
LLMAgent.event.Event updates while the model streams.Example: examples/multiagent
Example: examples/graph
GraphAgent – demonstrates building and executing complex, conditional
workflows using the graph and agent/graph packages. It shows
how to construct a graph-based agent, manage state safely, implement
conditional routing, and orchestrate execution with the Runner.
Multi-conditional fan-out routing:
// Return multiple branch keys and run targets in parallel.
sg := graph.NewStateGraph(schema)
sg.AddNode("router", func(ctx context.Context, s graph.State) (any, error) {
return nil, nil
})
sg.AddNode("A", func(ctx context.Context, s graph.State) (any, error) {
return graph.State{"a": 1}, nil
})
sg.AddNode("B", func(ctx context.Context, s graph.State) (any, error) {
return graph.State{"b": 1}, nil
})
sg.SetEntryPoint("router")
sg.AddMultiConditionalEdges(
"router",
func(ctx context.Context, s graph.State) ([]string, error) {
return []string{"goA", "goB"}, nil
},
map[string]string{"goA": "A", "goB": "B"}, // Path map or ends map
)
sg.SetFinishPoint("A").SetFinishPoint("B")
Example: examples/memory
Example: examples/knowledge
Example: examples/telemetry
Example: examples/mcptool
Example: examples/agui
Example: examples/evaluation
Examples: examples/skillrun, examples/skillfind
SKILL.md spec + optional docs/scripts.skill_load, skill_list_docs, skill_select_docs,
skill_run, and (when the executor supports interactive sessions)
skill_exec, skill_write_stdin, skill_poll_session,
skill_kill_session.skill_run is the default one-shot command runner in an isolated
workspace.skill_exec and the session tools cover interactive stdin/TTY flows
without inlining full scripts into the prompt. They are registered
only when the code executor exposes InteractiveProgramRunner
(or falls back to a local engine that does).skill.NewFSRepository(...) can scan multiple roots, such as a shared
skills directory plus a user-private directory. Use
(*skill.FSRepository).Refresh() after skill installation or removal
in long-lived processes.skill_run only for commands required by the selected skill
docs, not for generic shell exploration.LLMAgent uses WithCodeExecutor(...) only to support skill_run,
disable the response code execution processor with
llmagent.WithEnableCodeExecutionResponseProcessor(false). The
skill-focused examples (examples/skill, examples/skillrun,
examples/skilldynamicschema, and
examples/structuredoutputskills) follow this pattern so fenced code
blocks embedded in assistant text do not auto-execute.examples/skillfind demonstrates a real end-to-end discovery flow:
the model uses a built-in skill-find skill to search the public web,
install a public GitHub skill into a user-private directory, refresh
the repository, and use the new skill in the same conversation.
Local execution stays off by default and can be enabled explicitly
when you want to run an installed skill.Example: examples/evolution
SKILL.md workflows.runner.WithEvolutionService(...).Example: examples/artifact
Example: examples/a2aadk
Example: openclaw
Other notable examples:
See individual README.md files in each example folder for usage details.