{"record":{"id":"767a91c2b51344b9","repo":"Hmbown/CodeWhale","slug":"notfound","errorCode":"NotFound","errorMessage":"gh not found","messagePattern":"gh not found","errorType":"exception","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/commands/groups/project/share.rs","lineNumber":164,"sourceCode":"}\n\n/// Write HTML to a secure temp file and keep it alive for upload.\nfn write_temp_html(html: &str) -> Result<tempfile::NamedTempFile, String> {\n    let mut tmp = tempfile::Builder::new()\n        .prefix(\"codewhale-share-\")\n        .suffix(\".html\")\n        .tempfile()\n        .map_err(|e| format!(\"{e}\"))?;\n    tmp.write_all(html.as_bytes()).map_err(|e| format!(\"{e}\"))?;\n    Ok(tmp)\n}\n\n/// Upload a file as a GitHub Gist using the `gh` CLI.\nasync fn upload_gist(path: &Path) -> Result<String, String> {\n    let path_owned = path.to_path_buf();\n    let output = tokio::task::spawn_blocking(move || {\n        let mut cmd = crate::dependencies::Gh::command()\n            .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, \"gh not found\"))?;\n        cmd.args([\n            \"gist\",\n            \"create\",\n            \"--public\",\n            path_owned.to_string_lossy().as_ref(),\n            \"--filename\",\n            \"session-export.html\",\n            \"--desc\",\n            \"codewhale Session Export\",\n        ])\n        .output()\n    })\n    .await\n    .map_err(|join_err| format!(\"gh gist create panicked: {join_err}\"))?\n    .map_err(|e| format!(\"Failed to run `gh gist create`: {e}\"))?;\n\n    if !output.status.success() {\n        let stderr = String::from_utf8_lossy(&output.stderr);","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/commands/groups/project/share.rs#L146-L182","documentation":"The /project share flow writes an HTML session export to a temp file and uploads it as a public GitHub Gist via the `gh` CLI. Gh::command() resolves gh on PATH by probing candidates with --version (cached per process); when it returns None, upload_gist maps that to this NotFound error. The gist upload is therefore entirely dependent on gh being installed, authenticated, and visible to the codewhale process.","triggerScenarios":"Running the share/upload command on a machine without GitHub CLI installed, with gh present but not on the PATH of the codewhale process (launched from a GUI, service, or container), or with a broken gh install whose --version probe fails so resolution caches None for the process lifetime.","commonSituations":"Fresh machines; gh installed via brew/winget but the shell or TUI was not restarted; Windows gh under %LOCALAPPDATA%\\GitHub CLI missing from system PATH; minimal dev containers without gh.","solutions":["Install GitHub CLI: brew install gh, winget install GitHub.cli, or see cli.github.com","Confirm `gh --version` and `gh auth login` succeed in the same environment that launches codewhale, then restart the TUI (probe result is cached per process)","Fix PATH so the gh binary's directory is included for the launching process, not only your interactive shell"],"exampleFix":"# before\ncodewhale /project share   # NotFound: gh not found\n\n# after\nbrew install gh && gh auth login\nexec $SHELL   # restart so the probe re-runs\ncodewhale /project share","handlingStrategy":"validation","validationCode":"if !crate::dependencies::Gh::available() {\n    return Err(\"GitHub CLI (gh) is required to share: install from cli.github.com and run gh auth login\".into());\n}","typeGuard":"fn share_available() -> bool {\n    crate::dependencies::Gh::command().is_some()\n}","tryCatchPattern":null,"preventionTips":["Check Gh::available() before offering the share action in the UI","Install and authenticate gh (gh auth login) once per machine","Restart the TUI after installing gh — resolution is cached per process"],"tags":["github","gh-cli","dependencies","share","path"],"backgroundTag":"missing-cli-on-path","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}