Commit graph

401 commits

Author SHA1 Message Date
Snider
a2db3989e1
docs(audit): add dependency security audit report (#248)
* feat(devops): migrate filesystem operations to io.Local abstraction

Migrate config.go:
- os.ReadFile → io.Local.Read

Migrate devops.go:
- os.Stat → io.Local.IsFile

Migrate images.go:
- os.MkdirAll → io.Local.EnsureDir
- os.Stat → io.Local.IsFile
- os.ReadFile → io.Local.Read
- os.WriteFile → io.Local.Write

Migrate test.go:
- os.ReadFile → io.Local.Read
- os.Stat → io.Local.IsFile

Migrate claude.go:
- os.Stat → io.Local.IsDir

Updated tests to reflect improved behavior:
- Manifest.Save() now creates parent directories
- hasFile() correctly returns false for directories

Part of #101 (io.Medium migration tracking issue).

Closes #107

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore(io): migrate remaining packages to io.Local abstraction

Migrate filesystem operations to use the io.Local abstraction for
improved security, testability, and consistency:

- pkg/cache: Replace os.ReadFile, WriteFile, Remove, RemoveAll with
  io.Local equivalents. io.Local.Write creates parent dirs automatically.
- pkg/agentic: Migrate config.go and context.go to use io.Local for
  reading config files and gathering file context.
- pkg/repos: Use io.Local.Read, Exists, IsDir, List for registry
  operations and git repo detection.
- pkg/release: Use io.Local for config loading, existence checks,
  and artifact discovery.
- pkg/devops/sources: Use io.Local.EnsureDir for CDN download.

All paths are converted to absolute using filepath.Abs() before
calling io.Local methods to handle relative paths correctly.

Closes #104, closes #106, closes #108, closes #111

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore(io): migrate pkg/cli and pkg/container to io.Local abstraction

Continue io.Medium migration for the remaining packages:

- pkg/cli/daemon.go: PIDFile Acquire/Release now use io.Local.Read,
  Delete, and Write for managing daemon PID files.
- pkg/container/state.go: LoadState and SaveState use io.Local for
  JSON state persistence. EnsureLogsDir uses io.Local.EnsureDir.
- pkg/container/templates.go: Template loading and directory scanning
  now use io.Local.IsFile, IsDir, Read, and List.
- pkg/container/linuxkit.go: Image validation uses io.Local.IsFile,
  log file check uses io.Local.IsFile. Streaming log file creation
  (os.Create) remains unchanged as io.Local doesn't support streaming.

Closes #105, closes #107

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* docs(audit): add dependency security audit report

Complete security audit of all project dependencies:

- Run govulncheck: No vulnerabilities found
- Run go mod verify: All modules verified
- Document 15 direct dependencies and 161 indirect
- Assess supply chain risks: Low risk overall
- Verify lock files are committed with integrity hashes
- Provide CI integration recommendations

Closes #185

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ci): build core CLI from source instead of downloading release

The workflows were trying to download from a non-existent release URL.
Now builds the CLI directly using `go build` with version injection.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: trigger CI with updated workflow

