Compare commits

...

12 Commits

Author SHA1 Message Date
Shantur Rathore
d6462ef524 Min version 0.11.4 2026-02-22 17:32:28 +00:00
Shantur Rathore
a06884ebce Bump to v0.11.4 2026-02-22 16:53:51 +00:00
Shantur Rathore
62bd88f6a4 chore(plugin): Upgrade dependency version 2026-02-22 16:48:49 +00:00
Shantur Rathore
6479561779 fix(ui): auto-expand session thread when child starts working 2026-02-22 16:47:04 +00:00
Shantur Rathore
635237c258 fix(ui): render task prompt consistently while running 2026-02-22 08:58:39 +00:00
Shantur Rathore
33f0aa5714 ci: run dev prerelease nightly
Replace dev push builds with nightly schedule that only runs when dev head advances; still runs on manual dispatch. Plumb a ref input through reusable workflows so scheduled runs build the dev commit.
2026-02-20 13:58:32 +00:00
Shantur Rathore
7ca6285d58 Merge branch 'dev' of github.com:NeuralNomadsAI/CodeNomad into dev 2026-02-20 13:49:03 +00:00
Shantur Rathore
377c8e2249 Merge branch 'dev' of github.com:NeuralNomadsAI/CodeNomad into dev 2026-02-20 13:31:52 +00:00
Shantur Rathore
5fabf286e8 ui: restyle command palette button 2026-02-20 00:32:44 +00:00
Shantur Rathore
e8947d61b1 ui: emphasize command palette button 2026-02-20 00:32:39 +00:00
Shantur Rathore
1ccd14eae8 ui: use Check icon for completed status 2026-02-20 00:32:27 +00:00
Shantur Rathore
b162764ccb ui: use lucide status icons for tool calls 2026-02-20 00:32:15 +00:00
21 changed files with 173 additions and 37 deletions

View File

@@ -3,6 +3,11 @@ name: Build and Upload Binaries
on:
workflow_call:
inputs:
ref:
description: "Git ref (branch, tag, or SHA) to build from"
required: false
default: ""
type: string
version:
description: "Version to apply to workspace packages (release builds)"
required: false
@@ -45,6 +50,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ inputs.ref || github.ref }}
- name: Setup Node
uses: actions/setup-node@v4
@@ -85,6 +92,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ inputs.ref || github.ref }}
- name: Setup Node
uses: actions/setup-node@v4
@@ -124,6 +133,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ inputs.ref || github.ref }}
- name: Setup Node
uses: actions/setup-node@v4
@@ -164,6 +175,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ inputs.ref || github.ref }}
- name: Setup Node
uses: actions/setup-node@v4
@@ -237,6 +250,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ inputs.ref || github.ref }}
- name: Setup Node
uses: actions/setup-node@v4
@@ -310,6 +325,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ inputs.ref || github.ref }}
- name: Setup Node
uses: actions/setup-node@v4
@@ -388,6 +405,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ inputs.ref || github.ref }}
- name: Setup Node
uses: actions/setup-node@v4
@@ -490,6 +509,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ inputs.ref || github.ref }}
- name: Setup QEMU
uses: docker/setup-qemu-action@v3
@@ -587,6 +608,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ inputs.ref || github.ref }}
- name: Setup Node
uses: actions/setup-node@v4

View File

