cli/pkg/agentic/completion.go

339 lines
8.1 KiB
Go
Raw Normal View History

// Package agentic provides AI collaboration features for task management.
package agentic
import (
"bytes"
"context"
"fmt"
"os/exec"
"strings"
feat(errors): Unify errors and logging (#180) * feat(help): Add CLI help command Fixes #136 * chore: remove binary * feat(mcp): Add TCP transport Fixes #126 * feat(io): Migrate pkg/mcp to use Medium abstraction Fixes #103 * feat(io): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(errors): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/docs/* to Medium abstraction Fixes #113 * chore(io): Migrate internal/cmd/dev/* to Medium abstraction Fixes #114 * chore(io): Migrate internal/cmd/setup/* to Medium abstraction * chore(io): Complete migration of internal/cmd/dev/* to Medium abstraction * feat(io): extend Medium interface with Delete, Rename, List, Stat operations Adds the following methods to the Medium interface: - Delete(path) - remove a file or empty directory - DeleteAll(path) - recursively remove a file or directory - Rename(old, new) - move/rename a file or directory - List(path) - list directory entries (returns []fs.DirEntry) - Stat(path) - get file information (returns fs.FileInfo) - Exists(path) - check if path exists - IsDir(path) - check if path is a directory Implements these methods in both local.Medium (using os package) and MockMedium (in-memory for testing). Includes FileInfo and DirEntry types for mock implementations. This enables migration of direct os.* calls to the Medium abstraction for consistent path validation and testability. Refs #101 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/sdk, pkgcmd, and workspace to Medium abstraction * chore(io): migrate internal/cmd/docs and internal/cmd/dev to Medium - internal/cmd/docs: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.RemoveAll with io.Local equivalents - internal/cmd/dev: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.ReadDir with io.Local equivalents - Fix local.Medium to allow absolute paths when root is "/" for full filesystem access (io.Local use case) Refs #113, #114 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): migrate internal/cmd/setup to Medium abstraction Migrated all direct os.* filesystem calls to use io.Local: - cmd_repo.go: os.MkdirAll -> io.Local.EnsureDir, os.WriteFile -> io.Local.Write, os.Stat -> io.Local.IsFile - cmd_bootstrap.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.IsDir/Exists, os.ReadDir -> io.Local.List - cmd_registry.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.Exists - cmd_ci.go: os.ReadFile -> io.Local.Read - github_config.go: os.ReadFile -> io.Local.Read, os.Stat -> io.Local.Exists Refs #116 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): add error creation and log-and-return helpers Implements issues #129 and #132: - Add Err struct with Op, Msg, Err, Code fields for structured errors - Add E(), Wrap(), WrapCode(), NewCode() for error creation - Add Is(), As(), NewError(), Join() as stdlib wrappers - Add Op(), ErrCode(), Message(), Root() for introspection - Add LogError(), LogWarn(), Must() for combined log-and-return Closes #129 Closes #132 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(errors): create deprecation alias pointing to pkg/log Makes pkg/errors a thin compatibility layer that re-exports from pkg/log. All error handling functions now have canonical implementations in pkg/log. Migration guide in package documentation: - errors.Error -> log.Err - errors.E -> log.E - errors.Code -> log.NewCode - errors.New -> log.NewError Fixes behavior consistency: - E(op, msg, nil) now creates an error (for errors without cause) - Wrap(nil, op, msg) returns nil (for conditional wrapping) - WrapCode returns nil only when both err is nil AND code is empty Closes #128 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(log): migrate pkg/errors imports to pkg/log Migrates all internal packages from pkg/errors to pkg/log: - internal/cmd/monitor - internal/cmd/qa - internal/cmd/dev - pkg/agentic Closes #130 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): address Copilot review feedback - Fix MockMedium.Rename: collect keys before mutating maps during iteration - Fix .git checks to use Exists instead of List (handles worktrees/submodules) - Fix cmd_sync.go: use DeleteAll for recursive directory removal Files updated: - pkg/io/io.go: safe map iteration in Rename - internal/cmd/setup/cmd_bootstrap.go: Exists for .git checks - internal/cmd/setup/cmd_registry.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_install.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_manage.go: Exists for .git checks - internal/cmd/docs/cmd_sync.go: DeleteAll for recursive delete Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(updater): resolve PkgVersion duplicate declaration Remove var PkgVersion from updater.go since go generate creates const PkgVersion in version.go. Track version.go in git to ensure builds work without running go generate first. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting in internal/variants Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting across migrated files Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(io): simplify local Medium implementation Rewrote to match the simpler TypeScript pattern: - path() sanitizes and returns string directly - Each method calls path() once - No complex symlink validation - Less code, less attack surface Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): remove duplicate method declarations Clean up the client.go file that had duplicate method declarations from a bad cherry-pick merge. Now has 127 lines of simple, clean code. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(io): fix traversal test to match sanitization behavior The simplified path() sanitizes .. to . without returning errors. Update test to verify sanitization works correctly. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(mcp): update sandboxing tests for simplified Medium The simplified io/local.Medium implementation: - Sanitizes .. to . (no error, path is cleaned) - Allows absolute paths through (caller validates if needed) - Follows symlinks (no traversal blocking) Update tests to match this simplified behavior. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 06:48:40 +00:00
"github.com/host-uk/core/pkg/log"
)
// PROptions contains options for creating a pull request.
type PROptions struct {
// Title is the PR title.
Title string `json:"title"`
// Body is the PR description.
Body string `json:"body"`
// Draft marks the PR as a draft.
Draft bool `json:"draft"`
// Labels are labels to add to the PR.
Labels []string `json:"labels"`
// Base is the base branch (defaults to main).
Base string `json:"base"`
}
// AutoCommit creates a git commit with a task reference.
// The commit message follows the format:
//
// feat(scope): description
//
// Task: #123
// Co-Authored-By: Claude <noreply@anthropic.com>
func AutoCommit(ctx context.Context, task *Task, dir string, message string) error {
const op = "agentic.AutoCommit"
if task == nil {
feat(errors): Unify errors and logging (#180) * feat(help): Add CLI help command Fixes #136 * chore: remove binary * feat(mcp): Add TCP transport Fixes #126 * feat(io): Migrate pkg/mcp to use Medium abstraction Fixes #103 * feat(io): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(errors): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/docs/* to Medium abstraction Fixes #113 * chore(io): Migrate internal/cmd/dev/* to Medium abstraction Fixes #114 * chore(io): Migrate internal/cmd/setup/* to Medium abstraction * chore(io): Complete migration of internal/cmd/dev/* to Medium abstraction * feat(io): extend Medium interface with Delete, Rename, List, Stat operations Adds the following methods to the Medium interface: - Delete(path) - remove a file or empty directory - DeleteAll(path) - recursively remove a file or directory - Rename(old, new) - move/rename a file or directory - List(path) - list directory entries (returns []fs.DirEntry) - Stat(path) - get file information (returns fs.FileInfo) - Exists(path) - check if path exists - IsDir(path) - check if path is a directory Implements these methods in both local.Medium (using os package) and MockMedium (in-memory for testing). Includes FileInfo and DirEntry types for mock implementations. This enables migration of direct os.* calls to the Medium abstraction for consistent path validation and testability. Refs #101 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/sdk, pkgcmd, and workspace to Medium abstraction * chore(io): migrate internal/cmd/docs and internal/cmd/dev to Medium - internal/cmd/docs: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.RemoveAll with io.Local equivalents - internal/cmd/dev: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.ReadDir with io.Local equivalents - Fix local.Medium to allow absolute paths when root is "/" for full filesystem access (io.Local use case) Refs #113, #114 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): migrate internal/cmd/setup to Medium abstraction Migrated all direct os.* filesystem calls to use io.Local: - cmd_repo.go: os.MkdirAll -> io.Local.EnsureDir, os.WriteFile -> io.Local.Write, os.Stat -> io.Local.IsFile - cmd_bootstrap.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.IsDir/Exists, os.ReadDir -> io.Local.List - cmd_registry.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.Exists - cmd_ci.go: os.ReadFile -> io.Local.Read - github_config.go: os.ReadFile -> io.Local.Read, os.Stat -> io.Local.Exists Refs #116 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): add error creation and log-and-return helpers Implements issues #129 and #132: - Add Err struct with Op, Msg, Err, Code fields for structured errors - Add E(), Wrap(), WrapCode(), NewCode() for error creation - Add Is(), As(), NewError(), Join() as stdlib wrappers - Add Op(), ErrCode(), Message(), Root() for introspection - Add LogError(), LogWarn(), Must() for combined log-and-return Closes #129 Closes #132 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(errors): create deprecation alias pointing to pkg/log Makes pkg/errors a thin compatibility layer that re-exports from pkg/log. All error handling functions now have canonical implementations in pkg/log. Migration guide in package documentation: - errors.Error -> log.Err - errors.E -> log.E - errors.Code -> log.NewCode - errors.New -> log.NewError Fixes behavior consistency: - E(op, msg, nil) now creates an error (for errors without cause) - Wrap(nil, op, msg) returns nil (for conditional wrapping) - WrapCode returns nil only when both err is nil AND code is empty Closes #128 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(log): migrate pkg/errors imports to pkg/log Migrates all internal packages from pkg/errors to pkg/log: - internal/cmd/monitor - internal/cmd/qa - internal/cmd/dev - pkg/agentic Closes #130 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): address Copilot review feedback - Fix MockMedium.Rename: collect keys before mutating maps during iteration - Fix .git checks to use Exists instead of List (handles worktrees/submodules) - Fix cmd_sync.go: use DeleteAll for recursive directory removal Files updated: - pkg/io/io.go: safe map iteration in Rename - internal/cmd/setup/cmd_bootstrap.go: Exists for .git checks - internal/cmd/setup/cmd_registry.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_install.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_manage.go: Exists for .git checks - internal/cmd/docs/cmd_sync.go: DeleteAll for recursive delete Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(updater): resolve PkgVersion duplicate declaration Remove var PkgVersion from updater.go since go generate creates const PkgVersion in version.go. Track version.go in git to ensure builds work without running go generate first. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting in internal/variants Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting across migrated files Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(io): simplify local Medium implementation Rewrote to match the simpler TypeScript pattern: - path() sanitizes and returns string directly - Each method calls path() once - No complex symlink validation - Less code, less attack surface Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): remove duplicate method declarations Clean up the client.go file that had duplicate method declarations from a bad cherry-pick merge. Now has 127 lines of simple, clean code. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(io): fix traversal test to match sanitization behavior The simplified path() sanitizes .. to . without returning errors. Update test to verify sanitization works correctly. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(mcp): update sandboxing tests for simplified Medium The simplified io/local.Medium implementation: - Sanitizes .. to . (no error, path is cleaned) - Allows absolute paths through (caller validates if needed) - Follows symlinks (no traversal blocking) Update tests to match this simplified behavior. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 06:48:40 +00:00
return log.E(op, "task is required", nil)
}
if message == "" {
feat(errors): Unify errors and logging (#180) * feat(help): Add CLI help command Fixes #136 * chore: remove binary * feat(mcp): Add TCP transport Fixes #126 * feat(io): Migrate pkg/mcp to use Medium abstraction Fixes #103 * feat(io): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(errors): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/docs/* to Medium abstraction Fixes #113 * chore(io): Migrate internal/cmd/dev/* to Medium abstraction Fixes #114 * chore(io): Migrate internal/cmd/setup/* to Medium abstraction * chore(io): Complete migration of internal/cmd/dev/* to Medium abstraction * feat(io): extend Medium interface with Delete, Rename, List, Stat operations Adds the following methods to the Medium interface: - Delete(path) - remove a file or empty directory - DeleteAll(path) - recursively remove a file or directory - Rename(old, new) - move/rename a file or directory - List(path) - list directory entries (returns []fs.DirEntry) - Stat(path) - get file information (returns fs.FileInfo) - Exists(path) - check if path exists - IsDir(path) - check if path is a directory Implements these methods in both local.Medium (using os package) and MockMedium (in-memory for testing). Includes FileInfo and DirEntry types for mock implementations. This enables migration of direct os.* calls to the Medium abstraction for consistent path validation and testability. Refs #101 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/sdk, pkgcmd, and workspace to Medium abstraction * chore(io): migrate internal/cmd/docs and internal/cmd/dev to Medium - internal/cmd/docs: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.RemoveAll with io.Local equivalents - internal/cmd/dev: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.ReadDir with io.Local equivalents - Fix local.Medium to allow absolute paths when root is "/" for full filesystem access (io.Local use case) Refs #113, #114 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): migrate internal/cmd/setup to Medium abstraction Migrated all direct os.* filesystem calls to use io.Local: - cmd_repo.go: os.MkdirAll -> io.Local.EnsureDir, os.WriteFile -> io.Local.Write, os.Stat -> io.Local.IsFile - cmd_bootstrap.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.IsDir/Exists, os.ReadDir -> io.Local.List - cmd_registry.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.Exists - cmd_ci.go: os.ReadFile -> io.Local.Read - github_config.go: os.ReadFile -> io.Local.Read, os.Stat -> io.Local.Exists Refs #116 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): add error creation and log-and-return helpers Implements issues #129 and #132: - Add Err struct with Op, Msg, Err, Code fields for structured errors - Add E(), Wrap(), WrapCode(), NewCode() for error creation - Add Is(), As(), NewError(), Join() as stdlib wrappers - Add Op(), ErrCode(), Message(), Root() for introspection - Add LogError(), LogWarn(), Must() for combined log-and-return Closes #129 Closes #132 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(errors): create deprecation alias pointing to pkg/log Makes pkg/errors a thin compatibility layer that re-exports from pkg/log. All error handling functions now have canonical implementations in pkg/log. Migration guide in package documentation: - errors.Error -> log.Err - errors.E -> log.E - errors.Code -> log.NewCode - errors.New -> log.NewError Fixes behavior consistency: - E(op, msg, nil) now creates an error (for errors without cause) - Wrap(nil, op, msg) returns nil (for conditional wrapping) - WrapCode returns nil only when both err is nil AND code is empty Closes #128 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(log): migrate pkg/errors imports to pkg/log Migrates all internal packages from pkg/errors to pkg/log: - internal/cmd/monitor - internal/cmd/qa - internal/cmd/dev - pkg/agentic Closes #130 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): address Copilot review feedback - Fix MockMedium.Rename: collect keys before mutating maps during iteration - Fix .git checks to use Exists instead of List (handles worktrees/submodules) - Fix cmd_sync.go: use DeleteAll for recursive directory removal Files updated: - pkg/io/io.go: safe map iteration in Rename - internal/cmd/setup/cmd_bootstrap.go: Exists for .git checks - internal/cmd/setup/cmd_registry.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_install.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_manage.go: Exists for .git checks - internal/cmd/docs/cmd_sync.go: DeleteAll for recursive delete Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(updater): resolve PkgVersion duplicate declaration Remove var PkgVersion from updater.go since go generate creates const PkgVersion in version.go. Track version.go in git to ensure builds work without running go generate first. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting in internal/variants Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting across migrated files Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(io): simplify local Medium implementation Rewrote to match the simpler TypeScript pattern: - path() sanitizes and returns string directly - Each method calls path() once - No complex symlink validation - Less code, less attack surface Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): remove duplicate method declarations Clean up the client.go file that had duplicate method declarations from a bad cherry-pick merge. Now has 127 lines of simple, clean code. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(io): fix traversal test to match sanitization behavior The simplified path() sanitizes .. to . without returning errors. Update test to verify sanitization works correctly. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(mcp): update sandboxing tests for simplified Medium The simplified io/local.Medium implementation: - Sanitizes .. to . (no error, path is cleaned) - Allows absolute paths through (caller validates if needed) - Follows symlinks (no traversal blocking) Update tests to match this simplified behavior. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 06:48:40 +00:00
return log.E(op, "commit message is required", nil)
}
// Build full commit message
fullMessage := buildCommitMessage(task, message)
// Stage all changes
if _, err := runGitCommandCtx(ctx, dir, "add", "-A"); err != nil {
feat(errors): Unify errors and logging (#180) * feat(help): Add CLI help command Fixes #136 * chore: remove binary * feat(mcp): Add TCP transport Fixes #126 * feat(io): Migrate pkg/mcp to use Medium abstraction Fixes #103 * feat(io): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(errors): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/docs/* to Medium abstraction Fixes #113 * chore(io): Migrate internal/cmd/dev/* to Medium abstraction Fixes #114 * chore(io): Migrate internal/cmd/setup/* to Medium abstraction * chore(io): Complete migration of internal/cmd/dev/* to Medium abstraction * feat(io): extend Medium interface with Delete, Rename, List, Stat operations Adds the following methods to the Medium interface: - Delete(path) - remove a file or empty directory - DeleteAll(path) - recursively remove a file or directory - Rename(old, new) - move/rename a file or directory - List(path) - list directory entries (returns []fs.DirEntry) - Stat(path) - get file information (returns fs.FileInfo) - Exists(path) - check if path exists - IsDir(path) - check if path is a directory Implements these methods in both local.Medium (using os package) and MockMedium (in-memory for testing). Includes FileInfo and DirEntry types for mock implementations. This enables migration of direct os.* calls to the Medium abstraction for consistent path validation and testability. Refs #101 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/sdk, pkgcmd, and workspace to Medium abstraction * chore(io): migrate internal/cmd/docs and internal/cmd/dev to Medium - internal/cmd/docs: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.RemoveAll with io.Local equivalents - internal/cmd/dev: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.ReadDir with io.Local equivalents - Fix local.Medium to allow absolute paths when root is "/" for full filesystem access (io.Local use case) Refs #113, #114 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): migrate internal/cmd/setup to Medium abstraction Migrated all direct os.* filesystem calls to use io.Local: - cmd_repo.go: os.MkdirAll -> io.Local.EnsureDir, os.WriteFile -> io.Local.Write, os.Stat -> io.Local.IsFile - cmd_bootstrap.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.IsDir/Exists, os.ReadDir -> io.Local.List - cmd_registry.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.Exists - cmd_ci.go: os.ReadFile -> io.Local.Read - github_config.go: os.ReadFile -> io.Local.Read, os.Stat -> io.Local.Exists Refs #116 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): add error creation and log-and-return helpers Implements issues #129 and #132: - Add Err struct with Op, Msg, Err, Code fields for structured errors - Add E(), Wrap(), WrapCode(), NewCode() for error creation - Add Is(), As(), NewError(), Join() as stdlib wrappers - Add Op(), ErrCode(), Message(), Root() for introspection - Add LogError(), LogWarn(), Must() for combined log-and-return Closes #129 Closes #132 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(errors): create deprecation alias pointing to pkg/log Makes pkg/errors a thin compatibility layer that re-exports from pkg/log. All error handling functions now have canonical implementations in pkg/log. Migration guide in package documentation: - errors.Error -> log.Err - errors.E -> log.E - errors.Code -> log.NewCode - errors.New -> log.NewError Fixes behavior consistency: - E(op, msg, nil) now creates an error (for errors without cause) - Wrap(nil, op, msg) returns nil (for conditional wrapping) - WrapCode returns nil only when both err is nil AND code is empty Closes #128 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(log): migrate pkg/errors imports to pkg/log Migrates all internal packages from pkg/errors to pkg/log: - internal/cmd/monitor - internal/cmd/qa - internal/cmd/dev - pkg/agentic Closes #130 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): address Copilot review feedback - Fix MockMedium.Rename: collect keys before mutating maps during iteration - Fix .git checks to use Exists instead of List (handles worktrees/submodules) - Fix cmd_sync.go: use DeleteAll for recursive directory removal Files updated: - pkg/io/io.go: safe map iteration in Rename - internal/cmd/setup/cmd_bootstrap.go: Exists for .git checks - internal/cmd/setup/cmd_registry.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_install.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_manage.go: Exists for .git checks - internal/cmd/docs/cmd_sync.go: DeleteAll for recursive delete Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(updater): resolve PkgVersion duplicate declaration Remove var PkgVersion from updater.go since go generate creates const PkgVersion in version.go. Track version.go in git to ensure builds work without running go generate first. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting in internal/variants Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting across migrated files Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(io): simplify local Medium implementation Rewrote to match the simpler TypeScript pattern: - path() sanitizes and returns string directly - Each method calls path() once - No complex symlink validation - Less code, less attack surface Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): remove duplicate method declarations Clean up the client.go file that had duplicate method declarations from a bad cherry-pick merge. Now has 127 lines of simple, clean code. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(io): fix traversal test to match sanitization behavior The simplified path() sanitizes .. to . without returning errors. Update test to verify sanitization works correctly. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(mcp): update sandboxing tests for simplified Medium The simplified io/local.Medium implementation: - Sanitizes .. to . (no error, path is cleaned) - Allows absolute paths through (caller validates if needed) - Follows symlinks (no traversal blocking) Update tests to match this simplified behavior. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 06:48:40 +00:00
return log.E(op, "failed to stage changes", err)
}
// Create commit
if _, err := runGitCommandCtx(ctx, dir, "commit", "-m", fullMessage); err != nil {
feat(errors): Unify errors and logging (#180) * feat(help): Add CLI help command Fixes #136 * chore: remove binary * feat(mcp): Add TCP transport Fixes #126 * feat(io): Migrate pkg/mcp to use Medium abstraction Fixes #103 * feat(io): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(errors): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/docs/* to Medium abstraction Fixes #113 * chore(io): Migrate internal/cmd/dev/* to Medium abstraction Fixes #114 * chore(io): Migrate internal/cmd/setup/* to Medium abstraction * chore(io): Complete migration of internal/cmd/dev/* to Medium abstraction * feat(io): extend Medium interface with Delete, Rename, List, Stat operations Adds the following methods to the Medium interface: - Delete(path) - remove a file or empty directory - DeleteAll(path) - recursively remove a file or directory - Rename(old, new) - move/rename a file or directory - List(path) - list directory entries (returns []fs.DirEntry) - Stat(path) - get file information (returns fs.FileInfo) - Exists(path) - check if path exists - IsDir(path) - check if path is a directory Implements these methods in both local.Medium (using os package) and MockMedium (in-memory for testing). Includes FileInfo and DirEntry types for mock implementations. This enables migration of direct os.* calls to the Medium abstraction for consistent path validation and testability. Refs #101 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/sdk, pkgcmd, and workspace to Medium abstraction * chore(io): migrate internal/cmd/docs and internal/cmd/dev to Medium - internal/cmd/docs: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.RemoveAll with io.Local equivalents - internal/cmd/dev: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.ReadDir with io.Local equivalents - Fix local.Medium to allow absolute paths when root is "/" for full filesystem access (io.Local use case) Refs #113, #114 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): migrate internal/cmd/setup to Medium abstraction Migrated all direct os.* filesystem calls to use io.Local: - cmd_repo.go: os.MkdirAll -> io.Local.EnsureDir, os.WriteFile -> io.Local.Write, os.Stat -> io.Local.IsFile - cmd_bootstrap.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.IsDir/Exists, os.ReadDir -> io.Local.List - cmd_registry.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.Exists - cmd_ci.go: os.ReadFile -> io.Local.Read - github_config.go: os.ReadFile -> io.Local.Read, os.Stat -> io.Local.Exists Refs #116 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): add error creation and log-and-return helpers Implements issues #129 and #132: - Add Err struct with Op, Msg, Err, Code fields for structured errors - Add E(), Wrap(), WrapCode(), NewCode() for error creation - Add Is(), As(), NewError(), Join() as stdlib wrappers - Add Op(), ErrCode(), Message(), Root() for introspection - Add LogError(), LogWarn(), Must() for combined log-and-return Closes #129 Closes #132 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(errors): create deprecation alias pointing to pkg/log Makes pkg/errors a thin compatibility layer that re-exports from pkg/log. All error handling functions now have canonical implementations in pkg/log. Migration guide in package documentation: - errors.Error -> log.Err - errors.E -> log.E - errors.Code -> log.NewCode - errors.New -> log.NewError Fixes behavior consistency: - E(op, msg, nil) now creates an error (for errors without cause) - Wrap(nil, op, msg) returns nil (for conditional wrapping) - WrapCode returns nil only when both err is nil AND code is empty Closes #128 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(log): migrate pkg/errors imports to pkg/log Migrates all internal packages from pkg/errors to pkg/log: - internal/cmd/monitor - internal/cmd/qa - internal/cmd/dev - pkg/agentic Closes #130 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): address Copilot review feedback - Fix MockMedium.Rename: collect keys before mutating maps during iteration - Fix .git checks to use Exists instead of List (handles worktrees/submodules) - Fix cmd_sync.go: use DeleteAll for recursive directory removal Files updated: - pkg/io/io.go: safe map iteration in Rename - internal/cmd/setup/cmd_bootstrap.go: Exists for .git checks - internal/cmd/setup/cmd_registry.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_install.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_manage.go: Exists for .git checks - internal/cmd/docs/cmd_sync.go: DeleteAll for recursive delete Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(updater): resolve PkgVersion duplicate declaration Remove var PkgVersion from updater.go since go generate creates const PkgVersion in version.go. Track version.go in git to ensure builds work without running go generate first. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting in internal/variants Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting across migrated files Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(io): simplify local Medium implementation Rewrote to match the simpler TypeScript pattern: - path() sanitizes and returns string directly - Each method calls path() once - No complex symlink validation - Less code, less attack surface Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): remove duplicate method declarations Clean up the client.go file that had duplicate method declarations from a bad cherry-pick merge. Now has 127 lines of simple, clean code. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(io): fix traversal test to match sanitization behavior The simplified path() sanitizes .. to . without returning errors. Update test to verify sanitization works correctly. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(mcp): update sandboxing tests for simplified Medium The simplified io/local.Medium implementation: - Sanitizes .. to . (no error, path is cleaned) - Allows absolute paths through (caller validates if needed) - Follows symlinks (no traversal blocking) Update tests to match this simplified behavior. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 06:48:40 +00:00
return log.E(op, "failed to create commit", err)
}
return nil
}
// buildCommitMessage formats a commit message with task reference.
func buildCommitMessage(task *Task, message string) string {
var sb strings.Builder
// Write the main message
sb.WriteString(message)
sb.WriteString("\n\n")
// Add task reference
sb.WriteString("Task: #")
sb.WriteString(task.ID)
sb.WriteString("\n")
// Add co-author
sb.WriteString("Co-Authored-By: Claude <noreply@anthropic.com>\n")
return sb.String()
}
// CreatePR creates a pull request using the gh CLI.
func CreatePR(ctx context.Context, task *Task, dir string, opts PROptions) (string, error) {
const op = "agentic.CreatePR"
if task == nil {
feat(errors): Unify errors and logging (#180) * feat(help): Add CLI help command Fixes #136 * chore: remove binary * feat(mcp): Add TCP transport Fixes #126 * feat(io): Migrate pkg/mcp to use Medium abstraction Fixes #103 * feat(io): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(errors): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/docs/* to Medium abstraction Fixes #113 * chore(io): Migrate internal/cmd/dev/* to Medium abstraction Fixes #114 * chore(io): Migrate internal/cmd/setup/* to Medium abstraction * chore(io): Complete migration of internal/cmd/dev/* to Medium abstraction * feat(io): extend Medium interface with Delete, Rename, List, Stat operations Adds the following methods to the Medium interface: - Delete(path) - remove a file or empty directory - DeleteAll(path) - recursively remove a file or directory - Rename(old, new) - move/rename a file or directory - List(path) - list directory entries (returns []fs.DirEntry) - Stat(path) - get file information (returns fs.FileInfo) - Exists(path) - check if path exists - IsDir(path) - check if path is a directory Implements these methods in both local.Medium (using os package) and MockMedium (in-memory for testing). Includes FileInfo and DirEntry types for mock implementations. This enables migration of direct os.* calls to the Medium abstraction for consistent path validation and testability. Refs #101 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/sdk, pkgcmd, and workspace to Medium abstraction * chore(io): migrate internal/cmd/docs and internal/cmd/dev to Medium - internal/cmd/docs: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.RemoveAll with io.Local equivalents - internal/cmd/dev: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.ReadDir with io.Local equivalents - Fix local.Medium to allow absolute paths when root is "/" for full filesystem access (io.Local use case) Refs #113, #114 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): migrate internal/cmd/setup to Medium abstraction Migrated all direct os.* filesystem calls to use io.Local: - cmd_repo.go: os.MkdirAll -> io.Local.EnsureDir, os.WriteFile -> io.Local.Write, os.Stat -> io.Local.IsFile - cmd_bootstrap.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.IsDir/Exists, os.ReadDir -> io.Local.List - cmd_registry.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.Exists - cmd_ci.go: os.ReadFile -> io.Local.Read - github_config.go: os.ReadFile -> io.Local.Read, os.Stat -> io.Local.Exists Refs #116 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): add error creation and log-and-return helpers Implements issues #129 and #132: - Add Err struct with Op, Msg, Err, Code fields for structured errors - Add E(), Wrap(), WrapCode(), NewCode() for error creation - Add Is(), As(), NewError(), Join() as stdlib wrappers - Add Op(), ErrCode(), Message(), Root() for introspection - Add LogError(), LogWarn(), Must() for combined log-and-return Closes #129 Closes #132 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(errors): create deprecation alias pointing to pkg/log Makes pkg/errors a thin compatibility layer that re-exports from pkg/log. All error handling functions now have canonical implementations in pkg/log. Migration guide in package documentation: - errors.Error -> log.Err - errors.E -> log.E - errors.Code -> log.NewCode - errors.New -> log.NewError Fixes behavior consistency: - E(op, msg, nil) now creates an error (for errors without cause) - Wrap(nil, op, msg) returns nil (for conditional wrapping) - WrapCode returns nil only when both err is nil AND code is empty Closes #128 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(log): migrate pkg/errors imports to pkg/log Migrates all internal packages from pkg/errors to pkg/log: - internal/cmd/monitor - internal/cmd/qa - internal/cmd/dev - pkg/agentic Closes #130 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): address Copilot review feedback - Fix MockMedium.Rename: collect keys before mutating maps during iteration - Fix .git checks to use Exists instead of List (handles worktrees/submodules) - Fix cmd_sync.go: use DeleteAll for recursive directory removal Files updated: - pkg/io/io.go: safe map iteration in Rename - internal/cmd/setup/cmd_bootstrap.go: Exists for .git checks - internal/cmd/setup/cmd_registry.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_install.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_manage.go: Exists for .git checks - internal/cmd/docs/cmd_sync.go: DeleteAll for recursive delete Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(updater): resolve PkgVersion duplicate declaration Remove var PkgVersion from updater.go since go generate creates const PkgVersion in version.go. Track version.go in git to ensure builds work without running go generate first. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting in internal/variants Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting across migrated files Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(io): simplify local Medium implementation Rewrote to match the simpler TypeScript pattern: - path() sanitizes and returns string directly - Each method calls path() once - No complex symlink validation - Less code, less attack surface Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): remove duplicate method declarations Clean up the client.go file that had duplicate method declarations from a bad cherry-pick merge. Now has 127 lines of simple, clean code. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(io): fix traversal test to match sanitization behavior The simplified path() sanitizes .. to . without returning errors. Update test to verify sanitization works correctly. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(mcp): update sandboxing tests for simplified Medium The simplified io/local.Medium implementation: - Sanitizes .. to . (no error, path is cleaned) - Allows absolute paths through (caller validates if needed) - Follows symlinks (no traversal blocking) Update tests to match this simplified behavior. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 06:48:40 +00:00
return "", log.E(op, "task is required", nil)
}
// Build title if not provided
title := opts.Title
if title == "" {
title = task.Title
}
// Build body if not provided
body := opts.Body
if body == "" {
body = buildPRBody(task)
}
// Build gh command arguments
args := []string{"pr", "create", "--title", title, "--body", body}
if opts.Draft {
args = append(args, "--draft")
}
if opts.Base != "" {
args = append(args, "--base", opts.Base)
}
for _, label := range opts.Labels {
args = append(args, "--label", label)
}
// Run gh pr create
output, err := runCommandCtx(ctx, dir, "gh", args...)
if err != nil {
feat(errors): Unify errors and logging (#180) * feat(help): Add CLI help command Fixes #136 * chore: remove binary * feat(mcp): Add TCP transport Fixes #126 * feat(io): Migrate pkg/mcp to use Medium abstraction Fixes #103 * feat(io): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(errors): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/docs/* to Medium abstraction Fixes #113 * chore(io): Migrate internal/cmd/dev/* to Medium abstraction Fixes #114 * chore(io): Migrate internal/cmd/setup/* to Medium abstraction * chore(io): Complete migration of internal/cmd/dev/* to Medium abstraction * feat(io): extend Medium interface with Delete, Rename, List, Stat operations Adds the following methods to the Medium interface: - Delete(path) - remove a file or empty directory - DeleteAll(path) - recursively remove a file or directory - Rename(old, new) - move/rename a file or directory - List(path) - list directory entries (returns []fs.DirEntry) - Stat(path) - get file information (returns fs.FileInfo) - Exists(path) - check if path exists - IsDir(path) - check if path is a directory Implements these methods in both local.Medium (using os package) and MockMedium (in-memory for testing). Includes FileInfo and DirEntry types for mock implementations. This enables migration of direct os.* calls to the Medium abstraction for consistent path validation and testability. Refs #101 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/sdk, pkgcmd, and workspace to Medium abstraction * chore(io): migrate internal/cmd/docs and internal/cmd/dev to Medium - internal/cmd/docs: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.RemoveAll with io.Local equivalents - internal/cmd/dev: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.ReadDir with io.Local equivalents - Fix local.Medium to allow absolute paths when root is "/" for full filesystem access (io.Local use case) Refs #113, #114 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): migrate internal/cmd/setup to Medium abstraction Migrated all direct os.* filesystem calls to use io.Local: - cmd_repo.go: os.MkdirAll -> io.Local.EnsureDir, os.WriteFile -> io.Local.Write, os.Stat -> io.Local.IsFile - cmd_bootstrap.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.IsDir/Exists, os.ReadDir -> io.Local.List - cmd_registry.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.Exists - cmd_ci.go: os.ReadFile -> io.Local.Read - github_config.go: os.ReadFile -> io.Local.Read, os.Stat -> io.Local.Exists Refs #116 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): add error creation and log-and-return helpers Implements issues #129 and #132: - Add Err struct with Op, Msg, Err, Code fields for structured errors - Add E(), Wrap(), WrapCode(), NewCode() for error creation - Add Is(), As(), NewError(), Join() as stdlib wrappers - Add Op(), ErrCode(), Message(), Root() for introspection - Add LogError(), LogWarn(), Must() for combined log-and-return Closes #129 Closes #132 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(errors): create deprecation alias pointing to pkg/log Makes pkg/errors a thin compatibility layer that re-exports from pkg/log. All error handling functions now have canonical implementations in pkg/log. Migration guide in package documentation: - errors.Error -> log.Err - errors.E -> log.E - errors.Code -> log.NewCode - errors.New -> log.NewError Fixes behavior consistency: - E(op, msg, nil) now creates an error (for errors without cause) - Wrap(nil, op, msg) returns nil (for conditional wrapping) - WrapCode returns nil only when both err is nil AND code is empty Closes #128 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(log): migrate pkg/errors imports to pkg/log Migrates all internal packages from pkg/errors to pkg/log: - internal/cmd/monitor - internal/cmd/qa - internal/cmd/dev - pkg/agentic Closes #130 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): address Copilot review feedback - Fix MockMedium.Rename: collect keys before mutating maps during iteration - Fix .git checks to use Exists instead of List (handles worktrees/submodules) - Fix cmd_sync.go: use DeleteAll for recursive directory removal Files updated: - pkg/io/io.go: safe map iteration in Rename - internal/cmd/setup/cmd_bootstrap.go: Exists for .git checks - internal/cmd/setup/cmd_registry.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_install.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_manage.go: Exists for .git checks - internal/cmd/docs/cmd_sync.go: DeleteAll for recursive delete Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(updater): resolve PkgVersion duplicate declaration Remove var PkgVersion from updater.go since go generate creates const PkgVersion in version.go. Track version.go in git to ensure builds work without running go generate first. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting in internal/variants Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting across migrated files Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(io): simplify local Medium implementation Rewrote to match the simpler TypeScript pattern: - path() sanitizes and returns string directly - Each method calls path() once - No complex symlink validation - Less code, less attack surface Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): remove duplicate method declarations Clean up the client.go file that had duplicate method declarations from a bad cherry-pick merge. Now has 127 lines of simple, clean code. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(io): fix traversal test to match sanitization behavior The simplified path() sanitizes .. to . without returning errors. Update test to verify sanitization works correctly. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(mcp): update sandboxing tests for simplified Medium The simplified io/local.Medium implementation: - Sanitizes .. to . (no error, path is cleaned) - Allows absolute paths through (caller validates if needed) - Follows symlinks (no traversal blocking) Update tests to match this simplified behavior. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 06:48:40 +00:00
return "", log.E(op, "failed to create PR", err)
}
// Extract PR URL from output
prURL := strings.TrimSpace(output)
return prURL, nil
}
// buildPRBody creates a PR body from task details.
func buildPRBody(task *Task) string {
var sb strings.Builder
sb.WriteString("## Summary\n\n")
sb.WriteString(task.Description)
sb.WriteString("\n\n")
sb.WriteString("## Task Reference\n\n")
sb.WriteString("- Task ID: #")
sb.WriteString(task.ID)
sb.WriteString("\n")
sb.WriteString("- Priority: ")
sb.WriteString(string(task.Priority))
sb.WriteString("\n")
if len(task.Labels) > 0 {
sb.WriteString("- Labels: ")
sb.WriteString(strings.Join(task.Labels, ", "))
sb.WriteString("\n")
}
sb.WriteString("\n---\n")
sb.WriteString("Generated with AI assistance\n")
return sb.String()
}
// SyncStatus syncs the task status back to the agentic service.
func SyncStatus(ctx context.Context, client *Client, task *Task, update TaskUpdate) error {
const op = "agentic.SyncStatus"
if client == nil {
feat(errors): Unify errors and logging (#180) * feat(help): Add CLI help command Fixes #136 * chore: remove binary * feat(mcp): Add TCP transport Fixes #126 * feat(io): Migrate pkg/mcp to use Medium abstraction Fixes #103 * feat(io): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(errors): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/docs/* to Medium abstraction Fixes #113 * chore(io): Migrate internal/cmd/dev/* to Medium abstraction Fixes #114 * chore(io): Migrate internal/cmd/setup/* to Medium abstraction * chore(io): Complete migration of internal/cmd/dev/* to Medium abstraction * feat(io): extend Medium interface with Delete, Rename, List, Stat operations Adds the following methods to the Medium interface: - Delete(path) - remove a file or empty directory - DeleteAll(path) - recursively remove a file or directory - Rename(old, new) - move/rename a file or directory - List(path) - list directory entries (returns []fs.DirEntry) - Stat(path) - get file information (returns fs.FileInfo) - Exists(path) - check if path exists - IsDir(path) - check if path is a directory Implements these methods in both local.Medium (using os package) and MockMedium (in-memory for testing). Includes FileInfo and DirEntry types for mock implementations. This enables migration of direct os.* calls to the Medium abstraction for consistent path validation and testability. Refs #101 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/sdk, pkgcmd, and workspace to Medium abstraction * chore(io): migrate internal/cmd/docs and internal/cmd/dev to Medium - internal/cmd/docs: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.RemoveAll with io.Local equivalents - internal/cmd/dev: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.ReadDir with io.Local equivalents - Fix local.Medium to allow absolute paths when root is "/" for full filesystem access (io.Local use case) Refs #113, #114 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): migrate internal/cmd/setup to Medium abstraction Migrated all direct os.* filesystem calls to use io.Local: - cmd_repo.go: os.MkdirAll -> io.Local.EnsureDir, os.WriteFile -> io.Local.Write, os.Stat -> io.Local.IsFile - cmd_bootstrap.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.IsDir/Exists, os.ReadDir -> io.Local.List - cmd_registry.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.Exists - cmd_ci.go: os.ReadFile -> io.Local.Read - github_config.go: os.ReadFile -> io.Local.Read, os.Stat -> io.Local.Exists Refs #116 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): add error creation and log-and-return helpers Implements issues #129 and #132: - Add Err struct with Op, Msg, Err, Code fields for structured errors - Add E(), Wrap(), WrapCode(), NewCode() for error creation - Add Is(), As(), NewError(), Join() as stdlib wrappers - Add Op(), ErrCode(), Message(), Root() for introspection - Add LogError(), LogWarn(), Must() for combined log-and-return Closes #129 Closes #132 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(errors): create deprecation alias pointing to pkg/log Makes pkg/errors a thin compatibility layer that re-exports from pkg/log. All error handling functions now have canonical implementations in pkg/log. Migration guide in package documentation: - errors.Error -> log.Err - errors.E -> log.E - errors.Code -> log.NewCode - errors.New -> log.NewError Fixes behavior consistency: - E(op, msg, nil) now creates an error (for errors without cause) - Wrap(nil, op, msg) returns nil (for conditional wrapping) - WrapCode returns nil only when both err is nil AND code is empty Closes #128 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(log): migrate pkg/errors imports to pkg/log Migrates all internal packages from pkg/errors to pkg/log: - internal/cmd/monitor - internal/cmd/qa - internal/cmd/dev - pkg/agentic Closes #130 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): address Copilot review feedback - Fix MockMedium.Rename: collect keys before mutating maps during iteration - Fix .git checks to use Exists instead of List (handles worktrees/submodules) - Fix cmd_sync.go: use DeleteAll for recursive directory removal Files updated: - pkg/io/io.go: safe map iteration in Rename - internal/cmd/setup/cmd_bootstrap.go: Exists for .git checks - internal/cmd/setup/cmd_registry.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_install.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_manage.go: Exists for .git checks - internal/cmd/docs/cmd_sync.go: DeleteAll for recursive delete Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(updater): resolve PkgVersion duplicate declaration Remove var PkgVersion from updater.go since go generate creates const PkgVersion in version.go. Track version.go in git to ensure builds work without running go generate first. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting in internal/variants Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting across migrated files Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(io): simplify local Medium implementation Rewrote to match the simpler TypeScript pattern: - path() sanitizes and returns string directly - Each method calls path() once - No complex symlink validation - Less code, less attack surface Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): remove duplicate method declarations Clean up the client.go file that had duplicate method declarations from a bad cherry-pick merge. Now has 127 lines of simple, clean code. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(io): fix traversal test to match sanitization behavior The simplified path() sanitizes .. to . without returning errors. Update test to verify sanitization works correctly. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(mcp): update sandboxing tests for simplified Medium The simplified io/local.Medium implementation: - Sanitizes .. to . (no error, path is cleaned) - Allows absolute paths through (caller validates if needed) - Follows symlinks (no traversal blocking) Update tests to match this simplified behavior. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 06:48:40 +00:00
return log.E(op, "client is required", nil)
}
if task == nil {
feat(errors): Unify errors and logging (#180) * feat(help): Add CLI help command Fixes #136 * chore: remove binary * feat(mcp): Add TCP transport Fixes #126 * feat(io): Migrate pkg/mcp to use Medium abstraction Fixes #103 * feat(io): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(errors): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/docs/* to Medium abstraction Fixes #113 * chore(io): Migrate internal/cmd/dev/* to Medium abstraction Fixes #114 * chore(io): Migrate internal/cmd/setup/* to Medium abstraction * chore(io): Complete migration of internal/cmd/dev/* to Medium abstraction * feat(io): extend Medium interface with Delete, Rename, List, Stat operations Adds the following methods to the Medium interface: - Delete(path) - remove a file or empty directory - DeleteAll(path) - recursively remove a file or directory - Rename(old, new) - move/rename a file or directory - List(path) - list directory entries (returns []fs.DirEntry) - Stat(path) - get file information (returns fs.FileInfo) - Exists(path) - check if path exists - IsDir(path) - check if path is a directory Implements these methods in both local.Medium (using os package) and MockMedium (in-memory for testing). Includes FileInfo and DirEntry types for mock implementations. This enables migration of direct os.* calls to the Medium abstraction for consistent path validation and testability. Refs #101 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/sdk, pkgcmd, and workspace to Medium abstraction * chore(io): migrate internal/cmd/docs and internal/cmd/dev to Medium - internal/cmd/docs: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.RemoveAll with io.Local equivalents - internal/cmd/dev: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.ReadDir with io.Local equivalents - Fix local.Medium to allow absolute paths when root is "/" for full filesystem access (io.Local use case) Refs #113, #114 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): migrate internal/cmd/setup to Medium abstraction Migrated all direct os.* filesystem calls to use io.Local: - cmd_repo.go: os.MkdirAll -> io.Local.EnsureDir, os.WriteFile -> io.Local.Write, os.Stat -> io.Local.IsFile - cmd_bootstrap.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.IsDir/Exists, os.ReadDir -> io.Local.List - cmd_registry.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.Exists - cmd_ci.go: os.ReadFile -> io.Local.Read - github_config.go: os.ReadFile -> io.Local.Read, os.Stat -> io.Local.Exists Refs #116 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): add error creation and log-and-return helpers Implements issues #129 and #132: - Add Err struct with Op, Msg, Err, Code fields for structured errors - Add E(), Wrap(), WrapCode(), NewCode() for error creation - Add Is(), As(), NewError(), Join() as stdlib wrappers - Add Op(), ErrCode(), Message(), Root() for introspection - Add LogError(), LogWarn(), Must() for combined log-and-return Closes #129 Closes #132 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(errors): create deprecation alias pointing to pkg/log Makes pkg/errors a thin compatibility layer that re-exports from pkg/log. All error handling functions now have canonical implementations in pkg/log. Migration guide in package documentation: - errors.Error -> log.Err - errors.E -> log.E - errors.Code -> log.NewCode - errors.New -> log.NewError Fixes behavior consistency: - E(op, msg, nil) now creates an error (for errors without cause) - Wrap(nil, op, msg) returns nil (for conditional wrapping) - WrapCode returns nil only when both err is nil AND code is empty Closes #128 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(log): migrate pkg/errors imports to pkg/log Migrates all internal packages from pkg/errors to pkg/log: - internal/cmd/monitor - internal/cmd/qa - internal/cmd/dev - pkg/agentic Closes #130 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): address Copilot review feedback - Fix MockMedium.Rename: collect keys before mutating maps during iteration - Fix .git checks to use Exists instead of List (handles worktrees/submodules) - Fix cmd_sync.go: use DeleteAll for recursive directory removal Files updated: - pkg/io/io.go: safe map iteration in Rename - internal/cmd/setup/cmd_bootstrap.go: Exists for .git checks - internal/cmd/setup/cmd_registry.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_install.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_manage.go: Exists for .git checks - internal/cmd/docs/cmd_sync.go: DeleteAll for recursive delete Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(updater): resolve PkgVersion duplicate declaration Remove var PkgVersion from updater.go since go generate creates const PkgVersion in version.go. Track version.go in git to ensure builds work without running go generate first. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting in internal/variants Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting across migrated files Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(io): simplify local Medium implementation Rewrote to match the simpler TypeScript pattern: - path() sanitizes and returns string directly - Each method calls path() once - No complex symlink validation - Less code, less attack surface Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): remove duplicate method declarations Clean up the client.go file that had duplicate method declarations from a bad cherry-pick merge. Now has 127 lines of simple, clean code. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(io): fix traversal test to match sanitization behavior The simplified path() sanitizes .. to . without returning errors. Update test to verify sanitization works correctly. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(mcp): update sandboxing tests for simplified Medium The simplified io/local.Medium implementation: - Sanitizes .. to . (no error, path is cleaned) - Allows absolute paths through (caller validates if needed) - Follows symlinks (no traversal blocking) Update tests to match this simplified behavior. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 06:48:40 +00:00
return log.E(op, "task is required", nil)
}
return client.UpdateTask(ctx, task.ID, update)
}
// CommitAndSync commits changes and syncs task status.
func CommitAndSync(ctx context.Context, client *Client, task *Task, dir string, message string, progress int) error {
const op = "agentic.CommitAndSync"
// Create commit
if err := AutoCommit(ctx, task, dir, message); err != nil {
feat(errors): Unify errors and logging (#180) * feat(help): Add CLI help command Fixes #136 * chore: remove binary * feat(mcp): Add TCP transport Fixes #126 * feat(io): Migrate pkg/mcp to use Medium abstraction Fixes #103 * feat(io): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(errors): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/docs/* to Medium abstraction Fixes #113 * chore(io): Migrate internal/cmd/dev/* to Medium abstraction Fixes #114 * chore(io): Migrate internal/cmd/setup/* to Medium abstraction * chore(io): Complete migration of internal/cmd/dev/* to Medium abstraction * feat(io): extend Medium interface with Delete, Rename, List, Stat operations Adds the following methods to the Medium interface: - Delete(path) - remove a file or empty directory - DeleteAll(path) - recursively remove a file or directory - Rename(old, new) - move/rename a file or directory - List(path) - list directory entries (returns []fs.DirEntry) - Stat(path) - get file information (returns fs.FileInfo) - Exists(path) - check if path exists - IsDir(path) - check if path is a directory Implements these methods in both local.Medium (using os package) and MockMedium (in-memory for testing). Includes FileInfo and DirEntry types for mock implementations. This enables migration of direct os.* calls to the Medium abstraction for consistent path validation and testability. Refs #101 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/sdk, pkgcmd, and workspace to Medium abstraction * chore(io): migrate internal/cmd/docs and internal/cmd/dev to Medium - internal/cmd/docs: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.RemoveAll with io.Local equivalents - internal/cmd/dev: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.ReadDir with io.Local equivalents - Fix local.Medium to allow absolute paths when root is "/" for full filesystem access (io.Local use case) Refs #113, #114 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): migrate internal/cmd/setup to Medium abstraction Migrated all direct os.* filesystem calls to use io.Local: - cmd_repo.go: os.MkdirAll -> io.Local.EnsureDir, os.WriteFile -> io.Local.Write, os.Stat -> io.Local.IsFile - cmd_bootstrap.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.IsDir/Exists, os.ReadDir -> io.Local.List - cmd_registry.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.Exists - cmd_ci.go: os.ReadFile -> io.Local.Read - github_config.go: os.ReadFile -> io.Local.Read, os.Stat -> io.Local.Exists Refs #116 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): add error creation and log-and-return helpers Implements issues #129 and #132: - Add Err struct with Op, Msg, Err, Code fields for structured errors - Add E(), Wrap(), WrapCode(), NewCode() for error creation - Add Is(), As(), NewError(), Join() as stdlib wrappers - Add Op(), ErrCode(), Message(), Root() for introspection - Add LogError(), LogWarn(), Must() for combined log-and-return Closes #129 Closes #132 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(errors): create deprecation alias pointing to pkg/log Makes pkg/errors a thin compatibility layer that re-exports from pkg/log. All error handling functions now have canonical implementations in pkg/log. Migration guide in package documentation: - errors.Error -> log.Err - errors.E -> log.E - errors.Code -> log.NewCode - errors.New -> log.NewError Fixes behavior consistency: - E(op, msg, nil) now creates an error (for errors without cause) - Wrap(nil, op, msg) returns nil (for conditional wrapping) - WrapCode returns nil only when both err is nil AND code is empty Closes #128 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(log): migrate pkg/errors imports to pkg/log Migrates all internal packages from pkg/errors to pkg/log: - internal/cmd/monitor - internal/cmd/qa - internal/cmd/dev - pkg/agentic Closes #130 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): address Copilot review feedback - Fix MockMedium.Rename: collect keys before mutating maps during iteration - Fix .git checks to use Exists instead of List (handles worktrees/submodules) - Fix cmd_sync.go: use DeleteAll for recursive directory removal Files updated: - pkg/io/io.go: safe map iteration in Rename - internal/cmd/setup/cmd_bootstrap.go: Exists for .git checks - internal/cmd/setup/cmd_registry.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_install.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_manage.go: Exists for .git checks - internal/cmd/docs/cmd_sync.go: DeleteAll for recursive delete Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(updater): resolve PkgVersion duplicate declaration Remove var PkgVersion from updater.go since go generate creates const PkgVersion in version.go. Track version.go in git to ensure builds work without running go generate first. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting in internal/variants Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting across migrated files Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(io): simplify local Medium implementation Rewrote to match the simpler TypeScript pattern: - path() sanitizes and returns string directly - Each method calls path() once - No complex symlink validation - Less code, less attack surface Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): remove duplicate method declarations Clean up the client.go file that had duplicate method declarations from a bad cherry-pick merge. Now has 127 lines of simple, clean code. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(io): fix traversal test to match sanitization behavior The simplified path() sanitizes .. to . without returning errors. Update test to verify sanitization works correctly. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(mcp): update sandboxing tests for simplified Medium The simplified io/local.Medium implementation: - Sanitizes .. to . (no error, path is cleaned) - Allows absolute paths through (caller validates if needed) - Follows symlinks (no traversal blocking) Update tests to match this simplified behavior. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 06:48:40 +00:00
return log.E(op, "failed to commit", err)
}
// Sync status if client provided
if client != nil {
update := TaskUpdate{
Status: StatusInProgress,
Progress: progress,
Notes: "Committed: " + message,
}
if err := SyncStatus(ctx, client, task, update); err != nil {
// Log but don't fail on sync errors
feat(errors): Unify errors and logging (#180) * feat(help): Add CLI help command Fixes #136 * chore: remove binary * feat(mcp): Add TCP transport Fixes #126 * feat(io): Migrate pkg/mcp to use Medium abstraction Fixes #103 * feat(io): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(errors): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/docs/* to Medium abstraction Fixes #113 * chore(io): Migrate internal/cmd/dev/* to Medium abstraction Fixes #114 * chore(io): Migrate internal/cmd/setup/* to Medium abstraction * chore(io): Complete migration of internal/cmd/dev/* to Medium abstraction * feat(io): extend Medium interface with Delete, Rename, List, Stat operations Adds the following methods to the Medium interface: - Delete(path) - remove a file or empty directory - DeleteAll(path) - recursively remove a file or directory - Rename(old, new) - move/rename a file or directory - List(path) - list directory entries (returns []fs.DirEntry) - Stat(path) - get file information (returns fs.FileInfo) - Exists(path) - check if path exists - IsDir(path) - check if path is a directory Implements these methods in both local.Medium (using os package) and MockMedium (in-memory for testing). Includes FileInfo and DirEntry types for mock implementations. This enables migration of direct os.* calls to the Medium abstraction for consistent path validation and testability. Refs #101 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/sdk, pkgcmd, and workspace to Medium abstraction * chore(io): migrate internal/cmd/docs and internal/cmd/dev to Medium - internal/cmd/docs: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.RemoveAll with io.Local equivalents - internal/cmd/dev: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.ReadDir with io.Local equivalents - Fix local.Medium to allow absolute paths when root is "/" for full filesystem access (io.Local use case) Refs #113, #114 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): migrate internal/cmd/setup to Medium abstraction Migrated all direct os.* filesystem calls to use io.Local: - cmd_repo.go: os.MkdirAll -> io.Local.EnsureDir, os.WriteFile -> io.Local.Write, os.Stat -> io.Local.IsFile - cmd_bootstrap.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.IsDir/Exists, os.ReadDir -> io.Local.List - cmd_registry.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.Exists - cmd_ci.go: os.ReadFile -> io.Local.Read - github_config.go: os.ReadFile -> io.Local.Read, os.Stat -> io.Local.Exists Refs #116 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): add error creation and log-and-return helpers Implements issues #129 and #132: - Add Err struct with Op, Msg, Err, Code fields for structured errors - Add E(), Wrap(), WrapCode(), NewCode() for error creation - Add Is(), As(), NewError(), Join() as stdlib wrappers - Add Op(), ErrCode(), Message(), Root() for introspection - Add LogError(), LogWarn(), Must() for combined log-and-return Closes #129 Closes #132 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(errors): create deprecation alias pointing to pkg/log Makes pkg/errors a thin compatibility layer that re-exports from pkg/log. All error handling functions now have canonical implementations in pkg/log. Migration guide in package documentation: - errors.Error -> log.Err - errors.E -> log.E - errors.Code -> log.NewCode - errors.New -> log.NewError Fixes behavior consistency: - E(op, msg, nil) now creates an error (for errors without cause) - Wrap(nil, op, msg) returns nil (for conditional wrapping) - WrapCode returns nil only when both err is nil AND code is empty Closes #128 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(log): migrate pkg/errors imports to pkg/log Migrates all internal packages from pkg/errors to pkg/log: - internal/cmd/monitor - internal/cmd/qa - internal/cmd/dev - pkg/agentic Closes #130 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): address Copilot review feedback - Fix MockMedium.Rename: collect keys before mutating maps during iteration - Fix .git checks to use Exists instead of List (handles worktrees/submodules) - Fix cmd_sync.go: use DeleteAll for recursive directory removal Files updated: - pkg/io/io.go: safe map iteration in Rename - internal/cmd/setup/cmd_bootstrap.go: Exists for .git checks - internal/cmd/setup/cmd_registry.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_install.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_manage.go: Exists for .git checks - internal/cmd/docs/cmd_sync.go: DeleteAll for recursive delete Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(updater): resolve PkgVersion duplicate declaration Remove var PkgVersion from updater.go since go generate creates const PkgVersion in version.go. Track version.go in git to ensure builds work without running go generate first. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting in internal/variants Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting across migrated files Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(io): simplify local Medium implementation Rewrote to match the simpler TypeScript pattern: - path() sanitizes and returns string directly - Each method calls path() once - No complex symlink validation - Less code, less attack surface Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): remove duplicate method declarations Clean up the client.go file that had duplicate method declarations from a bad cherry-pick merge. Now has 127 lines of simple, clean code. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(io): fix traversal test to match sanitization behavior The simplified path() sanitizes .. to . without returning errors. Update test to verify sanitization works correctly. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(mcp): update sandboxing tests for simplified Medium The simplified io/local.Medium implementation: - Sanitizes .. to . (no error, path is cleaned) - Allows absolute paths through (caller validates if needed) - Follows symlinks (no traversal blocking) Update tests to match this simplified behavior. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 06:48:40 +00:00
return log.E(op, "commit succeeded but sync failed", err)
}
}
return nil
}
// PushChanges pushes committed changes to the remote.
func PushChanges(ctx context.Context, dir string) error {
const op = "agentic.PushChanges"
_, err := runGitCommandCtx(ctx, dir, "push")
if err != nil {
feat(errors): Unify errors and logging (#180) * feat(help): Add CLI help command Fixes #136 * chore: remove binary * feat(mcp): Add TCP transport Fixes #126 * feat(io): Migrate pkg/mcp to use Medium abstraction Fixes #103 * feat(io): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(errors): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/docs/* to Medium abstraction Fixes #113 * chore(io): Migrate internal/cmd/dev/* to Medium abstraction Fixes #114 * chore(io): Migrate internal/cmd/setup/* to Medium abstraction * chore(io): Complete migration of internal/cmd/dev/* to Medium abstraction * feat(io): extend Medium interface with Delete, Rename, List, Stat operations Adds the following methods to the Medium interface: - Delete(path) - remove a file or empty directory - DeleteAll(path) - recursively remove a file or directory - Rename(old, new) - move/rename a file or directory - List(path) - list directory entries (returns []fs.DirEntry) - Stat(path) - get file information (returns fs.FileInfo) - Exists(path) - check if path exists - IsDir(path) - check if path is a directory Implements these methods in both local.Medium (using os package) and MockMedium (in-memory for testing). Includes FileInfo and DirEntry types for mock implementations. This enables migration of direct os.* calls to the Medium abstraction for consistent path validation and testability. Refs #101 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/sdk, pkgcmd, and workspace to Medium abstraction * chore(io): migrate internal/cmd/docs and internal/cmd/dev to Medium - internal/cmd/docs: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.RemoveAll with io.Local equivalents - internal/cmd/dev: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.ReadDir with io.Local equivalents - Fix local.Medium to allow absolute paths when root is "/" for full filesystem access (io.Local use case) Refs #113, #114 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): migrate internal/cmd/setup to Medium abstraction Migrated all direct os.* filesystem calls to use io.Local: - cmd_repo.go: os.MkdirAll -> io.Local.EnsureDir, os.WriteFile -> io.Local.Write, os.Stat -> io.Local.IsFile - cmd_bootstrap.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.IsDir/Exists, os.ReadDir -> io.Local.List - cmd_registry.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.Exists - cmd_ci.go: os.ReadFile -> io.Local.Read - github_config.go: os.ReadFile -> io.Local.Read, os.Stat -> io.Local.Exists Refs #116 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): add error creation and log-and-return helpers Implements issues #129 and #132: - Add Err struct with Op, Msg, Err, Code fields for structured errors - Add E(), Wrap(), WrapCode(), NewCode() for error creation - Add Is(), As(), NewError(), Join() as stdlib wrappers - Add Op(), ErrCode(), Message(), Root() for introspection - Add LogError(), LogWarn(), Must() for combined log-and-return Closes #129 Closes #132 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(errors): create deprecation alias pointing to pkg/log Makes pkg/errors a thin compatibility layer that re-exports from pkg/log. All error handling functions now have canonical implementations in pkg/log. Migration guide in package documentation: - errors.Error -> log.Err - errors.E -> log.E - errors.Code -> log.NewCode - errors.New -> log.NewError Fixes behavior consistency: - E(op, msg, nil) now creates an error (for errors without cause) - Wrap(nil, op, msg) returns nil (for conditional wrapping) - WrapCode returns nil only when both err is nil AND code is empty Closes #128 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(log): migrate pkg/errors imports to pkg/log Migrates all internal packages from pkg/errors to pkg/log: - internal/cmd/monitor - internal/cmd/qa - internal/cmd/dev - pkg/agentic Closes #130 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): address Copilot review feedback - Fix MockMedium.Rename: collect keys before mutating maps during iteration - Fix .git checks to use Exists instead of List (handles worktrees/submodules) - Fix cmd_sync.go: use DeleteAll for recursive directory removal Files updated: - pkg/io/io.go: safe map iteration in Rename - internal/cmd/setup/cmd_bootstrap.go: Exists for .git checks - internal/cmd/setup/cmd_registry.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_install.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_manage.go: Exists for .git checks - internal/cmd/docs/cmd_sync.go: DeleteAll for recursive delete Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(updater): resolve PkgVersion duplicate declaration Remove var PkgVersion from updater.go since go generate creates const PkgVersion in version.go. Track version.go in git to ensure builds work without running go generate first. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting in internal/variants Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting across migrated files Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(io): simplify local Medium implementation Rewrote to match the simpler TypeScript pattern: - path() sanitizes and returns string directly - Each method calls path() once - No complex symlink validation - Less code, less attack surface Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): remove duplicate method declarations Clean up the client.go file that had duplicate method declarations from a bad cherry-pick merge. Now has 127 lines of simple, clean code. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(io): fix traversal test to match sanitization behavior The simplified path() sanitizes .. to . without returning errors. Update test to verify sanitization works correctly. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(mcp): update sandboxing tests for simplified Medium The simplified io/local.Medium implementation: - Sanitizes .. to . (no error, path is cleaned) - Allows absolute paths through (caller validates if needed) - Follows symlinks (no traversal blocking) Update tests to match this simplified behavior. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 06:48:40 +00:00
return log.E(op, "failed to push changes", err)
}
return nil
}
// CreateBranch creates a new branch for the task.
func CreateBranch(ctx context.Context, task *Task, dir string) (string, error) {
const op = "agentic.CreateBranch"
if task == nil {
feat(errors): Unify errors and logging (#180) * feat(help): Add CLI help command Fixes #136 * chore: remove binary * feat(mcp): Add TCP transport Fixes #126 * feat(io): Migrate pkg/mcp to use Medium abstraction Fixes #103 * feat(io): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(errors): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/docs/* to Medium abstraction Fixes #113 * chore(io): Migrate internal/cmd/dev/* to Medium abstraction Fixes #114 * chore(io): Migrate internal/cmd/setup/* to Medium abstraction * chore(io): Complete migration of internal/cmd/dev/* to Medium abstraction * feat(io): extend Medium interface with Delete, Rename, List, Stat operations Adds the following methods to the Medium interface: - Delete(path) - remove a file or empty directory - DeleteAll(path) - recursively remove a file or directory - Rename(old, new) - move/rename a file or directory - List(path) - list directory entries (returns []fs.DirEntry) - Stat(path) - get file information (returns fs.FileInfo) - Exists(path) - check if path exists - IsDir(path) - check if path is a directory Implements these methods in both local.Medium (using os package) and MockMedium (in-memory for testing). Includes FileInfo and DirEntry types for mock implementations. This enables migration of direct os.* calls to the Medium abstraction for consistent path validation and testability. Refs #101 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/sdk, pkgcmd, and workspace to Medium abstraction * chore(io): migrate internal/cmd/docs and internal/cmd/dev to Medium - internal/cmd/docs: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.RemoveAll with io.Local equivalents - internal/cmd/dev: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.ReadDir with io.Local equivalents - Fix local.Medium to allow absolute paths when root is "/" for full filesystem access (io.Local use case) Refs #113, #114 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): migrate internal/cmd/setup to Medium abstraction Migrated all direct os.* filesystem calls to use io.Local: - cmd_repo.go: os.MkdirAll -> io.Local.EnsureDir, os.WriteFile -> io.Local.Write, os.Stat -> io.Local.IsFile - cmd_bootstrap.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.IsDir/Exists, os.ReadDir -> io.Local.List - cmd_registry.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.Exists - cmd_ci.go: os.ReadFile -> io.Local.Read - github_config.go: os.ReadFile -> io.Local.Read, os.Stat -> io.Local.Exists Refs #116 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): add error creation and log-and-return helpers Implements issues #129 and #132: - Add Err struct with Op, Msg, Err, Code fields for structured errors - Add E(), Wrap(), WrapCode(), NewCode() for error creation - Add Is(), As(), NewError(), Join() as stdlib wrappers - Add Op(), ErrCode(), Message(), Root() for introspection - Add LogError(), LogWarn(), Must() for combined log-and-return Closes #129 Closes #132 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(errors): create deprecation alias pointing to pkg/log Makes pkg/errors a thin compatibility layer that re-exports from pkg/log. All error handling functions now have canonical implementations in pkg/log. Migration guide in package documentation: - errors.Error -> log.Err - errors.E -> log.E - errors.Code -> log.NewCode - errors.New -> log.NewError Fixes behavior consistency: - E(op, msg, nil) now creates an error (for errors without cause) - Wrap(nil, op, msg) returns nil (for conditional wrapping) - WrapCode returns nil only when both err is nil AND code is empty Closes #128 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(log): migrate pkg/errors imports to pkg/log Migrates all internal packages from pkg/errors to pkg/log: - internal/cmd/monitor - internal/cmd/qa - internal/cmd/dev - pkg/agentic Closes #130 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): address Copilot review feedback - Fix MockMedium.Rename: collect keys before mutating maps during iteration - Fix .git checks to use Exists instead of List (handles worktrees/submodules) - Fix cmd_sync.go: use DeleteAll for recursive directory removal Files updated: - pkg/io/io.go: safe map iteration in Rename - internal/cmd/setup/cmd_bootstrap.go: Exists for .git checks - internal/cmd/setup/cmd_registry.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_install.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_manage.go: Exists for .git checks - internal/cmd/docs/cmd_sync.go: DeleteAll for recursive delete Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(updater): resolve PkgVersion duplicate declaration Remove var PkgVersion from updater.go since go generate creates const PkgVersion in version.go. Track version.go in git to ensure builds work without running go generate first. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting in internal/variants Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting across migrated files Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(io): simplify local Medium implementation Rewrote to match the simpler TypeScript pattern: - path() sanitizes and returns string directly - Each method calls path() once - No complex symlink validation - Less code, less attack surface Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): remove duplicate method declarations Clean up the client.go file that had duplicate method declarations from a bad cherry-pick merge. Now has 127 lines of simple, clean code. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(io): fix traversal test to match sanitization behavior The simplified path() sanitizes .. to . without returning errors. Update test to verify sanitization works correctly. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(mcp): update sandboxing tests for simplified Medium The simplified io/local.Medium implementation: - Sanitizes .. to . (no error, path is cleaned) - Allows absolute paths through (caller validates if needed) - Follows symlinks (no traversal blocking) Update tests to match this simplified behavior. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 06:48:40 +00:00
return "", log.E(op, "task is required", nil)
}
// Generate branch name from task
branchName := generateBranchName(task)
// Create and checkout branch
_, err := runGitCommandCtx(ctx, dir, "checkout", "-b", branchName)
if err != nil {
feat(errors): Unify errors and logging (#180) * feat(help): Add CLI help command Fixes #136 * chore: remove binary * feat(mcp): Add TCP transport Fixes #126 * feat(io): Migrate pkg/mcp to use Medium abstraction Fixes #103 * feat(io): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(errors): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/docs/* to Medium abstraction Fixes #113 * chore(io): Migrate internal/cmd/dev/* to Medium abstraction Fixes #114 * chore(io): Migrate internal/cmd/setup/* to Medium abstraction * chore(io): Complete migration of internal/cmd/dev/* to Medium abstraction * feat(io): extend Medium interface with Delete, Rename, List, Stat operations Adds the following methods to the Medium interface: - Delete(path) - remove a file or empty directory - DeleteAll(path) - recursively remove a file or directory - Rename(old, new) - move/rename a file or directory - List(path) - list directory entries (returns []fs.DirEntry) - Stat(path) - get file information (returns fs.FileInfo) - Exists(path) - check if path exists - IsDir(path) - check if path is a directory Implements these methods in both local.Medium (using os package) and MockMedium (in-memory for testing). Includes FileInfo and DirEntry types for mock implementations. This enables migration of direct os.* calls to the Medium abstraction for consistent path validation and testability. Refs #101 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/sdk, pkgcmd, and workspace to Medium abstraction * chore(io): migrate internal/cmd/docs and internal/cmd/dev to Medium - internal/cmd/docs: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.RemoveAll with io.Local equivalents - internal/cmd/dev: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.ReadDir with io.Local equivalents - Fix local.Medium to allow absolute paths when root is "/" for full filesystem access (io.Local use case) Refs #113, #114 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): migrate internal/cmd/setup to Medium abstraction Migrated all direct os.* filesystem calls to use io.Local: - cmd_repo.go: os.MkdirAll -> io.Local.EnsureDir, os.WriteFile -> io.Local.Write, os.Stat -> io.Local.IsFile - cmd_bootstrap.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.IsDir/Exists, os.ReadDir -> io.Local.List - cmd_registry.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.Exists - cmd_ci.go: os.ReadFile -> io.Local.Read - github_config.go: os.ReadFile -> io.Local.Read, os.Stat -> io.Local.Exists Refs #116 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): add error creation and log-and-return helpers Implements issues #129 and #132: - Add Err struct with Op, Msg, Err, Code fields for structured errors - Add E(), Wrap(), WrapCode(), NewCode() for error creation - Add Is(), As(), NewError(), Join() as stdlib wrappers - Add Op(), ErrCode(), Message(), Root() for introspection - Add LogError(), LogWarn(), Must() for combined log-and-return Closes #129 Closes #132 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(errors): create deprecation alias pointing to pkg/log Makes pkg/errors a thin compatibility layer that re-exports from pkg/log. All error handling functions now have canonical implementations in pkg/log. Migration guide in package documentation: - errors.Error -> log.Err - errors.E -> log.E - errors.Code -> log.NewCode - errors.New -> log.NewError Fixes behavior consistency: - E(op, msg, nil) now creates an error (for errors without cause) - Wrap(nil, op, msg) returns nil (for conditional wrapping) - WrapCode returns nil only when both err is nil AND code is empty Closes #128 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(log): migrate pkg/errors imports to pkg/log Migrates all internal packages from pkg/errors to pkg/log: - internal/cmd/monitor - internal/cmd/qa - internal/cmd/dev - pkg/agentic Closes #130 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): address Copilot review feedback - Fix MockMedium.Rename: collect keys before mutating maps during iteration - Fix .git checks to use Exists instead of List (handles worktrees/submodules) - Fix cmd_sync.go: use DeleteAll for recursive directory removal Files updated: - pkg/io/io.go: safe map iteration in Rename - internal/cmd/setup/cmd_bootstrap.go: Exists for .git checks - internal/cmd/setup/cmd_registry.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_install.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_manage.go: Exists for .git checks - internal/cmd/docs/cmd_sync.go: DeleteAll for recursive delete Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(updater): resolve PkgVersion duplicate declaration Remove var PkgVersion from updater.go since go generate creates const PkgVersion in version.go. Track version.go in git to ensure builds work without running go generate first. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting in internal/variants Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting across migrated files Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(io): simplify local Medium implementation Rewrote to match the simpler TypeScript pattern: - path() sanitizes and returns string directly - Each method calls path() once - No complex symlink validation - Less code, less attack surface Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): remove duplicate method declarations Clean up the client.go file that had duplicate method declarations from a bad cherry-pick merge. Now has 127 lines of simple, clean code. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(io): fix traversal test to match sanitization behavior The simplified path() sanitizes .. to . without returning errors. Update test to verify sanitization works correctly. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(mcp): update sandboxing tests for simplified Medium The simplified io/local.Medium implementation: - Sanitizes .. to . (no error, path is cleaned) - Allows absolute paths through (caller validates if needed) - Follows symlinks (no traversal blocking) Update tests to match this simplified behavior. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 06:48:40 +00:00
return "", log.E(op, "failed to create branch", err)
}
return branchName, nil
}
// generateBranchName creates a branch name from task details.
func generateBranchName(task *Task) string {
// Determine prefix based on labels
prefix := "feat"
for _, label := range task.Labels {
switch strings.ToLower(label) {
case "bug", "bugfix", "fix":
prefix = "fix"
case "docs", "documentation":
prefix = "docs"
case "refactor":
prefix = "refactor"
case "test", "tests":
prefix = "test"
case "chore":
prefix = "chore"
}
}
// Sanitize title for branch name
title := strings.ToLower(task.Title)
title = strings.Map(func(r rune) rune {
if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') {
return r
}
if r == ' ' || r == '-' || r == '_' {
return '-'
}
return -1
}, title)
// Remove consecutive dashes
for strings.Contains(title, "--") {
title = strings.ReplaceAll(title, "--", "-")
}
title = strings.Trim(title, "-")
// Truncate if too long
if len(title) > 40 {
title = title[:40]
title = strings.TrimRight(title, "-")
}
return fmt.Sprintf("%s/%s-%s", prefix, task.ID, title)
}
// runGitCommandCtx runs a git command with context.
func runGitCommandCtx(ctx context.Context, dir string, args ...string) (string, error) {
return runCommandCtx(ctx, dir, "git", args...)
}
// runCommandCtx runs an arbitrary command with context.
func runCommandCtx(ctx context.Context, dir string, command string, args ...string) (string, error) {
cmd := exec.CommandContext(ctx, command, args...)
cmd.Dir = dir
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
if stderr.Len() > 0 {
return "", fmt.Errorf("%w: %s", err, stderr.String())
}
return "", err
}
return stdout.String(), nil
}
// GetCurrentBranch returns the current git branch name.
func GetCurrentBranch(ctx context.Context, dir string) (string, error) {
const op = "agentic.GetCurrentBranch"
output, err := runGitCommandCtx(ctx, dir, "rev-parse", "--abbrev-ref", "HEAD")
if err != nil {
feat(errors): Unify errors and logging (#180) * feat(help): Add CLI help command Fixes #136 * chore: remove binary * feat(mcp): Add TCP transport Fixes #126 * feat(io): Migrate pkg/mcp to use Medium abstraction Fixes #103 * feat(io): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(errors): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/docs/* to Medium abstraction Fixes #113 * chore(io): Migrate internal/cmd/dev/* to Medium abstraction Fixes #114 * chore(io): Migrate internal/cmd/setup/* to Medium abstraction * chore(io): Complete migration of internal/cmd/dev/* to Medium abstraction * feat(io): extend Medium interface with Delete, Rename, List, Stat operations Adds the following methods to the Medium interface: - Delete(path) - remove a file or empty directory - DeleteAll(path) - recursively remove a file or directory - Rename(old, new) - move/rename a file or directory - List(path) - list directory entries (returns []fs.DirEntry) - Stat(path) - get file information (returns fs.FileInfo) - Exists(path) - check if path exists - IsDir(path) - check if path is a directory Implements these methods in both local.Medium (using os package) and MockMedium (in-memory for testing). Includes FileInfo and DirEntry types for mock implementations. This enables migration of direct os.* calls to the Medium abstraction for consistent path validation and testability. Refs #101 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/sdk, pkgcmd, and workspace to Medium abstraction * chore(io): migrate internal/cmd/docs and internal/cmd/dev to Medium - internal/cmd/docs: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.RemoveAll with io.Local equivalents - internal/cmd/dev: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.ReadDir with io.Local equivalents - Fix local.Medium to allow absolute paths when root is "/" for full filesystem access (io.Local use case) Refs #113, #114 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): migrate internal/cmd/setup to Medium abstraction Migrated all direct os.* filesystem calls to use io.Local: - cmd_repo.go: os.MkdirAll -> io.Local.EnsureDir, os.WriteFile -> io.Local.Write, os.Stat -> io.Local.IsFile - cmd_bootstrap.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.IsDir/Exists, os.ReadDir -> io.Local.List - cmd_registry.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.Exists - cmd_ci.go: os.ReadFile -> io.Local.Read - github_config.go: os.ReadFile -> io.Local.Read, os.Stat -> io.Local.Exists Refs #116 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): add error creation and log-and-return helpers Implements issues #129 and #132: - Add Err struct with Op, Msg, Err, Code fields for structured errors - Add E(), Wrap(), WrapCode(), NewCode() for error creation - Add Is(), As(), NewError(), Join() as stdlib wrappers - Add Op(), ErrCode(), Message(), Root() for introspection - Add LogError(), LogWarn(), Must() for combined log-and-return Closes #129 Closes #132 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(errors): create deprecation alias pointing to pkg/log Makes pkg/errors a thin compatibility layer that re-exports from pkg/log. All error handling functions now have canonical implementations in pkg/log. Migration guide in package documentation: - errors.Error -> log.Err - errors.E -> log.E - errors.Code -> log.NewCode - errors.New -> log.NewError Fixes behavior consistency: - E(op, msg, nil) now creates an error (for errors without cause) - Wrap(nil, op, msg) returns nil (for conditional wrapping) - WrapCode returns nil only when both err is nil AND code is empty Closes #128 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(log): migrate pkg/errors imports to pkg/log Migrates all internal packages from pkg/errors to pkg/log: - internal/cmd/monitor - internal/cmd/qa - internal/cmd/dev - pkg/agentic Closes #130 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): address Copilot review feedback - Fix MockMedium.Rename: collect keys before mutating maps during iteration - Fix .git checks to use Exists instead of List (handles worktrees/submodules) - Fix cmd_sync.go: use DeleteAll for recursive directory removal Files updated: - pkg/io/io.go: safe map iteration in Rename - internal/cmd/setup/cmd_bootstrap.go: Exists for .git checks - internal/cmd/setup/cmd_registry.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_install.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_manage.go: Exists for .git checks - internal/cmd/docs/cmd_sync.go: DeleteAll for recursive delete Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(updater): resolve PkgVersion duplicate declaration Remove var PkgVersion from updater.go since go generate creates const PkgVersion in version.go. Track version.go in git to ensure builds work without running go generate first. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting in internal/variants Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting across migrated files Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(io): simplify local Medium implementation Rewrote to match the simpler TypeScript pattern: - path() sanitizes and returns string directly - Each method calls path() once - No complex symlink validation - Less code, less attack surface Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): remove duplicate method declarations Clean up the client.go file that had duplicate method declarations from a bad cherry-pick merge. Now has 127 lines of simple, clean code. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(io): fix traversal test to match sanitization behavior The simplified path() sanitizes .. to . without returning errors. Update test to verify sanitization works correctly. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(mcp): update sandboxing tests for simplified Medium The simplified io/local.Medium implementation: - Sanitizes .. to . (no error, path is cleaned) - Allows absolute paths through (caller validates if needed) - Follows symlinks (no traversal blocking) Update tests to match this simplified behavior. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 06:48:40 +00:00
return "", log.E(op, "failed to get current branch", err)
}
return strings.TrimSpace(output), nil
}
// HasUncommittedChanges checks if there are uncommitted changes.
func HasUncommittedChanges(ctx context.Context, dir string) (bool, error) {
const op = "agentic.HasUncommittedChanges"
output, err := runGitCommandCtx(ctx, dir, "status", "--porcelain")
if err != nil {
feat(errors): Unify errors and logging (#180) * feat(help): Add CLI help command Fixes #136 * chore: remove binary * feat(mcp): Add TCP transport Fixes #126 * feat(io): Migrate pkg/mcp to use Medium abstraction Fixes #103 * feat(io): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(errors): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/docs/* to Medium abstraction Fixes #113 * chore(io): Migrate internal/cmd/dev/* to Medium abstraction Fixes #114 * chore(io): Migrate internal/cmd/setup/* to Medium abstraction * chore(io): Complete migration of internal/cmd/dev/* to Medium abstraction * feat(io): extend Medium interface with Delete, Rename, List, Stat operations Adds the following methods to the Medium interface: - Delete(path) - remove a file or empty directory - DeleteAll(path) - recursively remove a file or directory - Rename(old, new) - move/rename a file or directory - List(path) - list directory entries (returns []fs.DirEntry) - Stat(path) - get file information (returns fs.FileInfo) - Exists(path) - check if path exists - IsDir(path) - check if path is a directory Implements these methods in both local.Medium (using os package) and MockMedium (in-memory for testing). Includes FileInfo and DirEntry types for mock implementations. This enables migration of direct os.* calls to the Medium abstraction for consistent path validation and testability. Refs #101 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/sdk, pkgcmd, and workspace to Medium abstraction * chore(io): migrate internal/cmd/docs and internal/cmd/dev to Medium - internal/cmd/docs: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.RemoveAll with io.Local equivalents - internal/cmd/dev: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.ReadDir with io.Local equivalents - Fix local.Medium to allow absolute paths when root is "/" for full filesystem access (io.Local use case) Refs #113, #114 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): migrate internal/cmd/setup to Medium abstraction Migrated all direct os.* filesystem calls to use io.Local: - cmd_repo.go: os.MkdirAll -> io.Local.EnsureDir, os.WriteFile -> io.Local.Write, os.Stat -> io.Local.IsFile - cmd_bootstrap.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.IsDir/Exists, os.ReadDir -> io.Local.List - cmd_registry.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.Exists - cmd_ci.go: os.ReadFile -> io.Local.Read - github_config.go: os.ReadFile -> io.Local.Read, os.Stat -> io.Local.Exists Refs #116 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): add error creation and log-and-return helpers Implements issues #129 and #132: - Add Err struct with Op, Msg, Err, Code fields for structured errors - Add E(), Wrap(), WrapCode(), NewCode() for error creation - Add Is(), As(), NewError(), Join() as stdlib wrappers - Add Op(), ErrCode(), Message(), Root() for introspection - Add LogError(), LogWarn(), Must() for combined log-and-return Closes #129 Closes #132 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(errors): create deprecation alias pointing to pkg/log Makes pkg/errors a thin compatibility layer that re-exports from pkg/log. All error handling functions now have canonical implementations in pkg/log. Migration guide in package documentation: - errors.Error -> log.Err - errors.E -> log.E - errors.Code -> log.NewCode - errors.New -> log.NewError Fixes behavior consistency: - E(op, msg, nil) now creates an error (for errors without cause) - Wrap(nil, op, msg) returns nil (for conditional wrapping) - WrapCode returns nil only when both err is nil AND code is empty Closes #128 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(log): migrate pkg/errors imports to pkg/log Migrates all internal packages from pkg/errors to pkg/log: - internal/cmd/monitor - internal/cmd/qa - internal/cmd/dev - pkg/agentic Closes #130 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): address Copilot review feedback - Fix MockMedium.Rename: collect keys before mutating maps during iteration - Fix .git checks to use Exists instead of List (handles worktrees/submodules) - Fix cmd_sync.go: use DeleteAll for recursive directory removal Files updated: - pkg/io/io.go: safe map iteration in Rename - internal/cmd/setup/cmd_bootstrap.go: Exists for .git checks - internal/cmd/setup/cmd_registry.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_install.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_manage.go: Exists for .git checks - internal/cmd/docs/cmd_sync.go: DeleteAll for recursive delete Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(updater): resolve PkgVersion duplicate declaration Remove var PkgVersion from updater.go since go generate creates const PkgVersion in version.go. Track version.go in git to ensure builds work without running go generate first. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting in internal/variants Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting across migrated files Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(io): simplify local Medium implementation Rewrote to match the simpler TypeScript pattern: - path() sanitizes and returns string directly - Each method calls path() once - No complex symlink validation - Less code, less attack surface Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): remove duplicate method declarations Clean up the client.go file that had duplicate method declarations from a bad cherry-pick merge. Now has 127 lines of simple, clean code. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(io): fix traversal test to match sanitization behavior The simplified path() sanitizes .. to . without returning errors. Update test to verify sanitization works correctly. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(mcp): update sandboxing tests for simplified Medium The simplified io/local.Medium implementation: - Sanitizes .. to . (no error, path is cleaned) - Allows absolute paths through (caller validates if needed) - Follows symlinks (no traversal blocking) Update tests to match this simplified behavior. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 06:48:40 +00:00
return false, log.E(op, "failed to get git status", err)
}
return strings.TrimSpace(output) != "", nil
}
// GetDiff returns the current diff for staged and unstaged changes.
func GetDiff(ctx context.Context, dir string, staged bool) (string, error) {
const op = "agentic.GetDiff"
args := []string{"diff"}
if staged {
args = append(args, "--staged")
}
output, err := runGitCommandCtx(ctx, dir, args...)
if err != nil {
feat(errors): Unify errors and logging (#180) * feat(help): Add CLI help command Fixes #136 * chore: remove binary * feat(mcp): Add TCP transport Fixes #126 * feat(io): Migrate pkg/mcp to use Medium abstraction Fixes #103 * feat(io): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(errors): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): batch implementation placeholder Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/docs/* to Medium abstraction Fixes #113 * chore(io): Migrate internal/cmd/dev/* to Medium abstraction Fixes #114 * chore(io): Migrate internal/cmd/setup/* to Medium abstraction * chore(io): Complete migration of internal/cmd/dev/* to Medium abstraction * feat(io): extend Medium interface with Delete, Rename, List, Stat operations Adds the following methods to the Medium interface: - Delete(path) - remove a file or empty directory - DeleteAll(path) - recursively remove a file or directory - Rename(old, new) - move/rename a file or directory - List(path) - list directory entries (returns []fs.DirEntry) - Stat(path) - get file information (returns fs.FileInfo) - Exists(path) - check if path exists - IsDir(path) - check if path is a directory Implements these methods in both local.Medium (using os package) and MockMedium (in-memory for testing). Includes FileInfo and DirEntry types for mock implementations. This enables migration of direct os.* calls to the Medium abstraction for consistent path validation and testability. Refs #101 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): Migrate internal/cmd/sdk, pkgcmd, and workspace to Medium abstraction * chore(io): migrate internal/cmd/docs and internal/cmd/dev to Medium - internal/cmd/docs: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.RemoveAll with io.Local equivalents - internal/cmd/dev: Replace os.Stat, os.ReadFile, os.WriteFile, os.MkdirAll, os.ReadDir with io.Local equivalents - Fix local.Medium to allow absolute paths when root is "/" for full filesystem access (io.Local use case) Refs #113, #114 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(io): migrate internal/cmd/setup to Medium abstraction Migrated all direct os.* filesystem calls to use io.Local: - cmd_repo.go: os.MkdirAll -> io.Local.EnsureDir, os.WriteFile -> io.Local.Write, os.Stat -> io.Local.IsFile - cmd_bootstrap.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.IsDir/Exists, os.ReadDir -> io.Local.List - cmd_registry.go: os.MkdirAll -> io.Local.EnsureDir, os.Stat -> io.Local.Exists - cmd_ci.go: os.ReadFile -> io.Local.Read - github_config.go: os.ReadFile -> io.Local.Read, os.Stat -> io.Local.Exists Refs #116 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(log): add error creation and log-and-return helpers Implements issues #129 and #132: - Add Err struct with Op, Msg, Err, Code fields for structured errors - Add E(), Wrap(), WrapCode(), NewCode() for error creation - Add Is(), As(), NewError(), Join() as stdlib wrappers - Add Op(), ErrCode(), Message(), Root() for introspection - Add LogError(), LogWarn(), Must() for combined log-and-return Closes #129 Closes #132 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(errors): create deprecation alias pointing to pkg/log Makes pkg/errors a thin compatibility layer that re-exports from pkg/log. All error handling functions now have canonical implementations in pkg/log. Migration guide in package documentation: - errors.Error -> log.Err - errors.E -> log.E - errors.Code -> log.NewCode - errors.New -> log.NewError Fixes behavior consistency: - E(op, msg, nil) now creates an error (for errors without cause) - Wrap(nil, op, msg) returns nil (for conditional wrapping) - WrapCode returns nil only when both err is nil AND code is empty Closes #128 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(log): migrate pkg/errors imports to pkg/log Migrates all internal packages from pkg/errors to pkg/log: - internal/cmd/monitor - internal/cmd/qa - internal/cmd/dev - pkg/agentic Closes #130 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): address Copilot review feedback - Fix MockMedium.Rename: collect keys before mutating maps during iteration - Fix .git checks to use Exists instead of List (handles worktrees/submodules) - Fix cmd_sync.go: use DeleteAll for recursive directory removal Files updated: - pkg/io/io.go: safe map iteration in Rename - internal/cmd/setup/cmd_bootstrap.go: Exists for .git checks - internal/cmd/setup/cmd_registry.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_install.go: Exists for .git checks - internal/cmd/pkgcmd/cmd_manage.go: Exists for .git checks - internal/cmd/docs/cmd_sync.go: DeleteAll for recursive delete Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(updater): resolve PkgVersion duplicate declaration Remove var PkgVersion from updater.go since go generate creates const PkgVersion in version.go. Track version.go in git to ensure builds work without running go generate first. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting in internal/variants Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * style: fix formatting across migrated files Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(io): simplify local Medium implementation Rewrote to match the simpler TypeScript pattern: - path() sanitizes and returns string directly - Each method calls path() once - No complex symlink validation - Less code, less attack surface Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(io): remove duplicate method declarations Clean up the client.go file that had duplicate method declarations from a bad cherry-pick merge. Now has 127 lines of simple, clean code. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(io): fix traversal test to match sanitization behavior The simplified path() sanitizes .. to . without returning errors. Update test to verify sanitization works correctly. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(mcp): update sandboxing tests for simplified Medium The simplified io/local.Medium implementation: - Sanitizes .. to . (no error, path is cleaned) - Allows absolute paths through (caller validates if needed) - Follows symlinks (no traversal blocking) Update tests to match this simplified behavior. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 06:48:40 +00:00
return "", log.E(op, "failed to get diff", err)
}
return output, nil
}