Merge pull request '[agent/claude] Update all Go files that import forge.lthn.ai/core/go or for...' (#5) from agent/update-all-go-files-that-import-forge-lt into main
This commit is contained in:
commit
33968f32bc
7 changed files with 115 additions and 438 deletions
|
|
@ -2,10 +2,9 @@ package dev
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
agentic "forge.lthn.ai/core/agent/pkg/lifecycle"
|
||||
"forge.lthn.ai/core/go/pkg/core"
|
||||
"forge.lthn.ai/core/go-scm/git"
|
||||
"dappco.re/go/core"
|
||||
)
|
||||
|
||||
// WorkBundle contains the Core instance for dev work operations.
|
||||
|
|
@ -16,71 +15,52 @@ type WorkBundle struct {
|
|||
// WorkBundleOptions configures the work bundle.
|
||||
type WorkBundleOptions struct {
|
||||
RegistryPath string
|
||||
AllowEdit bool // Allow agentic to use Write/Edit tools
|
||||
}
|
||||
|
||||
// NewWorkBundle creates a bundle for dev work operations.
|
||||
// Includes: dev (orchestration), git, agentic services.
|
||||
// Includes: dev (orchestration) service.
|
||||
func NewWorkBundle(opts WorkBundleOptions) (*WorkBundle, error) {
|
||||
c, err := core.New(
|
||||
core.WithService(NewService(ServiceOptions{
|
||||
c := core.New()
|
||||
|
||||
svc := &Service{
|
||||
ServiceRuntime: core.NewServiceRuntime(c, ServiceOptions{
|
||||
RegistryPath: opts.RegistryPath,
|
||||
})),
|
||||
core.WithService(git.NewService(git.ServiceOptions{})),
|
||||
core.WithService(agentic.NewService(agentic.ServiceOptions{
|
||||
AllowEdit: opts.AllowEdit,
|
||||
})),
|
||||
core.WithServiceLock(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}),
|
||||
}
|
||||
|
||||
c.Service("dev", core.Service{
|
||||
OnStart: func() core.Result {
|
||||
c.RegisterTask(svc.handleTask)
|
||||
return core.Result{OK: true}
|
||||
},
|
||||
})
|
||||
|
||||
c.LockEnable()
|
||||
c.LockApply()
|
||||
|
||||
return &WorkBundle{Core: c}, nil
|
||||
}
|
||||
|
||||
// Start initialises the bundle services.
|
||||
func (b *WorkBundle) Start(ctx context.Context) error {
|
||||
return b.Core.ServiceStartup(ctx, nil)
|
||||
return resultError(b.Core.ServiceStartup(ctx, nil))
|
||||
}
|
||||
|
||||
// Stop shuts down the bundle services.
|
||||
func (b *WorkBundle) Stop(ctx context.Context) error {
|
||||
return b.Core.ServiceShutdown(ctx)
|
||||
return resultError(b.Core.ServiceShutdown(ctx))
|
||||
}
|
||||
|
||||
// StatusBundle contains the Core instance for status-only operations.
|
||||
type StatusBundle struct {
|
||||
Core *core.Core
|
||||
}
|
||||
|
||||
// StatusBundleOptions configures the status bundle.
|
||||
type StatusBundleOptions struct {
|
||||
RegistryPath string
|
||||
}
|
||||
|
||||
// NewStatusBundle creates a bundle for status-only operations.
|
||||
// Includes: dev (orchestration), git services. No agentic - commits not available.
|
||||
func NewStatusBundle(opts StatusBundleOptions) (*StatusBundle, error) {
|
||||
c, err := core.New(
|
||||
core.WithService(NewService(ServiceOptions(opts))),
|
||||
core.WithService(git.NewService(git.ServiceOptions{})),
|
||||
// No agentic service - TaskCommit will be unhandled
|
||||
core.WithServiceLock(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// resultError extracts an error from a failed core.Result, returning nil on success.
|
||||
func resultError(r core.Result) error {
|
||||
if !r.OK {
|
||||
if err, ok := r.Value.(error); ok {
|
||||
return err
|
||||
}
|
||||
if r.Value != nil {
|
||||
return fmt.Errorf("service operation failed: %v", r.Value)
|
||||
}
|
||||
return fmt.Errorf("service operation failed")
|
||||
}
|
||||
|
||||
return &StatusBundle{Core: c}, nil
|
||||
}
|
||||
|
||||
// Start initialises the bundle services.
|
||||
func (b *StatusBundle) Start(ctx context.Context) error {
|
||||
return b.Core.ServiceStartup(ctx, nil)
|
||||
}
|
||||
|
||||
// Stop shuts down the bundle services.
|
||||
func (b *StatusBundle) Stop(ctx context.Context) error {
|
||||
return b.Core.ServiceShutdown(ctx)
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ func runCommit(registryPath string, all bool) error {
|
|||
for _, s := range dirtyRepos {
|
||||
cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.committing")), s.Name)
|
||||
|
||||
if err := claudeCommit(ctx, s.Path, s.Name, registryPath); err != nil {
|
||||
if err := doCommit(ctx, s.Path, false); err != nil {
|
||||
cli.Print(" %s %s\n", errorStyle.Render("x"), err)
|
||||
failed++
|
||||
} else {
|
||||
|
|
@ -192,7 +192,7 @@ func runCommitSingleRepo(ctx context.Context, repoPath string, all bool) error {
|
|||
cli.Blank()
|
||||
|
||||
// Commit
|
||||
if err := claudeCommit(ctx, repoPath, repoName, ""); err != nil {
|
||||
if err := doCommit(ctx, repoPath, false); err != nil {
|
||||
cli.Print(" %s %s\n", errorStyle.Render("x"), err)
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ func runPushSingleRepo(ctx context.Context, repoPath string, force bool) error {
|
|||
// Use edit-enabled commit if only untracked files (may need .gitignore fix)
|
||||
var err error
|
||||
if s.Modified == 0 && s.Staged == 0 && s.Untracked > 0 {
|
||||
err = claudeEditCommit(ctx, repoPath, repoName, "")
|
||||
err = doCommit(ctx, repoPath, true)
|
||||
} else {
|
||||
err = runCommitSingleRepo(ctx, repoPath, false)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,15 +3,12 @@ package dev
|
|||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"forge.lthn.ai/core/cli/pkg/cli"
|
||||
agentic "forge.lthn.ai/core/agent/pkg/lifecycle"
|
||||
"forge.lthn.ai/core/go-scm/git"
|
||||
"forge.lthn.ai/core/go-i18n"
|
||||
"forge.lthn.ai/core/go-scm/git"
|
||||
)
|
||||
|
||||
// Work command flags
|
||||
|
|
@ -57,42 +54,30 @@ func runWork(registryPath string, statusOnly, autoCommit bool) error {
|
|||
defer func() { _ = bundle.Stop(ctx) }()
|
||||
|
||||
// Load registry and get paths
|
||||
paths, names, err := func() ([]string, map[string]string, error) {
|
||||
reg, _, err := loadRegistryWithConfig(registryPath)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
var paths []string
|
||||
names := make(map[string]string)
|
||||
for _, repo := range reg.List() {
|
||||
if repo.IsGitRepo() {
|
||||
paths = append(paths, repo.Path)
|
||||
names[repo.Path] = repo.Name
|
||||
}
|
||||
}
|
||||
return paths, names, nil
|
||||
}()
|
||||
reg, _, err := loadRegistryWithConfig(registryPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var paths []string
|
||||
names := make(map[string]string)
|
||||
for _, repo := range reg.List() {
|
||||
if repo.IsGitRepo() {
|
||||
paths = append(paths, repo.Path)
|
||||
names[repo.Path] = repo.Name
|
||||
}
|
||||
}
|
||||
|
||||
if len(paths) == 0 {
|
||||
cli.Text(i18n.T("cmd.dev.no_git_repos"))
|
||||
return nil
|
||||
}
|
||||
|
||||
// QUERY git status
|
||||
result, handled, err := bundle.Core.QUERY(git.QueryStatus{
|
||||
// Query git status directly
|
||||
statuses := git.Status(ctx, git.StatusOptions{
|
||||
Paths: paths,
|
||||
Names: names,
|
||||
})
|
||||
if !handled {
|
||||
return cli.Err("git service not available")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
statuses := result.([]git.RepoStatus)
|
||||
|
||||
// Sort by repo name for consistent output
|
||||
slices.SortFunc(statuses, func(a, b git.RepoStatus) int {
|
||||
|
|
@ -125,15 +110,7 @@ func runWork(registryPath string, statusOnly, autoCommit bool) error {
|
|||
cli.Blank()
|
||||
|
||||
for _, s := range dirtyRepos {
|
||||
// PERFORM commit via agentic service
|
||||
_, handled, err := bundle.Core.PERFORM(agentic.TaskCommit{
|
||||
Path: s.Path,
|
||||
Name: s.Name,
|
||||
})
|
||||
if !handled {
|
||||
cli.Print(" %s %s: %s\n", warningStyle.Render("!"), s.Name, "agentic service not available")
|
||||
continue
|
||||
}
|
||||
err := doCommit(ctx, s.Path, false)
|
||||
if err != nil {
|
||||
cli.Print(" %s %s: %s\n", errorStyle.Render("x"), s.Name, err)
|
||||
} else {
|
||||
|
|
@ -141,12 +118,11 @@ func runWork(registryPath string, statusOnly, autoCommit bool) error {
|
|||
}
|
||||
}
|
||||
|
||||
// Re-QUERY status after commits
|
||||
result, _, _ = bundle.Core.QUERY(git.QueryStatus{
|
||||
// Re-query status after commits
|
||||
statuses = git.Status(ctx, git.StatusOptions{
|
||||
Paths: paths,
|
||||
Names: names,
|
||||
})
|
||||
statuses = result.([]git.RepoStatus)
|
||||
|
||||
// Rebuild ahead repos list
|
||||
aheadRepos = nil
|
||||
|
|
@ -187,18 +163,11 @@ func runWork(registryPath string, statusOnly, autoCommit bool) error {
|
|||
|
||||
cli.Blank()
|
||||
|
||||
// PERFORM push for each repo
|
||||
// Push each repo directly
|
||||
var divergedRepos []git.RepoStatus
|
||||
|
||||
for _, s := range aheadRepos {
|
||||
_, handled, err := bundle.Core.PERFORM(git.TaskPush{
|
||||
Path: s.Path,
|
||||
Name: s.Name,
|
||||
})
|
||||
if !handled {
|
||||
cli.Print(" %s %s: %s\n", errorStyle.Render("x"), s.Name, "git service not available")
|
||||
continue
|
||||
}
|
||||
err := git.Push(ctx, s.Path)
|
||||
if err != nil {
|
||||
if git.IsNonFastForward(err) {
|
||||
cli.Print(" %s %s: %s\n", warningStyle.Render("!"), s.Name, i18n.T("cmd.dev.push.diverged"))
|
||||
|
|
@ -220,8 +189,8 @@ func runWork(registryPath string, statusOnly, autoCommit bool) error {
|
|||
for _, s := range divergedRepos {
|
||||
cli.Print(" %s %s...\n", dimStyle.Render("↓"), s.Name)
|
||||
|
||||
// PERFORM pull
|
||||
_, _, err := bundle.Core.PERFORM(git.TaskPull{Path: s.Path, Name: s.Name})
|
||||
// Pull directly
|
||||
err := git.Pull(ctx, s.Path)
|
||||
if err != nil {
|
||||
cli.Print(" %s %s: %s\n", errorStyle.Render("x"), s.Name, err)
|
||||
continue
|
||||
|
|
@ -229,8 +198,8 @@ func runWork(registryPath string, statusOnly, autoCommit bool) error {
|
|||
|
||||
cli.Print(" %s %s...\n", dimStyle.Render("↑"), s.Name)
|
||||
|
||||
// PERFORM push
|
||||
_, _, err = bundle.Core.PERFORM(git.TaskPush{Path: s.Path, Name: s.Name})
|
||||
// Push directly
|
||||
err = git.Push(ctx, s.Path)
|
||||
if err != nil {
|
||||
cli.Print(" %s %s: %s\n", errorStyle.Render("x"), s.Name, err)
|
||||
continue
|
||||
|
|
@ -318,28 +287,3 @@ func printStatusTable(statuses []git.RepoStatus) {
|
|||
}
|
||||
}
|
||||
|
||||
// claudeCommit shells out to claude for committing (legacy helper for other commands)
|
||||
func claudeCommit(ctx context.Context, repoPath, repoName, registryPath string) error {
|
||||
prompt := agentic.Prompt("commit")
|
||||
|
||||
cmd := exec.CommandContext(ctx, "claude", "-p", prompt, "--allowedTools", "Bash,Read,Glob,Grep")
|
||||
cmd.Dir = repoPath
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdin = os.Stdin
|
||||
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// claudeEditCommit shells out to claude with edit permissions (legacy helper)
|
||||
func claudeEditCommit(ctx context.Context, repoPath, repoName, registryPath string) error {
|
||||
prompt := agentic.Prompt("commit")
|
||||
|
||||
cmd := exec.CommandContext(ctx, "claude", "-p", prompt, "--allowedTools", "Bash,Read,Write,Edit,Glob,Grep")
|
||||
cmd.Dir = repoPath
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdin = os.Stdin
|
||||
|
||||
return cmd.Run()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +1,14 @@
|
|||
package dev
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"slices"
|
||||
"strings"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"forge.lthn.ai/core/cli/pkg/cli"
|
||||
"dappco.re/go/core"
|
||||
agentic "forge.lthn.ai/core/agent/pkg/lifecycle"
|
||||
"forge.lthn.ai/core/go-scm/git"
|
||||
"forge.lthn.ai/core/go/pkg/core"
|
||||
)
|
||||
|
||||
// Tasks for dev service
|
||||
|
||||
// TaskWork runs the full dev workflow: status, commit, push.
|
||||
type TaskWork struct {
|
||||
RegistryPath string
|
||||
StatusOnly bool
|
||||
AutoCommit bool
|
||||
AutoPush bool
|
||||
}
|
||||
|
||||
// TaskStatus displays git status for all repos.
|
||||
type TaskStatus struct {
|
||||
RegistryPath string
|
||||
}
|
||||
|
||||
// ServiceOptions for configuring the dev service.
|
||||
type ServiceOptions struct {
|
||||
RegistryPath string
|
||||
|
|
@ -37,256 +19,24 @@ type Service struct {
|
|||
*core.ServiceRuntime[ServiceOptions]
|
||||
}
|
||||
|
||||
// NewService creates a dev service factory.
|
||||
func NewService(opts ServiceOptions) func(*core.Core) (any, error) {
|
||||
return func(c *core.Core) (any, error) {
|
||||
return &Service{
|
||||
ServiceRuntime: core.NewServiceRuntime(c, opts),
|
||||
}, nil
|
||||
}
|
||||
func (s *Service) handleTask(_ *core.Core, _ core.Task) core.Result {
|
||||
return core.Result{}
|
||||
}
|
||||
|
||||
// OnStartup registers task handlers.
|
||||
func (s *Service) OnStartup(ctx context.Context) error {
|
||||
s.Core().RegisterTask(s.handleTask)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) handleTask(c *core.Core, t core.Task) (any, bool, error) {
|
||||
switch m := t.(type) {
|
||||
case TaskWork:
|
||||
err := s.runWork(m)
|
||||
return nil, true, err
|
||||
|
||||
case TaskStatus:
|
||||
err := s.runStatus(m)
|
||||
return nil, true, err
|
||||
}
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
func (s *Service) runWork(task TaskWork) error {
|
||||
// Load registry
|
||||
paths, names, err := s.loadRegistry(task.RegistryPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(paths) == 0 {
|
||||
cli.Println("No git repositories found")
|
||||
return nil
|
||||
}
|
||||
|
||||
// QUERY git status
|
||||
result, handled, err := s.Core().QUERY(git.QueryStatus{
|
||||
Paths: paths,
|
||||
Names: names,
|
||||
})
|
||||
if !handled {
|
||||
return cli.Err("git service not available")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
statuses := result.([]git.RepoStatus)
|
||||
|
||||
// Sort by name
|
||||
slices.SortFunc(statuses, func(a, b git.RepoStatus) int {
|
||||
return cmp.Compare(a.Name, b.Name)
|
||||
})
|
||||
|
||||
// Display status table
|
||||
s.printStatusTable(statuses)
|
||||
|
||||
// Collect dirty and ahead repos
|
||||
var dirtyRepos []git.RepoStatus
|
||||
var aheadRepos []git.RepoStatus
|
||||
|
||||
for _, st := range statuses {
|
||||
if st.Error != nil {
|
||||
continue
|
||||
}
|
||||
if st.IsDirty() {
|
||||
dirtyRepos = append(dirtyRepos, st)
|
||||
}
|
||||
if st.HasUnpushed() {
|
||||
aheadRepos = append(aheadRepos, st)
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-commit dirty repos if requested
|
||||
if task.AutoCommit && len(dirtyRepos) > 0 {
|
||||
cli.Blank()
|
||||
cli.Println("Committing changes...")
|
||||
cli.Blank()
|
||||
|
||||
for _, repo := range dirtyRepos {
|
||||
_, handled, err := s.Core().PERFORM(agentic.TaskCommit{
|
||||
Path: repo.Path,
|
||||
Name: repo.Name,
|
||||
})
|
||||
if !handled {
|
||||
// Agentic service not available - skip silently
|
||||
cli.Print(" - %s: agentic service not available\n", repo.Name)
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
cli.Print(" x %s: %s\n", repo.Name, err)
|
||||
} else {
|
||||
cli.Print(" v %s\n", repo.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// Re-query status after commits
|
||||
result, _, _ = s.Core().QUERY(git.QueryStatus{
|
||||
Paths: paths,
|
||||
Names: names,
|
||||
})
|
||||
statuses = result.([]git.RepoStatus)
|
||||
|
||||
// Rebuild ahead repos list
|
||||
aheadRepos = nil
|
||||
for _, st := range statuses {
|
||||
if st.Error == nil && st.HasUnpushed() {
|
||||
aheadRepos = append(aheadRepos, st)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If status only, we're done
|
||||
if task.StatusOnly {
|
||||
if len(dirtyRepos) > 0 && !task.AutoCommit {
|
||||
cli.Blank()
|
||||
cli.Println("Use --commit flag to auto-commit dirty repos")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Push repos with unpushed commits
|
||||
if len(aheadRepos) == 0 {
|
||||
cli.Blank()
|
||||
cli.Println("All repositories are up to date")
|
||||
return nil
|
||||
}
|
||||
|
||||
cli.Blank()
|
||||
cli.Print("%d repos with unpushed commits:\n", len(aheadRepos))
|
||||
for _, st := range aheadRepos {
|
||||
cli.Print(" %s: %d commits\n", st.Name, st.Ahead)
|
||||
}
|
||||
|
||||
if !task.AutoPush {
|
||||
cli.Blank()
|
||||
cli.Print("Push all? [y/N] ")
|
||||
var answer string
|
||||
_, _ = cli.Scanln(&answer)
|
||||
if strings.ToLower(answer) != "y" {
|
||||
cli.Println("Aborted")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
cli.Blank()
|
||||
|
||||
// Push each repo
|
||||
for _, st := range aheadRepos {
|
||||
_, handled, err := s.Core().PERFORM(git.TaskPush{
|
||||
Path: st.Path,
|
||||
Name: st.Name,
|
||||
})
|
||||
if !handled {
|
||||
cli.Print(" x %s: git service not available\n", st.Name)
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
if git.IsNonFastForward(err) {
|
||||
cli.Print(" ! %s: branch has diverged\n", st.Name)
|
||||
} else {
|
||||
cli.Print(" x %s: %s\n", st.Name, err)
|
||||
}
|
||||
} else {
|
||||
cli.Print(" v %s\n", st.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) runStatus(task TaskStatus) error {
|
||||
paths, names, err := s.loadRegistry(task.RegistryPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(paths) == 0 {
|
||||
cli.Println("No git repositories found")
|
||||
return nil
|
||||
}
|
||||
|
||||
result, handled, err := s.Core().QUERY(git.QueryStatus{
|
||||
Paths: paths,
|
||||
Names: names,
|
||||
})
|
||||
if !handled {
|
||||
return cli.Err("git service not available")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
statuses := result.([]git.RepoStatus)
|
||||
slices.SortFunc(statuses, func(a, b git.RepoStatus) int {
|
||||
return cmp.Compare(a.Name, b.Name)
|
||||
})
|
||||
|
||||
s.printStatusTable(statuses)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) loadRegistry(registryPath string) ([]string, map[string]string, error) {
|
||||
reg, _, err := loadRegistryWithConfig(registryPath)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var paths []string
|
||||
names := make(map[string]string)
|
||||
|
||||
for _, repo := range reg.List() {
|
||||
if repo.IsGitRepo() {
|
||||
paths = append(paths, repo.Path)
|
||||
names[repo.Path] = repo.Name
|
||||
}
|
||||
}
|
||||
|
||||
return paths, names, nil
|
||||
}
|
||||
|
||||
func (s *Service) printStatusTable(statuses []git.RepoStatus) {
|
||||
// Calculate column widths
|
||||
nameWidth := 4 // "Repo"
|
||||
for _, st := range statuses {
|
||||
if len(st.Name) > nameWidth {
|
||||
nameWidth = len(st.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// Print header
|
||||
cli.Print("%-*s %8s %9s %6s %5s\n",
|
||||
nameWidth, "Repo", "Modified", "Untracked", "Staged", "Ahead")
|
||||
|
||||
// Print separator
|
||||
cli.Text(strings.Repeat("-", nameWidth+2+10+11+8+7))
|
||||
|
||||
// Print rows
|
||||
for _, st := range statuses {
|
||||
if st.Error != nil {
|
||||
cli.Print("%-*s error: %s\n", nameWidth, st.Name, st.Error)
|
||||
continue
|
||||
}
|
||||
|
||||
cli.Print("%-*s %8d %9d %6d %5d\n",
|
||||
nameWidth, st.Name,
|
||||
st.Modified, st.Untracked, st.Staged, st.Ahead)
|
||||
}
|
||||
// doCommit shells out to claude for AI-assisted commit.
|
||||
func doCommit(ctx context.Context, repoPath string, allowEdit bool) error {
|
||||
prompt := agentic.Prompt("commit")
|
||||
|
||||
tools := "Bash,Read,Glob,Grep"
|
||||
if allowEdit {
|
||||
tools = "Bash,Read,Write,Edit,Glob,Grep"
|
||||
}
|
||||
|
||||
cmd := exec.CommandContext(ctx, "claude", "-p", prompt, "--allowedTools", tools)
|
||||
cmd.Dir = repoPath
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdin = os.Stdin
|
||||
|
||||
return cmd.Run()
|
||||
}
|
||||
|
|
|
|||
23
go.mod
23
go.mod
|
|
@ -4,14 +4,14 @@ go 1.26.0
|
|||
|
||||
require (
|
||||
code.gitea.io/sdk/gitea v0.23.2
|
||||
forge.lthn.ai/core/agent v0.3.2
|
||||
forge.lthn.ai/core/cli v0.3.5
|
||||
forge.lthn.ai/core/go v0.3.1
|
||||
forge.lthn.ai/core/go-container v0.1.5
|
||||
forge.lthn.ai/core/go-i18n v0.1.6
|
||||
forge.lthn.ai/core/go-io v0.1.5
|
||||
dappco.re/go/core v0.4.7
|
||||
forge.lthn.ai/core/agent v0.3.3
|
||||
forge.lthn.ai/core/cli v0.3.7
|
||||
forge.lthn.ai/core/go-container v0.1.7
|
||||
forge.lthn.ai/core/go-i18n v0.1.7
|
||||
forge.lthn.ai/core/go-io v0.1.7
|
||||
forge.lthn.ai/core/go-log v0.0.4
|
||||
forge.lthn.ai/core/go-scm v0.3.4
|
||||
forge.lthn.ai/core/go-scm v0.3.6
|
||||
github.com/kluctl/go-embed-python v0.0.0-3.13.1-20241219-1
|
||||
github.com/stretchr/testify v1.11.1
|
||||
golang.org/x/term v0.41.0
|
||||
|
|
@ -21,9 +21,10 @@ require (
|
|||
|
||||
require (
|
||||
codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2 v2.2.0 // indirect
|
||||
forge.lthn.ai/core/config v0.1.6 // indirect
|
||||
forge.lthn.ai/core/go-inference v0.1.5 // indirect
|
||||
forge.lthn.ai/core/go-store v0.1.8 // indirect
|
||||
forge.lthn.ai/core/config v0.1.8 // indirect
|
||||
forge.lthn.ai/core/go v0.3.3 // indirect
|
||||
forge.lthn.ai/core/go-inference v0.1.6 // indirect
|
||||
forge.lthn.ai/core/go-store v0.1.9 // indirect
|
||||
github.com/42wim/httpsig v1.2.3 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
|
|
@ -77,5 +78,5 @@ require (
|
|||
modernc.org/libc v1.70.0 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
modernc.org/sqlite v1.46.2 // indirect
|
||||
modernc.org/sqlite v1.47.0 // indirect
|
||||
)
|
||||
|
|
|
|||
50
go.sum
50
go.sum
|
|
@ -2,28 +2,30 @@ code.gitea.io/sdk/gitea v0.23.2 h1:iJB1FDmLegwfwjX8gotBDHdPSbk/ZR8V9VmEJaVsJYg=
|
|||
code.gitea.io/sdk/gitea v0.23.2/go.mod h1:yyF5+GhljqvA30sRDreoyHILruNiy4ASufugzYg0VHM=
|
||||
codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2 v2.2.0 h1:HTCWpzyWQOHDWt3LzI6/d2jvUDsw/vgGRWm/8BTvcqI=
|
||||
codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2 v2.2.0/go.mod h1:ZglEEDj+qkxYUb+SQIeqGtFxQrbaMYqIOgahNKb7uxs=
|
||||
forge.lthn.ai/core/agent v0.3.2 h1:KMtHpQl28PGaulrsoysS6szEiklKK8aDiGzHMWAI1ms=
|
||||
forge.lthn.ai/core/agent v0.3.2/go.mod h1:Yf7m03wD0mogHouV7tDEZVHbq4jGJOf67tNjbhd6P6M=
|
||||
forge.lthn.ai/core/cli v0.3.5 h1:P7yK0DmSA1QnUMFuCjJZf/fk/akKPIxopQ6OwD8Sar8=
|
||||
forge.lthn.ai/core/cli v0.3.5/go.mod h1:SeArHx+hbpX5iZqgASCD7Q1EDoc6uaaGiGBotmNzIx4=
|
||||
forge.lthn.ai/core/config v0.1.6 h1:TSynnKJpoXpvsS//TSnwVH31GyMJF/lbT/oIiMaODNk=
|
||||
forge.lthn.ai/core/config v0.1.6/go.mod h1:AIm7VlO/h4s1LmGSn0HZb+RqAbhmZFJppVGivcsJmGE=
|
||||
forge.lthn.ai/core/go v0.3.1 h1:5FMTsUhLcxSr07F9q3uG0Goy4zq4eLivoqi8shSY4UM=
|
||||
forge.lthn.ai/core/go v0.3.1/go.mod h1:gE6c8h+PJ2287qNhVUJ5SOe1kopEwHEquvinstpuyJc=
|
||||
forge.lthn.ai/core/go-container v0.1.5 h1:EWSg0nlMeeGV+KQenRn8XSqJBu2VHdafELIuvFwregc=
|
||||
forge.lthn.ai/core/go-container v0.1.5/go.mod h1:WdG6PMdscBhUj1AV/Sur4Zc+VakQRN2ejOmLCpaggcg=
|
||||
forge.lthn.ai/core/go-i18n v0.1.6 h1:Z9h6sEZsgJmWlkkq3ZPZyfgWipeeqN5lDCpzltpamHU=
|
||||
forge.lthn.ai/core/go-i18n v0.1.6/go.mod h1:C6CbwdN7sejTx/lbutBPrxm77b8paMHBO6uHVLHOdqQ=
|
||||
forge.lthn.ai/core/go-inference v0.1.5 h1:Az/Euv1DusJQJz/Eca0Ey7sVXQkFLPHW0TBrs9g+Qwg=
|
||||
forge.lthn.ai/core/go-inference v0.1.5/go.mod h1:jfWz+IJX55wAH98+ic6FEqqGB6/P31CHlg7VY7pxREw=
|
||||
forge.lthn.ai/core/go-io v0.1.5 h1:+XJ1YhaGGFLGtcNbPtVlndTjk+pO0Ydi2hRDj5/cHOM=
|
||||
forge.lthn.ai/core/go-io v0.1.5/go.mod h1:FRtXSsi8W+U9vewCU+LBAqqbIj3wjXA4dBdSv3SAtWI=
|
||||
dappco.re/go/core v0.4.7 h1:KmIA/2lo6rl1NMtLrKqCWfMlUqpDZYH3q0/d10dTtGA=
|
||||
dappco.re/go/core v0.4.7/go.mod h1:f2/tBZ3+3IqDrg2F5F598llv0nmb/4gJVCFzM5geE4A=
|
||||
forge.lthn.ai/core/agent v0.3.3 h1:lGpoD5OgvdJ5z+qofw8fBWkDB186QM7I2jjXEbtzSdA=
|
||||
forge.lthn.ai/core/agent v0.3.3/go.mod h1:UnrGApmKd/GzHEFcgy/tYuSfeJwxRx8UsxPhTjU5Ntw=
|
||||
forge.lthn.ai/core/cli v0.3.7 h1:1GrbaGg0wDGHr6+klSbbGyN/9sSbHvFbdySJznymhwg=
|
||||
forge.lthn.ai/core/cli v0.3.7/go.mod h1:DBUppJkA9P45ZFGgI2B8VXw1rAZxamHoI/KG7fRvTNs=
|
||||
forge.lthn.ai/core/config v0.1.8 h1:xP2hys7T94QGVF/OTh84/Zr5Dm/dL/0vzjht8zi+LOg=
|
||||
forge.lthn.ai/core/config v0.1.8/go.mod h1:8epZrkwoCt+5ayrqdinOUU/+w6UoxOyv9ZrdgVOgYfQ=
|
||||
forge.lthn.ai/core/go v0.3.3 h1:kYYZ2nRYy0/Be3cyuLJspRjLqTMxpckVyhb/7Sw2gd0=
|
||||
forge.lthn.ai/core/go v0.3.3/go.mod h1:Cp4ac25pghvO2iqOu59t1GyngTKVOzKB5/VPdhRi9CQ=
|
||||
forge.lthn.ai/core/go-container v0.1.7 h1:+/6NIu7OWyK2LSi2obnFF5fVpWhKiWduMiEkmnbZS6U=
|
||||
forge.lthn.ai/core/go-container v0.1.7/go.mod h1:k0z4yhfZC05bYB5ANdpDFC3AcefnOWJvosXSBSydjs4=
|
||||
forge.lthn.ai/core/go-i18n v0.1.7 h1:aHkAoc3W8fw3RPNvw/UszQbjyFWXHszzbZgty3SwyAA=
|
||||
forge.lthn.ai/core/go-i18n v0.1.7/go.mod h1:0VDjwtY99NSj2iqwrI09h5GUsJeM9s48MLkr+/Dn4G8=
|
||||
forge.lthn.ai/core/go-inference v0.1.6 h1:ce42zC0zO8PuISUyAukAN1NACEdWp5wF1mRgnh5+58E=
|
||||
forge.lthn.ai/core/go-inference v0.1.6/go.mod h1:jfWz+IJX55wAH98+ic6FEqqGB6/P31CHlg7VY7pxREw=
|
||||
forge.lthn.ai/core/go-io v0.1.7 h1:Tdb6sqh+zz1lsGJaNX9RFWM6MJ/RhSAyxfulLXrJsbk=
|
||||
forge.lthn.ai/core/go-io v0.1.7/go.mod h1:8lRLFk4Dnp5cR/Cyzh9WclD5566TbpdRgwcH7UZLWn4=
|
||||
forge.lthn.ai/core/go-log v0.0.4 h1:KTuCEPgFmuM8KJfnyQ8vPOU1Jg654W74h8IJvfQMfv0=
|
||||
forge.lthn.ai/core/go-log v0.0.4/go.mod h1:r14MXKOD3LF/sI8XUJQhRk/SZHBE7jAFVuCfgkXoZPw=
|
||||
forge.lthn.ai/core/go-scm v0.3.4 h1:McZvp2gI3wEPCF/jim8O4F1+Vp477N81TUiiklTq5hw=
|
||||
forge.lthn.ai/core/go-scm v0.3.4/go.mod h1:AOrx4CEmV8/Q73Cvd2LkbFniYGpk46mticpYmK5MnJA=
|
||||
forge.lthn.ai/core/go-store v0.1.8 h1:jeFqxilifa/hXtQqCeXX/+Vwy6M/XZE7uCP8XQ0ercw=
|
||||
forge.lthn.ai/core/go-store v0.1.8/go.mod h1:DJocTeTCjFBPn5ppQT/IDheFJhOfwlHeoxEUtDH07zE=
|
||||
forge.lthn.ai/core/go-scm v0.3.6 h1:LFNx8Fs82mrpxro/MPUM6tMiD4DqPmdu83UknXztQjc=
|
||||
forge.lthn.ai/core/go-scm v0.3.6/go.mod h1:IWFIYDfRH0mtRdqY5zV06l/RkmkPpBM6FcbKWhg1Qa8=
|
||||
forge.lthn.ai/core/go-store v0.1.9 h1:DGO2sUo2i/csWbhw7zxU7oyGF2FJT72/8w47GhZ1joM=
|
||||
forge.lthn.ai/core/go-store v0.1.9/go.mod h1:VNnHh94TMD3+L+sSgvxn0GHtDKhJR8FD6JiuIuRtjuk=
|
||||
github.com/42wim/httpsig v1.2.3 h1:xb0YyWhkYj57SPtfSttIobJUPJZB9as1nsfo7KWVcEs=
|
||||
github.com/42wim/httpsig v1.2.3/go.mod h1:nZq9OlYKDrUBhptd77IHx4/sZZD+IxTBADvAPI9G/EM=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
|
|
@ -143,8 +145,8 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8
|
|||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
|
||||
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
|
||||
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
|
||||
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
|
|
@ -206,8 +208,8 @@ modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
|||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.46.2 h1:gkXQ6R0+AjxFC/fTDaeIVLbNLNrRoOK7YYVz5BKhTcE=
|
||||
modernc.org/sqlite v1.46.2/go.mod h1:hWjRO6Tj/5Ik8ieqxQybiEOUXy0NJFNp2tpvVpKlvig=
|
||||
modernc.org/sqlite v1.47.0 h1:R1XyaNpoW4Et9yly+I2EeX7pBza/w+pmYee/0HJDyKk=
|
||||
modernc.org/sqlite v1.47.0/go.mod h1:hWjRO6Tj/5Ik8ieqxQybiEOUXy0NJFNp2tpvVpKlvig=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue