From 4c14966ae3a813d007ce2e8fd9c7ad2609bad644 Mon Sep 17 00:00:00 2001 From: Developer Date: Sun, 14 Jun 2026 08:48:39 +0000 Subject: [PATCH] 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 --- .pi-map.index.md | 2 +- .pi-map.md | 4 +-- apps/.pi-map.index.md | 2 +- apps/.pi-map.md | 4 +-- apps/api/.pi-map.index.md | 2 +- apps/api/.pi-map.md | 4 +-- apps/api/alembic/.pi-map.md | 2 +- apps/api/alembic/versions/.pi-map.index.md | 2 +- apps/api/alembic/versions/.pi-map.md | 16 +++++----- .../20260527160017_add_pi_agent_tool_type.py | 4 +-- ...026_05_28_add_tool_definition_manifests.py | 4 +-- .../changes/fix-tmux-mouse-config/change.md | 30 +++++++++++++++++++ .../changes/fix-tmux-mouse-config/tasks.md | 11 +++++++ tool-images/.pi-map.index.md | 2 +- tool-images/.pi-map.md | 6 ++-- tool-images/pi-agent.dockerfile | 4 +-- 16 files changed, 70 insertions(+), 29 deletions(-) create mode 100644 openspec/changes/fix-tmux-mouse-config/change.md create mode 100644 openspec/changes/fix-tmux-mouse-config/tasks.md diff --git a/.pi-map.index.md b/.pi-map.index.md index a5dc048..874982b 100644 --- a/.pi-map.index.md +++ b/.pi-map.index.md @@ -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 diff --git a/.pi-map.md b/.pi-map.md index e75dd64..67d5f81 100644 --- a/.pi-map.md +++ b/.pi-map.md @@ -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 diff --git a/apps/.pi-map.index.md b/apps/.pi-map.index.md index 87ba2d0..6fc23cf 100644 --- a/apps/.pi-map.index.md +++ b/apps/.pi-map.index.md @@ -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 diff --git a/apps/.pi-map.md b/apps/.pi-map.md index bb719ae..1c83227 100644 --- a/apps/.pi-map.md +++ b/apps/.pi-map.md @@ -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 diff --git a/apps/api/.pi-map.index.md b/apps/api/.pi-map.index.md index 52afd12..52461c9 100644 --- a/apps/api/.pi-map.index.md +++ b/apps/api/.pi-map.index.md @@ -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 diff --git a/apps/api/.pi-map.md b/apps/api/.pi-map.md index 5ce788f..a742201 100644 --- a/apps/api/.pi-map.md +++ b/apps/api/.pi-map.md @@ -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 diff --git a/apps/api/alembic/.pi-map.md b/apps/api/alembic/.pi-map.md index fc958f1..b5ef2d9 100644 --- a/apps/api/alembic/.pi-map.md +++ b/apps/api/alembic/.pi-map.md @@ -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 diff --git a/apps/api/alembic/versions/.pi-map.index.md b/apps/api/alembic/versions/.pi-map.index.md index 9ff526e..b157d86 100644 --- a/apps/api/alembic/versions/.pi-map.index.md +++ b/apps/api/alembic/versions/.pi-map.index.md @@ -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 diff --git a/apps/api/alembic/versions/.pi-map.md b/apps/api/alembic/versions/.pi-map.md index 59c43ae..e7017cf 100644 --- a/apps/api/alembic/versions/.pi-map.md +++ b/apps/api/alembic/versions/.pi-map.md @@ -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 diff --git a/apps/api/alembic/versions/20260527160017_add_pi_agent_tool_type.py b/apps/api/alembic/versions/20260527160017_add_pi_agent_tool_type.py index 0b7136e..fcb719d 100644 --- a/apps/api/alembic/versions/20260527160017_add_pi_agent_tool_type.py +++ b/apps/api/alembic/versions/20260527160017_add_pi_agent_tool_type.py @@ -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 diff --git a/apps/api/alembic/versions/2026_05_28_add_tool_definition_manifests.py b/apps/api/alembic/versions/2026_05_28_add_tool_definition_manifests.py index 9f2d125..c97247c 100644 --- a/apps/api/alembic/versions/2026_05_28_add_tool_definition_manifests.py +++ b/apps/api/alembic/versions/2026_05_28_add_tool_definition_manifests.py @@ -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 diff --git a/openspec/changes/fix-tmux-mouse-config/change.md b/openspec/changes/fix-tmux-mouse-config/change.md new file mode 100644 index 0000000..0959735 --- /dev/null +++ b/openspec/changes/fix-tmux-mouse-config/change.md @@ -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` diff --git a/openspec/changes/fix-tmux-mouse-config/tasks.md b/openspec/changes/fix-tmux-mouse-config/tasks.md new file mode 100644 index 0000000..db5469e --- /dev/null +++ b/openspec/changes/fix-tmux-mouse-config/tasks.md @@ -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. diff --git a/tool-images/.pi-map.index.md b/tool-images/.pi-map.index.md index 6fda382..6302bf0 100644 --- a/tool-images/.pi-map.index.md +++ b/tool-images/.pi-map.index.md @@ -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 diff --git a/tool-images/.pi-map.md b/tool-images/.pi-map.md index 6974b19..ac433bf 100644 --- a/tool-images/.pi-map.md +++ b/tool-images/.pi-map.md @@ -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 diff --git a/tool-images/pi-agent.dockerfile b/tool-images/pi-agent.dockerfile index c7bc686..b71c117 100644 --- a/tool-images/pi-agent.dockerfile +++ b/tool-images/pi-agent.dockerfile @@ -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