fix: write valid multi-line tmux config in Pi Agent images

The Pi Agent dockerfile templates created ~/.tmux.conf with a literal
\n because the RUN command used single-quoted echo. Tmux never parsed
the malformed line, so mouse mode stayed off. Without tmux mouse mode,
mouse-wheel events in xterm.js fell back to Up/Down arrow keys and
cycled shell command history instead of scrolling the terminal buffer.

- Use printf '%s\n' to write real newlines in .tmux.conf.
- Apply the same fix to the ranger rc.conf where the same bug existed.
- Update tool-images/pi-agent.dockerfile and both affected alembic
  migration dockerfile strings.

Quality gates: npm run typecheck, npm run lint, npm test -- --run (87 passed),
py_compile on changed migrations.

Refs: openspec/changes/fix-tmux-mouse-config
This commit is contained in:
Developer
2026-06-14 08:48:39 +00:00
parent 6c31ef9577
commit 4c14966ae3
16 changed files with 70 additions and 29 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ dir: .
Trust boundary: index routes, map orients, source decides.
## role
A self-hosted web-based project management and git repository platform with OAuth2 authentication, built as a containerized multi-service application with PostgreSQL, Redis, and Traefik deployment support.
Infrastructure and deployment configuration for a self-hosted project management platform with OAuth2 authentication, providing Docker Compose orchestration, environment templates, and development tooling.
## parent
-
## children
+2 -2
View File
@@ -18,7 +18,7 @@ index: ./.pi-map.index.md
Trust boundary: index routes, map orients, source decides.
## role
A self-hosted web-based project management and git repository platform with OAuth2 authentication, built as a containerized multi-service application with PostgreSQL, Redis, and Traefik deployment support.
Infrastructure and deployment configuration for a self-hosted project management platform with OAuth2 authentication, providing Docker Compose orchestration, environment templates, and development tooling.
## files
- .env.example | Provides a template of environment variables for configuring a Headquarter application with PostgreSQL, Redis, Authentik SSO, and Docker/Traefik deployment
- .gitignore | Specifies files and directories for Git to ignore across a multi-language project with Python, Node, and custom tooling | dep: Git
@@ -31,7 +31,7 @@ A self-hosted web-based project management and git repository platform with OAut
- progress.md | Tracks completed and remaining tasks for a backend-frontend code refactoring project organized in 7 phases
- swap-pane | Empty file with no functionality
## arch
Monolithic full-stack architecture with separate frontend/backend services, Docker Compose orchestration, environment-driven configuration via .env files, and reverse proxy integration (Traefik) for TLS-terminated deployment; follows OpenSpec-driven AI-assisted development workflows with phased refactoring approach.
Containerized microservices architecture using Docker Compose with PostgreSQL and Redis data layers, Traefik reverse proxy for TLS/ingress, environment-based configuration management, and Makefile-driven development lifecycle.
## tags
docker, redis, git, application, postgresql, compose, traefik, project
## symbols
+1 -1
View File
@@ -2,7 +2,7 @@
dir: apps
## role
Contains the main application entry points and executable targets for the project.
Container for independent, deployable application modules that compose the overall system.
## parent
index: ./.pi-map.index.md
map: ./.pi-map.md
+2 -2
View File
@@ -4,10 +4,10 @@ dir: apps
index: apps/.pi-map.index.md
## role
This appears to be an empty or minimal apps directory, typically serving as a container for application-specific modules or entry points in a larger project structure.
Container for independent, deployable application modules that compose the overall system.
## files
## arch
Standard monorepo or multi-app directory structure following organizational conventions where individual applications or services would be housed as subdirectories.
Monorepo-style multi-app structure where each sub-directory is a self-contained application sharing common infrastructure and dependencies.
## tags
-
## symbols
+1 -1
View File
@@ -2,7 +2,7 @@
dir: apps/api
## role
Self-hosted FastAPI backend API that manages projects, git repositories, and development tools via Docker instances.
FastAPI backend API that manages projects, git repositories, and development tools via Docker instances.
## parent
index: apps/.pi-map.index.md
map: apps/.pi-map.md
+2 -2
View File
@@ -4,7 +4,7 @@ dir: apps/api
index: apps/api/.pi-map.index.md
## role
Self-hosted FastAPI backend API that manages projects, git repositories, and development tools via Docker instances.
FastAPI backend API that manages projects, git repositories, and development tools via Docker instances.
## files
- .dockerignore | Specifies files and directories to exclude from Docker build context to reduce image size and avoid copying unnecessary files into containers. | dep: Docker
- Dockerfile | Multi-stage Docker build for a Python application with Docker socket access, Cloudflare tunneling, and database dependency waiting | dep: python:3.11-slim, gcc, libpq-dev, docker-ce-cli, docker-compose-plugin, cloudflared, uvicorn, pyproject.toml dependencies
@@ -14,7 +14,7 @@ Self-hosted FastAPI backend API that manages projects, git repositories, and dev
- uv.lock | Lock file for the uv Python package manager that pins exact dependency versions and their artifact hashes for reproducible installations | dep: uv, Python 3.11+, aiosqlite, alembic, annotated-doc, annotated-types, anyio, ast-serialize, asyncpg, and many other PyPI packages
- wait-for-db.sh | Wait for a PostgreSQL database to become available before executing a command, with configurable retry logic. | dep: nc (netcat), sh (POSIX shell), sleep
## arch
Async Python/FastAPI with PostgreSQL (Alembic migrations), multi-stage Docker deployment with Cloudflare tunneling, uv package management, and containerized service orchestration.
Async FastAPI with PostgreSQL (Alembic migrations), multi-stage Docker containerization with Docker socket access and Cloudflare tunneling, uv package management.
## tags
docker, alembic, python, database, fastapi, postgresql, asyncpg, uvicorn
## symbols
+1 -1
View File
@@ -9,7 +9,7 @@ Database migration infrastructure for the API application, providing version-con
- env.py | Configures Alembic database migration environment with async SQLAlchemy support for a project. | exp: func:run_migrations_offline() → None, call:context.configure, call:context.begin_transaction, call:context.run_migrations, func:do_run_migrations(connection: Connection) → None, call:context.configure, call:context.begin_transaction, call:context.run_migrations, func:run_async_migrations() → None, call:async_engine_from_config, call:config.get_section, call:connectable.connect, call:connection.run_sync, call:connectable.dispose, func:run_migrations_online() → None, call:asyncio.run, call:run_async_migrations | dep: logging.config, alembic, sqlalchemy, sqlalchemy.engine, sqlalchemy.ext.asyncio, src.config, src.models, asyncio
- script.py.mako | Alembic database migration script template that generates upgrade/downgrade functions for SQLAlchemy schema migrations | dep: alembic, sqlalchemy
## arch
Alembic migration framework with Mako templating for generating revision scripts, async SQLAlchemy engine configuration, and autogenerate capabilities for schema change tracking.
Standard Alembic migration framework with async SQLAlchemy integration, using Mako templating for migration script generation and environment-based configuration for database connection management.
## tags
migrations, run, sqlalchemy, async, alembic, call:context.configure, call:context.begin, transaction
## symbols
+1 -1
View File
@@ -2,7 +2,7 @@
dir: apps/api/alembic/versions
## role
Manages incremental database schema evolution for the API application using Alembic migrations, tracking all table creations, column additions, relationship changes, and data transformations over the project's lifecycle.
Contains sequential Alembic database migration scripts that evolve the API's relational schema over time, tracking structural changes from initial tables through complex features like tool instances, config profiles, workspaces, manifests, and monitoring.
## parent
index: apps/api/alembic/.pi-map.index.md
map: apps/api/alembic/.pi-map.md
+8 -8
View File
@@ -4,9 +4,9 @@ dir: apps/api/alembic/versions
index: apps/api/alembic/versions/.pi-map.index.md
## role
Manages incremental database schema evolution for the API application using Alembic migrations, tracking all table creations, column additions, relationship changes, and data transformations over the project's lifecycle.
Contains sequential Alembic database migration scripts that evolve the API's relational schema over time, tracking structural changes from initial tables through complex features like tool instances, config profiles, workspaces, manifests, and monitoring.
## files
- 0001_initial_schema.py | Defines the initial database schema migration creating five tables (users, ssh_keys, projects, git_repositories, user_configs) with relationships, indexes, and constraints using Alembic. | exp: func:upgrade() → None, call:op.create_table, call:sa.Column, call:sa.String, call:postgresql.UUID, call:sa.DateTime, call:sa.func.now, call:sa.PrimaryKeyConstraint, call:sa.UniqueConstraint, call:op.create_index, call:op.f, call:sa.Text, call:sa.ForeignKeyConstraint, call:sa.Boolean, call:postgresql.JSONB, func:downgrade() → None, call:op.drop_table, call:op.drop_index, call:op.f | dep: alembic, sqlalchemy.dialects, sqlalchemy, postgresql dialect
- 0001_initial_schema.py | Defines the initial database schema migration creating five tables (users, ssh_keys, projects, git_repositories, user_configs) with relationships, indexes, and constraints using Alembic. | dep: sqlalchemy, alembic, postgresql dialect
- 0002_refresh_tokens.py | Alembic database migration that creates a refresh_tokens table with indexes for user authentication token management | exp: func:upgrade() → None, call:op.get_bind, call:sa.inspect, call:inspector.has_table, call:op.create_table, call:sa.Column, call:postgresql.UUID, call:sa.String, call:sa.DateTime, call:sa.ForeignKeyConstraint, call:sa.PrimaryKeyConstraint, call:sa.UniqueConstraint, call:inspector.get_indexes, call:op.f, call:op.create_index, func:downgrade() → None, call:op.get_bind, call:sa.inspect, call:inspector.has_table, call:inspector.get_indexes, call:op.f, call:op.drop_index, call:op.drop_table | dep: alembic, sqlalchemy.dialects, sqlalchemy, sqlalchemy.dialects.postgresql
- 0003_user_configs.py | Alembic database migration that creates a user_configs table with JSON configuration storage linked to users | exp: func:upgrade() → None, call:op.create_table, call:sa.Column, call:sa.UUID, call:sa.JSON, call:sa.DateTime, call:sa.text, call:sa.ForeignKeyConstraint, call:sa.PrimaryKeyConstraint, call:sa.UniqueConstraint, func:downgrade() → None, call:op.drop_table | dep: typing, alembic, sqlalchemy
- 0004_tool_types.py | Alembic database migration that creates a tool_types table with metadata, templates, and versioning columns for a tool management system. | exp: func:upgrade() → None, call:op.create_table, call:sa.Column, call:sa.Uuid, call:sa.String, call:sa.Text, call:sa.JSON, call:sa.Boolean, call:sa.ForeignKey, call:sa.DateTime, call:sa.text, func:downgrade() → None, call:op.drop_table | dep: typing, alembic, sqlalchemy
@@ -24,7 +24,7 @@ Manages incremental database schema evolution for the API application using Alem
- 0014_merge_heads.py | Alembic merge migration that reconciles two divergent migration branches into a single history line | exp: func:upgrade() → None, func:downgrade() → None | dep: typing, alembic
- 0015_single_interface.py | Alembic database migration that replaces a JSON array `interfaces` column with `interface_type` string and `requires_port` boolean columns in the `tool_types` table, with dialect-specific data migration for PostgreSQL and SQLite. | exp: func:_get_dialect() → str, call:op.get_bind, func:upgrade() → None, call:_get_dialect, call:op.add_column, call:sa.Column, call:sa.String, call:sa.Boolean, call:op.execute, call:op.alter_column, call:op.drop_column, call:op.create_check_constraint, call:sa.text, func:downgrade() → None, call:_get_dialect, call:op.drop_constraint, call:op.add_column, call:sa.Column, call:postgresql.JSONB, call:sa.Text, call:op.execute, call:sa.JSON, call:op.drop_column | dep: typing, alembic, sqlalchemy.dialects, sqlalchemy, postgresql (dialect)
- 069d3da4dc9b_add_ssh_key_id_to_config_profiles.py | Alembic database migration that adds a nullable UUID foreign key column `ssh_key_id` to the `config_profiles` table referencing `ssh_keys.id` with SET NULL on delete | exp: func:upgrade() → None, call:op.add_column, call:sa.Column, call:sa.Uuid, call:sa.ForeignKey, func:downgrade() → None, call:op.drop_column | dep: alembic, sqlalchemy
- 20260527160017_add_pi_agent_tool_type.py | Alembic database migration that adds a "pi-agent" terminal-based coding tool type to a tool_types table with Docker configuration templates | exp: func:upgrade() → None, call:op.get_bind, call:conn.execute( sa.text("SELECT id FROM tool_types WHERE name = 'pi-agent'") ).fetchone, call:sa.text, call:json.dumps, func:downgrade() → None, call:op.get_bind, call:conn.execute, call:sa.text | dep: json, typing, alembic, uuid, sqlalchemy
- 20260527160017_add_pi_agent_tool_type.py | Alembic database migration that adds a new "pi-agent" tool type with terminal-based Docker environment for the Pi coding agent | exp: func:upgrade() → None, call:op.get_bind, call:conn.execute( sa.text("SELECT id FROM tool_types WHERE name = 'pi-agent'") ).fetchone, call:sa.text, call:json.dumps, func:downgrade() → None, call:op.get_bind, call:conn.execute, call:sa.text | dep: json, typing, alembic, uuid, sqlalchemy
- 2026_05_22_add_clone_mode.py | Alembic database migration that adds ssh_key_id foreign key to git_repositories table and clone_mode/branch columns to tool_instances table | exp: func:upgrade() → None, call:op.add_column, call:sa.Column, call:postgresql.UUID, call:op.create_foreign_key, call:sa.String, func:downgrade() → None, call:op.drop_column, call:op.drop_constraint | dep: alembic, sqlalchemy.dialects, sqlalchemy, sqlalchemy.dialects.postgresql
- 2026_05_23_remove_is_builtin.py | Alembic database migration to remove the `is_builtin` column from the `tool_types` table | exp: func:upgrade() → None, call:op.execute, func:downgrade() → None, call:op.add_column, call:sa.Column, call:sa.Boolean | dep: alembic, sqlalchemy
- 2026_05_24_220141_add_startup_command.py | Alembic database migration that adds a nullable `startup_command` text column to the `tool_types` table. | exp: func:upgrade() → None, call:op.add_column, call:sa.Column, call:sa.Text, func:downgrade() → None, call:op.drop_column | dep: typing, alembic, sqlalchemy
@@ -33,7 +33,7 @@ Manages incremental database schema evolution for the API application using Alem
- 2026_05_27_external_repos.py | Alembic database migration that makes project_id nullable in git_repositories table to support external repositories and expands alembic_version version_num column to 64 characters. | exp: func:upgrade() → None, call:op.execute, call:op.alter_column, call:sa.UUID, func:downgrade() → None, call:op.alter_column, call:sa.UUID, call:op.execute | dep: typing, alembic, sqlalchemy
- 2026_05_28_add_monitoring_tables.py | Alembic database migration that creates monitoring tables (instance_events and health_checks) with indexes for tracking tool instance events and health checks | exp: func:upgrade() → None, call:op.create_table, call:sa.Column, call:sa.Uuid, call:sa.String, call:sa.Text, call:sa.JSON, call:sa.DateTime, call:sa.func.now, call:sa.ForeignKeyConstraint, call:sa.PrimaryKeyConstraint, call:op.create_index, call:sa.Boolean, call:sa.Integer, func:downgrade() → None, call:op.drop_index, call:op.drop_table | dep: collections.abc, alembic, sqlalchemy
- 2026_05_28_add_terminal_sessions_table.py | Alembic database migration that creates a terminal_sessions table with tracking columns and foreign key to tool_instances | exp: func:upgrade() → None, call:op.create_table, call:sa.Column, call:sa.UUID, call:sa.String, call:sa.DateTime, call:sa.text, call:sa.ForeignKeyConstraint, call:sa.PrimaryKeyConstraint, call:op.create_index, call:op.f, func:downgrade() → None, call:op.drop_index, call:op.f, call:op.drop_table | dep: collections.abc, alembic, sqlalchemy
- 2026_05_28_add_tool_definition_manifests.py | Alembic database migration that creates a tool_definition_manifests table, adds manifest-related columns to tool_types and tool_instances, and migrates the pi-agent tool from Dockerfile-based to manifest-based definitions with seed data. | exp: func:upgrade() → None, call:op.get_bind, call:op.create_table, call:sa.Column, call:sa.UUID, call:sa.String, call:sa.Text, call:sa.JSON, call:sa.Boolean, call:sa.TIMESTAMP, call:sa.func.now, call:sa.PrimaryKeyConstraint, call:sa.UniqueConstraint, call:sa.ForeignKeyConstraint, call:sa.CheckConstraint, call:conn.execute, call:sa.text, call:result.fetchone, call:op.add_column, call:op.create_foreign_key, call:op.drop_constraint, call:op.execute, call:json.dumps, call:str, func:downgrade() → None, call:op.get_bind, call:conn.execute, call:sa.text, call:result.fetchone, call:op.drop_column, call:op.drop_constraint, call:op.drop_table | dep: json, uuid, typing, alembic, sqlalchemy
- 2026_05_28_add_tool_definition_manifests.py | Alembic database migration that creates a tool_definition_manifests table, adds manifest-related columns to tool_types and tool_instances, and migrates the pi-agent tool from legacy dockerfile/compose templates to a JSON-based manifest system with a base Ubuntu image definition. | exp: func:upgrade() → None, call:op.get_bind, call:op.create_table, call:sa.Column, call:sa.UUID, call:sa.String, call:sa.Text, call:sa.JSON, call:sa.Boolean, call:sa.TIMESTAMP, call:sa.func.now, call:sa.PrimaryKeyConstraint, call:sa.UniqueConstraint, call:sa.ForeignKeyConstraint, call:sa.CheckConstraint, call:conn.execute, call:sa.text, call:result.fetchone, call:op.add_column, call:op.create_foreign_key, call:op.drop_constraint, call:op.execute, call:json.dumps, call:str, func:downgrade() → None, call:op.get_bind, call:conn.execute, call:sa.text, call:result.fetchone, call:op.drop_column, call:op.drop_constraint, call:op.drop_table | dep: json, uuid, typing, alembic, sqlalchemy
- 2026_05_28_drop_tool_configs_and_config_folders.py | Alembic database migration that drops `tool_configs` and `config_folders` tables with conditional existence checks and full downgrade recreation | exp: func:upgrade() → None, call:op.get_bind, call:conn.execute, call:sa.text, call:result.fetchone, call:op.drop_table, func:downgrade() → None, call:op.create_table, call:sa.Column, call:sa.UUID, call:sa.String, call:sa.Text, call:sa.JSON, call:sa.Boolean, call:sa.TIMESTAMP, call:sa.func.now, call:sa.PrimaryKeyConstraint, call:sa.Integer | dep: typing, alembic, sqlalchemy
- 2026_05_29_add_notifications_table.py | Alembic database migration that creates a notifications table with user-linked, categorized, severity-graded messages supporting read/dismissed tracking and optimized querying indexes. | exp: func:upgrade() → None, call:op.create_table, call:sa.Column, call:sa.Uuid, call:sa.String, call:sa.Text, call:sa.JSON, call:sa.DateTime, call:sa.func.now, call:sa.ForeignKeyConstraint, call:sa.PrimaryKeyConstraint, call:op.create_index, call:sa.text, func:downgrade() → None, call:op.drop_index, call:op.drop_table | dep: collections.abc, alembic, sqlalchemy
- 2026_05_29_add_ssh_key_ids_to_tool_instances.py | Alembic database migration that adds a JSON column named ssh_key_ids to the tool_instances table | exp: func:upgrade() → None, call:op.add_column, call:sa.Column, call:sa.JSON, func:downgrade() → None, call:op.drop_column | dep: alembic, sqlalchemy
@@ -44,18 +44,18 @@ Manages incremental database schema evolution for the API application using Alem
- 2026_05_29_remove_lsio_command_override.py | Alembic database migration that removes broken command overrides containing --bind-addr or --host flags from LinuxServer.io code-server Docker Compose templates in both database tool_types records and on-disk instance compose files. | exp: func:upgrade() → None, call:op.get_bind, call:conn.execute( sa.text(""" SELECT id, compose_template FROM tool_types WHERE name = 'code-server' """) ).fetchall, call:sa.text, call:yaml.safe_load, call:data["services"].values, call:svc.get, call:yaml.dump, call:print, call:conn.execute( sa.text(""" SELECT column_name FROM information_schema.columns WHERE table_name = 'tool_instances' AND column_name = 'compose_path' """) ).fetchone, call:conn.execute( sa.text(""" SELECT id, compose_path FROM tool_instances WHERE compose_path IS NOT NULL """) ).fetchall, call:Path, call:path.exists, call:path.read_text, call:path.write_text, func:downgrade() → None | dep: collections.abc, alembic, yaml, pathlib, sqlalchemy, pathlib.Path, information_schema
- 2026_05_29_remove_ssh_keys_mount_from_manifest.py | Alembic database migration that removes (or restores) the ssh_keys mount from a JSON manifest stored in the tool_definition_manifests table for the pi-agent tool definition. | exp: func:upgrade() → None, call:op.get_bind, call:conn.execute, call:sa.text, call:result.fetchone, call:isinstance, call:json.loads, call:manifest.get, call:len, call:m.get, call:json.dumps, func:downgrade() → None, call:op.get_bind, call:conn.execute, call:sa.text, call:result.fetchone, call:isinstance, call:json.loads, call:manifest.get, call:any, call:m.get, call:mounts.append, call:json.dumps | dep: json, typing, alembic, sqlalchemy
- 2026_06_01_add_workspaces.py | Alembic database migration that creates a workspaces table with foreign keys to git_repositories and users, adds indexes, and adds a workspace_id column to tool_instances | exp: func:upgrade() → None, call:op.create_table, call:sa.Column, call:sa.Uuid, call:sa.String, call:sa.ForeignKey, call:sa.DateTime, call:sa.text, call:sa.UniqueConstraint, call:op.create_index, call:op.add_column, func:downgrade() → None, call:op.drop_index, call:op.drop_column, call:op.drop_table | dep: collections.abc, alembic, sqlalchemy
- 2026_06_13_make_clone_mode_nullable.py | Alembic database migration that makes the `clone_mode` column in `tool_instances` table nullable to allow NULL values for new rows | exp: func:upgrade() → None, call:op.alter_column, call:sa.String, func:downgrade() → None, call:op.alter_column, call:sa.String | dep: alembic, sqlalchemy
- 2026_06_13_make_clone_mode_nullable.py | Alembic database migration that makes the `clone_mode` column in `tool_instances` table nullable to support workspace-first cleanup workflow. | exp: func:upgrade() → None, call:op.alter_column, call:sa.String, func:downgrade() → None, call:op.alter_column, call:sa.String | dep: alembic, sqlalchemy
- 398082499c30_add_tool_config_fields.py | Alembic database migration that adds five new columns (port_override, start_command, working_directory, environment_variables, volumes) to the tool_configs table with a port range check constraint. | exp: func:upgrade() → None, call:op.add_column, call:sa.Column, call:sa.Integer, call:sa.Text, call:postgresql.JSONB, call:op.create_check_constraint, call:sa.text, func:downgrade() → None, call:op.drop_constraint, call:op.drop_column | dep: alembic, sqlalchemy.dialects, sqlalchemy, sqlalchemy.dialects.postgresql
- 6fc7bfcf199f_merge_remove_is_builtin_and_add_config_.py | Alembic database migration that merges two parallel revision branches (removing is_builtin and adding config_profiles) into a single history line | exp: func:upgrade() → None, func:downgrade() → None | dep: alembic
- 86cec91fdb00_merge_profile_resolver_and_workspaces_.py | Alembic database migration that merges two divergent migration branches (profile resolver and workspaces) into a single head | exp: func:upgrade() → None, func:downgrade() → None | dep: alembic
- 8c6d1dbd4798_remove_pi_config_and_state_mounts_from_.py | Alembic database migration that removes pi_state and pi_config mounts from the pi-agent manifest in upgrade, and restores them in downgrade | exp: func:_load_manifest(manifest_json), call:isinstance, call:json.loads, func:upgrade() → None, call:op.get_bind, call:conn.execute, call:sa.text, call:result.fetchone, call:_load_manifest, call:manifest.get, call:len, call:m.get, call:json.dumps, func:downgrade() → None, call:op.get_bind, call:conn.execute, call:sa.text, call:result.fetchone, call:_load_manifest, call:manifest.get, call:m.get, call:mounts.append, call:json.dumps | dep: json, alembic, sqlalchemy
- 8c6d1dbd4798_remove_pi_config_and_state_mounts_from_.py | Alembic database migration that removes or restores pi_state and pi_config mounts from the pi-agent manifest stored in tool_definition_manifests table | exp: func:_load_manifest(manifest_json), call:isinstance, call:json.loads, func:upgrade() → None, call:op.get_bind, call:conn.execute, call:sa.text, call:result.fetchone, call:_load_manifest, call:manifest.get, call:len, call:m.get, call:json.dumps, func:downgrade() → None, call:op.get_bind, call:conn.execute, call:sa.text, call:result.fetchone, call:_load_manifest, call:manifest.get, call:m.get, call:mounts.append, call:json.dumps | dep: json, alembic, sqlalchemy
- 8ed7dd80973d_create_config_folders_table.py | Alembic database migration that creates a config_folders table with user-owned configuration folders supporting JSONB file storage and project overrides | exp: func:upgrade() → None, call:op.create_table, call:sa.Column, call:postgresql.UUID, call:sa.text, call:sa.ForeignKey, call:sa.String, call:sa.Text, call:postgresql.JSONB, call:sa.Boolean, call:sa.DateTime, call:sa.UniqueConstraint, call:op.create_index, func:downgrade() → None, call:op.drop_index, call:op.drop_table | dep: alembic, sqlalchemy.dialects, sqlalchemy, sqlalchemy.dialects.postgresql
- af8512103d67_add_tool_type_fields.py | Alembic database migration that adds new columns (definition_type, dockerfile_template, build_context, readiness_probe) to the tool_types table with a CHECK constraint on definition_type. | exp: func:upgrade() → None, call:op.add_column, call:sa.Column, call:sa.String, call:sa.Text, call:postgresql.JSONB, call:op.create_check_constraint, call:sa.text, func:downgrade() → None, call:op.drop_constraint, call:op.drop_column | dep: alembic, sqlalchemy.dialects, sqlalchemy, sqlalchemy.dialects.postgresql
- f3d2dc90ba3a_merge_single_interface_and_clone_mode.py | Alembic database migration that merges two prior revisions (single_interface and clone_mode) into a single migration path | exp: func:upgrade() → None, func:downgrade() → None | dep: typing, alembic
## arch
Linear and branched migration pattern using Alembic's revision system with merge migrations to reconcile divergent branches; each migration is an imperative upgrade/downgrade script containing raw SQL/DDL operations, with some migrations including data seeding and dialect-specific logic (PostgreSQL/SQLite), but lacks consistent naming convention (mixed timestamp and numeric prefixes) indicating organic evolution rather than planned schema design.
Linear migration history with periodic merge heads to reconcile divergent branches; each migration is self-contained with upgrade/downgrade operations, using declarative SQLAlchemy operations, conditional checks for idempotency, and occasional data migrations alongside schema changes.
## tags
column, table, call:op.drop, downgrade, alembic, upgrade, key, call:sa.text
column, table, call:op.drop, alembic, downgrade, upgrade, key, call:sa.text
## symbols
- upgrade
- downgrade
@@ -104,11 +104,11 @@ RUN git config --global init.defaultBranch main \\
&& git config --global user.name "Developer"
# Create default tmux config
RUN echo 'set -g mouse on\\nset -g default-terminal "screen-256color"' > /home/user/.tmux.conf
RUN printf '%s\\n' 'set -g mouse on' 'set -g default-terminal "screen-256color"' > /home/user/.tmux.conf
# Create default ranger config
RUN mkdir -p /home/user/.config/ranger \\
&& echo 'set preview_files true\\nset use_preview_script true' > /home/user/.config/ranger/rc.conf
&& printf '%s\\n' 'set preview_files true' 'set use_preview_script true' > /home/user/.config/ranger/rc.conf
# Set up Pi config directory
RUN mkdir -p /home/user/.pi/agent
@@ -308,10 +308,10 @@ RUN git config --global init.defaultBranch main \\
&& git config --global user.email "dev@headquarter.local" \\
&& git config --global user.name "Developer"
RUN echo 'set -g mouse on\\nset -g default-terminal "screen-256color"' > /home/user/.tmux.conf
RUN printf '%s\\n' 'set -g mouse on' 'set -g default-terminal "screen-256color"' > /home/user/.tmux.conf
RUN mkdir -p /home/user/.config/ranger \\
&& echo 'set preview_files true\\nset use_preview_script true' > /home/user/.config/ranger/rc.conf
&& printf '%s\\n' 'set preview_files true' 'set use_preview_script true' > /home/user/.config/ranger/rc.conf
RUN mkdir -p /home/user/.pi/agent
@@ -0,0 +1,30 @@
# Fix tmux Mouse Mode Configuration in Pi Agent Images
## Summary
The Pi Agent dockerfile templates create `/home/user/.tmux.conf` using `echo '... \n ...'` with single quotes. In both Dockerfile `RUN` commands and Python-embedded dockerfile strings, single quotes prevent `\n` from being interpreted as a newline, so the resulting `.tmux.conf` contains a single malformed line such as:
```
set -g mouse on\nset -g default-terminal "screen-256color"
```
Tmux fails to parse this, so `set -g mouse on` is never applied. Without tmux mouse mode, xterm.js sends mouse-wheel events as Up/Down arrow keys to the shell, causing wheel scroll to cycle command history instead of scrolling the terminal/tmux buffer.
## Scope
- `tool-images/pi-agent.dockerfile`
- `apps/api/alembic/versions/20260527160017_add_pi_agent_tool_type.py`
- `apps/api/alembic/versions/2026_05_28_add_tool_definition_manifests.py`
## Acceptance Criteria
- [x] Pi Agent image builds write a valid multi-line `.tmux.conf`.
- [x] `.tmux.conf` contains `set -g mouse on` on its own line.
- [x] `.tmux.conf` contains `set -g default-terminal "screen-256color"` on its own line.
- [x] Ranger config is also fixed where the same literal `\n` bug exists.
- [x] Alembic migration dockerfile strings produce the same valid commands as the standalone Dockerfile.
## Related
- `openspec/specs/tool-terminal`
- `tool-images/pi-agent.dockerfile`
@@ -0,0 +1,11 @@
# Fix tmux Mouse Mode Configuration in Pi Agent Images — Tasks
- [x] Identify the literal `\n` bug in `.tmux.conf` creation commands across Pi Agent dockerfiles and migrations.
- [x] Update `tool-images/pi-agent.dockerfile` to use `printf '%s\n'` for `.tmux.conf` and ranger `rc.conf`.
- [x] Update `apps/api/alembic/versions/20260527160017_add_pi_agent_tool_type.py` dockerfile string with the same fix.
- [x] Update `apps/api/alembic/versions/2026_05_28_add_tool_definition_manifests.py` dockerfile string with the same fix.
- [x] Verify resulting Dockerfile commands produce valid multi-line config files.
- [x] Run `npm run typecheck` and `npm run lint` in `apps/web`.
- [x] Run `npm test -- --run` in `apps/web` (87 passed).
- [x] Verify Python migration files compile with `py_compile`.
- [x] Update project maps for changed files.
+1 -1
View File
@@ -2,7 +2,7 @@
dir: tool-images
## role
Provides containerized development environment templates for various IDE, notebook, and AI agent tools used in the Headquarter platform.
Provides Docker image templates for various development environment types used in the Headquarter platform.
## parent
index: ./.pi-map.index.md
map: ./.pi-map.md
+3 -3
View File
@@ -4,16 +4,16 @@ dir: tool-images
index: tool-images/.pi-map.index.md
## role
Provides containerized development environment templates for various IDE, notebook, and AI agent tools used in the Headquarter platform.
Provides Docker image templates for various development environment types used in the Headquarter platform.
## files
- README.md | Documents a directory of Dockerfile templates for base tool types used in the Headquarter platform | dep: Docker, git, neovim, ranger, tmux, htop, tree, jq, Node.js 20, Pi Coding Agent
- base.dockerfile | Defines a base Docker image for development environments with common CLI tools, Node.js, and a non-root user setup. | dep: ubuntu:24.04, curl, wget, git, neovim, ranger, tmux, htop, tree, jq, ca-certificates, nodejs
- code-server.dockerfile | Builds a Docker image for browser-based VS Code with additional development tools and Node.js | dep: linuxserver/code-server, git, neovim, ranger, tmux, htop, tree, jq, Node.js 20
- jupyter.dockerfile | Builds a Docker image for Jupyter Notebook/Lab with additional CLI tools and git configuration | dep: jupyter/scipy-notebook, apt, git, neovim, ranger, tmux, htop, tree, jq
- opencode.dockerfile | Builds a Docker container for running the OpenCode AI agent as a server | dep: ubuntu:24.04, nodejs, npm, python3, git, opencode
- pi-agent.dockerfile | Builds a Docker image for a terminal-based coding environment with the Pi Coding Agent, development tools, and a non-root user setup. | dep: ubuntu:24.04, nodejs, npm, @earendil-works/pi-coding-agent, neovim, ranger, tmux, git, python3, build-essential
- pi-agent.dockerfile | Builds a Docker container providing a terminal-based development environment with the Pi coding agent and supporting tools (neovim, tmux, ranger, etc.) | dep: ubuntu:24.04, nodejs, npm, @earendil-works/pi-coding-agent, git, tmux, neovim, ranger, python3, build-essential
## arch
Multi-stage Dockerfile pattern with shared base image containing common CLI utilities, Node.js, and non-root user security model, specialized per tool type (VS Code server, Jupyter, OpenCode agent, Pi agent).
Template-based container architecture with layered Dockerfiles extending a common base image pattern for specialized IDE, notebook, and AI agent environments.
## tags
git, docker, neovim, ranger, tmux, agent, base, htop
## symbols
+2 -2
View File
@@ -38,11 +38,11 @@ RUN git config --global init.defaultBranch main \
&& git config --global user.name "Developer"
# Create default tmux config
RUN echo 'set -g mouse on\nset -g default-terminal "screen-256color"' > /home/user/.tmux.conf
RUN printf '%s\n' 'set -g mouse on' 'set -g default-terminal "screen-256color"' > /home/user/.tmux.conf
# Create default ranger config
RUN mkdir -p /home/user/.config/ranger \
&& echo 'set preview_files true\nset use_preview_script true' > /home/user/.config/ranger/rc.conf
&& printf '%s\n' 'set preview_files true' 'set use_preview_script true' > /home/user/.config/ranger/rc.conf
# Set up Pi config directory
RUN mkdir -p /home/user/.pi/agent