2026-01-30 19:28:50 +00:00
|
|
|
// Package cli provides the CLI runtime and utilities.
|
|
|
|
|
package cli
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"fmt"
|
|
|
|
|
"net"
|
|
|
|
|
"net/http"
|
|
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"strconv"
|
|
|
|
|
"sync"
|
|
|
|
|
"syscall"
|
|
|
|
|
"time"
|
|
|
|
|
|
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
|
|
|
"github.com/host-uk/core/pkg/io"
|
2026-01-30 19:28:50 +00:00
|
|
|
"golang.org/x/term"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Mode represents the CLI execution mode.
|
|
|
|
|
type Mode int
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
// ModeInteractive indicates TTY attached with coloured output.
|
|
|
|
|
ModeInteractive Mode = iota
|
|
|
|
|
// ModePipe indicates stdout is piped, colours disabled.
|
|
|
|
|
ModePipe
|
|
|
|
|
// ModeDaemon indicates headless execution, log-only output.
|
|
|
|
|
ModeDaemon
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// String returns the string representation of the Mode.
|
|
|
|
|
func (m Mode) String() string {
|
|
|
|
|
switch m {
|
|
|
|
|
case ModeInteractive:
|
|
|
|
|
return "interactive"
|
|
|
|
|
case ModePipe:
|
|
|
|
|
return "pipe"
|
|
|
|
|
case ModeDaemon:
|
|
|
|
|
return "daemon"
|
|
|
|
|
default:
|
|
|
|
|
return "unknown"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DetectMode determines the execution mode based on environment.
|
|
|
|
|
// Checks CORE_DAEMON env var first, then TTY status.
|
|
|
|
|
func DetectMode() Mode {
|
|
|
|
|
if os.Getenv("CORE_DAEMON") == "1" {
|
|
|
|
|
return ModeDaemon
|
|
|
|
|
}
|
|
|
|
|
if !IsTTY() {
|
|
|
|
|
return ModePipe
|
|
|
|
|
}
|
|
|
|
|
return ModeInteractive
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// IsTTY returns true if stdout is a terminal.
|
|
|
|
|
func IsTTY() bool {
|
|
|
|
|
return term.IsTerminal(int(os.Stdout.Fd()))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// IsStdinTTY returns true if stdin is a terminal.
|
|
|
|
|
func IsStdinTTY() bool {
|
|
|
|
|
return term.IsTerminal(int(os.Stdin.Fd()))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// IsStderrTTY returns true if stderr is a terminal.
|
|
|
|
|
func IsStderrTTY() bool {
|
|
|
|
|
return term.IsTerminal(int(os.Stderr.Fd()))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- PID File Management ---
|
|
|
|
|
|
|
|
|
|
// PIDFile manages a process ID file for single-instance enforcement.
|
|
|
|
|
type PIDFile struct {
|
2026-02-04 18:03:54 +00:00
|
|
|
medium io.Medium
|
|
|
|
|
path string
|
|
|
|
|
mu sync.Mutex
|
2026-01-30 19:28:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewPIDFile creates a PID file manager.
|
2026-02-04 18:03:54 +00:00
|
|
|
func NewPIDFile(m io.Medium, path string) *PIDFile {
|
|
|
|
|
return &PIDFile{medium: m, path: path}
|
2026-01-30 19:28:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Acquire writes the current PID to the file.
|
|
|
|
|
// Returns error if another instance is running.
|
|
|
|
|
func (p *PIDFile) Acquire() error {
|
|
|
|
|
p.mu.Lock()
|
|
|
|
|
defer p.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
// Check if PID file exists
|
2026-02-04 18:03:54 +00:00
|
|
|
if data, err := p.medium.Read(p.path); err == nil {
|
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
|
|
|
pid, err := strconv.Atoi(data)
|
2026-01-30 19:28:50 +00:00
|
|
|
if err == nil && pid > 0 {
|
|
|
|
|
// Check if process is still running
|
|
|
|
|
if process, err := os.FindProcess(pid); err == nil {
|
|
|
|
|
if err := process.Signal(syscall.Signal(0)); err == nil {
|
|
|
|
|
return fmt.Errorf("another instance is running (PID %d)", pid)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Stale PID file, remove it
|
2026-02-04 18:03:54 +00:00
|
|
|
_ = p.medium.Delete(p.path)
|
2026-01-30 19:28:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ensure directory exists
|
|
|
|
|
if dir := filepath.Dir(p.path); dir != "." {
|
2026-02-04 18:03:54 +00:00
|
|
|
if err := p.medium.EnsureDir(dir); err != nil {
|
2026-01-30 19:28:50 +00:00
|
|
|
return fmt.Errorf("failed to create PID directory: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Write current PID
|
|
|
|
|
pid := os.Getpid()
|
2026-02-04 18:03:54 +00:00
|
|
|
if err := p.medium.Write(p.path, strconv.Itoa(pid)); err != nil {
|
2026-01-30 19:28:50 +00:00
|
|
|
return fmt.Errorf("failed to write PID file: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Release removes the PID file.
|
|
|
|
|
func (p *PIDFile) Release() error {
|
|
|
|
|
p.mu.Lock()
|
|
|
|
|
defer p.mu.Unlock()
|
2026-02-04 18:03:54 +00:00
|
|
|
return p.medium.Delete(p.path)
|
2026-01-30 19:28:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Path returns the PID file path.
|
|
|
|
|
func (p *PIDFile) Path() string {
|
|
|
|
|
return p.path
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Health Check Server ---
|
|
|
|
|
|
|
|
|
|
// HealthServer provides a minimal HTTP health check endpoint.
|
|
|
|
|
type HealthServer struct {
|
|
|
|
|
addr string
|
|
|
|
|
server *http.Server
|
|
|
|
|
listener net.Listener
|
|
|
|
|
mu sync.Mutex
|
|
|
|
|
ready bool
|
|
|
|
|
checks []HealthCheck
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// HealthCheck is a function that returns nil if healthy.
|
|
|
|
|
type HealthCheck func() error
|
|
|
|
|
|
|
|
|
|
// NewHealthServer creates a health check server.
|
|
|
|
|
func NewHealthServer(addr string) *HealthServer {
|
|
|
|
|
return &HealthServer{
|
|
|
|
|
addr: addr,
|
|
|
|
|
ready: true,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AddCheck registers a health check function.
|
|
|
|
|
func (h *HealthServer) AddCheck(check HealthCheck) {
|
|
|
|
|
h.mu.Lock()
|
|
|
|
|
h.checks = append(h.checks, check)
|
|
|
|
|
h.mu.Unlock()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetReady sets the readiness status.
|
|
|
|
|
func (h *HealthServer) SetReady(ready bool) {
|
|
|
|
|
h.mu.Lock()
|
|
|
|
|
h.ready = ready
|
|
|
|
|
h.mu.Unlock()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Start begins serving health check endpoints.
|
|
|
|
|
// Endpoints:
|
|
|
|
|
// - /health - liveness probe (always 200 if server is up)
|
|
|
|
|
// - /ready - readiness probe (200 if ready, 503 if not)
|
|
|
|
|
func (h *HealthServer) Start() error {
|
|
|
|
|
mux := http.NewServeMux()
|
|
|
|
|
|
|
|
|
|
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
h.mu.Lock()
|
|
|
|
|
checks := h.checks
|
|
|
|
|
h.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
for _, check := range checks {
|
|
|
|
|
if err := check(); err != nil {
|
|
|
|
|
w.WriteHeader(http.StatusServiceUnavailable)
|
feat: git command, build improvements, and go fmt git-aware (#74)
* feat(go): make go fmt git-aware by default
- By default, only check changed Go files (modified, staged, untracked)
- Add --all flag to check all files (previous behaviour)
- Reduces noise when running fmt on large codebases
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(build): minimal output by default, add missing i18n
- Default output now shows single line: "Success Built N artifacts (dir)"
- Add --verbose/-v flag to show full detailed output
- Add all missing i18n translations for build commands
- Errors still show failure reason in minimal mode
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add root-level `core git` command
- Create pkg/gitcmd with git workflow commands as root menu
- Export command builders from pkg/dev (AddCommitCommand, etc.)
- Commands available under both `core git` and `core dev` for compatibility
- Git commands: health, commit, push, pull, work, sync, apply
- GitHub orchestration stays in dev: issues, reviews, ci, impact
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(qa): add docblock coverage checking
Implement docblock/docstring coverage analysis for Go code:
- New `core qa docblock` command to check coverage
- Shows compact file:line list when under threshold
- Integrate with `core go qa` as a default check
- Add --docblock-threshold flag (default 80%)
The checker uses Go AST parsing to find exported symbols
(functions, types, consts, vars) without documentation.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: address CodeRabbit review feedback
- Fix doc comment: "status" → "health" in gitcmd package
- Implement --check flag for `core go fmt` (exits non-zero if files need formatting)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* docs: add docstrings for 100% coverage
Add documentation comments to all exported symbols:
- pkg/build: ProjectType constants
- pkg/cli: LogLevel, RenderStyle, TableStyle
- pkg/framework: ServiceFor, MustServiceFor, Core.Core
- pkg/git: GitError.Error, GitError.Unwrap
- pkg/i18n: Handler Match/Handle methods
- pkg/log: Level constants
- pkg/mcp: Tool input/output types
- pkg/php: Service constants, QA types, service methods
- pkg/process: ServiceError.Error
- pkg/repos: RepoType constants
- pkg/setup: ChangeType, ChangeCategory constants
- pkg/workspace: AddWorkspaceCommands
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* chore: standardize line endings to LF
Add .gitattributes to enforce LF line endings for all text files.
Normalize all existing files to use Unix-style line endings.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: address CodeRabbit review feedback
- cmd_format.go: validate --check/--fix mutual exclusivity, capture stderr
- cmd_docblock.go: return error instead of os.Exit(1) for proper error handling
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: address CodeRabbit review feedback (round 2)
- linuxkit.go: propagate state update errors, handle cmd.Wait() errors in waitForExit
- mcp.go: guard against empty old_string in editDiff to prevent runaway edits
- cmd_docblock.go: log parse errors instead of silently skipping
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 10:48:44 +00:00
|
|
|
_, _ = fmt.Fprintf(w, "unhealthy: %v\n", err)
|
2026-01-30 19:28:50 +00:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
feat: git command, build improvements, and go fmt git-aware (#74)
* feat(go): make go fmt git-aware by default
- By default, only check changed Go files (modified, staged, untracked)
- Add --all flag to check all files (previous behaviour)
- Reduces noise when running fmt on large codebases
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(build): minimal output by default, add missing i18n
- Default output now shows single line: "Success Built N artifacts (dir)"
- Add --verbose/-v flag to show full detailed output
- Add all missing i18n translations for build commands
- Errors still show failure reason in minimal mode
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add root-level `core git` command
- Create pkg/gitcmd with git workflow commands as root menu
- Export command builders from pkg/dev (AddCommitCommand, etc.)
- Commands available under both `core git` and `core dev` for compatibility
- Git commands: health, commit, push, pull, work, sync, apply
- GitHub orchestration stays in dev: issues, reviews, ci, impact
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(qa): add docblock coverage checking
Implement docblock/docstring coverage analysis for Go code:
- New `core qa docblock` command to check coverage
- Shows compact file:line list when under threshold
- Integrate with `core go qa` as a default check
- Add --docblock-threshold flag (default 80%)
The checker uses Go AST parsing to find exported symbols
(functions, types, consts, vars) without documentation.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: address CodeRabbit review feedback
- Fix doc comment: "status" → "health" in gitcmd package
- Implement --check flag for `core go fmt` (exits non-zero if files need formatting)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* docs: add docstrings for 100% coverage
Add documentation comments to all exported symbols:
- pkg/build: ProjectType constants
- pkg/cli: LogLevel, RenderStyle, TableStyle
- pkg/framework: ServiceFor, MustServiceFor, Core.Core
- pkg/git: GitError.Error, GitError.Unwrap
- pkg/i18n: Handler Match/Handle methods
- pkg/log: Level constants
- pkg/mcp: Tool input/output types
- pkg/php: Service constants, QA types, service methods
- pkg/process: ServiceError.Error
- pkg/repos: RepoType constants
- pkg/setup: ChangeType, ChangeCategory constants
- pkg/workspace: AddWorkspaceCommands
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* chore: standardize line endings to LF
Add .gitattributes to enforce LF line endings for all text files.
Normalize all existing files to use Unix-style line endings.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: address CodeRabbit review feedback
- cmd_format.go: validate --check/--fix mutual exclusivity, capture stderr
- cmd_docblock.go: return error instead of os.Exit(1) for proper error handling
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: address CodeRabbit review feedback (round 2)
- linuxkit.go: propagate state update errors, handle cmd.Wait() errors in waitForExit
- mcp.go: guard against empty old_string in editDiff to prevent runaway edits
- cmd_docblock.go: log parse errors instead of silently skipping
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 10:48:44 +00:00
|
|
|
_, _ = fmt.Fprintln(w, "ok")
|
2026-01-30 19:28:50 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
mux.HandleFunc("/ready", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
h.mu.Lock()
|
|
|
|
|
ready := h.ready
|
|
|
|
|
h.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
if !ready {
|
|
|
|
|
w.WriteHeader(http.StatusServiceUnavailable)
|
feat: git command, build improvements, and go fmt git-aware (#74)
* feat(go): make go fmt git-aware by default
- By default, only check changed Go files (modified, staged, untracked)
- Add --all flag to check all files (previous behaviour)
- Reduces noise when running fmt on large codebases
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(build): minimal output by default, add missing i18n
- Default output now shows single line: "Success Built N artifacts (dir)"
- Add --verbose/-v flag to show full detailed output
- Add all missing i18n translations for build commands
- Errors still show failure reason in minimal mode
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add root-level `core git` command
- Create pkg/gitcmd with git workflow commands as root menu
- Export command builders from pkg/dev (AddCommitCommand, etc.)
- Commands available under both `core git` and `core dev` for compatibility
- Git commands: health, commit, push, pull, work, sync, apply
- GitHub orchestration stays in dev: issues, reviews, ci, impact
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(qa): add docblock coverage checking
Implement docblock/docstring coverage analysis for Go code:
- New `core qa docblock` command to check coverage
- Shows compact file:line list when under threshold
- Integrate with `core go qa` as a default check
- Add --docblock-threshold flag (default 80%)
The checker uses Go AST parsing to find exported symbols
(functions, types, consts, vars) without documentation.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: address CodeRabbit review feedback
- Fix doc comment: "status" → "health" in gitcmd package
- Implement --check flag for `core go fmt` (exits non-zero if files need formatting)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* docs: add docstrings for 100% coverage
Add documentation comments to all exported symbols:
- pkg/build: ProjectType constants
- pkg/cli: LogLevel, RenderStyle, TableStyle
- pkg/framework: ServiceFor, MustServiceFor, Core.Core
- pkg/git: GitError.Error, GitError.Unwrap
- pkg/i18n: Handler Match/Handle methods
- pkg/log: Level constants
- pkg/mcp: Tool input/output types
- pkg/php: Service constants, QA types, service methods
- pkg/process: ServiceError.Error
- pkg/repos: RepoType constants
- pkg/setup: ChangeType, ChangeCategory constants
- pkg/workspace: AddWorkspaceCommands
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* chore: standardize line endings to LF
Add .gitattributes to enforce LF line endings for all text files.
Normalize all existing files to use Unix-style line endings.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: address CodeRabbit review feedback
- cmd_format.go: validate --check/--fix mutual exclusivity, capture stderr
- cmd_docblock.go: return error instead of os.Exit(1) for proper error handling
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: address CodeRabbit review feedback (round 2)
- linuxkit.go: propagate state update errors, handle cmd.Wait() errors in waitForExit
- mcp.go: guard against empty old_string in editDiff to prevent runaway edits
- cmd_docblock.go: log parse errors instead of silently skipping
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 10:48:44 +00:00
|
|
|
_, _ = fmt.Fprintln(w, "not ready")
|
2026-01-30 19:28:50 +00:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
feat: git command, build improvements, and go fmt git-aware (#74)
* feat(go): make go fmt git-aware by default
- By default, only check changed Go files (modified, staged, untracked)
- Add --all flag to check all files (previous behaviour)
- Reduces noise when running fmt on large codebases
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(build): minimal output by default, add missing i18n
- Default output now shows single line: "Success Built N artifacts (dir)"
- Add --verbose/-v flag to show full detailed output
- Add all missing i18n translations for build commands
- Errors still show failure reason in minimal mode
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add root-level `core git` command
- Create pkg/gitcmd with git workflow commands as root menu
- Export command builders from pkg/dev (AddCommitCommand, etc.)
- Commands available under both `core git` and `core dev` for compatibility
- Git commands: health, commit, push, pull, work, sync, apply
- GitHub orchestration stays in dev: issues, reviews, ci, impact
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(qa): add docblock coverage checking
Implement docblock/docstring coverage analysis for Go code:
- New `core qa docblock` command to check coverage
- Shows compact file:line list when under threshold
- Integrate with `core go qa` as a default check
- Add --docblock-threshold flag (default 80%)
The checker uses Go AST parsing to find exported symbols
(functions, types, consts, vars) without documentation.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: address CodeRabbit review feedback
- Fix doc comment: "status" → "health" in gitcmd package
- Implement --check flag for `core go fmt` (exits non-zero if files need formatting)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* docs: add docstrings for 100% coverage
Add documentation comments to all exported symbols:
- pkg/build: ProjectType constants
- pkg/cli: LogLevel, RenderStyle, TableStyle
- pkg/framework: ServiceFor, MustServiceFor, Core.Core
- pkg/git: GitError.Error, GitError.Unwrap
- pkg/i18n: Handler Match/Handle methods
- pkg/log: Level constants
- pkg/mcp: Tool input/output types
- pkg/php: Service constants, QA types, service methods
- pkg/process: ServiceError.Error
- pkg/repos: RepoType constants
- pkg/setup: ChangeType, ChangeCategory constants
- pkg/workspace: AddWorkspaceCommands
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* chore: standardize line endings to LF
Add .gitattributes to enforce LF line endings for all text files.
Normalize all existing files to use Unix-style line endings.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: address CodeRabbit review feedback
- cmd_format.go: validate --check/--fix mutual exclusivity, capture stderr
- cmd_docblock.go: return error instead of os.Exit(1) for proper error handling
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: address CodeRabbit review feedback (round 2)
- linuxkit.go: propagate state update errors, handle cmd.Wait() errors in waitForExit
- mcp.go: guard against empty old_string in editDiff to prevent runaway edits
- cmd_docblock.go: log parse errors instead of silently skipping
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 10:48:44 +00:00
|
|
|
_, _ = fmt.Fprintln(w, "ready")
|
2026-01-30 19:28:50 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
listener, err := net.Listen("tcp", h.addr)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to listen on %s: %w", h.addr, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
h.listener = listener
|
|
|
|
|
h.server = &http.Server{Handler: mux}
|
|
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
|
if err := h.server.Serve(listener); err != http.ErrServerClosed {
|
feat: BugSETI app, WebSocket hub, browser automation, and MCP tools (#336)
* feat: add security logging and fix framework regressions
This commit implements comprehensive security event logging and resolves critical regressions in the core framework.
Security Logging:
- Enhanced `pkg/log` with a `Security` level and helper.
- Added `log.Username()` to consistently identify the executing user.
- Instrumented GitHub CLI auth, Agentic configuration, filesystem sandbox, MCP handlers, and MCP TCP transport with security logs.
- Added `SecurityStyle` to the CLI for consistent visual representation of security events.
UniFi Security (CodeQL):
- Refactored `pkg/unifi` to remove hardcoded `InsecureSkipVerify`, resolving a high-severity alert.
- Added a `--verify-tls` flag and configuration option to control TLS verification.
- Updated command handlers to support the new verification parameter.
Framework Fixes:
- Restored original signatures for `MustServiceFor`, `Config()`, and `Display()` in `pkg/framework/core`, which had been corrupted during a merge.
- Fixed `pkg/framework/framework.go` and `pkg/framework/core/runtime_pkg.go` to match the restored signatures.
- These fixes resolve project-wide compilation errors caused by the signature mismatches.
I encountered significant blockers due to a corrupted state of the `dev` branch after a merge, which introduced breaking changes in the core framework's DI system. I had to manually reconcile these signatures with the expected usage across the codebase to restore build stability.
* feat(mcp): add RAG tools (query, ingest, collections)
Add vector database tools to the MCP server for RAG operations:
- rag_query: Search for relevant documentation using semantic similarity
- rag_ingest: Ingest files or directories into the vector database
- rag_collections: List available collections
Uses existing internal/cmd/rag exports (QueryDocs, IngestDirectory, IngestFile)
and pkg/rag for Qdrant client access. Default collection is "hostuk-docs"
with topK=5 for queries.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(mcp): add metrics tools (record, query)
Add MCP tools for recording and querying AI/security metrics events.
The metrics_record tool writes events to daily JSONL files, and the
metrics_query tool provides aggregated statistics by type, repo, and agent.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add 'core mcp serve' command
Add CLI command to start the MCP server for AI tool integration.
- Create internal/cmd/mcpcmd package with serve subcommand
- Support --workspace flag for directory restriction
- Handle SIGINT/SIGTERM for clean shutdown
- Register in full.go build variant
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(ws): add WebSocket hub package for real-time streaming
Add pkg/ws package implementing a hub pattern for WebSocket connections:
- Hub manages client connections, broadcasts, and channel subscriptions
- Client struct represents connected WebSocket clients
- Message types: process_output, process_status, event, error, ping/pong
- Channel-based subscription system (subscribe/unsubscribe)
- SendProcessOutput and SendProcessStatus for process streaming integration
- Full test coverage including concurrency tests
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(mcp): add process management and WebSocket MCP tools
Add MCP tools for process management:
- process_start: Start a new external process
- process_stop: Gracefully stop a running process
- process_kill: Force kill a process
- process_list: List all managed processes
- process_output: Get captured process output
- process_input: Send input to process stdin
Add MCP tools for WebSocket:
- ws_start: Start WebSocket server for real-time streaming
- ws_info: Get hub statistics (clients, channels)
Update Service struct with optional process.Service and ws.Hub fields,
new WithProcessService and WithWSHub options, getter methods, and
Shutdown method for cleanup.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(webview): add browser automation package via Chrome DevTools Protocol
Add pkg/webview package for browser automation:
- webview.go: Main interface with Connect, Navigate, Click, Type, QuerySelector, Screenshot, Evaluate
- cdp.go: Chrome DevTools Protocol WebSocket client implementation
- actions.go: DOM action types (Click, Type, Hover, Scroll, etc.) and ActionSequence builder
- console.go: Console message capture and filtering with ConsoleWatcher and ExceptionWatcher
- angular.go: Angular-specific helpers for router navigation, component access, and Zone.js stability
Add MCP tools for webview:
- webview_connect/disconnect: Connection management
- webview_navigate: Page navigation
- webview_click/type/query/wait: DOM interaction
- webview_console: Console output capture
- webview_eval: JavaScript execution
- webview_screenshot: Screenshot capture
Add documentation:
- docs/mcp/angular-testing.md: Guide for Angular application testing
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* docs: document new packages and BugSETI application
- Update CLAUDE.md with documentation for:
- pkg/ws (WebSocket hub for real-time streaming)
- pkg/webview (Browser automation via CDP)
- pkg/mcp (MCP server tools: process, ws, webview)
- BugSETI application overview
- Add comprehensive README for BugSETI with:
- Installation and configuration guide
- Usage workflow documentation
- Architecture overview
- Contributing guidelines
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(bugseti): add BugSETI system tray app with auto-update
BugSETI - Distributed Bug Fixing like SETI@home but for code
Features:
- System tray app with Wails v3
- GitHub issue fetching with label filters
- Issue queue with priority management
- AI context seeding via seed-agent-developer skill
- Automated PR submission flow
- Stats tracking and leaderboard
- Cross-platform notifications
- Self-updating with stable/beta/nightly channels
Includes:
- cmd/bugseti: Main application with Angular frontend
- internal/bugseti: Core services (fetcher, queue, seeder, submit, config, stats, notify)
- internal/bugseti/updater: Auto-update system (checker, downloader, installer)
- .github/workflows/bugseti-release.yml: CI/CD for all platforms
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: resolve import cycle and code duplication
- Remove pkg/log import from pkg/io/local to break import cycle
(pkg/log/rotation.go imports pkg/io, creating circular dependency)
- Use stderr logging for security events in sandbox escape detection
- Remove unused sync/atomic import from core.go
- Fix duplicate LogSecurity function declarations in cli/log.go
- Update workspace/service.go Crypt() call to match interface
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: update tests for new function signatures and format code
- Update core_test.go: Config(), Display() now panic instead of returning error
- Update runtime_pkg_test.go: sr.Config() now panics instead of returning error
- Update MustServiceFor tests to use assert.Panics
- Format BugSETI, MCP tools, and webview packages with gofmt
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Snider <631881+Snider@users.noreply.github.com>
Co-authored-by: Claude <developers@lethean.io>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 17:22:05 +00:00
|
|
|
LogError("health server error", "err", err)
|
2026-01-30 19:28:50 +00:00
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Stop gracefully shuts down the health server.
|
|
|
|
|
func (h *HealthServer) Stop(ctx context.Context) error {
|
|
|
|
|
if h.server == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return h.server.Shutdown(ctx)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Addr returns the actual address the server is listening on.
|
|
|
|
|
// Useful when using port 0 for dynamic port assignment.
|
|
|
|
|
func (h *HealthServer) Addr() string {
|
|
|
|
|
if h.listener != nil {
|
|
|
|
|
return h.listener.Addr().String()
|
|
|
|
|
}
|
|
|
|
|
return h.addr
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Daemon Runner ---
|
|
|
|
|
|
|
|
|
|
// DaemonOptions configures daemon mode execution.
|
|
|
|
|
type DaemonOptions struct {
|
2026-02-04 18:03:54 +00:00
|
|
|
// Medium is the storage backend for PID files.
|
|
|
|
|
// Defaults to io.Local if not set.
|
|
|
|
|
Medium io.Medium
|
|
|
|
|
|
2026-01-30 19:28:50 +00:00
|
|
|
// PIDFile path for single-instance enforcement.
|
|
|
|
|
// Leave empty to skip PID file management.
|
|
|
|
|
PIDFile string
|
|
|
|
|
|
|
|
|
|
// ShutdownTimeout is the maximum time to wait for graceful shutdown.
|
|
|
|
|
// Default: 30 seconds.
|
|
|
|
|
ShutdownTimeout time.Duration
|
|
|
|
|
|
|
|
|
|
// HealthAddr is the address for health check endpoints.
|
|
|
|
|
// Example: ":8080", "127.0.0.1:9000"
|
|
|
|
|
// Leave empty to disable health checks.
|
|
|
|
|
HealthAddr string
|
|
|
|
|
|
|
|
|
|
// HealthChecks are additional health check functions.
|
|
|
|
|
HealthChecks []HealthCheck
|
|
|
|
|
|
|
|
|
|
// OnReload is called when SIGHUP is received.
|
|
|
|
|
// Use for config reloading. Leave nil to ignore SIGHUP.
|
|
|
|
|
OnReload func() error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Daemon manages daemon lifecycle.
|
|
|
|
|
type Daemon struct {
|
|
|
|
|
opts DaemonOptions
|
|
|
|
|
pid *PIDFile
|
|
|
|
|
health *HealthServer
|
|
|
|
|
reload chan struct{}
|
|
|
|
|
running bool
|
|
|
|
|
mu sync.Mutex
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewDaemon creates a daemon runner with the given options.
|
|
|
|
|
func NewDaemon(opts DaemonOptions) *Daemon {
|
|
|
|
|
if opts.ShutdownTimeout == 0 {
|
|
|
|
|
opts.ShutdownTimeout = 30 * time.Second
|
|
|
|
|
}
|
2026-02-04 18:03:54 +00:00
|
|
|
if opts.Medium == nil {
|
|
|
|
|
opts.Medium = io.Local
|
|
|
|
|
}
|
2026-01-30 19:28:50 +00:00
|
|
|
|
|
|
|
|
d := &Daemon{
|
|
|
|
|
opts: opts,
|
|
|
|
|
reload: make(chan struct{}, 1),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if opts.PIDFile != "" {
|
2026-02-04 18:03:54 +00:00
|
|
|
d.pid = NewPIDFile(opts.Medium, opts.PIDFile)
|
2026-01-30 19:28:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if opts.HealthAddr != "" {
|
|
|
|
|
d.health = NewHealthServer(opts.HealthAddr)
|
|
|
|
|
for _, check := range opts.HealthChecks {
|
|
|
|
|
d.health.AddCheck(check)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return d
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Start initialises the daemon (PID file, health server).
|
|
|
|
|
// Call this after cli.Init().
|
|
|
|
|
func (d *Daemon) Start() error {
|
|
|
|
|
d.mu.Lock()
|
|
|
|
|
defer d.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
if d.running {
|
|
|
|
|
return fmt.Errorf("daemon already running")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Acquire PID file
|
|
|
|
|
if d.pid != nil {
|
|
|
|
|
if err := d.pid.Acquire(); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Start health server
|
|
|
|
|
if d.health != nil {
|
|
|
|
|
if err := d.health.Start(); err != nil {
|
|
|
|
|
if d.pid != nil {
|
feat: git command, build improvements, and go fmt git-aware (#74)
* feat(go): make go fmt git-aware by default
- By default, only check changed Go files (modified, staged, untracked)
- Add --all flag to check all files (previous behaviour)
- Reduces noise when running fmt on large codebases
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(build): minimal output by default, add missing i18n
- Default output now shows single line: "Success Built N artifacts (dir)"
- Add --verbose/-v flag to show full detailed output
- Add all missing i18n translations for build commands
- Errors still show failure reason in minimal mode
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add root-level `core git` command
- Create pkg/gitcmd with git workflow commands as root menu
- Export command builders from pkg/dev (AddCommitCommand, etc.)
- Commands available under both `core git` and `core dev` for compatibility
- Git commands: health, commit, push, pull, work, sync, apply
- GitHub orchestration stays in dev: issues, reviews, ci, impact
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(qa): add docblock coverage checking
Implement docblock/docstring coverage analysis for Go code:
- New `core qa docblock` command to check coverage
- Shows compact file:line list when under threshold
- Integrate with `core go qa` as a default check
- Add --docblock-threshold flag (default 80%)
The checker uses Go AST parsing to find exported symbols
(functions, types, consts, vars) without documentation.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: address CodeRabbit review feedback
- Fix doc comment: "status" → "health" in gitcmd package
- Implement --check flag for `core go fmt` (exits non-zero if files need formatting)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* docs: add docstrings for 100% coverage
Add documentation comments to all exported symbols:
- pkg/build: ProjectType constants
- pkg/cli: LogLevel, RenderStyle, TableStyle
- pkg/framework: ServiceFor, MustServiceFor, Core.Core
- pkg/git: GitError.Error, GitError.Unwrap
- pkg/i18n: Handler Match/Handle methods
- pkg/log: Level constants
- pkg/mcp: Tool input/output types
- pkg/php: Service constants, QA types, service methods
- pkg/process: ServiceError.Error
- pkg/repos: RepoType constants
- pkg/setup: ChangeType, ChangeCategory constants
- pkg/workspace: AddWorkspaceCommands
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* chore: standardize line endings to LF
Add .gitattributes to enforce LF line endings for all text files.
Normalize all existing files to use Unix-style line endings.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: address CodeRabbit review feedback
- cmd_format.go: validate --check/--fix mutual exclusivity, capture stderr
- cmd_docblock.go: return error instead of os.Exit(1) for proper error handling
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: address CodeRabbit review feedback (round 2)
- linuxkit.go: propagate state update errors, handle cmd.Wait() errors in waitForExit
- mcp.go: guard against empty old_string in editDiff to prevent runaway edits
- cmd_docblock.go: log parse errors instead of silently skipping
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 10:48:44 +00:00
|
|
|
_ = d.pid.Release()
|
2026-01-30 19:28:50 +00:00
|
|
|
}
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
d.running = true
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Run blocks until the context is cancelled or a signal is received.
|
|
|
|
|
// Handles graceful shutdown with the configured timeout.
|
|
|
|
|
func (d *Daemon) Run(ctx context.Context) error {
|
|
|
|
|
d.mu.Lock()
|
|
|
|
|
if !d.running {
|
|
|
|
|
d.mu.Unlock()
|
|
|
|
|
return fmt.Errorf("daemon not started - call Start() first")
|
|
|
|
|
}
|
|
|
|
|
d.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
// Wait for context cancellation (from signal handler)
|
|
|
|
|
<-ctx.Done()
|
|
|
|
|
|
|
|
|
|
return d.Stop()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Stop performs graceful shutdown.
|
|
|
|
|
func (d *Daemon) Stop() error {
|
|
|
|
|
d.mu.Lock()
|
|
|
|
|
defer d.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
if !d.running {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var errs []error
|
|
|
|
|
|
|
|
|
|
// Create shutdown context with timeout
|
|
|
|
|
shutdownCtx, cancel := context.WithTimeout(context.Background(), d.opts.ShutdownTimeout)
|
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
|
|
// Stop health server
|
|
|
|
|
if d.health != nil {
|
|
|
|
|
d.health.SetReady(false)
|
|
|
|
|
if err := d.health.Stop(shutdownCtx); err != nil {
|
|
|
|
|
errs = append(errs, fmt.Errorf("health server: %w", err))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Release PID file
|
|
|
|
|
if d.pid != nil {
|
|
|
|
|
if err := d.pid.Release(); err != nil && !os.IsNotExist(err) {
|
|
|
|
|
errs = append(errs, fmt.Errorf("pid file: %w", err))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
d.running = false
|
|
|
|
|
|
|
|
|
|
if len(errs) > 0 {
|
|
|
|
|
return fmt.Errorf("shutdown errors: %v", errs)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetReady sets the daemon readiness status for health checks.
|
|
|
|
|
func (d *Daemon) SetReady(ready bool) {
|
|
|
|
|
if d.health != nil {
|
|
|
|
|
d.health.SetReady(ready)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// HealthAddr returns the health server address, or empty if disabled.
|
|
|
|
|
func (d *Daemon) HealthAddr() string {
|
|
|
|
|
if d.health != nil {
|
|
|
|
|
return d.health.Addr()
|
|
|
|
|
}
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Convenience Functions ---
|
|
|
|
|
|
|
|
|
|
// Run blocks until context is cancelled or signal received.
|
|
|
|
|
// Simple helper for daemon mode without advanced features.
|
|
|
|
|
//
|
|
|
|
|
// cli.Init(cli.Options{AppName: "myapp"})
|
|
|
|
|
// defer cli.Shutdown()
|
|
|
|
|
// cli.Run(cli.Context())
|
|
|
|
|
func Run(ctx context.Context) error {
|
|
|
|
|
mustInit()
|
|
|
|
|
<-ctx.Done()
|
|
|
|
|
return ctx.Err()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RunWithTimeout wraps Run with a graceful shutdown timeout.
|
|
|
|
|
// The returned function should be deferred to replace cli.Shutdown().
|
|
|
|
|
//
|
|
|
|
|
// cli.Init(cli.Options{AppName: "myapp"})
|
|
|
|
|
// shutdown := cli.RunWithTimeout(30 * time.Second)
|
|
|
|
|
// defer shutdown()
|
|
|
|
|
// cli.Run(cli.Context())
|
|
|
|
|
func RunWithTimeout(timeout time.Duration) func() {
|
|
|
|
|
return func() {
|
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
|
|
// Create done channel for shutdown completion
|
|
|
|
|
done := make(chan struct{})
|
|
|
|
|
go func() {
|
|
|
|
|
Shutdown()
|
|
|
|
|
close(done)
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
select {
|
|
|
|
|
case <-done:
|
|
|
|
|
// Clean shutdown
|
|
|
|
|
case <-ctx.Done():
|
|
|
|
|
// Timeout - force exit
|
|
|
|
|
LogWarn("shutdown timeout exceeded, forcing exit")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|