From 55f0fe5767abbb0bb59ad630183dd1905af91168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pascal=20Andr=C3=A9?= Date: Tue, 6 Oct 2026 11:00:48 +0200 Subject: [PATCH] docs: add concise English help for OpenCode power users Add seven short user-guide pages focused on CodeNomad controls, session and worktree workflows, history navigation, file readers, and configuration scope. Assume familiarity with OpenCode V2 rather than teaching agents or prompt basics; keep native configuration details in the upstream documentation. Render the Markdown as a responsive static site using the existing marked dependency, with local assets, relative project-site links, light/dark appearance, and no client runtime. Prepare a GitHub Pages workflow that checks pull requests and deploys from dev after Pages is explicitly configured. Document that one-time setup; desktop Help-menu wiring and updater changes remain outside this PR. Validate generated links, anchors, assets, English page metadata, and navigation with the standalone help test. Check all seven pages at desktop and mobile widths in light and dark modes, and inspect the local Tauri browser preview. --- .github/workflows/help-pages.yml | 60 +++++++++++++++++++++ AGENTS.md | 2 + README.md | 2 + dev-docs/USER_HELP.md | 44 +++++++++++++++ docs/help/conversations.md | 32 +++++++++++ docs/help/files-and-tools.md | 37 +++++++++++++ docs/help/getting-started.md | 31 +++++++++++ docs/help/help.css | 93 ++++++++++++++++++++++++++++++++ docs/help/index.md | 26 +++++++++ docs/help/projects.md | 31 +++++++++++ docs/help/settings.md | 39 ++++++++++++++ docs/help/troubleshooting.md | 45 ++++++++++++++++ package.json | 4 +- scripts/build-help.mjs | 79 +++++++++++++++++++++++++++ scripts/test-help.mjs | 35 ++++++++++++ 15 files changed, 559 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/help-pages.yml create mode 100644 dev-docs/USER_HELP.md create mode 100644 docs/help/conversations.md create mode 100644 docs/help/files-and-tools.md create mode 100644 docs/help/getting-started.md create mode 100644 docs/help/help.css create mode 100644 docs/help/index.md create mode 100644 docs/help/projects.md create mode 100644 docs/help/settings.md create mode 100644 docs/help/troubleshooting.md create mode 100644 scripts/build-help.mjs create mode 100644 scripts/test-help.mjs diff --git a/.github/workflows/help-pages.yml b/.github/workflows/help-pages.yml new file mode 100644 index 000000000..3350d5ef7 --- /dev/null +++ b/.github/workflows/help-pages.yml @@ -0,0 +1,60 @@ +name: Publish help + +on: + push: + branches: [dev] + paths: + - 'docs/help/**' + - 'docs/screenshots/workspace-0.20.png' + - 'images/CodeNomad-Icon.png' + - 'scripts/build-help.mjs' + - 'scripts/test-help.mjs' + - 'package-lock.json' + - '.github/workflows/help-pages.yml' + pull_request: + paths: + - 'docs/help/**' + - 'docs/screenshots/workspace-0.20.png' + - 'images/CodeNomad-Icon.png' + - 'scripts/build-help.mjs' + - 'scripts/test-help.mjs' + - 'package-lock.json' + - '.github/workflows/help-pages.yml' + workflow_dispatch: + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version-file: .node-version + cache: npm + - run: npm ci --ignore-scripts + - run: node --test scripts/test-help.mjs + - run: node scripts/build-help.mjs + - uses: actions/upload-pages-artifact@v3 + with: + path: dist/help + + deploy: + if: github.ref == 'refs/heads/dev' && github.event_name != 'pull_request' + needs: build + runs-on: ubuntu-24.04 + permissions: + pages: write + id-token: write + concurrency: + group: help-pages + cancel-in-progress: false + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - uses: actions/configure-pages@v5 + - uses: actions/deploy-pages@v4 + id: deployment diff --git a/AGENTS.md b/AGENTS.md index 24545d716..a067f10b1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -94,6 +94,8 @@ ## Multi-Language Support (i18n) +- The separate public user guide lives in `docs/help/` and is intentionally English-only for now. `scripts/build-help.mjs` renders its Markdown using the existing `marked` dependency; `docs/help/help.css` owns the standalone site's square, responsive chrome. It does not import the application runtime or add an app locale. Validate it with `npm run test:help`; GitHub Pages publishes `dist/help` through `.github/workflows/help-pages.yml`. + The UI uses a small custom i18n layer (no ICU/messageformat). When building features, never hardcode user-visible strings. - **Runtime API:** use `useI18n()` in components (`const { t } = useI18n();`) and `tGlobal(...)` in stores/non-component code. diff --git a/README.md b/README.md index 422dc6014..bf98db61f 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ CodeNomad is a **desktop and browser workspace for OpenCode V2** — built for d *The 0.20 interface, rendered with demonstration data. [Download the latest stable release](https://github.com/NeuralNomadsAI/CodeNomad/releases/latest).* +[User help](https://neuralnomadsai.github.io/CodeNomad/) · [Read the guide on GitHub](docs/help/index.md) — a short English guide to current CodeNomad and OpenCode V2. + --- ## Features diff --git a/dev-docs/USER_HELP.md b/dev-docs/USER_HELP.md new file mode 100644 index 000000000..f9225cb4c --- /dev/null +++ b/dev-docs/USER_HELP.md @@ -0,0 +1,44 @@ +# Public user help + +The English guide in `docs/help/` covers current CodeNomad with OpenCode V2, +not the old runtime or every upstream configuration option. Assume readers +already know OpenCode: document CodeNomad's controls, workflow differences, +and state/scope boundaries instead of teaching agents, prompts, Git, or MCP. +Keep pages short, use current English interface labels, and link to the +official V2 docs for native configuration details. + +## Build and check + +```sh +npm ci --ignore-scripts +npm run test:help +npm run build:help +``` + +Output is `dist/help/`. Serve that directory with any static HTTP server to +preview it; no app backend, credentials, or OpenCode service is needed. The +builder uses the existing `marked` dependency, copies local screenshots/logo, +and generates relative links that work under a GitHub project-site path. +The site has no client JavaScript, remote fonts, analytics, or runtime requests. + +## Publish on GitHub Pages + +In **Repository Settings → Pages → Build and deployment**, select **GitHub +Actions** as the source. The repository currently has no Pages site; this is a +one-time administrator setup, not something the workflow silently enables. +Allow the `github-pages` environment to deploy from `dev` if its protection +rules require a branch allowlist. + +After merging the workflow into `dev`, it builds and publishes changes to the +help sources. It can also be started manually from `dev`. Pull requests build +and check the site but never deploy it. Deployment needs `pages: write` and +`id-token: write`; the build job only reads repository contents. + +Expected URL: **https://neuralnomadsai.github.io/CodeNomad/**. No separate +repository, paid hosting, domain, or additional deployment secret is needed. +The site follows `dev` (the current implementation); it is not an archive of +versioned guides. Update the help alongside user-visible behavior changes. + +The desktop Help link should use this URL once deployment is available. Native +Help menu items are maintained in both desktop hosts; the updater work from +issue #606 remains separate from documentation hosting. diff --git a/docs/help/conversations.md b/docs/help/conversations.md new file mode 100644 index 000000000..489668d41 --- /dev/null +++ b/docs/help/conversations.md @@ -0,0 +1,32 @@ +# Conversations + +The controls around your OpenCode sessions: composer context, pending requests, and full-history navigation. + +## Composer controls + +**Agent**, **model**, and **thinking** selectors live in the composer footer, alongside the session's worktree selector. Resize the composer without changing its draft; long drafts scroll within it. + +The composer remains available during execution. Subsequent submissions may enter the session's pending queue; **Stop** targets the current execution. + +## Context attachments + +- **`@`** searches project files, agents, and skills available in the session's Location. Selecting a skill attaches a removable badge rather than inserting its instructions into your draft. +- The **attachment** action, drop, and paste use files from your client device. Project file references resolve on the execution host. +- **`/`** lists the Location's commands. **`/btw`** opens a temporary side-question window without replacing the main conversation. + +## Requests across conversations + +Pending questions and permissions are edited **above the composer**, not in transcript cards. Request drafts survive queue refreshes and session navigation. + +The request area includes the open session, its descendants, and other conversations. Origin labels identify the source; **View conversation** navigates there. External requests start compact and can be answered in place. Arrival does not replace the request being edited or switch your session. Use previous/next controls when several requests are pending. + +CodeNomad's **Yolo mode** applies to the session family: it automatically approves permissions, not questions or other Forms. + +## History navigation + +- **Search** queries full history, not just mounted transcript rows. +- **Timeline** markers jump to messages and tool activity. Hover/focus previews let you inspect a point without loading a whole transcript section. At narrow conversation widths, the rail is hidden and header actions move into overflow. +- **Message content visibility** controls reasoning and tool presentation without changing stored history. +- **Fork** continues from a history point in a separate session. + +Subsessions appear beneath their parent in normal browsing. Session search uses flat results, with an option to include subsessions. View closure, session deletion, and history pruning are separate operations; the latter two can remove stored content. diff --git a/docs/help/files-and-tools.md b/docs/help/files-and-tools.md new file mode 100644 index 000000000..29628959c --- /dev/null +++ b/docs/help/files-and-tools.md @@ -0,0 +1,37 @@ +# Files and tools + +The right panel combines workspace browsing, Git inspection, and project services. Readers open centrally, above the composer. + +## Workspace files + +In **Files → Workspace**, expand folders and select a row. Use its **eye** action to open the central file reader; selecting a row alone does not open it. + +Text files, including Markdown source, are editable. Use **Save** or **Ctrl/Cmd+S** to write changes. If the file changed on disk, CodeNomad asks you to resolve that conflict rather than silently replacing it. Images and binary previews are read-only. + +Close the reader with its **X** to return to the conversation. + +## Review Git changes + +Switch **Files** to **Changes** to inspect staged and unstaged files. Open a diff with the eye action, stage or unstage files, and use the commit controls at the top of the staged section. + +Switch to **Commits** to browse repository history and inspect historical diffs. Diffs are read-only; use the layout switch to choose unified or split views. + +## Check Status + +The **Status** panel shows project services and background activity, including **MCP Servers** and **Plugins**. + +- **MCP Servers** exposes connection state and activation controls. +- **Plugins** have separate **Global** and **Project** activation switches. Global changes can affect other projects; a project rule can override the global choice. +- Background shell activity is distinct from an interactive terminal and from the agent's current reply. + +## Preview a web app + +Open the session's **browser preview** from the conversation toolbar. It is attached to that session; the agent can target the visible preview through CodeNomad's browser tools. Opening it does not start a development server. + +Agent-driven native browser control is available in Electron and **Windows Tauri**. Other Tauri platforms use an iframe preview; that is not equivalent to native browser automation. Some sites also prevent iframe embedding. + +## Add a SideCar + +**SideCars** are tabs for web tools running on the CodeNomad server host, such as an editor or terminal service. + +Start the tool separately, then add it in **Settings → SideCars** with its name, port, and protocol. The port refers to the **server host**, not necessarily the device displaying CodeNomad. Match the prefix mode to the tool's base-path support; see the [SideCar examples](https://github.com/NeuralNomadsAI/CodeNomad#sidecars). diff --git a/docs/help/getting-started.md b/docs/help/getting-started.md new file mode 100644 index 000000000..2e7660c89 --- /dev/null +++ b/docs/help/getting-started.md @@ -0,0 +1,31 @@ +# Connect OpenCode + +Use your existing OpenCode V2 setup from a local desktop host or through a CodeNomad server. + +## Local desktop + +Download [the current desktop build](https://github.com/NeuralNomadsAI/CodeNomad/releases/latest), then select your OpenCode executable in setup. CodeNomad prefers executables on `PATH`; **Settings → OpenCode** exposes the selected CLI and the running shared service separately. An installation action is available if you need it. Desktop builds bundle Node.js/npm. + +Open the repository or folder whose sessions you want to access. CodeNomad uses directory ownership to determine which sessions belong to that project; see [projects and worktrees](projects.md). + +## Backend versus execution host + +Git must be available in the **CodeNomad backend's `PATH`**, even when OpenCode executes inside WSL. Git installed only in WSL does not satisfy a Windows backend. Restart the backend after changing its environment. + +With Git unavailable, folder-only sessions remain usable, but Git/worktree features and cross-checkout discovery are unavailable. + +## Browser and remote desktop + +Run CodeNomad on the host containing your projects and OpenCode setup. The standalone server requires Node.js 24 LTS/npm: + +```sh +npx @neuralnomads/codenomad --password "your-password" --launch +``` + +Use the printed connection URL in a browser or CodeNomad's remote connection screen. The server binds to loopback by default; use the [server guide](https://github.com/NeuralNomadsAI/CodeNomad/blob/dev/packages/server/README.md#remote-access-binding-rules) for remote binding, authentication, and TLS options. + +Project paths, commands, and SideCar ports refer to the **server host**, not the client displaying CodeNomad. Device attachments are the exception: they come from the client device. + +## CLI updates are not service restarts + +Updating OpenCode through CodeNomad changes the installed executable, not the running service. Restart explicitly when you want to activate it; this can interrupt work in all connected clients. Closing CodeNomad leaves the service running. diff --git a/docs/help/help.css b/docs/help/help.css new file mode 100644 index 000000000..7a53207c4 --- /dev/null +++ b/docs/help/help.css @@ -0,0 +1,93 @@ +:root { + color-scheme: light dark; + --canvas: #edf0f4; + --panel: #e0e6ed; + --text: #283a4a; + --muted: #526373; + --accent: #375c9b; + --border: #bdc9d6; + --code: #dce3ec; + font-family: "Segoe UI", system-ui, sans-serif; + line-height: 1.65; + background: var(--canvas); + color: var(--text); +} +@media (prefers-color-scheme: dark) { + :root { + --canvas: #272f40; + --panel: #333e52; + --text: #dce3ef; + --muted: #adbed4; + --accent: #c6b58c; + --border: #53647c; + --code: #333e52; + } +} +* { box-sizing: border-box; } +body { margin: 0; } +a { color: var(--accent); text-underline-offset: .2em; } +a:hover { text-decoration-thickness: 2px; } +:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; } +.skip-link { position: fixed; top: -100px; left: 1rem; padding: .5rem 1rem; background: var(--canvas); z-index: 3; } +.skip-link:focus { top: 1rem; } +.site-header { display: flex; justify-content: space-between; align-items: center; gap: 1rem; padding: 1rem 2rem; border-bottom: 1px solid var(--border); } +.brand { display: flex; align-items: center; gap: .7rem; color: var(--text); font-size: 1.25rem; font-weight: 650; text-decoration: none; } +.brand span { font-size: .875rem; font-weight: 400; color: var(--muted); border-left: 1px solid var(--border); padding-left: .7rem; } +.site-header nav { display: flex; gap: 1.5rem; font-size: .875rem; } +.layout { display: grid; grid-template-columns: 230px minmax(0, 760px) 190px; gap: 3rem; max-width: 1400px; margin: auto; padding: 2.5rem 2rem; } +.sidebar, .outline { position: sticky; top: 2rem; align-self: start; max-height: calc(100vh - 4rem); overflow: auto; font-size: .875rem; } +.eyebrow { margin: 0 0 .75rem; text-transform: uppercase; letter-spacing: .1em; font-size: .75rem; color: var(--muted); font-family: ui-monospace, monospace; } +.sidebar nav { display: grid; gap: .2rem; } +.sidebar nav a { padding: .6rem .75rem; color: var(--text); text-decoration: none; border-left: 3px solid transparent; } +.sidebar nav a:hover { background: var(--panel); } +.sidebar nav a[aria-current] { background: var(--panel); border-color: var(--accent); font-weight: 600; } +.scope { color: var(--muted); border-top: 1px solid var(--border); margin-top: 1.5rem; padding: 1rem .75rem; font-size: .8rem; } +main { min-width: 0; } +main:focus { outline: none; } +h1, h2, h3 { font-family: "Segoe UI", system-ui, sans-serif; line-height: 1.25; font-weight: 650; scroll-margin-top: 1.5rem; } +h1 { font-size: clamp(2rem, 3vw, 2.8rem); letter-spacing: -.035em; margin: 0 0 1.2rem; } +h2 { font-size: 1.4rem; margin: 2.5rem 0 1rem; } +h3 { font-size: 1.1rem; margin-top: 2rem; } +article > p:first-of-type { font-size: 1.15rem; color: var(--muted); } +p, ul, ol { margin: 0 0 1.2rem; } +ul, ol { padding-left: 1.5rem; } +li { margin: .4rem 0; } +article img { max-width: 100%; height: auto; display: block; border: 1px solid var(--border); margin: 1.5rem 0; } +strong { font-weight: 650; } +code, kbd, pre { font-family: "Cascadia Code", "SFMono-Regular", Consolas, monospace; font-size: .875em; } +code { background: var(--code); padding: .15em .3em; overflow-wrap: anywhere; } +pre { background: var(--code); border-left: 3px solid var(--accent); padding: 1rem; overflow-x: auto; } +pre code { padding: 0; overflow-wrap: normal; } +kbd { border: 1px solid var(--border); padding: .1em .3em; } +table { border-collapse: collapse; width: 100%; font-size: .95rem; margin: 1.5rem 0; } +th, td { border-bottom: 1px solid var(--border); padding: .65rem .5rem; text-align: left; overflow-wrap: anywhere; } +th { font-weight: 600; } +blockquote { border-left: 3px solid var(--accent); margin: 1.5rem 0; padding-left: 1rem; color: var(--muted); } +.outline { color: var(--muted); font-size: .8rem; } +summary { cursor: pointer; color: var(--text); font-weight: 600; } +.outline ul { list-style: none; padding: .5rem 0; margin-bottom: 1rem; } +.outline a { color: var(--muted); text-decoration: none; display: block; padding: .2rem 0; } +.outline a:hover { color: var(--accent); } +footer { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 1rem; margin-top: 3rem; padding-top: 1rem; border-top: 1px solid var(--border); font-size: .8rem; } +@media (max-width: 1150px) { + .layout { grid-template-columns: 210px minmax(0, 760px); gap: 2rem; } + .outline { display: none; } +} +@media (max-width: 720px) { + .site-header { padding: 1rem; gap: .5rem; } + .brand { font-size: 1rem; gap: .4rem; } + .brand span { padding-left: .4rem; } + .site-header nav { gap: .75rem; font-size: .8rem; } + .layout { display: block; padding: 1rem; } + .sidebar { position: static; max-height: none; overflow: visible; border-bottom: 1px solid var(--border); margin-bottom: 2rem; padding-bottom: 1rem; } + .sidebar nav { display: flex; flex-wrap: wrap; gap: .25rem; } + .sidebar nav a { padding: .45rem .6rem; } + .scope { border: 0; margin: .75rem 0 0; padding: 0; } + .scope br { display: none; } + td:last-child { width: 42%; } +} +@media print { + .site-header, .sidebar, .outline, footer, .skip-link { display: none; } + .layout { display: block; padding: 0; } + :root { --canvas: white; --text: black; --muted: #333; --accent: #222; --code: #eee; } +} diff --git a/docs/help/index.md b/docs/help/index.md new file mode 100644 index 000000000..2fa56bf4e --- /dev/null +++ b/docs/help/index.md @@ -0,0 +1,26 @@ +# CodeNomad help + +An OpenCode workspace, across projects, sessions, and worktrees. + +This guide assumes you already use **OpenCode V2**. It covers CodeNomad's interface and workflow, not the basics of agents or OpenCode configuration. + +![CodeNomad workspace with project tabs, sessions, a conversation, and the Status panel](../screenshots/workspace-0.20.png) + +## Find the control you need + +| I want to… | Read | +|---|---| +| Connect CodeNomad to my OpenCode setup | [Connect OpenCode](getting-started.md) | +| Navigate history, attach context, handle requests across sessions | [Conversations](conversations.md) | +| Move a session family between checkouts | [Projects and worktrees](projects.md) | +| Use the file reader, Git views, browser preview, and SideCars | [Files and tools](files-and-tools.md) | +| Control model visibility, accounts, and Global/Project settings | [Settings](settings.md) | +| Diagnose a CodeNomad-specific problem | [Troubleshooting](troubleshooting.md) | + +## What stays shared + +CodeNomad uses the shared OpenCode service. Sessions and history remain available to compatible OpenCode clients; tabs, drafts, and layout are restored separately per CodeNomad window. Closing a tab or window does **not** stop the service. + +## Reference + +For native configuration, use the [OpenCode V2 documentation](https://opencode.ai/v2/docs/). For hosting options, use the [CodeNomad server guide](https://github.com/NeuralNomadsAI/CodeNomad/blob/dev/packages/server/README.md). diff --git a/docs/help/projects.md b/docs/help/projects.md new file mode 100644 index 000000000..cae529f18 --- /dev/null +++ b/docs/help/projects.md @@ -0,0 +1,31 @@ +# Projects and worktrees + +CodeNomad groups an opened repository and its registered Git worktrees into one project. Session placement still follows its execution directory. + +## Switch projects and sessions + +Use project tabs to switch folders and the session list to switch conversations. Selecting another conversation does not move its files or change its working directory. + +The session list normally shows parent sessions and their subsessions. Search and filters let you find sessions directly, including subsessions when that option is enabled. + +Desktop windows can show different projects or sessions. Tabs, drafts, and layout are restored per window; session history belongs to the shared OpenCode service. + +## Use a Git worktree + +In the composer's worktree selector, select an existing checkout or choose **Create and use worktree**. Both actions move the session and its subsessions to the destination. + +Uncommitted file changes stay in the original checkout; they are not copied to the destination. Creating a worktree outside this selector, for example with Git, does not by itself move a CodeNomad session. + +CodeNomad's default worktree location is **`.codenomad/worktrees`** inside the main project checkout. The Files panel can browse another worktree without moving your session or checking out a branch. + +## Know where execution happens + +Prompts and session commands run in the session's working directory on its execution host. A remote desktop client or browser is only the interface: your project files and tools live on the server host. + +On Windows, a configured WSL OpenCode executable runs inside its selected distribution. Keep its tools and paths available there; Git must also be available to the CodeNomad backend for repository features. + +## Close without stopping work + +Closing a project tab or CodeNomad window detaches that view. It does not shut down the shared OpenCode service. + +**Stop**, workspace-stop actions, and shared-service restarts have different scopes. Read the confirmation before interrupting work beyond the selected session. diff --git a/docs/help/settings.md b/docs/help/settings.md new file mode 100644 index 000000000..5e183669d --- /dev/null +++ b/docs/help/settings.md @@ -0,0 +1,39 @@ +# Settings + +Open **Settings** for CodeNomad's display preferences and native configuration controls. On a local desktop, this opens a separate **Preferences** window. + +## Providers and models + +**Settings → Providers** separates **Models** from **Web search**. + +**Manage models** filters which models CodeNomad shows in selectors. **Active account** switches the provider credential when multiple accounts are available; environment-based connections are read-only. **Connect** adds a connection through the provider's native flow. + +Provider credentials are shared by projects using the same OpenCode service. Changing the active account can affect other clients connected to that service. + +## Web search + +In the Web search group, connect a search provider, then set **Default search** or **For the current project**. A project override takes precedence over the global choice. Search credentials are global; connecting a provider does not by itself select it for every project. + +## OpenCode + +**Settings → OpenCode** shows the selected executable and running service. You can install or update the CLI, check status, and use explicit service controls. + +- **Update** changes the installed executable. +- **Restart shared service** replaces the running OpenCode process and can interrupt work in every connected client. +- **Reload OpenCode configuration** is different from restarting. It rebuilds loaded Locations and can cancel pending requests and close terminals/background commands. Use it deliberately, not as a routine refresh. + +Follow the compatibility information shown by the current CodeNomad release. A newer installed CLI and an older running service can coexist until you restart it. + +## Appearance and interaction + +Choose a light, dark, or automatic appearance and a palette for each mode. You can also adjust language, font sizing, notifications, and conversation content visibility. + +**Ctrl/Cmd+Shift+P** opens the **Command Palette**. **Settings → General → Enter to submit** controls composer submission; shortcut hints reflect the active behavior. + +## Environment variables + +CodeNomad applies the profile's environment variables before each prompt or session command on the execution host. Editing them does not restart the service or change a session until the next send. The client device's environment is not substituted for a remote host's. + +## Voice and speech + +Configure voice input and text-to-speech in their settings sections. Microphone use requires device/browser permission. Availability depends on the selected service and your setup. diff --git a/docs/help/troubleshooting.md b/docs/help/troubleshooting.md new file mode 100644 index 000000000..2d10416ee --- /dev/null +++ b/docs/help/troubleshooting.md @@ -0,0 +1,45 @@ +# Troubleshooting + +Checks for the CodeNomad interface and its connection to OpenCode. Shared-service restarts affect other connected clients too. + +## OpenCode will not connect + +Open **Settings → OpenCode** and check the selected executable, running-service status, and compatibility details. Use **Check status and updates**; install or select a supported OpenCode V2 executable if needed. + +If an update is installed but the old service is still running, use the explicit restart action when it is safe to interrupt shared work. A configuration reload is not an executable upgrade. + +## No model is available + +If a model works in OpenCode but is missing in CodeNomad, check **Settings → Providers → Manage models** for selector visibility, then check the active account. **Models** and **Web search** are separate groups. + +## Git or worktrees are unavailable + +Check Git in the CodeNomad **backend process's `PATH`**, not only in the agent's shell. Restart CodeNomad or its standalone server after changing that environment. + +Git inside WSL alone does not satisfy a Windows backend. Until Git is available, folder-only conversations can still work, but repository and worktree features cannot. + +## A reply seems stuck + +Pending requests are in the area **above the composer**, including requests from subsessions and other conversations. Check that area rather than looking for an editable transcript form. **Status** shows background activity; message-content filters can hide reasoning and tool details. + +Use **Stop** if you want to interrupt the execution. If sending failed, verify the current state before trying again—do not assume the server rejected it before doing any work. + +If the interface itself freezes, [report it](https://github.com/NeuralNomadsAI/CodeNomad/issues) with your CodeNomad version, OpenCode version, OS, desktop host (Tauri/Electron) or browser, and whether the server is remote. Include repeatable steps where possible, but remove credentials and private project content. + +## Remote login or HTTPS fails + +The remote URL and login belong to the **CodeNomad server**, not directly to the OpenCode service. Check its reachability and authentication independently of provider credentials. + +Only trust a certificate warning after verifying that the server is yours. For remote hosts, prefer a trusted certificate or secure tunnel. See the [server authentication and TLS guide](https://github.com/NeuralNomadsAI/CodeNomad/blob/dev/packages/server/README.md). + +## Desktop-specific startup issues + +For an unnotarized macOS download that Gatekeeper marks as damaged, use the documented [macOS steps](https://github.com/NeuralNomadsAI/CodeNomad#troubleshooting) only after verifying the download's source. + +On Linux with Wayland/NVIDIA, a Tauri WebKitGTK startup failure may be a graphics issue. The [same troubleshooting section](https://github.com/NeuralNomadsAI/CodeNomad#troubleshooting) lists the workaround; Electron is an alternative host. The Tauri Debian package is currently qualified on Ubuntu 24.04, not every Debian-based distribution. + +## Go deeper + +- [OpenCode V2 documentation](https://opencode.ai/v2/docs/) for agents, commands, skills, and tool configuration. +- [CodeNomad server guide](https://github.com/NeuralNomadsAI/CodeNomad/blob/dev/packages/server/README.md) for hosting and connection options. +- [CodeNomad issues](https://github.com/NeuralNomadsAI/CodeNomad/issues) for bugs and feature requests. diff --git a/package.json b/package.json index c3e4b863c..b6f86f5f7 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,9 @@ "build:mac-x64": "npm run build:mac-x64 --workspace @neuralnomads/codenomad-electron-app", "build:binaries": "npm run build:binaries --workspace @neuralnomads/codenomad-electron-app", "typecheck": "npm run typecheck --workspace @codenomad/ui && npm run typecheck --workspace @neuralnomads/codenomad-electron-app", - "bumpVersion": "node ./scripts/bump-version.js" + "bumpVersion": "node ./scripts/bump-version.js", + "build:help": "node scripts/build-help.mjs", + "test:help": "node --test scripts/test-help.mjs" }, "dependencies": { "7zip-bin": "^5.2.0", diff --git a/scripts/build-help.mjs b/scripts/build-help.mjs new file mode 100644 index 000000000..19ef79b12 --- /dev/null +++ b/scripts/build-help.mjs @@ -0,0 +1,79 @@ +import { cp, mkdir, readFile, writeFile } from "node:fs/promises" +import { fileURLToPath } from "node:url" +import { resolve } from "node:path" +import { Marked } from "marked" + +const root = fileURLToPath(new URL("../", import.meta.url)) +const source = resolve(root, "docs/help") +const output = resolve(process.argv[2] ?? resolve(root, "dist/help")) +const pages = [ + ["index", "Overview"], + ["getting-started", "Connect OpenCode"], + ["conversations", "Conversations"], + ["projects", "Projects and worktrees"], + ["files-and-tools", "Files and tools"], + ["settings", "Settings"], + ["troubleshooting", "Troubleshooting"], +] +const repo = "https://github.com/NeuralNomadsAI/CodeNomad" +const escape = (value) => value.replace(/[&<>"']/g, (char) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[char]) + +await mkdir(resolve(output, "assets"), { recursive: true }) +await Promise.all([ + cp(resolve(source, "help.css"), resolve(output, "assets/help.css")), + cp(resolve(root, "docs/screenshots/workspace-0.20.png"), resolve(output, "assets/workspace.png")), + cp(resolve(root, "images/CodeNomad-Icon.png"), resolve(output, "assets/logo.png")), + writeFile(resolve(output, ".nojekyll"), ""), +]) + +for (const [name, label] of pages) { + const headings = [] + const used = new Map() + const markdown = new Marked({ renderer: { + heading(text, level, raw) { + const slug = raw.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") + const count = used.get(slug) ?? 0 + used.set(slug, count + 1) + const id = count ? `${slug}-${count}` : slug + if (level === 2) headings.push({ text: raw, id }) + return `${text}\n` + }, + link(href, title, text) { + const target = href.replace(/^([\w-]+)\.md(?=#|$)/, "$1.html") + return `${text}` + }, + image(href, title, text) { + const target = href === "../screenshots/workspace-0.20.png" ? "assets/workspace.png" : href + return `${escape(text)}` + }, + } }) + const content = markdown.parse(await readFile(resolve(source, `${name}.md`), "utf8")) + const nav = pages.map(([id, title]) => `${title}`).join("\n") + const toc = headings.map(({ text, id }) => `
  • ${escape(text)}
  • `).join("\n") + await writeFile(resolve(output, `${name}.html`), ` + + + + + + + ${label} — CodeNomad help + + + + + + +
    + +
    ${content}
    + +
    + + +`) +} +console.log(`Built ${pages.length} help pages in ${output}`) diff --git a/scripts/test-help.mjs b/scripts/test-help.mjs new file mode 100644 index 000000000..b264f77e8 --- /dev/null +++ b/scripts/test-help.mjs @@ -0,0 +1,35 @@ +import assert from "node:assert/strict" +import { test } from "node:test" +import { mkdtemp, readFile, readdir, rm } from "node:fs/promises" +import { tmpdir } from "node:os" +import { join, resolve } from "node:path" +import { fileURLToPath } from "node:url" +import { execFileSync } from "node:child_process" + +const root = fileURLToPath(new URL("../", import.meta.url)) + +test("English help builds standalone pages with working local links, assets, and anchors", async (ctx) => { + const output = await mkdtemp(join(tmpdir(), "codenomad-help-")) + ctx.after(() => rm(output, { recursive: true, force: true })) + execFileSync(process.execPath, [resolve(root, "scripts/build-help.mjs"), output], { cwd: root }) + const pages = (await readdir(output)).filter((name) => name.endsWith(".html")) + assert.equal(pages.length, 7) + for (const page of pages) { + const html = await readFile(join(output, page), "utf8") + assert.match(html, //) + assert.equal((html.match(/

    match[1]) + assert.equal(ids.length, new Set(ids).size, "unique anchors") + for (const [, url] of html.matchAll(/(?:href|src)="([^"]+)"/g)) { + if (url.startsWith("https://")) continue + assert.ok(!url.startsWith("/"), `project Pages links must stay relative: ${url}`) + assert.ok(!url.includes(".md"), `Markdown link not converted: ${url}`) + const [path, fragment] = url.split("#") + const target = path ? await readFile(join(output, path), "utf8") : html + if (fragment) assert.ok(target.includes(`id="${fragment}"`), `broken anchor: ${page} → ${url}`) + } + } +})