Skip to content
13 changes: 7 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
.aidy/
refrax
coverage.out
*.cast
.aider*
tmp/
tmp-copy/
.aidy/
.claude/
.env
*.cast
.idea/
coverage.out
out/
refrax
tmp-copy/
tmp/
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ refrax refactor --output="./out" --ai=deepseek test/test_data/java/person

### Checking Results

`refrax` does not inherently verify whether the applied changes are correct or cause any issues.
`refrax` does not inherently verify whether the applied changes are correct or cause any issues.
To address this, you can use the `--check` option to validate the changes. Multiple `--check` options can be provided, as shown below:

```sh
Expand Down
8 changes: 4 additions & 4 deletions cmd/refactor.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ func newRefactorCmd(params *client.Params) *cobra.Command {
var checks []string
command := &cobra.Command{
Use: "refactor [path]",
Short: "refactor code in the given directory (defaults to current)",
Short: "Refactor code in the given directory (defaults to current)",
Args: cobra.MaximumNArgs(1),
Aliases: []string{"r"},
RunE: func(_ *cobra.Command, args []string) error {
Expand All @@ -28,8 +28,8 @@ func newRefactorCmd(params *client.Params) *cobra.Command {
return err
},
}
command.Flags().StringVarP(&output, "output", "o", "", "output path for the refactored code")
command.Flags().IntVar(&maxSize, "max-size", 200, "maximum number of changes allowed in a single refactoring cycle")
command.Flags().StringSliceVar(&checks, "check", make([]string, 0), "check commands to run after refactoring")
command.Flags().StringVarP(&output, "output", "o", "", "Output path for the refactored code")
command.Flags().IntVar(&maxSize, "max-size", 200, "Maximum number of changes allowed in a single refactoring cycle")
command.Flags().StringSliceVar(&checks, "check", make([]string, 0), "Check commands to run after refactoring")
return command
}
10 changes: 5 additions & 5 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ func NewRootCmd(out, _ io.Writer) *cobra.Command {
Long: "Refrax is an AI-powered refactoring agent for Java code. It communicates using the A2A protocol",
PersistentPreRun: func(_ *cobra.Command, _ []string) { params.Log = out },
}
root.PersistentFlags().StringVarP(&params.Provider, "ai", "a", "none", "AI provider to use (e.g., openai, deepseek, none, etc.)")
root.PersistentFlags().StringVarP(&params.Provider, "ai", "a", "none", "AI provider to use (openai, deepseek, none)")
root.PersistentFlags().StringVarP(&params.Token, "token", "t", "", "Token for the AI provider (if required)")
root.PersistentFlags().StringVar(&params.Playbook, "playbook", "", "Path to a user-defined YAML playbook for AI integration.")
root.PersistentFlags().StringVar(&params.Playbook, "playbook", "", "Path to a user-defined YAML playbook for AI integration")
root.PersistentFlags().BoolVar(&params.MockProject, "mock-project", false, "Use mock project")
root.PersistentFlags().BoolVarP(&params.Debug, "debug", "d", false, "print debug logs")
root.PersistentFlags().BoolVarP(&params.Debug, "debug", "d", false, "Print debug logs")
root.PersistentFlags().BoolVar(&params.Stats, "stats", false, "Print internal interaction statistics")
root.PersistentFlags().StringVar(&params.Format, "stats-format", "std", "Format for statistics output (e.g., std, csv, etc.)")
root.PersistentFlags().StringVar(&params.Soutput, "stats-output", "stats", "Output path for statistics (default: stats)")
root.PersistentFlags().StringVar(&params.Format, "stats-format", "std", "Format for statistics output (std, csv)")
root.PersistentFlags().StringVar(&params.Soutput, "stats-output", "stats", "Output path for statistics")
root.AddCommand(
newRefactorCmd(&params),
newStartCmd(),
Expand Down
4 changes: 2 additions & 2 deletions cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import (
func newStartCmd() *cobra.Command {
command := &cobra.Command{
Use: "start [agent]",
Short: "Starts a particular agent like fixer, critic, or facilitator",
Short: "Start a particular agent (fixer, critic, facilitator)",
Args: cobra.MaximumNArgs(1),
Aliases: []string{"st"},
RunE: func(_ *cobra.Command, _ []string) error {
panic("start command is not implemented yet")
panic("Start command is not implemented yet")
},
}
return command
Expand Down
6 changes: 3 additions & 3 deletions internal/brain/deepseek.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type deepseekMsg struct {
}

// NewDeepSeek creates a new deepSeek instance with the provided API key.
func NewDeepSeek(apiKey, system string) Brain {
func NewDeepSeek(apiKey, _ string) Brain {
return &deepSeek{
token: apiKey,
url: "https://api.deepseek.com/chat/completions",
Expand All @@ -50,13 +50,13 @@ func NewDeepSeek(apiKey, system string) Brain {

// Ask sends a question to the deepSeek API and retrieves an answer.
func (d *deepSeek) Ask(question string) (string, error) {
log.Debug("deepSeek: asking question: %s", question)
log.Debug("DeepSeek: asking question: %s", question)
return d.send(d.system, question)
}

func (d *deepSeek) send(system, user string) (answer string, err error) {
content := trimmed(user)
log.Debug("deepSeek: sending request with system promt: '%s' and userPrompt: '%s'", system, content)
log.Debug("DeepSeek: sending request with system prompt: '%s' and userPrompt: '%s'", system, content)
temp := float64(0.0)
body := deepseekReq{
Model: d.model,
Expand Down
48 changes: 24 additions & 24 deletions internal/client/refrax_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@ func Refactor(params *Params) (domain.Project, error) {

// Refactor performs refactoring on the given project using the RefraxClient.
func (c *RefraxClient) Refactor(proj domain.Project) (domain.Project, error) {
log.Debug("starting refactoring for project %s", proj)
log.Debug("Starting refactoring for project %s", proj)
classes, err := proj.Classes()
if err != nil {
return nil, fmt.Errorf("failed to get classes from project %s: %w", proj, err)
}
if len(classes) == 0 {
return proj, fmt.Errorf("no java classes found in the project %s, add java files to the appropriate directory", proj)
}
log.Debug("found %d classes in the project: %v", len(classes), classes)
log.Debug("Found %d classes in the project: %v", len(classes), classes)

criticStats := &stats.Stats{Name: "critic"}
criticSystemPrompt := prompts.System{
Expand Down Expand Up @@ -160,25 +160,25 @@ func (c *RefraxClient) Refactor(proj domain.Project) (domain.Project, error) {
go func() {
faerr := fclttor.ListenAndServe()
if faerr != nil && faerr != http.ErrServerClosed {
panic(fmt.Sprintf("failed to start facilitator server: %v", faerr))
panic(fmt.Sprintf("Failed to start facilitator server: %v", faerr))
}
}()
go func() {
ferr := fxr.ListenAndServe()
if ferr != nil && ferr != http.ErrServerClosed {
panic(fmt.Sprintf("failed to start fixer server: %v", ferr))
panic(fmt.Sprintf("Failed to start fixer server: %v", ferr))
}
}()
go func() {
cerr := ctc.ListenAndServe()
if cerr != nil && cerr != http.ErrServerClosed {
panic(fmt.Sprintf("failed to start critic server: %v", cerr))
panic(fmt.Sprintf("Failed to start critic server: %v", cerr))
}
}()
go func() {
rerr := rvwr.ListenAndServe()
if rerr != nil && rerr != http.ErrServerClosed {
panic(fmt.Sprintf("failed to start reviewer server: %v", rerr))
panic(fmt.Sprintf("Failed to start reviewer server: %v", rerr))
}
}()

Expand All @@ -192,17 +192,17 @@ func (c *RefraxClient) Refactor(proj domain.Project) (domain.Project, error) {
<-fxr.Ready()
<-rvwr.Ready()

log.Info("all servers are ready: facilitator %d, critic %d, fixer %d, reviewer %d", facilitatorPort, criticPort, fixerPort, reviewerPort)
log.Info("begin refactoring for project %s with %d classes", proj, len(classes))
log.Info("All servers are ready: facilitator %d, critic %d, fixer %d, reviewer %d", facilitatorPort, criticPort, fixerPort, reviewerPort)
log.Info("Begin refactoring for project %s with %d classes", proj, len(classes))
ch := make(chan refactoring, len(classes))
go refactor(fclttor, proj, c.params.MaxSize, ch)
for range len(classes) {
res := <-ch
if res.class != nil && res.content != "" {
log.Info("received refactored class: %s, content length: %d", res.class.Name(), len(res.content))
log.Info("Received refactored class: %s, content length: %d", res.class.Name(), len(res.content))
}
}
log.Info("refactoring is finished")
log.Info("Refactoring is finished")
err = printStats(c.params, criticStats, fixerStats, facilitatorStats)
if err != nil {
return nil, fmt.Errorf("failed to print statistics: %w", err)
Expand All @@ -217,7 +217,7 @@ type refactoring struct {
}

func refactor(f domain.Facilitator, p domain.Project, size int, ch chan<- refactoring) {
log.Debug("refactoring project %q", p)
log.Debug("Refactoring project %q", p)
all, err := p.Classes()
if err != nil {
ch <- refactoring{err: fmt.Errorf("failed to get classes from project %s: %w", p, err)}
Expand All @@ -239,15 +239,15 @@ func refactor(f domain.Facilitator, p domain.Project, size int, ch chan<- refact
}
artifacts, err := f.Refactor(&job)
if err != nil {
log.Error("failed to refactor project %s: %v", p, err)
log.Error("Failed to refactor project %s: %v", p, err)
ch <- refactoring{err: fmt.Errorf("failed to refactor project %s: %w", p, err)}
close(ch)
return
}
refactored := artifacts.Classes
log.Info("refactored %d classes in project %s", len(refactored), p)
log.Info("Refactored %d classes in project %s", len(refactored), p)
for _, c := range refactored {
log.Debug("rececived refactored class: ", c)
log.Debug("Received refactored class: ", c)
ch <- refactoring{class: before[c.Name()], content: c.Content(), err: nil}
}
close(ch)
Expand All @@ -259,7 +259,7 @@ type shudownable interface {

func shutdown(s shudownable) {
if cerr := s.Shutdown(); cerr != nil {
panic(fmt.Sprintf("failed to close resource: %v", cerr))
panic(fmt.Sprintf("Failed to close resource: %v", cerr))
}
}

Expand All @@ -275,14 +275,14 @@ func printStats(p Params, s ...*stats.Stats) error {
if p.Stats {
var swriter stats.Writer
if p.Format == "csv" {
log.Info("using csv file for statistics output")
log.Info("Using csv file for statistics output")
output := p.Soutput
if output == "" {
output = "stats.csv"
}
swriter = stats.NewCSVWriter(output)
} else {
log.Info("using stdout format for statistics output")
log.Info("Using stdout format for statistics output")
swriter = stats.NewStdWriter(log.Default())
}
var res []*stats.Stats
Expand All @@ -307,31 +307,31 @@ func mind(p Params, system *prompts.System, s *stats.Stats) (brain.Brain, error)
}

func token(p Params) string {
log.Debug("refactoring provider: %s", p.Provider)
log.Debug("project path to refactor: %s", p.Input)
log.Debug("Refactoring provider: %s", p.Provider)
log.Debug("Project path to refactor: %s", p.Input)
var token string
if p.Token != "" {
token = p.Token
} else {
log.Info("token not provided, trying to find token in .env file")
log.Info("Token not provided, trying to find token in .env file")
token = env.Token(".env", p.Provider)
}
log.Debug("using provided token: %s...", mask(token))
log.Debug("Using provided token: %s...", mask(token))
return token
}

func proj(params Params) (domain.Project, error) {
if params.MockProject {
log.Debug("using mock project")
log.Debug("Using mock project")
return domain.NewMock(), nil
}
input := domain.NewFilesystem(params.Input)
output := params.Output
if output != "" {
log.Debug("copy project to %q", output)
log.Debug("Copy project to %q", output)
return domain.NewMirrorProject(input, output)
}
log.Debug("no output path provided, changing project in place %q", params.Input)
log.Debug("No output path provided, changing project in place %q", params.Input)
return input, nil
}

Expand Down
10 changes: 5 additions & 5 deletions internal/critic/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ type promptData struct {
// Review sends the provided Java class to the Critic for analysis and returns suggested improvements.
func (c *agent) Review(job *domain.Job) (*domain.Artifacts, error) {
class := job.Classes[0]
c.log.Info("received class %q for analysis", class.Name())
c.log.Info("Received class %q for analysis", class.Name())
data := promptData{
Code: class.Content(),
Defects: []string{tool.NewCombined(c.tools...).Imperfections()},
Expand All @@ -40,7 +40,7 @@ func (c *agent) Review(job *domain.Job) (*domain.Artifacts, error) {
Data: data,
Name: "critic/critic.md.tmpl",
}
c.log.Debug("rendered prompt for class %s: %s", class.Name(), prompt)
c.log.Debug("Rendered prompt for class %s: %s", class.Name(), prompt)
answer, err := c.brain.Ask(prompt.String())
if err != nil {
return nil, fmt.Errorf("failed to get answer from brain: %w", err)
Expand Down Expand Up @@ -74,15 +74,15 @@ func parseAnswer(answer string) []string {
return suggestions
}

func (a *agent) associated(suggestions []string, class string) []domain.Suggestion {
func (c *agent) associated(suggestions []string, class string) []domain.Suggestion {
res := make([]domain.Suggestion, 0)
for i, suggestion := range suggestions {
if strings.EqualFold(suggestion, notFound) {
a.log.Info("no suggestions found for the class #%d: %s", i+1, class)
c.log.Info("No suggestions found for the class #%d: %s", i+1, class)
} else {
res = append(res, *domain.NewSuggestion(suggestion, class))
}
}
a.log.Info("total suggestions associated with class %s: %d", class, len(res))
c.log.Info("Total suggestions associated with class %s: %d", class, len(res))
return res
}
12 changes: 6 additions & 6 deletions internal/critic/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ func NewCritic(ai brain.Brain, port int, tools ...tool.Tool) *Critic {
agent: &agent{brain: ai, log: logger, tools: tools},
}
server.MsgHandler(critic.think)
critic.log.Debug("preparing the Critic server on port %d with ai provider %s", port, ai)
critic.log.Debug("Preparing the Critic server on port %d with ai provider %s", port, ai)
return critic
}

// ListenAndServe starts the Critic server and signals readiness via the provided channel.
func (c *Critic) ListenAndServe() error {
c.log.Info("starting critic server on port %d...", c.port)
c.log.Info("Starting critic server on port %d...", c.port)
var err error
if err = c.server.ListenAndServe(); err != nil && http.ErrServerClosed != err {
return fmt.Errorf("failed to start critic server: %w", err)
Expand All @@ -49,7 +49,7 @@ func (c *Critic) ListenAndServe() error {
// Review sends the provided Java class to the Critic for analysis and returns suggested improvements.
func (c *Critic) Review(job *domain.Job) (*domain.Artifacts, error) {
address := fmt.Sprintf("http://localhost:%d", c.port)
c.log.Info("asking critic (%s) to lint the class...", address)
c.log.Info("Asking critic (%s) to lint the class...", address)
critic := protocol.NewClient(address)
resp, err := critic.SendMessage(job.Marshal())
if err != nil {
Expand All @@ -60,11 +60,11 @@ func (c *Critic) Review(job *domain.Job) (*domain.Artifacts, error) {

// Shutdown gracefully shuts down the Critic server.
func (c *Critic) Shutdown() error {
c.log.Info("stopping critic server...")
c.log.Info("Stopping critic server...")
if err := c.server.Shutdown(); err != nil {
return fmt.Errorf("failed to stop critic server: %w", err)
}
c.log.Info("critic server stopped successfully")
c.log.Info("Critic server stopped successfully")
return nil
}

Expand All @@ -88,7 +88,7 @@ func (c *Critic) think(ctx context.Context, m *protocol.Message) (*protocol.Mess
}

func (c *Critic) thinkLong(m *protocol.Message) (*protocol.Message, error) {
c.log.Debug("received message: #%s", m.MessageID)
c.log.Debug("Received message: #%s", m.MessageID)
tsk, err := domain.UnmarshalJob(m)
if err != nil {
return nil, fmt.Errorf("failed to parse task from message: %w", err)
Expand Down
1 change: 1 addition & 0 deletions internal/domain/a2a.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Package domain is for the domain.
package domain

import (
Expand Down
Loading
Loading