| name | happier-diagnose |
| description | Diagnose a problem with a Happier session, the daemon, a provider (Claude/Codex/OpenCode), auth, or connectivity. Pulls the correct logs, finds a true root cause from evidence only, presents findings, and optionally uploads a private diagnostics bundle to Happier developers and/or files a sanitized public GitHub issue (the two are complementary). Use when the user reports a bug, says Happier is broken/stuck/misbehaving, asks to debug/diagnose/triage/troubleshoot Happier, or shares a Happier session ID and asks what went wrong. |
| metadata | {"openclaw":{"requires":{"bins":["happier"]},"homepage":"https://github.com/happier-dev/happier"}} |
Happier Diagnose
Investigate a Happier issue from real evidence (logs, doctor output, source), determine the root cause at high confidence, and present findings. Then — only with explicit user consent — upload a private diagnostics bundle to Happier developers and/or open a sanitized public GitHub issue. The two paths are complementary: the private bundle gives maintainers raw artifacts; the public issue gives them (and the community) a searchable, durable record. Doing both is normal. Mostly hands-off; minimize questions to the user.
Evidence rules (read first)
- High confidence only. Never guess. Every claim in the final root cause must be traceable to a specific log line, a
happier doctor field, or a specific source file (with line numbers). If you cannot reach high confidence, say "Inconclusive — here's what I observed and what's missing" rather than inventing a story.
- Symptom ≠ root cause. A failed RPC call is a symptom; the daemon being stale, the access key being missing, or a provider returning 429 is a root cause.
- No fabricated paths or flags. If a path, file, or command is not in this skill, in the user's logs, or in code you have actually read, do not include it.
- Concrete code references are welcomed. The Happier maintainers prefer issues that name specific files (
apps/ui/sources/.../AgentInput.tsx:1759), include code snippets, and propose a hypothesis. This is repo-relative — different from the user's filesystem paths, which must be sanitized in public issues. See CONTRIBUTING.md: "A well-written issue ... is often more useful than a PR."
Process
1. Capture the problem (one question max)
If the user has not already described the issue, ask exactly one question: "What's happening, and is there a Happier session ID involved?" Then start investigating immediately. Do not interrogate.
2. Get the session context
Ask the user to send either of the following (prefer the first):
- Preferred: Open the affected session in the Happier app → Session info screen → press Copy Metadata, then paste the JSON. This contains every field needed (
sessionLogPath, flavor, claudeSessionId/codexSessionId, host, path, version, os, hostPid, happyHomeDir, machineId, startedBy).
- Fallback: The Happier session ID alone, then run
happier session status <id> --json to fetch metadata server-side. (See the happier-session-control skill for the JSON contract.)
If the issue is daemon-wide and not session-specific, skip the session metadata and go straight to step 3.
3. Run doctor before reading any log
happier doctor --json
happier auth status --json
doctor answers most questions without log digging: daemon up, control port reachable, server reachable, auth state, runaway processes, version mismatch, settings sanity. Read its output before opening logs.
4. Pull the right logs
Always trust metadata.sessionLogPath and metadata.happyHomeDir over any guess. Different binaries use different home dirs, and $HAPPIER_HOME_DIR overrides them. Common values seen in the wild:
- Release CLI →
~/.happier/logs/
- Preview CLI →
~/.happier-preview/logs/
- Dev CLI →
~/.happier-dev/logs/
- Custom → wherever
$HAPPIER_HOME_DIR points
If metadata.sessionLogPath is missing, fall through the candidate dirs above (in order) and use a glob; never assume a single fixed location.
Happier session log
Primary: read metadata.sessionLogPath (absolute path). Fallback if absent — search for the file matching metadata.hostPid:
find "<happyHomeDir>/logs" -maxdepth 1 -name "*-pid-<metadata.hostPid>.log" -not -name "*-daemon.log"
Filename format is YYYY-MM-DD-HH-MM-SS-pid-<pid>.log (verified on disk).
Daemon log
Most recent *-daemon.log in <happyHomeDir>/logs/. Correlate timestamps with the failure window. There is usually only one active daemon at a time, but stale daemons leave their logs behind, so sort by mtime.
Claude transcript (when metadata.flavor === 'claude' and metadata.claudeSessionId is set)
The directory name is the cwd with / replaced by - (no hash for short paths; only very long paths get a SHA-256 suffix). The session JSONL is named <claudeSessionId>.jsonl. The reliable way is a recursive glob, not computing the path:
find ~/.claude/projects -maxdepth 2 -name "<claudeSessionId>.jsonl" -type f
If nothing matches, ask the user whether their Claude data dir is non-default ($CLAUDE_CONFIG_DIR or similar) and re-run with that root.
Note: alongside <claudeSessionId>.jsonl you may see a sibling directory of the same name — these are sub-session artifacts. The .jsonl file itself is the transcript.
Note on --resume: when a Happier session resumes a Claude session, Claude writes a NEW <new-uuid>.jsonl containing the full prior history with all sessionId fields rewritten to the new UUID. The original <old-uuid>.jsonl remains as a historical artifact. If you only have the older ID, the latest transcript may live in a different filename — sort the project dir's .jsonl files by mtime to find the active one.
Codex transcript (when metadata.flavor === 'codex' and metadata.codexSessionId is set)
Codex stores rollouts under date-partitioned subdirs (and an archived_sessions/ dir for older ones). Do not assume ~/.codex/sessions/rollout-*.jsonl flat. Use a recursive glob:
find "${CODEX_HOME:-$HOME/.codex}" -type f \( -name "rollout-*-<codexSessionId>.jsonl" -o -name "rollout-*-<codexSessionId>.json" \) 2>/dev/null
Both .jsonl (current) and .json (legacy) extensions exist. Filename pattern: rollout-YYYY-MM-DDTHH-MM-SS-<codexSessionId>.jsonl. Real-world locations include ~/.codex/sessions/<year>/<month>/<day>/, ~/.codex/sessions/ directly (legacy flat), and ~/.codex/archived_sessions/ (rotated).
If the agent is also running on a connected-services daemon, Codex sessions can live under <happyHomeDir>/servers/<serverId>/daemon/connected-services/homes/<connectedServiceId>/<profileId>/codex/codex-home/sessions/... — same filename pattern, different root. Search this root only if the standard glob returns nothing.
OpenCode transcript (when metadata.flavor === 'opencode' and metadata.opencodeSessionId is set)
OpenCode follows XDG storage. Base dir is $XDG_DATA_HOME/opencode/ (defaults to ~/.local/share/opencode/ on macOS/Linux). There is no single transcript file — session content is keyed by session id (ses_<...>) across category subfolders inside <base>/storage/ (e.g. session_diff/, directory-readme/, agent-usage-reminder/). Find every file for the session at once:
find "${XDG_DATA_HOME:-$HOME/.local/share}/opencode/storage" -type f -name "<opencodeSessionId>.json" 2>/dev/null
Concatenate or Read each file. If a Happier connected-services daemon owns the OpenCode session, the same layout lives under <happyHomeDir>/servers/<serverId>/daemon/connected-services/homes/<connectedServiceId>/<profileId>/opencode/....
OpenCode also writes global timestamped logs to <base>/opencode/log/<YYYY-MM-DDThhmmss>.log. These are not per-session — correlate by the failure timestamp from the session log.
Reading & searching
Read with Read (not cat/tail via Bash) so secrets stay out of the shell pipeline. Search with ripgrep. Anchor on the failure timestamp and metadata.hostPid to filter noise.
5. Cross-reference source code only when logs are insufficient
If a log message points to specific behavior you cannot interpret without seeing the code, clone the repo at the user's installed version into a tempdir:
git clone --depth 1 --branch v<metadata.version> https://github.com/happier-dev/happier /tmp/happier-diagnose-<sessionId> \
|| git clone --depth 1 https://github.com/happier-dev/happier /tmp/happier-diagnose-<sessionId>
Anchor reading on these directories: apps/cli/src/, apps/server/sources/, packages/protocol/src/, and the docs in docs/ (cli-architecture.md, protocol.md, encryption.md, and the per-provider *-feature-matrix.md). Skip the clone if logs already explain the failure.
6. Form the root cause
Synthesize evidence into a single root cause. Reject a candidate cause unless you have at least one of:
- A log line that names it (with timestamp).
- A
doctor field whose value contradicts a healthy state.
- Source code that demonstrates the failure mode given the observed inputs.
Common, evidence-anchored root causes to recognize (each with the log/field that confirms it):
- Auth missing/expired —
happier auth status --json returns unauthenticated, or access.key absent in <happyHomeDir>/.
- Daemon down or stale —
doctor reports daemon not reachable; daemon.state.json PID does not match a running process; runaway happier processes listed.
- Server unreachable —
happier server test fails; daemon log shows Socket.IO connect timeouts.
- Provider rate limit / credentials — provider transcript shows 429 or 401; CLI session log surfaces the provider error.
- Encryption / key mismatch — session log mentions decrypt failure; client
dataEncryptionKey does not match server's.
- RPC method not available — error code
RPC_METHOD_NOT_AVAILABLE; capabilities probe shows the method missing for the provider/server policy.
- Version mismatch —
metadata.version (CLI) older than daemon's recorded version in daemon.state.json.
- Tmux/terminal attach —
metadata.terminal.tmuxFallbackReason populated.
If evidence is thin: say so. Do not pad.
7. Present findings to the user
Output this template directly in the chat, before offering any uploads:
Root cause — One sentence naming the cause (not the symptom).
Evidence — Bulleted, each with the source:
<repo-path-or-doctor-field>:<line-or-key> — quoted snippet (≤120 chars, redact secrets).
Impact — What's broken because of this, and what's not.
Recommended fix — The smallest change that resolves the cause. If user-side (re-auth, restart daemon, set env var), give the exact command. If it's a Happier bug, name the file(s) and a concrete proposed change — the maintainers welcome this level of detail in issues.
Confidence — high / medium / inconclusive, with one sentence on why.
8. Offer to share — two paths, complementary, explicit consent for each
After presenting findings, ask the user (verbatim):
"Want me to send this to Happier developers? I can do either or both — they're complementary:
A) Private diagnostics upload to Happier's bug-report service (logs and the relevant transcripts — only Happier developers see it).
B) Public GitHub issue at happier-dev/happier with reproduction steps, root cause, and (if Path A ran) the diagnostics reportId so maintainers can correlate."
Wait for the user to pick. Confirm before each action. Never run either without an explicit "yes". If the user picks both, run Path A first so its reportId can be referenced in Path B's body.
Path A — Private upload via happier bug-report
This collects diagnostics, redacts and sanitizes them, uploads to Happier's bug-report service, and prints a reportId and issueUrl on success. Attach the specific session log and provider transcript you located in step 4 — they're the most useful artifacts for the maintainers.
happier bug-report \
--title "<short, specific title from root cause>" \
--summary "<2-4 sentence summary>" \
--current-behavior "<what user observed>" \
--expected-behavior "<what should have happened>" \
--repro-step "<step 1>" --repro-step "<step 2>" \
--frequency <always|often|sometimes|once> \
--severity <blocker|high|medium|low> \
--session-id <happier-session-id-from-metadata> \
--attach-session-log <metadata.sessionLogPath> \
--attach-provider-transcript <claude-or-codex-or-opencode-transcript-path> \
--include-diagnostics \
--accept-privacy-notice
Notes:
- Repeat
--attach-provider-transcript <path> (or --attach <path>) if there are multiple files.
- Add
--existing-issue-number <N> to comment on an existing issue instead of opening a new one.
- Use
--no-include-diagnostics (and drop the --attach-* flags) if the user wants to submit without artifacts.
- Capture the printed
reportId and issueUrl and show both back to the user.
Path B — Public GitHub issue via gh
Only when the user opted into Path B. First verify gh is installed and authenticated (gh auth status); if not, tell the user and stop — do not proceed.
Match the format of happier-dev/happier#91 and #93 (the maintainers' canonical examples). Title: specific and descriptive, with a platform prefix when relevant — e.g. Android: Expand/open icon on tool items unresponsive to touch, not icon broken. Body sections, in this order: ## Description, optional ### Observed behavior (numbered), ## Root Cause (with code snippets and repo-relative path/to/file.ts:line references), ## Suggested Fix (concrete code or approach), ## Affected Files (bulleted with line numbers), and ## Environment (CLI version, OS, provider).
Repo-relative paths (apps/ui/sources/components/...:1759) and code snippets are encouraged. Do not include: log contents, the user's absolute filesystem paths, hostnames, machine IDs, full Happier session IDs, full provider session IDs, access keys, or API tokens. If Path A ran, include the reportId so maintainers can correlate.
gh issue create --repo happier-dev/happier \
--title "<platform prefix if relevant>: <specific, descriptive title>" \
--body "$(cat <<'EOF'
## Description
<one paragraph: what the user did, what they expected, what actually happened>
### Observed behavior
1. <step + outcome>
2. <step + outcome>
3. <step + outcome>
## Root Cause
<one or two paragraphs naming the cause, then code snippets from the repo with file:line references. Quote 5-15 lines of relevant code in fenced blocks.>
## Suggested Fix
<concrete code change or approach. Code block if applicable.>
## Affected Files
- `apps/.../File.tsx` (line N — <one-line note>)
- `apps/.../Other.ts` (line M — <one-line note>)
## Environment
- Happier CLI: <metadata.version>
- OS: <metadata.os>
- Provider: <metadata.flavor>
- Diagnostics: reportId=<from Path A, if available>
EOF
)"
If the user only has a symptom and no concrete code-level finding (i.e., step 6 returned "inconclusive"), still file the issue — the contribution guidelines explicitly value "a well-written issue ... clear repro steps, platform context, observed vs expected behavior" even without a hypothesis. In that case, omit ## Root Cause and ## Suggested Fix and lead with ## Description + ### Steps to reproduce. Show the user the issue URL after creation.
Privacy
- Path A (private upload): only Happier developers see the bundle.
happier bug-report redacts and sanitizes for you — you don't need to pre-redact files before passing them to --attach-*.
- Path B (public GitHub issue): world-readable forever. Allowed in the body: repo-relative paths, code snippets from the public repo, reproduction steps, platform info. Never include in the body: log contents, the user's absolute filesystem paths, hostnames, machine IDs, full session IDs, access keys, OAuth tokens, or provider API keys.
When to stop and ask
- The fix would change shared state outside the user's machine (server, GitHub, another user's session).
- Evidence is inconclusive but the user is pushing for a fix anyway — say so and let them decide.
- The "fix" requires deleting files (
access.key, daemon.state.json, log files): confirm first.