Skip to content

feat: integrate RTK (Rust Token Killer) for LLM token optimization (Copilot + OpenCode)#431

Open
Copilot wants to merge 15 commits intomasterfrom
copilot/feature-integrate-rtk-token-optimization
Open

feat: integrate RTK (Rust Token Killer) for LLM token optimization (Copilot + OpenCode)#431
Copilot wants to merge 15 commits intomasterfrom
copilot/feature-integrate-rtk-token-optimization

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 28, 2026

Adds RTK (Rust Token Killer) to the macOS provisioning flow to enable token-optimization hooks for GitHub Copilot and OpenCode, with idempotent instruction file injection and CI validation.

Changes Made

  • Package: Added rtk to the default Homebrew package list
  • Ansible provisioning (ansible/macos/macos/base.yml):
    • Runs rtk init --copilot --auto-patch in an isolated temp directory
    • Copies RTK hooks and injects instructions via blockinfile to both Copilot paths:
      • ~/.github/hooks/ + ~/.github/copilot-instructions.md → VS Code Copilot Chat
      • ~/.copilot/hooks/ + ~/.copilot/copilot-instructions.md → Copilot CLI (terminal)
    • Runs rtk init -g --opencode --auto-patch for OpenCode (plugin-based, no instruction file merge needed)
    • Temp directory is cleaned up after all files are deployed
  • Idempotency: blockinfile with <!-- BEGIN/END RTK MANAGED BLOCK --> markers ensures re-provisioning replaces only the RTK content — user instructions outside the markers are never touched
  • CI (.github/workflows/test-ansible-playbook.yml): Extended with RTK validation steps that assert both ~/.github/ and ~/.copilot/ paths are populated on first run and that neither file accumulates duplicate blocks across re-provisions (user content preservation verified)
  • Merge conflict resolved: CHANGELOG.md conflict with master resolved, keeping both the RTK entry and the ~/.local/bin PATH entry

Copilot AI changed the title [WIP] Integrate RTK for LLM token optimization feat: integrate RTK (Rust Token Killer) for LLM token optimization Apr 28, 2026
Copilot AI requested a review from francescoben April 28, 2026 12:40
@francescoben
Copy link
Copy Markdown
Contributor

francescoben commented Apr 28, 2026

🤖 This was written by an AI agent on behalf of @francescoben.

@copilot

Review feedback

1. rtk init overwrites existing custom instructions (bug)

