21 KiB
Architecture: pos ai alias — AI Agent Alias Manager
Date: 2026-08-26 Author: Architect (big-pickle) Status: DECISION_READY
TL;DR
| Decision | Choice |
|---|---|
| Storage | ~/.config/linux_post_install/ai-aliases.env (pipe-delimited structured data) |
| Shell aliases | ~/.config/linux_post_install/ai-aliases.sh (generated, never hand-edited) |
| Tool | bin/pos-ai-alias — new ai category tool |
| No POS_CONFIG scope | Alias management is CRUD, not env-key editing; pos config is not involved |
| Source of truth | .env file; .sh file is regenerated on every write |
| .bashrc integration | One conditional source line, added by postinstall.sh |
| INSTALL_CHANGES | postinstall.sh adds .bashrc source line; bin/pos adds ai-alias to INTERACTIVE_CMDS |
Open items:
- None — all decisions are evidence-backed from project conventions.
Decision 1: Data Format
Problem: Store named AI aliases (provider, session name, system prompt) durably, with safe special-character handling and trivial parsing.
Options Considered
Option A: Shell alias format (one-liner shell aliases)
alias devbot='pos ai gemini ask --session devbot --system "You are a dev assistant"'
- Architecture: Store raw shell alias lines;
.bashrcsources the file directly. - Advantages: No generation step; shell sources it natively.
- Costs: Parsing aliases back into components (for edit/list) requires fragile quote-aware shell parsing; single quotes inside system prompts break the syntax.
- Risks: Prompt containing
'corrupts the file. Edit must read then reconstruct — fragile.
Option B: Pipe-delimited structured data + generated .sh
devbot|gemini|devbot|You are a dev assistant
- Architecture:
.envfile is the source of truth (pipe-delimited fields). A separate.shfile is regenerated from it on every write..bashrcsources the.shfile. - Advantages: Parsing is trivial (
IFS='|'); single-quote escaping is handled at generation time; data is safe togrep/sort/awk. - Costs: One extra file (
.sh); a_regen_aliases()helper function. - Risks: Generation must escape correctly — but this is a single, testable function.
Option C: Individual env files per alias
~/.config/linux_post_install/ai-aliases/devbot.env
- Architecture: One file per alias; load all at shell startup.
- Advantages: No parsing of multi-entry files.
- Costs: Directory management; glob at shell startup; harder to list all; no atomic operations.
Decision: Option B
Pipe-delimited structured data is the cleanest separation. The .env file is the source of truth. The .sh file is a generated artifact. This matches the project's own pattern of "generated code between GEN markers" — except here the generator lives inside the tool itself, not make gen.
Rationale: The project already has a pattern of generated files (e.g., GEN:START/GEN:END blocks, completions/pos.bash). The tool owns its own generation. Parsing shell aliases is fragile and error-prone; pipe-delimited data is trivially safe.
Data Format Specification
# ~/.config/linux_post_install/ai-aliases.env
# Managed by: pos ai alias (do not hand-edit)
# Format: alias_name|provider|session_name|system_prompt
# Pipe characters in system_prompt are not supported.
#
# agent_name|provider|session_name|system_prompt
devbot|gemini|devbot|You are a Linux dev assistant. Reply with commands only.
code|openrouter|codereview|You are a code reviewer. Be concise.
Field constraints:
| Field | Rules |
|---|---|
alias_name |
Shell-valid identifier: [a-zA-Z][a-zA-Z0-9_-]* |
provider |
Must match an installed provider: gemini, openrouter, etc. |
session_name |
Defaults to the alias name if empty; [a-zA-Z0-9_-]+ |
system_prompt |
Free text; no | (pipe) characters; may be empty (uses built-in prompt) |
Header: Two comment lines at the top (file description + format) are auto-maintained by the tool.
Empty session_name convention: When session_name is empty (field is blank between pipes), the alias uses the alias name as the session name. This avoids redundant repetition for the common case.
Decision 2: File Layout
New files
| File | Purpose |
|---|---|
bin/pos-ai-alias |
New CLI tool (100755) |
Modified files
| File | Change |
|---|---|
bin/pos |
Add ai-alias to INTERACTIVE_CMDS list |
postinstall.sh |
Add .bashrc source line for ai-aliases.sh (conditional, no-clobber) |
Runtime files (user config, NOT in repo)
| File | Purpose | Mutability |
|---|---|---|
~/.config/linux_post_install/ai-aliases.env |
Alias data (source of truth) | Created/modified by tool |
~/.config/linux_post_install/ai-aliases.sh |
Generated shell aliases | Regenerated on every write |
Files NOT modified
| File | Why not |
|---|---|
.gitignore |
~/.config/linux_post_install/ is a user directory, not in the repo. No new repo files to ignore. |
config/ |
No template file needed — the env file is user-created on first use. |
lib/ |
No new library. Tool sources common.sh + menu-lib.sh from existing libs. |
Decision 3: Tool Interface
POS Header
# POS: ai alias — Create/edit/remove named AI agent aliases
# POS_SUBCMDS: create edit remove list show
No POS_FLAGS: — this is a subcommand-based tool, not a flag-based tool.
CLI Interface
pos ai alias [subcommand] [args]
Subcommands:
(no args) Interactive menu (create/edit/remove/list)
create Create a new alias (interactive prompts)
create <name> Create with given name (interactive prompts for rest)
edit Pick an alias to edit (interactive)
edit <name> Edit a specific alias
remove Pick an alias to remove (interactive, with confirmation)
remove <name> Remove a specific alias (with confirmation)
list List all aliases (non-interactive, machine-readable)
show <name> Show one alias's details
Options:
-h|--help Show this help.
Examples:
pos ai alias # interactive menu
pos ai alias list # show all aliases
pos ai alias create # interactive create
pos ai alias create mybot # create 'mybot' alias
pos ai alias edit mybot # edit the 'mybot' alias
pos ai alias remove mybot # remove 'mybot' (with confirm)
pos ai alias show mybot # show alias details
Subcommand Details
pos ai alias (no args): Interactive menu using menu_run from lib/menu-lib.sh. Options:
- Create new alias
- Edit existing alias
- Remove alias
- List aliases
pos ai alias list: Non-interactive table output:
Aliases (3):
mybot gemini mybot You are a helpful assistant
code openrouter code You are a code reviewer
dev gemini devbot Dev assistant
Format: %-12s %-12s %-12s %s (name, provider, session, prompt-truncated-to-60).
pos ai alias show <name>: Full details including the resolved shell command.
Decision 4: POS_CONFIG
Decision: NO POS_CONFIG scope.
Rationale: The pos config / POS_CONFIG system is designed for simple KEY=VALUE env files (like ai.env, telegram.env, entertainment.env). Aliases are structured, multi-field records, not key-value pairs. The cfg_ui() pattern from lib/config-ui.sh doesn't apply here — it renders a numbered menu of KEY=VALUE pairs, not CRUD operations on named records.
The alias tool is self-contained with its own interactive menus. It does not participate in pos config.
Decision 5: Shell Alias Generation
The _regen_aliases() function
This function reads ai-aliases.env and writes ai-aliases.sh:
_regen_aliases() {
local env_file="$1" sh_file="$2" tmp
tmp="$(mktemp)"
printf '#!/usr/bin/env bash\n# Auto-generated by pos ai alias — do not hand-edit.\n# Source: %s\n\n' "$env_file" >"$tmp"
if [ -f "$env_file" ]; then
while IFS='|' read -r name provider session prompt _rest; do
# Skip comments and empty lines
[[ "$name" =~ ^[[:space:]]*# ]] && continue
[[ -z "$name" ]] && continue
# Validate alias name
[[ "$name" =~ ^[a-zA-Z][a-zA-Z0-9_-]*$ ]] || continue
# Session defaults to alias name if empty
[ -z "$session" ] && session="$name"
# Escape single quotes in the system prompt for shell-safe embedding
local escaped_prompt="${prompt//\'/\'\\\'\'}"
printf "alias %s='pos ai %s ask --session %s" "$name" "$provider" "$session" >>"$tmp"
if [ -n "$prompt" ]; then
printf " --system '%s'" "$escaped_prompt" >>"$tmp"
fi
printf "'\n" >>"$tmp"
done < <(grep -v '^[[:space:]]*#' "$env_file" | grep -v '^[[:space:]]*$' || true)
fi
mv "$tmp" "$sh_file"
chmod 644 "$sh_file"
}
Single-quote escaping: ${prompt//\'/\'\\\'\'} — bash parameter expansion replaces every ' with '\'' (close-quote, escaped-quote, open-quote). This is the standard and safe pattern for embedding arbitrary strings in single-quoted shell contexts.
Edge case — empty prompt: When the system prompt is empty, the --system flag is omitted entirely, letting pos ai use its built-in default prompt.
Syntax validation before commit
After generating the .sh file, run bash -n to verify syntax:
if ! bash -n "$sh_file" 2>/dev/null; then
warn "Generated alias file has syntax errors — keeping previous version"
rm -f "$tmp"
return 1
fi
Shell integration in .bashrc
# AI aliases (managed by pos ai alias)
[ -f ~/.config/linux_post_install/ai-aliases.sh ] && source ~/.config/linux_post_install/ai-aliases.sh
This line is added by postinstall.sh with the standard no-clobber grep check.
Decision 6: Interactive Flow
Main Menu (pos ai alias — no args)
Uses menu_run from lib/menu-lib.sh:
════════════════════════════════════════════
AI Agent Aliases
════════════════════════════════════════════
1) Create new alias
2) Edit existing alias
3) Remove alias
4) List aliases
0) Exit
----------------------------------------
Choose:
Create Flow
- Prompt for alias name:
menu_ask_value "Alias name" ""— validate format ([a-zA-Z][a-zA-Z0-9_-]*) - Check for duplicate name → warn and re-prompt if taken
- Prompt for provider: show available providers (read from
$PROVIDER_DIR/*.sh), defaultgemini - Prompt for session name: default = alias name
- Prompt for system prompt: default = empty (uses built-in)
- Confirm:
confirm "Create alias '<name>'?" y - Write to
.env, regenerate.sh, log success
Edit Flow
- List existing aliases (name + provider + first-40-chars of prompt)
- Pick one (if no arg given):
menu_pick "Pick alias" "${names[@]}" - Show current values
- For each field, prompt with current value as default (Enter = keep)
- Confirm changes
- Rewrite
.enventry, regenerate.sh
Remove Flow
- Pick alias:
menu_pickor named - Show alias details
confirm "Remove alias '<name>'? This cannot be undone." n(default = no)- Remove from
.env, regenerate.sh
List Flow (non-interactive)
Prints formatted table to stdout. Provider column left-aligned, name left-aligned, prompt truncated to 60 chars with ....
Show Flow (non-interactive)
Full details + resolved command:
Alias: mybot
Provider: gemini
Session: mybot
Prompt: You are a helpful assistant
Command: pos ai gemini ask --session mybot --system 'You are a helpful assistant'
Decision 7: Edge Cases and Error Handling
| Edge Case | Handling |
|---|---|
| Duplicate alias name on create | warn "Alias '$name' already exists — use 'pos ai alias edit $name' instead"; re-prompt |
| Empty alias name | err "Alias name cannot be empty" |
| Invalid alias name (contains spaces, starts with digit) | err "Invalid alias name '$name' — use letters, digits, hyphens, underscores" |
| Invalid provider | err "Unknown provider '$p' — available: $(ls ...)" — uses same provider discovery as pos-ai |
| Empty system prompt | Allowed — omit --system flag; pos ai uses its built-in default prompt |
| Very long system prompt | Truncate display in list output (60 chars + ...); full value preserved in .env and .sh. Warn if > 500 chars during creation. |
| Pipe character in system prompt | Rejected on input: err "System prompt must not contain '|' characters" |
| System prompt with single quotes | Handled by _regen_aliases() escaping: ' -> '\'' in the generated alias line |
| Editing an alias "in use" | No lock/detection needed. The user edits the .env; on next shell startup (or source ~/.bashrc), aliases update. No runtime state conflict. |
| File doesn't exist yet | First create auto-creates both .env and .sh |
| Corrupted/invalid .env line | Skipped by _regen_aliases() (name validation regex) |
| Concurrent edits | Not a concern — personal single-user tool. Last write wins. |
| Missing pos-ai dependency | Tool doesn't require pos-ai at runtime — it only writes config. No dep guard needed. |
Decision 8: Security Considerations
System prompt injection
System prompts are user-authored text that becomes a shell argument. Risks:
- Shell injection via alias execution: The prompt is single-quoted in the alias, so shell metacharacters (
$, backtick,!) are literal — safe. pos aiprompt injection: This is a user's own prompt for their own AI. No trust boundary crossing.- File permissions:
ai-aliases.envgetschmod 600(user-only read; consistent with other config files).ai-aliases.shgetschmod 644(needed by bashsource).
Escaping correctness
The single-quote escaping ${prompt//\'/\'\\\'\'} is the only place where correctness matters critically. If it fails, the generated alias has a syntax error and source will report it. Mitigation:
- The tool runs
bash -non the generated.shfile before committing it. - If syntax check fails, warn and skip the regeneration (keep the old
.sh).
No secrets in the alias file
System prompts are not secrets — they're user-authored instructions. API keys stay in ai.env (already managed by pos config ai). No new secret surface.
Decision 9: Integration with pos-ai
How aliases invoke pos-ai
Each generated alias calls:
alias <name>='pos ai <provider> ask --session <session> --system "<prompt>"'
This uses the existing pos-ai flags:
--provider <name>— supported since the beginning (line 633 ofbin/pos-ai)--session <name>— supported (line 639)--system <text>— supported (line 642)
No changes to bin/pos-ai are required. The alias tool is a standalone configuration tool that writes shell aliases calling pos ai.
Provider validation
The alias tool must validate the provider name against installed providers. It reuses the same discovery logic from pos-ai:
PROVIDER_DIR="$(dirname "$0")/../lib/ai-providers"
# Fallback for installed layout
[ -d "$PROVIDER_DIR" ] || PROVIDER_DIR="$(dirname "$0")/ai-providers"
This is the same pattern used in pos-ai at line 17-18. The alias tool discovers providers independently (no dependency on pos-ai being sourced).
Decision 10: Installation Changes
postinstall.sh modification
Add a block after the existing ai.env installation (around line 50):
# ── AI aliases shell integration ───────────────────────────────
ALIAS_SRC_LINE='# AI aliases (managed by pos ai alias)
[ -f ~/.config/linux_post_install/ai-aliases.sh ] && source ~/.config/linux_post_install/ai-aliases.sh'
if ! grep -qsF "ai-aliases.sh" "$BASHRC" 2>/dev/null; then
run printf '%s\n' "$ALIAS_SRC_LINE" >> "$BASHRC"
log "Added AI aliases source to ~/.bashrc"
fi
bin/pos modification
Add ai-alias to the INTERACTIVE_CMDS list (line 262). Current value:
INTERACTIVE_CMDS="docker-compose docker-vbox network-hotspot system-firewall media-mp4 media-sync system-backup system-uninstall share-usb-server share-smb-server share-smb-client share-nfs-client share-nfs-server communication-telegram-listener communication-matrix-listener ai ai-gemini ai-openrouter system-schedule entertainment-config config"
Add ai-alias to this space-separated list.
No other installation changes
- No new apt packages (no deps beyond bash)
- No new lib files (tool sources
common.sh+menu-lib.shfrom existing libs) - No systemd services
- No config template in
config/
Decision 11: Long Prompt Handling
System prompts can be arbitrarily long. Shell aliases have a practical limit (ARG_MAX, typically 2MB on Linux), so this is not a hard constraint. However:
- Display:
listoutput truncates to 60 chars +... - Storage: Full prompt in
.envand.sh— no truncation - Interactive edit: Shows full current value, allows full editing
- Warning: During creation, if prompt exceeds 500 chars:
warn "System prompt is long (${#prompt} chars) — consider keeping it concise"
Decision: No artificial length limit.
Implementation Phases
Phase 1: Core tool (single commit)
- Create
bin/pos-ai-aliaswith:- Shebang, strict mode,
common.shsource,menu-lib.shsource # POS:header +# POS_SUBCMDS:CONFIG_FILEandALIASES_SH_FILEpath constants_load_aliases()— reads.envinto parallel arrays (names, providers, sessions, prompts)_find_alias()— lookup by name, returns index_write_env_file()— writes entire.envfrom arrays_regen_aliases()— reads.env, writes.shwith proper escaping_list_aliases()— non-interactive table output_show_alias()— non-interactive single alias details_create_alias()— interactive create with validation_edit_alias()— interactive edit with field-level prompts_remove_alias()— interactive remove with confirm_main_menu()— interactive menu viamenu_run- Subcommand dispatch (
casepattern) usage()function
- Shebang, strict mode,
- Add
ai-aliastoINTERACTIVE_CMDSinbin/pos - Add
.bashrcsource line topostinstall.sh
Phase 2: Verification
chmod +x bin/pos-ai-aliasbash -n bin/pos-ai-aliasmake gen— regenerate tables (new tool appears in dispatch table, bin tree, file table)make check— self-consistency gatemake lint— convention gate (0 FAIL, 0 WARN)- Manual test: create, list, show, edit, remove aliases; verify
.shfile is correct - Source
.bashrcand verify aliases work
Phase 3: Documentation
DOC/POS.md— addai aliassection (hand-written)DOC/HOWTO.md— add index rowDOC/howto/ai.md— add aliases section (if ai.md exists; otherwise add to existing ai howto)DOC/AGENT_Context_Project.md— regenerated bymake gen; hand-add to Common Tasks table- Update
AGENT_TODO.mdDone section (dated)
Architectural Constraints
- Tool must source
lib/common.shvia the fallback chain (not self-contained) - Tool must source
lib/menu-lib.shfor interactive menus - Tool MUST be in
INTERACTIVE_CMDSinbin/pos - Generated
.shfile must passbash -nbefore commit - Env-seam: all file paths use
${CONFIG_DIR:-...}pattern (already incommon.sh) chmod 600for.env,chmod 644for.sh- No dependency on
pos-aibeing installed — tool writes config, doesn't runpos ai - Pipe delimiter — system prompts must not contain
|; validated on input
Verification Checklist
bash -n bin/pos-ai-aliaspassesshellcheck bin/pos-ai-aliaspasses (or only known false positives)make genregenerates tables correctlymake checkpasses (bash -n, exec bits, doc sync, smoke)make lintpasses (0 FAIL, 0 WARN)pos ai alias --helpshows helppos ai aliasshows interactive menu- Create -> list -> show -> edit -> remove cycle works
- Generated
.shfile has correct alias syntax bash -non generated.shpasses.bashrcsource line works (aliases available after source)- Special chars in system prompt (single quotes, spaces, $) survive round-trip
- Duplicate name is rejected
- Invalid alias name is rejected
- Invalid provider is rejected
Risks and Mitigations
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| Single-quote escaping fails for exotic prompts | Low | High (syntax error in .sh) | bash -n check before commit; warn + skip on failure |
| User has many aliases -> list becomes long | Low | Low | menu_pick already supports filtering |
.bashrc source line conflicts with existing alias definitions |
Very Low | Medium | Grep-check before adding; line is a conditional source, not an alias definition |
make lint rejects the new tool for a convention violation |
Low | Low (blocking) | Follow template exactly; deps guards before help; proper header |
End of architecture document.