@@ -1,12 +1,13 @@
name: Develop Pre-Release
on:
push:
branches:
- dev
schedule:
# Nightly build of dev (only if dev has new commits)
- cron: "0 1 * * *"
workflow_dispatch:
permissions:
actions: read
id-token: write
contents: write
@@ -15,25 +16,63 @@ concurrency:
cancel-in-progress: true
jobs:
prepare:
gate:
runs-on: ubuntu-latest
outputs:
version_suffix: ${{ steps.vars.outputs.version_suffix }}
run: ${{ steps.gate.outputs.run }}
dev_sha: ${{ steps.gate.outputs.dev_sha }}
version_suffix: ${{ steps.gate.outputs.version_suffix }}
steps:
- name: Compute version suffix
id: vars
- name: Decide whether to run
id: gate
shell: bash
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
SHA8="${GITHUB_SHA::8}"
api() {
curl -sS \
-H "Authorization: Bearer ${GH_TOKEN}" \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"$1"
}
DEV_SHA=$(api "https://api.github.com/repos/${GITHUB_REPOSITORY}/git/ref/heads/dev" | jq -r '.object.sha')
if [ -z "$DEV_SHA" ] || [ "$DEV_SHA" = "null" ]; then
echo "Failed to resolve dev head SHA" >&2
exit 1
fi
DATE=$(date -u +%Y%m%d)
echo "version_suffix=-dev-${DATE}-${SHA8}" >> "$GITHUB_OUTPUT"
SHA8="${DEV_SHA::8}"
VERSION_SUFFIX="-dev-${DATE}-${SHA8}"
SHOULD_RUN="false"
if [ "${GITHUB_EVENT_NAME}" = "workflow_dispatch" ]; then
SHOULD_RUN="true"
else
# Nightly: only run if dev has advanced since last successful dev-release build.
LAST_SHA=$(api "https://api.github.com/repos/${GITHUB_REPOSITORY}/actions/workflows/dev-release.yml/runs?branch=dev&status=success&per_page=1" | jq -r '.workflow_runs[0].head_sha // empty')
if [ -z "${LAST_SHA}" ]; then
SHOULD_RUN="true"
elif [ "${LAST_SHA}" != "${DEV_SHA}" ]; then
SHOULD_RUN="true"
fi
fi
echo "run=${SHOULD_RUN}" >> "$GITHUB_OUTPUT"
echo "dev_sha=${DEV_SHA}" >> "$GITHUB_OUTPUT"
echo "version_suffix=${VERSION_SUFFIX}" >> "$GITHUB_OUTPUT"
prerelease:
needs: prepare
needs: gate
if: ${{ needs.gate.outputs.run == 'true' }}
uses: ./.github/workflows/reusable-release.yml
with:
version_suffix: ${{ needs.prepare.outputs.version_suffix }}
ref: ${{ needs.gate.outputs.dev_sha }}
version_suffix: ${{ needs.gate.outputs.version_suffix }}
npm_package_name: "@neuralnomads/codenomad-dev"
dist_tag: latest
prerelease: true

View File

@@ -19,6 +19,10 @@ on:
type: string
workflow_call:
inputs:
ref:
required: false
default: ""
type: string
version:
required: true
type: string
@@ -46,6 +50,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ inputs.ref || github.ref }}
- name: Setup Node
uses: actions/setup-node@v4

View File

@@ -1,7 +1,13 @@
name: Release UI
on:
workflow_call: {}
workflow_call:
inputs:
ref:
description: "Git ref (branch, tag, or SHA) to build from"
required: false
default: ""
type: string
workflow_dispatch: {}
permissions:
@@ -18,6 +24,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ inputs.ref || github.ref }}
- name: Setup Node
uses: actions/setup-node@v4

View File

@@ -3,6 +3,11 @@ name: Reusable Release
on:
workflow_call:
inputs:
ref:
description: "Git ref (branch, tag, or SHA) to build from"
required: false
default: ""
type: string
version_suffix:
description: "Suffix appended to package.json version"
required: false
@@ -46,6 +51,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ inputs.ref || github.ref }}
- name: Setup Node
uses: actions/setup-node@v4
@@ -84,6 +91,7 @@ jobs:
needs: prepare-release
uses: ./.github/workflows/build-and-upload.yml
with:
ref: ${{ inputs.ref || github.ref }}
version: ${{ needs.prepare-release.outputs.version }}
tag: ${{ needs.prepare-release.outputs.tag }}
release_name: ${{ needs.prepare-release.outputs.release_name }}
@@ -95,6 +103,8 @@ jobs:
permissions:
contents: read
uses: ./.github/workflows/release-ui.yml
with:
ref: ${{ inputs.ref || github.ref }}
secrets: inherit
publish-server:
@@ -103,6 +113,7 @@ jobs:
- build-and-upload
uses: ./.github/workflows/manual-npm-publish.yml
with:
ref: ${{ inputs.ref || github.ref }}
version: ${{ needs.prepare-release.outputs.version }}
dist_tag: ${{ inputs.dist_tag }}
package_name: ${{ inputs.npm_package_name }}