We tested this and discovered that rtk init -g --copilot completely overwrites the existing custom-instructions.md file, losing any user-defined instructions. This is a known upstream bug (rtk-ai/rtk#834).

Workaround: backup the file before init and append it back after:

- name: Backup existing Copilot custom instructions
  copy:
    src: "~/.github-copilot/custom-instructions.md"
    dest: "~/.github-copilot/custom-instructions.md.bak"
    remote_src: true
  failed_when: false

- name: Initialize RTK for GitHub Copilot
  command: rtk init -g --copilot --auto-patch
  become: false

- name: Restore user custom instructions (append after RTK)
  shell: |
    if [ -f ~/.github-copilot/custom-instructions.md.bak ]; then
      cat ~/.github-copilot/custom-instructions.md.bak >> ~/.github-copilot/custom-instructions.md
      rm ~/.github-copilot/custom-instructions.md.bak
    fi
  become: false

Same approach should be applied for OpenCode and Claude Code if they have similar instruction files.

2. changed_when: false is incorrect

rtk init modifies files on disk (hooks, config). Using changed_when: false hides this from Ansible output. It should either be changed_when: true or ideally check the command output to determine if something actually changed.

3. Missing rtk init -g for Claude Code

The issue mentions Claude Code, and Sparkdock already provisions it. rtk init -g (without flags) configures the Claude Code hook. Please add a task for it alongside copilot and opencode.

Copy link
Copy Markdown
Contributor

@francescoben francescoben left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see comment #431 (comment)

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 28, 2026

🤖 This was written by an AI agent on behalf of @francescoben.

@copilot
...

All three points addressed in commit 8add4a2:

  1. Backup/restore for instruction files — for both Claude Code (~/.claude/CLAUDE.md) and Copilot (~/.github-copilot/custom-instructions.md): the existing file is backed up with ignore_errors: yes (safe when the file doesn't exist), then after rtk init the original content is appended (>>) to preserve the user's instructions alongside RTK's additions. OpenCode uses a TypeScript plugin and doesn't touch instruction files, so no backup is needed there.

  2. changed_when: falsechanged_when: true — fixed on all three init tasks.

  3. Claude Code — added rtk init -g --auto-patch with the same backup/restore pattern.

@francescoben
Copy link
Copy Markdown
Contributor

🤖 This was written by an AI agent on behalf of @francescoben.

@copilot

Idempotency issue with backup/restore approach

The current backup/restore pattern has a duplication problem: running sparkdock update multiple times will keep appending user content to the file (since the backup includes the previous RTK content + user content from the last run).

Proposed solution: temp dir + blockinfile with markers

Instead of running rtk init -g directly in the user's home, run it in a temp directory (without -g) and then use Ansible's blockinfile to inject the RTK content into the real files with markers. This is fully idempotent.

Example for Copilot:

- name: Initialize RTK for Copilot in temp directory
  shell: |
    TMPDIR=$(mktemp -d)
    cd "$TMPDIR"
    rtk init --copilot --auto-patch
    echo "$TMPDIR"
  register: rtk_tmpdir
  become: false

- name: Copy RTK hooks to Copilot config
  copy:
    src: "{{ rtk_tmpdir.stdout }}/.github-copilot/hooks/"
    dest: "{{ ansible_facts['env']['HOME'] }}/.github-copilot/hooks/"
    remote_src: true
  become: false

- name: Inject RTK instructions into Copilot custom-instructions.md
  blockinfile:
    path: "{{ ansible_facts['env']['HOME'] }}/.github-copilot/custom-instructions.md"
    marker: "<!-- {mark} RTK MANAGED BLOCK -->"
    block: "{{ lookup('file', rtk_tmpdir.stdout + '/.github-copilot/custom-instructions.md') }}"
    create: yes
  become: false

- name: Cleanup temp directory
  file:
    path: "{{ rtk_tmpdir.stdout }}"
    state: absent

Key points:

  • rtk init --copilot --auto-patch (without -g) writes to the current directory instead of the user's home
  • blockinfile with markers ensures idempotency: re-running replaces only the RTK block, user content outside the markers is never touched
  • Same pattern applies to Claude Code (rtk init --auto-patch) and OpenCode (rtk init --opencode --auto-patch)
  • Hooks JSON can be copied directly (overwrite is fine since it's fully RTK-managed)

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 28, 2026

🤖 This was written by an AI agent on behalf of @francescoben.

@copilot...

Addressed in commit c9376e3.

The backup/restore pattern is replaced with the tmpdir + blockinfile approach you proposed:

  • Copilot: runs rtk init --copilot --auto-patch in a tmpdir, copies hook files directly, then uses blockinfile with <!-- {mark} RTK MANAGED BLOCK --> markers to inject the RTK instructions into ~/.github-copilot/custom-instructions.md — fully idempotent, user content outside markers is never touched
  • Claude Code: uses rtk init -g --hook-only --auto-patch to install the global hook (safe overwrite), then runs rtk init --auto-patch in a tmpdir to get the CLAUDE.md content and injects it via blockinfile into ~/.claude/CLAUDE.md
  • OpenCode: no instruction file involved, runs rtk init -g --opencode --auto-patch directly as before

Both tmpdirs are cleaned up after their respective inject steps.

@francescoben
Copy link
Copy Markdown
Contributor

@copilot you can remove everything related to Claude code, we only need to support Copilot and Opencode

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 28, 2026

@copilot you can remove everything related to Claude code, we only need to support Copilot and Opencode

Done in commit 7a846ab — all Claude Code tasks removed. The RTK block now only handles GitHub Copilot and OpenCode.

Copilot AI changed the title feat: integrate RTK (Rust Token Killer) for LLM token optimization feat: integrate RTK (Rust Token Killer) for LLM token optimization (Copilot + OpenCode) Apr 28, 2026
@francescoben
Copy link
Copy Markdown
Contributor

@copilot please add some test for the ansible playbook. We already have a github workflow for that

@sparkfabrik-ai-bot
Copy link
Copy Markdown

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Ignored Errors

The "Copy RTK hooks to GitHub Copilot config" task uses ignore_errors: yes, which means failures in copying hooks will be silently swallowed. If the temp directory path is malformed or the hooks don't exist, the task will succeed without any indication of failure, potentially leaving Copilot misconfigured.

- name: Copy RTK hooks to GitHub Copilot config
  copy:
    src: "{{ rtk_copilot_tmpdir.stdout }}/.github/hooks/"
    dest: "{{ ansible_facts['env']['HOME'] }}/.github/hooks/"
    remote_src: true
  become: false
  ignore_errors: yes
Lookup After Cleanup

The blockinfile task uses lookup('file', rtk_copilot_tmpdir.stdout + '/.github/copilot-instructions.md') to read the RTK instructions. This lookup happens at task execution time, but if the temp directory is cleaned up or the file doesn't exist, the task will fail. More critically, the lookup plugin runs on the controller, not the remote host — if this is running remotely, the file won't be accessible. Also, the cleanup task runs after this, but if the blockinfile task fails, cleanup may not occur.

- name: Inject RTK instructions into Copilot copilot-instructions.md
  blockinfile:
    path: "{{ ansible_facts['env']['HOME'] }}/.github/copilot-instructions.md"
    marker: "<!-- {mark} RTK MANAGED BLOCK -->"
    block: "{{ lookup('file', rtk_copilot_tmpdir.stdout + '/.github/copilot-instructions.md') }}"
    create: yes
  become: false

- name: Cleanup GitHub Copilot temp directory
  file:
    path: "{{ rtk_copilot_tmpdir.stdout }}"
    state: absent
  become: false
Shell Module Usage

The "Initialize RTK for GitHub Copilot in temp directory" task uses the shell module without specifying executable and without using FQCN (ansible.builtin.shell). More importantly, the stdout of this task is used as a directory path (rtk_copilot_tmpdir.stdout), but if rtk init produces any extra output before the echo, the path will be corrupted since stdout is redirected to /dev/null only for the rtk command but the echo is separate. This is fragile and should be validated before use.

- name: Initialize RTK for GitHub Copilot in temp directory
  shell: |
    RTK_TMPDIR=$(mktemp -d)
    cd "${RTK_TMPDIR}"
    rtk init --copilot --auto-patch > /dev/null 2>&1
    echo "${RTK_TMPDIR}"
  register: rtk_copilot_tmpdir
  changed_when: true
  become: false

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds RTK (Rust Token Killer) to the macOS provisioning flow so developer machines get token-optimization hooks configured for GitHub Copilot and OpenCode, and extends CI to validate first-run behavior plus idempotency of the Copilot instructions injection.

Changes:

  • Add rtk to the default Homebrew package list.
  • Add an Ansible rtk-tagged block to initialize RTK for Copilot (hook copy + instructions blockinfile) and OpenCode.
  • Extend the Ansible playbook workflow to validate RTK setup on first run and ensure the managed block is idempotent and preserves user content.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
config/packages/all-packages.yml Adds rtk to default brew packages so it’s available during provisioning.
ansible/macos/macos/base.yml Implements RTK initialization + Copilot hook/instructions installation and OpenCode init.
CHANGELOG.md Documents the addition of RTK (currently mismatched with implementation).
.github/workflows/test-ansible-playbook.yml Adds CI checks for RTK presence + Copilot instructions idempotency.

Comment thread ansible/macos/macos/base.yml Outdated
Comment thread ansible/macos/macos/base.yml
Comment thread CHANGELOG.md Outdated
Comment thread .github/workflows/test-ansible-playbook.yml Outdated
@sparkfabrik sparkfabrik deleted a comment from sparkfabrik-ai-bot Bot Apr 28, 2026
Comment thread ansible/macos/macos/base.yml Outdated
@francescoben
Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 28, 2026

@copilot resolve the merge conflicts in this pull request

Done in commit e77b010. The only conflict was in CHANGELOG.md (### Added section) — both entries are now preserved, with the RTK entry first.

@francescoben
Copy link
Copy Markdown
Contributor

🤖 This was written by an AI agent on behalf of @francescoben.

@copilot

Wrong path for Copilot CLI hooks

Testing revealed that Copilot CLI does not read hooks from ~/.github/hooks/. That path is for VS Code Copilot Chat only.

Copilot CLI reads hooks and instructions from ~/.copilot/ in the user's home directory. After manually placing the hook file there, rtk rewrite works correctly.

Since we primarily use Copilot CLI (terminal), please update the Ansible tasks to copy the hooks and instructions to ~/.copilot/ instead of (or in addition to) ~/.github/.

If we want to support both VS Code Copilot Chat and Copilot CLI, we need to write to both paths:

  • ~/.github/hooks/ + ~/.github/copilot-instructions.md → VS Code Copilot Chat
  • ~/.copilot/hooks/ + ~/.copilot/copilot-instructions.md → Copilot CLI

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 28, 2026

🤖 This was written by an AI agent on behalf of @francescoben.

@copilot...

Done in commit 98d1635. RTK hooks and instructions are now written to both paths:

  • ~/.github/hooks/ + ~/.github/copilot-instructions.md → VS Code Copilot Chat
  • ~/.copilot/hooks/ + ~/.copilot/copilot-instructions.md → Copilot CLI

Both use the same tmpdir output (from a single rtk init --copilot --auto-patch run), so there's no duplicated init. The CI idempotency tests now validate both paths — including that user content outside the markers survives re-provisioning in each file.

@francescoben
Copy link
Copy Markdown
Contributor

@paolomainardi ready for your review. Here's a summary of the final implementation:

Implementation overview

RTK is installed via Homebrew (brew install rtk) and initialized for two AI coding tools: GitHub Copilot and OpenCode.

GitHub Copilot

rtk init has a known upstream bug rtk-ai/rtk#1512: it overwrites existing instruction files instead of appending. This means any custom instructions the user has would be lost.

We designed a workaround:

  1. Run rtk init in a temp directory so it can't touch the user's real files
  2. Copy hooks directly to the target path (these are fully RTK-managed, so overwriting is safe)
  3. Use Ansible's blockinfile to inject only the RTK content between <!-- BEGIN RTK MANAGED BLOCK --> / <!-- END RTK MANAGED BLOCK --> markers into the real instruction file
  4. Everything outside the markers (user's custom instructions) is preserved untouched

OpenCode

OpenCode uses a TypeScript plugin, so no instruction file is involved — rtk init -g --opencode runs directly without the temp directory workaround.

Idempotency

The whole flow is fully idempotent:

  • On re-run, blockinfile replaces the content between markers (no duplication)
  • User content outside markers survives any number of re-provisions
  • Hooks are overwritten (safe — they're RTK-managed)
  • The CI test suite validates both first-run correctness and idempotency (second run), including a check that user content is preserved after re-provisioning

Paths

  • GitHub Copilot CLI: ~/.copilot/hooks/ + ~/.copilot/copilot-instructions.md
  • GitHub Copilot VS Code: ~/.github/hooks/ + ~/.github/copilot-instructions.md
  • OpenCode: plugin-based (no file merge needed)

Testing

Both myself and @giuliobracci actively worked on this, from designing the workaround approach to testing the full flow on our machines:

  • First install works correctly
  • Re-running sparkdock update is idempotent (no duplication, user content preserved)
  • RTK rewrite is active for both Copilot CLI and OpenCode


- name: Display RTK initialization status
debug:
msg: "✅ RTK initialized for GitHub Copilot and OpenCode. Restart your AI coding tools to activate."
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@francescoben I want to move all that logic into a sjust task (the shared one) and a reusable install script, so that we can:

  1. Manual re-run from sjust
  2. Easy to integrate in the archlinux provisioner @Monska85

@francescoben

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Integrate RTK (Rust Token Killer) for LLM token optimization

4 participants