Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
All notable changes to `codesema` (the npm package in `packages/cli`) are documented here.
Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versioning: [SemVer](https://semver.org).

## [0.18.2] - 2026-08-28

### Fixed

- **The caged agent could not execute claude when the cage image was built through the native installer.** claude.ai/install.sh drops a symlink in ~/.local/bin pointing into ~/.local/share/claude; the image build runs as root, so moving that symlink to /usr/local/bin left its target under /root (mode 0700) and every caged turn died with "claude: Permission denied" for the non-root uid the turn runs as. The recipe now materializes the real binary with cp -L, chmods it 755 and removes the root-owned source tree in the same layer. First caught on the first production server's first ticket.

## [0.18.1] - 2026-08-27

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codesema-tools",
"version": "0.18.1",
"version": "0.18.2",
"private": true,
"type": "module",
"workspaces": [
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codesema",
"version": "0.18.1",
"version": "0.18.2",
"description": "Local merge request review, step by step. Your AI agent reviews, codesema displays.",
"license": "MIT",
"author": "Hasan TASKIN",
Expand Down
3 changes: 3 additions & 0 deletions packages/cli/src/task-isolation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,9 @@ describe('installCommandFor', () => {
expect(claude).toContain('$HOME/.local/bin/claude')
expect(claude).toContain('command -v claude')
expect(claude).toContain('/usr/local/bin/claude')
expect(claude).toContain('cp -L "$HOME/.local/bin/claude" /usr/local/bin/claude')
expect(claude).toContain('chmod 755 /usr/local/bin/claude')
expect(claude).not.toContain('mv "$HOME/.local/bin/claude"')
const opencode = installCommandFor('opencode')
expect(opencode).toContain('https://opencode.ai/install')
expect(opencode).toContain('npm cache clean --force')
Expand Down
7 changes: 6 additions & 1 deletion packages/cli/src/task-isolation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,12 @@ export function installCommandFor(agent: string, baseRef?: string): string {
'if [ -x "$HOME/.local/bin/opencode" ]; then mv "$HOME/.local/bin/opencode" /usr/local/bin/opencode; fi',
]
: [
'if [ -x "$HOME/.local/bin/claude" ]; then mv "$HOME/.local/bin/claude" /usr/local/bin/claude; fi',
// The native installer drops a SYMLINK into ~/.local/bin pointing at
// ~/.local/share/claude/versions/<v>. The build runs as root, so a
// moved symlink keeps its target under /root (0700) and the caged
// uid gets EACCES at run time. cp -L materializes the real binary;
// the source tree is removed in the same RUN so the layer holds one copy.
'if [ -x "$HOME/.local/bin/claude" ]; then cp -L "$HOME/.local/bin/claude" /usr/local/bin/claude && chmod 755 /usr/local/bin/claude && rm -rf "$HOME/.local/share/claude" "$HOME/.local/bin/claude"; fi',
]
return [
'if command -v curl >/dev/null 2>&1 && command -v tar >/dev/null 2>&1 && command -v bash >/dev/null 2>&1; then',
Expand Down
Loading