* chore(ci): add workflow_dispatch trigger for manual runs

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 08:04:26 +00:00
Snider
94237f915d
feat(cli): CLI enhancements (#182)
* 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(cli): 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>

* chore(io): migrate pkg/cli/daemon.go to Medium abstraction

Replaces direct os calls with io.Local:
- os.ReadFile -> io.Local.Read
- os.WriteFile -> io.Local.Write
- os.Remove -> io.Local.Delete
- os.MkdirAll -> io.Local.EnsureDir

Closes #107

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>

* 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 07:48:34 +00:00
Snider
7c0fb52b26 Merge branch 'dev' into feature/cli-batch
Resolve conflicts by taking dev branch versions which include
the CodeRabbit fixes from PR #181.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 07:46:56 +00:00
Snider
07191e92f8
feat(log): Logging enhancements (#181)
* 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(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>

* 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>

* 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>

* fix: address CodeRabbit review feedback for PR #181

- internal/cmd/dev/cmd_file_sync.go: Add EnsureDir error handling before Copy
- internal/cmd/docs/cmd_sync.go: Add EnsureDir error handling for parent dirs
- internal/cmd/sdk/generators/go.go: Use log.E() helper instead of fmt.Errorf
- pkg/io/local/client.go: Handle Windows drive-root paths in path()
- pkg/log/errors.go: Avoid leading colon when Op is empty, preserve Code in Wrap
- pkg/log/errors_test.go: Rename tests to follow _Good/_Bad/_Ugly suffix pattern
- pkg/mcp/transport_tcp.go: Fix ctx cancellation, increase scanner buffer, use io.EOF

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 07:44:29 +00:00
Snider
ca82668bad
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
Snider
0bf91842d7 feat(release): migrate filesystem operations to io.Local abstraction
Migrate config.go:
- os.ReadFile → io.Local.Read
- os.Stat → io.Local.IsFile
- os.MkdirAll → io.Local.EnsureDir
- os.WriteFile → io.Local.Write

Migrate release.go:
- os.Stat → io.Local.IsDir
- os.ReadDir → io.Local.List

Uses filepath.Abs for relative path support.

Part of #101 (io.Medium migration tracking issue).

Closes #106

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 04:56:07 +00:00
Snider
f343066200 feat(build): migrate filesystem operations to io.Local abstraction
Migrate config.go:
- os.ReadFile → io.Local.Read (with filepath.Abs for relative paths)

Migrate checksum.go:
- os.MkdirAll → io.Local.EnsureDir
- os.WriteFile → io.Local.Write

Migrate discovery.go:
- os.Stat → io.Local.IsFile (with filepath.Abs for relative paths)

Note: os.Open for file hashing remains unchanged as it requires io.Reader.

Part of #101 (io.Medium migration tracking issue).

Closes #105

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 04:52:14 +00:00
Snider
423e5f2080 feat(agentic): migrate filesystem operations to io.Local abstraction
Migrate config.go:
- os.Open + bufio.Scanner → io.Local.Read + strings.Split
- os.ReadFile → io.Local.Read
- os.MkdirAll → io.Local.EnsureDir
- os.WriteFile → io.Local.Write

Migrate context.go:
- os.ReadFile → io.Local.Read

Part of #101 (io.Medium migration tracking issue).

Closes #109

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 04:43:27 +00:00
Snider
f34775adda feat(repos): migrate filesystem operations to io.Local abstraction
Replace direct os package calls with io.Local methods:
- os.ReadFile → io.Local.Read
- os.Stat → io.Local.IsFile/IsDir
- os.ReadDir → io.Local.List

Part of #101 (io.Medium migration tracking issue).

Closes #111

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 04:38:38 +00:00
Snider
460227c5ce feat(container): migrate filesystem operations to io.Local abstraction
Migrate state.go:
- os.ReadFile → io.Local.Read
- os.MkdirAll → io.Local.EnsureDir
- os.WriteFile → io.Local.Write

Migrate templates.go:
- os.Stat → io.Local.IsFile/IsDir
- os.ReadFile → io.Local.Read
- os.ReadDir → io.Local.List

Part of #101 (io.Medium migration tracking issue).

Closes #108

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 04:35:09 +00:00
Snider
cfb7dee741 feat(cache): migrate filesystem operations to io.Local abstraction
Replace direct os package calls with io.Local methods:
- os.MkdirAll → io.Local.EnsureDir
- os.ReadFile → io.Local.Read
- os.WriteFile → io.Local.Write
- os.Remove → io.Local.Delete
- os.RemoveAll → io.Local.DeleteAll

This is part of the io.Medium abstraction migration (#101).

Closes #104

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 04:32:20 +00:00
Snider
d50e6982de
feat(io): extend Medium interface with DeleteAll, Stat, Exists, IsDir (#240)
Add missing methods to complete the Medium interface:
- DeleteAll: recursive delete
- Stat: file information
- Exists: check if path exists
- IsDir: check if path is a directory

Also update MockMedium to implement all interface methods.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 04:25:30 +00:00
Snider
99514d23e6
feat: Batch implementation of Gemini issues (#176)
* 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

* 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

* chore(io): Migrate internal/cmd/sdk, pkgcmd, and workspace to Medium abstraction

* style: fix formatting in internal/variants

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>

* 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>

* 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>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 04:20:18 +00:00
Snider
fe59faa043
fix(container): prevent data race in State.Get and State.All (#238)
Return copies of Container structs instead of pointers to the map
entries. This prevents data races when containers are modified
concurrently by waitForExit and Stop.

Fixes #76

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 04:16:05 +00:00
Snider
fcc48b3be5 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>
2026-02-02 03:32:38 +00:00
Snider
5082dde67d 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>
2026-02-02 03:30:27 +00:00
Snider
26b05a3b47 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>
2026-02-02 03:27:27 +00:00
Snider
60b070a7f5 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>
2026-02-02 03:26:27 +00:00
Snider
19ec7450fb 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>
2026-02-02 03:25:17 +00:00
Snider
2381013f19 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>
2026-02-02 03:22:24 +00:00
Snider
f38205d753 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>
2026-02-02 03:17:42 +00:00
Snider
bb85d99728 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>
2026-02-02 03:17:40 +00:00
Snider
9024b93527 Merge IO batch fixes 2026-02-02 02:04:53 +00:00
Snider
ef16dee5fc 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>
2026-02-02 02:03:47 +00:00
Snider
9f233f398e Merge IO batch 2026-02-02 01:47:54 +00:00
Snider
e4f7fd43ea Merge Gemini's IO migration work
Combines both IO migration efforts:
- Gemini's migrations: sdk, pkgcmd, workspace, dev, docs, setup
- Extended Medium interface with Delete, DeleteAll, Rename, List, Stat, Exists, IsDir

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 01:38:19 +00:00
Snider
32458abf1d chore(io): migrate pkg/cli/daemon.go to Medium abstraction
Replaces direct os calls with io.Local:
- os.ReadFile -> io.Local.Read
- os.WriteFile -> io.Local.Write
- os.Remove -> io.Local.Delete
- os.MkdirAll -> io.Local.EnsureDir

Closes #107

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 01:25:07 +00:00
Snider
d0c3874c11 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>
2026-02-02 00:52:29 +00:00
Snider
42272a7d87 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>
2026-02-02 00:37:52 +00:00
Snider
2a68bcd5da chore(io): Migrate internal/cmd/docs/* to Medium abstraction
Fixes #113
2026-02-02 00:26:24 +00:00
Snider
61fd51f7fc feat(io): Migrate pkg/mcp to use Medium abstraction
Fixes #103
2026-02-02 00:24:19 +00:00
Snider
126c799723 feat(mcp): Add TCP transport
Fixes #126
2026-02-02 00:22:06 +00:00
Snider
06a59f0436 feat(help): Add CLI help command
Fixes #136
2026-02-02 00:19:10 +00:00
Snider
7fede9d563
feat(help): add markdown parsing and section extraction (#174)
* feat(help): add markdown parsing and section extraction

Implements #137: markdown parsing and section extraction for help system.

- Add Topic and Section types for help content structure
- Add Frontmatter type for YAML metadata parsing
- Add ParseTopic() to parse markdown files into Topic structs
- Add ExtractFrontmatter() to extract YAML frontmatter
- Add ExtractSections() to extract headings and content
- Add GenerateID() to create URL-safe anchor IDs
- Add comprehensive tests following _Good/_Bad naming convention

This is the foundation for the display-agnostic help system (#133).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(test): use manual cleanup for TestDevOps_Boot_Good_FreshWithNoExisting

Fixes flaky test that fails with "TempDir RemoveAll cleanup: directory
not empty" by using os.MkdirTemp with t.Cleanup instead of t.TempDir().

This is the same fix applied to TestDevOps_Boot_Good_Success in 8effbda.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(help): address CodeRabbit review feedback

- Add CRLF line ending support to frontmatter regex
- Add empty frontmatter block support
- Use filepath.Base/Ext for cross-platform path handling
- Add tests for CRLF and empty frontmatter cases

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(help): add full-text search functionality (#175)

* fix(test): use manual cleanup for TestDevOps_Boot_Good_FreshWithNoExisting

Fixes flaky test that fails with "TempDir RemoveAll cleanup: directory
not empty" by using os.MkdirTemp with t.Cleanup instead of t.TempDir().

This is the same fix applied to TestDevOps_Boot_Good_Success in 8effbda.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(help): add full-text search functionality

Implements #139: full-text search for help topics.

- Add searchIndex with inverted index for fast lookups
- Add tokenize() for case-insensitive word extraction
- Add Search() with relevance ranking:
  - Exact word matches score 1.0
  - Prefix matches score 0.5
  - Title matches get 2.0 boost
- Add snippet extraction for search result context
- Add section-level matching for precise results
- Add comprehensive tests following _Good/_Bad naming

Search features:
- Case-insensitive matching
- Partial word matching (prefix)
- Title boost (matches in title rank higher)
- Section-level results
- Snippet extraction with context

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(help): address CodeRabbit review feedback

- Add CRLF line ending support to frontmatter regex
- Add empty frontmatter block support
- Use filepath.Base/Ext for cross-platform path handling
- Add tests for CRLF and empty frontmatter cases

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* fix(help): use rune-based slicing for UTF-8 safe snippets

Address CodeRabbit feedback: byte-based slicing can corrupt multi-byte
UTF-8 characters. Now uses rune-based indexing for snippet extraction.

- Convert content to []rune before slicing
- Convert byte position to rune position for match location
- Add UTF-8 validation tests with Japanese text

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(help): use correct string for byte-to-rune conversion in extractSnippet

strings.ToLower can change byte lengths for certain Unicode characters
(e.g., K U+212A 3 bytes → k 1 byte). Since matchPos is a byte index from
strings.Index(contentLower, word), the rune conversion must also use
contentLower to maintain correct index alignment.

Fixes CodeRabbit review feedback.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 00:07:32 +00:00
Snider
5631e04a12
feat(io): Migrate filesystem access to pkg/io Medium abstraction (#172)
* feat(io): add pkg/io with symlink-safe path validation

- Add pkg/io with Medium interface for filesystem abstraction
- Add pkg/io/local with sandboxed filesystem implementation
- Add symlink-safe path validation to prevent bypass attacks
- Add sentinel errors (ErrPathTraversal, ErrSymlinkTraversal)
- Add NewSandboxed() for creating sandboxed Medium instances
- Add MockMedium for testing

Closes #169

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(io): extend Medium interface with Delete, Rename, List, Stat operations

Add missing filesystem operations to Medium interface:
- Delete(path) - removes file or empty directory
- DeleteAll(path) - removes path and contents recursively
- Rename(old, new) - moves or renames files/directories
- Exists(path) - checks if path exists
- IsDir(path) - checks if path is a directory
- List(path) - returns directory contents as []os.DirEntry
- Stat(path) - returns file info as os.FileInfo

Implements both local.Medium and MockMedium with full support.

Closes #102

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(io): MockMedium.Read returns os.ErrNotExist for consistency

Ensures os.IsNotExist(err) works with MockMedium like with real filesystem.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 22:50:55 +00:00
Snider
3365bfd5ba
feat(mcp): add workspace root validation to prevent path traversal (#100)
* feat(mcp): add workspace root validation to prevent path traversal

- Add workspaceRoot field to Service for restricting file operations
- Add WithWorkspaceRoot() option for configuring the workspace directory
- Add validatePath() helper to check paths are within workspace
- Apply validation to all file operation handlers
- Default to current working directory for security
- Add comprehensive tests for path validation

Closes #82

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: move CLI commands from pkg/ to internal/cmd/

- Move 18 CLI command packages to internal/cmd/ (not externally importable)
- Keep 16 library packages in pkg/ (externally importable)
- Update all import paths throughout codebase
- Cleaner separation between CLI logic and reusable libraries

CLI commands moved: ai, ci, dev, docs, doctor, gitcmd, go, monitor,
php, pkgcmd, qa, sdk, security, setup, test, updater, vm, workspace

Libraries remaining: agentic, build, cache, cli, container, devops,
errors, framework, git, i18n, io, log, mcp, process, release, repos

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor(mcp): use pkg/io Medium for sandboxed file operations

Replace manual path validation with pkg/io.Medium for all file operations.
This delegates security (path traversal, symlink bypass) to the sandboxed
local.Medium implementation.

Changes:
- Add io.NewSandboxed() for creating sandboxed Medium instances
- Refactor MCP Service to use io.Medium instead of direct os.* calls
- Remove validatePath and resolvePathWithSymlinks functions
- Update tests to verify Medium-based behaviour

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: correct import path and workflow references

- Fix pkg/io/io.go import from core-gui to core
- Update CI workflows to use internal/cmd/updater path

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(security): address CodeRabbit review issues for path validation

- pkg/io/local: add symlink resolution and boundary-aware containment
  - Reject absolute paths in sandboxed Medium
  - Use filepath.EvalSymlinks to prevent symlink bypass attacks
  - Fix prefix check to prevent /tmp/root matching /tmp/root2

- pkg/mcp: fix resolvePath to validate and return errors
  - Changed resolvePath from (string) to (string, error)
  - Update deleteFile, renameFile, listDirectory, fileExists to handle errors
  - Changed New() to return (*Service, error) instead of *Service
  - Properly propagate option errors instead of silently discarding

- pkg/io: wrap errors with E() helper for consistent context
  - Copy() and MockMedium.Read() now use coreerr.E()

- tests: rename to use _Good/_Bad/_Ugly suffixes per coding guidelines
  - Fix hardcoded /tmp in TestPath to use t.TempDir()
  - Add TestResolvePath_Bad_SymlinkTraversal test

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* style: fix gofmt formatting

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* style: fix gofmt formatting across all files

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 21:59:34 +00:00
Snider
bc490f5a21
feat(cli): add NO_COLOR environment variable support (#98)
Implement the NO_COLOR standard (https://no-color.org/) for CLI output.
When NO_COLOR is set (to any value), ANSI color codes are disabled.

Changes:
- Add init() to check NO_COLOR and TERM=dumb environment variables
- Add ColorEnabled() to query current color state
- Add SetColorEnabled() to programmatically enable/disable colors
- Modify AnsiStyle.Render() to return plain text when colors disabled
- Update UseASCII() to also disable colors (consistent with ASCII mode)
- Add comprehensive tests for color enable/disable functionality

Usage:
  NO_COLOR=1 core dev status  # Runs without color output
  TERM=dumb core dev status   # Also disables colors

Closes #87

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-01 16:40:03 +00:00
Snider
1524e20c69
docs(process): add docstrings to Logger interface methods (#97)
Add missing documentation to Logger interface methods and NopLogger
implementation to satisfy 80% docstring coverage threshold.

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-01 16:12:01 +00:00
Snider
4f1c6926b2
feat(dev): add confirmation prompt to apply command (#96)
Add safety confirmation prompt to `core dev apply` before executing
shell commands. This prevents accidental execution of destructive
commands pasted from untrusted sources or generated by AI agents.

Changes:
- Add --yes/-y flag to skip confirmation prompt
- Show warning and require explicit "y" confirmation before execution
- Allow --dry-run to bypass confirmation (no actual execution)
- Use existing cli.Confirm with Required() for mandatory response

Usage:
  core dev apply --command="rm -rf ."     # Prompts for confirmation
  core dev apply --command="..." --yes    # Skips confirmation
  core dev apply --command="..." --dry-run # No execution, no prompt

Closes #81

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-01 16:06:04 +00:00
Snider
451e84aa76
fix(core): add thread-safety to global Core instance (#95)
Protect the global `instance` variable with sync.RWMutex to prevent
data races when SetInstance/App() are called concurrently (especially
in tests).

Changes:
- Add instanceMu mutex to protect instance variable
- Update App() to use RLock for reading
- Update SetInstance() to use Lock for writing
- Add GetInstance() for non-panicking access
- Add ClearInstance() for test cleanup
- Update tests to use new thread-safe functions
- Add concurrent access test with race detector

Closes #84

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-01 16:03:44 +00:00
Snider
d51d6f907a
fix(agentic): use context.TODO instead of nil Context (#94)
Replace nil Context parameters with context.TODO() to comply with
staticcheck SA1012: "do not pass a nil Context, even if a function
permits it; pass context.TODO if you are unsure about which Context
to use"

Closes #78

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-01 15:58:26 +00:00
Snider
33c55d9bdd
feat(process): add Logger interface for exec wrapper (#93)
- Define Logger interface with Debug and Error methods
- Add NopLogger as default (no-op implementation)
- Add SetDefaultLogger/DefaultLogger for package-level config
- Add WithLogger method for per-command logger injection
- Log commands at DEBUG level before execution
- Log failures at ERROR level with error details
- Add comprehensive tests for logger functionality

Compatible with pkg/log.Logger and other structured loggers.

Closes #90

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-01 15:55:26 +00:00
Snider
de0fac563e
feat(process): add standardized exec wrapper (#91)
* feat(process): add standardized exec wrapper

- Adds pkg/process/exec/exec.go with context and logging support
- Implements Command, Run, Output, CombinedOutput, RunQuiet helpers
- Enforces context usage (falls back to background if nil, pending strict enforcement)
- Standardizes error wrapping for exec.ExitError

* fix(process): remove unused cli import

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* style(process): fix trailing whitespace

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 15:39:44 +00:00
Snider
df2c335983 style(update): fix trailing newline in cmd.go
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 12:46:17 +00:00
Snider
f65d111751 fix(update): use build tags for platform-specific watcher code
Split platform-specific functions into separate files:
- cmd_unix.go: Unix implementation using Setpgid and signal 0
- cmd_windows.go: Windows implementation using CREATE_NEW_PROCESS_GROUP
  and OpenProcess for PID checking

Fixes Windows cross-compilation error where Setpgid field doesn't exist.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 12:19:32 +00:00
Snider
180ce7428f refactor(update): use watcher pattern for auto-restart
Replace the direct exec-based restart with a spawned watcher process:
- Add hidden --watch-pid flag for internal use
- spawnWatcher() spawns background process before update
- watchAndRestart() polls for parent death, then restarts binary
- Uses signal 0 on Unix to check if process is alive
- Windows fallback spawns new process and exits

This approach is safer because:
- Parent exits cleanly before restart (no file locking issues)
- Watcher is detached from parent process group
- Works reliably across platforms

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 12:14:51 +00:00
Snider
e41ed47264 feat(update): auto-restart after update to load new version
Uses syscall.Exec on Unix to replace the current process with the
updated binary, running --version to confirm. On Windows, falls back
to a message asking to restart manually.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 12:09:32 +00:00
Snider
7beaabbd63 feat(cli): add core update command for self-updating
- `core update` - Update to latest stable release
- `core update check` - Check for updates without applying
- `core update --channel=dev` - Update to latest dev build
- `core update --force` - Force update even if already on latest

Uses the existing updater package with GitHub releases support.
Automatically detects platform (OS/arch) and downloads correct binary.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 12:06:49 +00:00
Snider
8effbda7cb fix(test): use manual cleanup for TestDevOps_Boot_Good_Success
The test was flaky because t.TempDir() fails cleanup when files are
added asynchronously by the container manager. Using os.MkdirTemp with
manual os.RemoveAll cleanup handles this gracefully.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 11:55:10 +00:00
Snider
e28fc52456 feat(ci): add core setup ci and dogfood CLI in workflows
- Add `core setup ci` command for generating installation scripts
  - Supports bash, powershell, and GitHub Actions YAML output
  - Configurable via .core/ci.yaml
  - Auto-detects platform and uses Homebrew/Scoop/direct download

- Update all GitHub workflows to use global `core` binary:
  - ci.yml: Uses `core go qa` for all quality checks
  - coverage.yml: Uses `core go cov` for coverage
  - release.yml: Uses `core build --ci` for cross-compilation
  - dev-release.yml: Uses `core build --ci` for all targets

- Add .core/ci.yaml with default configuration

This ensures the CLI dogfoods itself across all CI operations,
validating the framework that the Web3 ecosystem builds from.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 11:36:59 +00:00