mirror of
https://github.com/fscotto/infra.git
synced 2026-09-27 11:02:47 +00:00
feat: manage coding agents independently
This commit is contained in:
@@ -44,6 +44,7 @@ Ansible-driven personal infrastructure repo for Fedora and Void desktops, FreeBS
|
||||
- Atlas NAS: `ansible-playbook ansible/site.yml --limit atlas --check --diff`
|
||||
- Focused checks:
|
||||
- Emacs is disabled by default; temporary Emacs check: `ansible-playbook ansible/site.yml --limit <host> --tags emacs --check --diff -e emacs_enabled=true`
|
||||
- AI coding agents: `ansible-playbook ansible/site.yml --limit <host> --tags ai_agents --check --diff`
|
||||
- Mail bootstrap: `sh -n scripts/bootstrap_mail.sh` and `shellcheck scripts/bootstrap_mail.sh`
|
||||
- Server compose render: `docker compose -f /opt/docker/server/docker-compose.yml config`
|
||||
|
||||
@@ -127,10 +128,12 @@ The dotfile vars follow the same split: `desktop_common_dotfiles` carries mode-i
|
||||
are validated; do not make it a dependency of the Atlas baseline.
|
||||
|
||||
## Coding Agent Notes
|
||||
- Shared agent packages live in `ai_agents_npm_packages` in `ansible/inventory/group_vars/all.yml`.
|
||||
- Shared agent definitions and lifecycle flags live in `ai_agents` in `ansible/inventory/group_vars/all.yml`.
|
||||
- Shared agent dotfiles live in `ai_agents_dotfiles`; rendered configs live in `ai_agents_templates`.
|
||||
- Desktop and WSL profiles consume the shared agent package list; do not duplicate package entries in profile-specific vars.
|
||||
- `dotfiles_common` copies common dotfiles plus `ai_agents_dotfiles`, then renders `ai_agents_templates`.
|
||||
- Every `ai_agents.<agent>` entry has independent `install_enabled`, `deploy_enabled`, and `uninstall_enabled` flags. Installation and removal must not both be true for the same agent; the common pre-task fails before changes when they conflict.
|
||||
- Fedora, Void desktop, and WSL workstation profiles consume the shared agent definitions; do not duplicate package entries in profile-specific vars. IBM Bob on the workstation follows its own flags.
|
||||
- `dotfiles_common` deploys `ai_agents_dotfiles` and renders `ai_agents_templates` only when deployment is enabled.
|
||||
- Removal is limited to the managed npm packages and `/usr/local/bin/bob`; never remove agent dotfiles, instructions, credentials, or user data.
|
||||
- Keep `.config/ai/` as the common instruction source; update agent-specific entrypoints to reference it rather than duplicating instruction text.
|
||||
|
||||
## Tooling Notes
|
||||
|
||||
39
README.md
39
README.md
@@ -165,6 +165,43 @@ Emacs is enabled on Fedora/GNOME and workstation profiles. `dotfiles_common` dep
|
||||
ansible-playbook ansible/site.yml --limit <host> --tags emacs -e emacs_enabled=true
|
||||
```
|
||||
|
||||
## AI coding agents
|
||||
|
||||
The shared npm-managed agents are OpenCode, Claude Code, Codex, Gemini CLI, and
|
||||
GitHub Copilot; IBM Bob is also managed on `deadalus`. Each agent has its own
|
||||
lifecycle flags in `ansible/inventory/group_vars/all.yml`, so one agent can be
|
||||
installed, configured, or removed without affecting the others:
|
||||
|
||||
```yaml
|
||||
ai_agents:
|
||||
opencode:
|
||||
npm_package: opencode-ai
|
||||
install_enabled: true
|
||||
deploy_enabled: true
|
||||
uninstall_enabled: false
|
||||
```
|
||||
|
||||
Installation uses the npm `latest` state; deployment copies/renders only the
|
||||
configuration belonging to each enabled agent. Removal deletes only the selected
|
||||
managed npm package or, for IBM Bob, `/usr/local/bin/bob`; it preserves dotfiles,
|
||||
instructions, credentials, and user data. Installation and removal are mutually
|
||||
exclusive per agent: the playbook fails before making changes when both flags are
|
||||
true for the same agent. Servers set `ai_agents: {}` and therefore manage none.
|
||||
|
||||
Run a focused dry run with:
|
||||
|
||||
```bash
|
||||
ansible-playbook ansible/site.yml --limit ikaros --tags ai_agents --check --diff
|
||||
ansible-playbook ansible/site.yml --limit deadalus --tags ai_agents --check --diff
|
||||
```
|
||||
|
||||
To preview removal, set `install_enabled: false` and `uninstall_enabled: true`
|
||||
only in the entry for the agent being removed, then run:
|
||||
|
||||
```bash
|
||||
ansible-playbook ansible/site.yml --limit deadalus --tags ai_agents --check --diff
|
||||
```
|
||||
|
||||
## Main roles
|
||||
|
||||
| Role | What it does |
|
||||
@@ -277,7 +314,7 @@ ansible-playbook ansible/site.yml --list-tags
|
||||
| Tag | Main scope |
|
||||
| --- | --- |
|
||||
| `always` | Common pre-tasks, including optional vault loading. |
|
||||
| `ai_agents` | Shared AI agent installation on Fedora and WSL. |
|
||||
| `ai_agents` | AI coding-agent install, configuration deployment, and managed-binary removal. |
|
||||
| `dotfiles` | User configuration across all profiles. |
|
||||
| `dotfiles:common` | Shared dotfiles. |
|
||||
| `dotfiles:desktop` | Void and Fedora/GNOME desktop dotfiles. |
|
||||
|
||||
@@ -90,40 +90,75 @@ common_dotfiles:
|
||||
dest: .config/bat/
|
||||
mode: preserve
|
||||
|
||||
ai_agents_npm_packages:
|
||||
- name: "opencode-ai"
|
||||
state: latest
|
||||
- name: "@anthropic-ai/claude-code"
|
||||
state: latest
|
||||
- name: "@openai/codex"
|
||||
state: latest
|
||||
- name: "@google/gemini-cli"
|
||||
state: latest
|
||||
- name: "@github/copilot"
|
||||
state: latest
|
||||
# AI coding-agent lifecycle controls are independent for every agent.
|
||||
# Installation and removal are mutually exclusive per agent; the common
|
||||
# pre-task enforces this before any changes are made.
|
||||
ai_agents:
|
||||
opencode:
|
||||
npm_package: opencode-ai
|
||||
install_enabled: true
|
||||
deploy_enabled: true
|
||||
uninstall_enabled: false
|
||||
claude_code:
|
||||
npm_package: "@anthropic-ai/claude-code"
|
||||
install_enabled: false
|
||||
deploy_enabled: false
|
||||
uninstall_enabled: true
|
||||
codex:
|
||||
npm_package: "@openai/codex"
|
||||
install_enabled: true
|
||||
deploy_enabled: true
|
||||
uninstall_enabled: false
|
||||
gemini_cli:
|
||||
npm_package: "@google/gemini-cli"
|
||||
install_enabled: true
|
||||
deploy_enabled: true
|
||||
uninstall_enabled: false
|
||||
github_copilot:
|
||||
npm_package: "@github/copilot"
|
||||
install_enabled: true
|
||||
deploy_enabled: true
|
||||
uninstall_enabled: false
|
||||
ibm_bob:
|
||||
install_enabled: true
|
||||
deploy_enabled: false
|
||||
uninstall_enabled: false
|
||||
|
||||
ai_agents_enabled: true
|
||||
ai_agents_deploy_enabled_names: >-
|
||||
{{ ai_agents | dict2items | selectattr('value.deploy_enabled') | map(attribute='key') | list }}
|
||||
|
||||
ai_agents_dotfiles:
|
||||
- name: AI common config
|
||||
src: .config/ai/
|
||||
dest: .config/ai/
|
||||
mode: preserve
|
||||
agents:
|
||||
- opencode
|
||||
- codex
|
||||
- gemini_cli
|
||||
- name: Gemini CLI config
|
||||
src: .gemini/
|
||||
dest: .gemini/
|
||||
mode: preserve
|
||||
agents:
|
||||
- gemini_cli
|
||||
- name: OpenCode config
|
||||
src: .config/opencode/
|
||||
dest: .config/opencode/
|
||||
mode: preserve
|
||||
agents:
|
||||
- opencode
|
||||
- name: Claude Code memory
|
||||
src: .claude/
|
||||
dest: .claude/
|
||||
mode: preserve
|
||||
agents:
|
||||
- claude_code
|
||||
|
||||
ai_agents_templates:
|
||||
- name: Codex config
|
||||
src: .codex/config.toml.j2
|
||||
dest: .codex/config.toml
|
||||
mode: "0644"
|
||||
agents:
|
||||
- codex
|
||||
|
||||
@@ -31,8 +31,7 @@ desktop_binary_tools: []
|
||||
|
||||
desktop_npm_packages: >-
|
||||
{{
|
||||
ai_agents_npm_packages
|
||||
+ [
|
||||
[
|
||||
{'name': '@mermaid-js/mermaid-cli', 'state': 'latest'},
|
||||
{'name': 'vscode-langservers-extracted', 'state': 'latest'}
|
||||
]
|
||||
|
||||
@@ -85,5 +85,3 @@ fedora_flatpak_packages:
|
||||
- com.spotify.Client
|
||||
- org.onlyoffice.desktopeditors
|
||||
- org.telegram.desktop
|
||||
|
||||
fedora_npm_packages: "{{ ai_agents_npm_packages }}"
|
||||
|
||||
@@ -6,7 +6,7 @@ effective_username: "{{ server_username }}"
|
||||
effective_user_group: "{{ server_user_group }}"
|
||||
effective_user_home: "{{ server_user_home }}"
|
||||
server_container_stack_dir: /opt/docker/server
|
||||
ai_agents_enabled: false
|
||||
ai_agents: {}
|
||||
vim_plugins_enabled: false
|
||||
|
||||
profile_packages:
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
---
|
||||
workstation_manage_opencode: true
|
||||
workstation_manage_ibm_bob: true
|
||||
workstation_ibm_bob_install_url: "https://bob.ibm.com/download/bobshell.sh"
|
||||
workstation_npm_packages: "{{ ai_agents_npm_packages + [] }}"
|
||||
|
||||
@@ -86,13 +86,25 @@
|
||||
group: "{{ effective_user_group }}"
|
||||
mode: "{{ item.mode }}"
|
||||
loop: >-
|
||||
{{
|
||||
(common_dotfiles | default([]))
|
||||
+ ((ai_agents_dotfiles | default([])) if (ai_agents_enabled | default(false)) else [])
|
||||
}}
|
||||
{{ common_dotfiles | default([]) }}
|
||||
loop_control:
|
||||
label: "{{ item.dest }}"
|
||||
|
||||
- name: Deploy AI coding-agent dotfiles
|
||||
tags: [dotfiles, dotfiles:common, ai_agents]
|
||||
ansible.builtin.copy:
|
||||
src: "{{ playbook_dir }}/../dotfiles/common/{{ item.src }}"
|
||||
dest: "{{ effective_user_home }}/{{ item.dest }}"
|
||||
owner: "{{ effective_username }}"
|
||||
group: "{{ effective_user_group }}"
|
||||
mode: "{{ item.mode }}"
|
||||
loop: "{{ ai_agents_dotfiles | default([]) }}"
|
||||
loop_control:
|
||||
label: "{{ item.dest }}"
|
||||
when:
|
||||
- (item.agents | intersect(ai_agents_deploy_enabled_names)) | length > 0
|
||||
- (ai_agents_dotfiles | default([])) | length > 0
|
||||
|
||||
- name: Install distro packages for Vim plugins
|
||||
tags: [packages, vim, fzf]
|
||||
ansible.builtin.package:
|
||||
@@ -141,7 +153,7 @@
|
||||
- (vim_plugin_source_plugins | default([])) | length > 0
|
||||
|
||||
- name: Ensure AI config directories exist
|
||||
tags: [dotfiles, dotfiles:common]
|
||||
tags: [dotfiles, dotfiles:common, ai_agents]
|
||||
ansible.builtin.file:
|
||||
path: "{{ effective_user_home }}/{{ item }}"
|
||||
state: directory
|
||||
@@ -151,11 +163,11 @@
|
||||
loop:
|
||||
- .codex
|
||||
when:
|
||||
- ai_agents_enabled | default(false)
|
||||
- "'codex' in ai_agents_deploy_enabled_names"
|
||||
- (ai_agents_templates | default([])) | length > 0
|
||||
|
||||
- name: Render AI agent templates
|
||||
tags: [dotfiles, dotfiles:common]
|
||||
- name: Render AI coding-agent templates
|
||||
tags: [dotfiles, dotfiles:common, ai_agents]
|
||||
ansible.builtin.template:
|
||||
src: "{{ playbook_dir }}/../dotfiles/common/{{ item.src }}"
|
||||
dest: "{{ effective_user_home }}/{{ item.dest }}"
|
||||
@@ -165,7 +177,9 @@
|
||||
loop: "{{ ai_agents_templates | default([]) }}"
|
||||
loop_control:
|
||||
label: "{{ item.dest }}"
|
||||
when: ai_agents_enabled | default(false)
|
||||
when:
|
||||
- (item.agents | intersect(ai_agents_deploy_enabled_names)) | length > 0
|
||||
- (ai_agents_templates | default([])) | length > 0
|
||||
|
||||
- name: Refresh bat cache
|
||||
tags: [dotfiles, dotfiles:common]
|
||||
|
||||
@@ -90,16 +90,31 @@
|
||||
- "'workstation_dev_fedora' in group_names"
|
||||
- (fedora_docker_packages | default([])) | length > 0
|
||||
|
||||
- name: Install Fedora npm packages
|
||||
- name: Install shared AI coding agents on Fedora
|
||||
tags: [packages, npm, ai_agents]
|
||||
community.general.npm:
|
||||
name: "{{ item.name }}"
|
||||
name: "{{ item.value.npm_package }}"
|
||||
global: true
|
||||
state: "{{ item.state | default('present') }}"
|
||||
loop: "{{ fedora_npm_packages | default([]) }}"
|
||||
when: (fedora_npm_packages | default([])) | length > 0
|
||||
state: latest
|
||||
loop: "{{ ai_agents | dict2items | selectattr('value.npm_package', 'defined') | list }}"
|
||||
when:
|
||||
- item.value.install_enabled | bool
|
||||
- "'workstation_dev_fedora' not in group_names"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
label: "{{ item.key }}"
|
||||
|
||||
- name: Uninstall shared AI coding agents on Fedora
|
||||
tags: [packages, npm, ai_agents]
|
||||
community.general.npm:
|
||||
name: "{{ item.value.npm_package }}"
|
||||
global: true
|
||||
state: absent
|
||||
loop: "{{ ai_agents | dict2items | selectattr('value.npm_package', 'defined') | list }}"
|
||||
when:
|
||||
- item.value.uninstall_enabled | bool
|
||||
- "'workstation_dev_fedora' not in group_names"
|
||||
loop_control:
|
||||
label: "{{ item.key }}"
|
||||
|
||||
- name: Define Fedora Flatpak applications
|
||||
tags: [packages]
|
||||
|
||||
@@ -556,6 +556,30 @@
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
|
||||
- name: Install shared AI coding agents on Void desktop
|
||||
tags: [packages, npm, ai_agents]
|
||||
community.general.npm:
|
||||
name: "{{ item.value.npm_package }}"
|
||||
global: true
|
||||
state: latest
|
||||
loop: "{{ ai_agents | dict2items | selectattr('value.npm_package', 'defined') | list }}"
|
||||
when:
|
||||
- item.value.install_enabled | bool
|
||||
loop_control:
|
||||
label: "{{ item.key }}"
|
||||
|
||||
- name: Uninstall shared AI coding agents on Void desktop
|
||||
tags: [packages, npm, ai_agents]
|
||||
community.general.npm:
|
||||
name: "{{ item.value.npm_package }}"
|
||||
global: true
|
||||
state: absent
|
||||
loop: "{{ ai_agents | dict2items | selectattr('value.npm_package', 'defined') | list }}"
|
||||
when:
|
||||
- item.value.uninstall_enabled | bool
|
||||
loop_control:
|
||||
label: "{{ item.key }}"
|
||||
|
||||
- name: Install desktop Python tools with uv
|
||||
tags: [packages]
|
||||
ansible.builtin.command:
|
||||
|
||||
@@ -35,19 +35,31 @@
|
||||
loop_control:
|
||||
label: "{{ item.dest }}"
|
||||
|
||||
- name: Install workstation npm packages
|
||||
tags: [packages, npm]
|
||||
- name: Install shared AI coding agents on workstation
|
||||
tags: [packages, npm, ai_agents]
|
||||
community.general.npm:
|
||||
name: "{{ item.name }}"
|
||||
name: "{{ item.value.npm_package }}"
|
||||
global: true
|
||||
state: "{{ item.state | default('present') }}"
|
||||
state: latest
|
||||
become: true
|
||||
loop: "{{ workstation_npm_packages | default([]) }}"
|
||||
loop: "{{ ai_agents | dict2items | selectattr('value.npm_package', 'defined') | list }}"
|
||||
when:
|
||||
- workstation_manage_opencode | default(false)
|
||||
- workstation_npm_packages | length > 0
|
||||
- item.value.install_enabled | bool
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
label: "{{ item.key }}"
|
||||
|
||||
- name: Uninstall shared AI coding agents on workstation
|
||||
tags: [packages, npm, ai_agents]
|
||||
community.general.npm:
|
||||
name: "{{ item.value.npm_package }}"
|
||||
global: true
|
||||
state: absent
|
||||
become: true
|
||||
loop: "{{ ai_agents | dict2items | selectattr('value.npm_package', 'defined') | list }}"
|
||||
when:
|
||||
- item.value.uninstall_enabled | bool
|
||||
loop_control:
|
||||
label: "{{ item.key }}"
|
||||
|
||||
- name: Install IBM Bob coding agent
|
||||
tags: [packages, ai_agents]
|
||||
@@ -59,4 +71,15 @@
|
||||
become: true
|
||||
when:
|
||||
- workstation_manage_ibm_bob | default(false)
|
||||
- ai_agents.ibm_bob.install_enabled | bool
|
||||
- workstation_ibm_bob_install_url | length > 0
|
||||
|
||||
- name: Uninstall IBM Bob coding agent binary
|
||||
tags: [packages, ai_agents]
|
||||
ansible.builtin.file:
|
||||
path: /usr/local/bin/bob
|
||||
state: absent
|
||||
become: true
|
||||
when:
|
||||
- workstation_manage_ibm_bob | default(false)
|
||||
- ai_agents.ibm_bob.uninstall_enabled | bool
|
||||
|
||||
@@ -19,6 +19,18 @@
|
||||
lookup('ansible.builtin.fileglob', playbook_dir + '/../secrets/vault.local.yml',
|
||||
errors='ignore', wantlist=True) | length > 0
|
||||
|
||||
- name: Reject conflicting AI coding-agent lifecycle flags
|
||||
tags: [always, ai_agents]
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- not (item.value.install_enabled | bool and item.value.uninstall_enabled | bool)
|
||||
fail_msg: >-
|
||||
{{ item.key }} has both install_enabled and uninstall_enabled set to true.
|
||||
Choose either installation or removal before running the playbook.
|
||||
loop: "{{ ai_agents | dict2items }}"
|
||||
loop_control:
|
||||
label: "{{ item.key }}"
|
||||
|
||||
roles:
|
||||
- role: dotfiles_common
|
||||
when: "'platform_rocky' not in group_names"
|
||||
|
||||
Reference in New Issue
Block a user