12
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "codenomad-workspace",
"version": "0.11.3",
"version": "0.11.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "codenomad-workspace",
"version": "0.11.3",
"version": "0.11.4",
"license": "MIT",
"dependencies": {
"7zip-bin": "^5.2.0",
@@ -11985,7 +11985,7 @@
},
"packages/electron-app": {
"name": "@neuralnomads/codenomad-electron-app",
"version": "0.11.3",
"version": "0.11.4",
"license": "MIT",
"dependencies": {
"@codenomad/ui": "file:../ui",
@@ -12021,7 +12021,7 @@
},
"packages/server": {
"name": "@neuralnomads/codenomad",
"version": "0.11.3",
"version": "0.11.4",
"license": "MIT",
"dependencies": {
"@fastify/cors": "^8.5.0",
@@ -12062,7 +12062,7 @@
},
"packages/tauri-app": {
"name": "@codenomad/tauri-app",
"version": "0.11.3",
"version": "0.11.4",
"license": "MIT",
"devDependencies": {
"@tauri-apps/cli": "^2.9.4"
@@ -12070,7 +12070,7 @@
},
"packages/ui": {
"name": "@codenomad/ui",
"version": "0.11.3",
"version": "0.11.4",
"license": "MIT",
"dependencies": {
"@git-diff-view/solid": "^0.0.8",

View File

@@ -1,6 +1,6 @@
{
"name": "codenomad-workspace",
"version": "0.11.3",
"version": "0.11.4",
"private": true,
"description": "CodeNomad monorepo workspace",
"license": "MIT",

View File

@@ -1,4 +1,4 @@
{
"minServerVersion": "0.11.1",
"minServerVersion": "0.11.4",
"latestServerUrl": "https://github.com/NeuralNomadsAI/CodeNomad/releases/latest"
}

View File

@@ -1,6 +1,6 @@
{
"name": "@neuralnomads/codenomad-electron-app",
"version": "0.11.3",
"version": "0.11.4",
"description": "CodeNomad - AI coding assistant",
"license": "MIT",
"author": {

View File

@@ -4,6 +4,6 @@
"private": true,
"license": "MIT",
"dependencies": {
"@opencode-ai/plugin": "1.2.6"
"@opencode-ai/plugin": "1.2.10"
}
}

View File

@@ -1,12 +1,12 @@
{
"name": "@neuralnomads/codenomad",
"version": "0.11.3",
"version": "0.11.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@neuralnomads/codenomad",
"version": "0.11.3",
"version": "0.11.4",
"dependencies": {
"@fastify/cors": "^8.5.0",
"@fastify/reply-from": "^9.8.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@neuralnomads/codenomad",
"version": "0.11.3",
"version": "0.11.4",
"description": "CodeNomad Server",
"license": "MIT",
"author": {

View File

@@ -1,6 +1,6 @@
{
"name": "@codenomad/tauri-app",
"version": "0.11.3",
"version": "0.11.4",
"private": true,
"license": "MIT",
"scripts": {

View File

@@ -1,6 +1,6 @@
{
"name": "@codenomad/ui",
"version": "0.11.3",
"version": "0.11.4",
"private": true,
"license": "MIT",
"type": "module",

View File

@@ -625,7 +625,7 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
<div class="flex flex-wrap items-center justify-center gap-1">
<button
type="button"
class="connection-status-button px-2 py-0.5 text-xs"
class="connection-status-button command-palette-button"
onClick={handleCommandPaletteClick}
aria-label={t("instanceShell.commandPalette.openAriaLabel")}
style={{ flex: "0 0 auto", width: "auto" }}
@@ -721,7 +721,7 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
<div class="session-toolbar-center flex items-center justify-center gap-2 min-w-[160px]">
<button
type="button"
class="connection-status-button px-2 py-0.5 text-xs"
class="connection-status-button command-palette-button"
onClick={handleCommandPaletteClick}
aria-label={t("instanceShell.commandPalette.openAriaLabel")}
style={{ flex: "0 0 auto", width: "auto" }}

View File

@@ -51,7 +51,7 @@ export default function MessageListHeader(props: MessageListHeaderProps) {
<div class="connection-status-shortcut-action">
<button
type="button"
class="connection-status-button"
class="connection-status-button command-palette-button"
onClick={props.onCommandPalette}
aria-label={t("messageListHeader.commandPalette.ariaLabel")}
>

View File

@@ -1,5 +1,5 @@
import { createSignal, Show, createEffect, createMemo, onCleanup } from "solid-js"
import { ArrowRightSquare, Copy } from "lucide-solid"
import { ArrowRightSquare, Check, Copy, Hourglass, Loader2, XCircle } from "lucide-solid"
import { stringify as stringifyYaml } from "yaml"
import { messageStoreBus } from "../stores/message-v2/bus"
import { useTheme } from "../lib/theme"
@@ -576,13 +576,13 @@ export default function ToolCall(props: ToolCallProps) {
const status = toolState()?.status || ""
switch (status) {
case "pending":
return "⏳"
return <Hourglass class="w-4 h-4" />
case "running":
return "🔄"
return <Loader2 class="w-4 h-4 animate-spin" />
case "completed":
return "✅"
return <Check class="w-4 h-4" />
case "error":
return "⚠️"
return <XCircle class="w-4 h-4" />
default:
return ""
}

View File

@@ -287,7 +287,9 @@ export const taskRenderer: ToolRenderer = {
content: promptContent()!,
cacheKey: "task:prompt",
disableScrollTracking: true,
disableHighlight: true,
// Always use the normal markdown render path for prompt (even while running)
// so the prompt doesn't visually change between running/completed states.
disableHighlight: false,
})}
</div>
</section>

View File

@@ -40,7 +40,7 @@ import {
} from "./instances"
import { showAlertDialog } from "./alerts"
import { createClientSession, mapSdkSessionStatus, type Session, type SessionStatus } from "../types/session"
import { sessions, setSessions, syncInstanceSessionIndicator, withSession } from "./session-state"
import { ensureSessionParentExpanded, sessions, setSessions, syncInstanceSessionIndicator, withSession } from "./session-state"
import { normalizeMessagePart } from "./message-v2/normalizers"
import { updateSessionInfo } from "./message-v2/session-info"
import { tGlobal } from "../lib/i18n"
@@ -108,6 +108,8 @@ interface TuiToastEvent {
const ALLOWED_TOAST_VARIANTS = new Set<ToastVariant>(["info", "success", "warning", "error"])
function applySessionStatus(instanceId: string, sessionId: string, status: SessionStatus) {
let parentToExpand: string | null = null
withSession(instanceId, sessionId, (session) => {
const current = session.status ?? "idle"
if (current === status) return false
@@ -117,7 +119,17 @@ function applySessionStatus(instanceId: string, sessionId: string, status: Sessi
}
session.status = status
// Auto-expand the parent thread when a child session starts working.
// Users can still collapse it; we only expand on the transition.
if (session.parentId && status === "working" && current !== "working") {
parentToExpand = session.parentId
}
})
if (parentToExpand) {
ensureSessionParentExpanded(instanceId, parentToExpand)
}
}
async function fetchSessionInfo(instanceId: string, sessionId: string, directory?: string): Promise<Session | null> {
@@ -158,6 +170,7 @@ async function fetchSessionInfo(instanceId: string, sessionId: string, directory
const fetched = createClientSession(info, instanceId, "", { providerId: "", modelId: "" }, fetchedStatus)
let updatedInstanceSessions: Map<string, Session> | undefined
let shouldExpandParent: string | null = null
setSessions((prev) => {
const next = new Map(prev)
@@ -174,11 +187,19 @@ async function fetchSessionInfo(instanceId: string, sessionId: string, directory
instanceSessions.set(sessionId, merged)
next.set(instanceId, instanceSessions)
updatedInstanceSessions = instanceSessions
if (merged.parentId && merged.status === "working" && (existing?.status ?? "idle") !== "working") {
shouldExpandParent = merged.parentId
}
return next
})
syncInstanceSessionIndicator(instanceId, updatedInstanceSessions)
if (shouldExpandParent) {
ensureSessionParentExpanded(instanceId, shouldExpandParent)
}
return fetched
} catch (error) {
log.error("Failed to fetch session info", error)

View File

@@ -347,10 +347,23 @@ function clearActiveParentSession(instanceId: string): void {
}
function setSessionStatus(instanceId: string, sessionId: string, status: SessionStatus): void {
let parentToExpand: string | null = null
withSession(instanceId, sessionId, (session) => {
if (session.status === status) return false
const previous = session.status
session.status = status
// If a child session starts working, auto-expand its parent thread once.
// Users can still collapse it afterwards; we only expand on the transition.
if (session.parentId && status === "working" && previous !== "working") {
parentToExpand = session.parentId
}
})
if (parentToExpand) {
ensureSessionParentExpanded(instanceId, parentToExpand)
}
}
function getActiveParentSession(instanceId: string): Session | null {

View File

@@ -130,6 +130,19 @@
color: var(--text-primary);
}
/* Make the command palette trigger stand out in the header. */
.connection-status-button.command-palette-button {
border-radius: 0;
@apply text-sm px-2 py-1 border border-base transition-colors;
background-color: var(--surface-base);
color: var(--text-primary);
}
.connection-status-button.command-palette-button:hover {
background-color: var(--surface-hover);
color: var(--text-primary);
}
.connection-status-button:hover {
background-color: var(--surface-hover);
}