Skip to content

Commit a38c83c

Browse files
AnnatarHeclaude
andauthored
feat(cli): rebuild doctor with fix guidance, --fix, --offline and json output (#313)
`shelltime doctor` now checks the whole setup and, for every problem, says what it breaks and the exact command or config change that fixes it: - system: shelltime on PATH, newer release available - storage: ~/.shelltime exists and is writable, log size vs logCleanup - configuration: which file is used, shadowed and unparseable local files, apiEndpoint, invalid exclude regexes, proxy URL, enableMetrics - account: token present and accepted by the server (401/403 vs unreachable) - privacy: data masking, encryption without a running daemon, legacy tokens without an encryption key - daemon: binary, service, socket, CLI/daemon version mismatch - shell hooks: rc lines, hook script, bash-preexec - Claude Code / Codex: OTEL config, legacy shell OTEL blocks, statusline, Codex sign-in - AI usage receiver: aiCodeOtel disabled, port mismatch, not listening - sync: queued failed uploads and heartbeats The report ends with a "How to fix" list grouped by action. `--fix` applies the safe fixes (hooks, cc/codex install, daemon install, log cleanup) after confirmation (`--yes` skips it), `--offline` skips the network checks and `--format json` prints a machine-readable report. doctor exits 1 when any check fails and no longer aborts on a config error or on an unsupported OS. Claude-Session: https://claude.ai/code/session_01KzQHztW9fdhyWDwPmanhLN Co-authored-by: Claude <noreply@anthropic.com>
1 parent 8feb0bc commit a38c83c

18 files changed

Lines changed: 2269 additions & 225 deletions

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ shelltime codex install
7272
| `shelltime init` | Bootstrap auth, hooks, daemon, and AI-code integrations |
7373
| `shelltime auth` | Authenticate with `shelltime.xyz` |
7474
| `shelltime update` | Download and install the latest release in place |
75-
| `shelltime doctor` | Check installation and environment health |
75+
| `shelltime doctor` | Diagnose setup problems and show how to fix each one (`--fix` applies the safe fixes, `--offline`, `--format json`) |
7676
| `shelltime web` | Open the ShellTime dashboard in a browser |
7777

7878
### Tracking and sync

‎cmd/cli/main.go‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package main
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"log/slog"
78
"os"
@@ -22,6 +23,11 @@ var (
2223
)
2324

2425
func main() {
26+
os.Exit(run())
27+
}
28+
29+
// run holds main's body so its deferred cleanup finishes before main sets the exit code.
30+
func run() int {
2531
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
2632
defer cancel()
2733
cli.VersionFlag = &cli.BoolFlag{
@@ -113,8 +119,14 @@ func main() {
113119
commands.UpdateCommand,
114120
}
115121
err = app.Run(os.Args)
116-
if err != nil {
122+
// doctor already reported its problems; it only needs a non-zero exit code.
123+
doctorFailed := errors.Is(err, commands.ErrDoctorFoundProblems)
124+
if err != nil && !doctorFailed {
117125
slog.Error("CLI error", slog.Any("err", err))
118126
}
119127
commands.CloseLogger()
128+
if doctorFailed {
129+
return 1
130+
}
131+
return 0
120132
}

‎commands/daemon.status.go‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ func requestDaemonStatus(socketPath string, timeout time.Duration) (*daemon.Stat
116116
return nil, 0, err
117117
}
118118
defer conn.Close()
119+
// Bound the whole exchange: a socket file whose owner never answers would otherwise block.
120+
if err := conn.SetDeadline(start.Add(timeout)); err != nil {
121+
return nil, 0, err
122+
}
119123

120124
// Send status request
121125
msg := daemon.SocketMessage{

0 commit comments

Comments
 (0)