Skip to content

Skills sync revamp - #124

Merged
shunkakinoki merged 2 commits into
mainfrom
skills-sync-revamp
Apr 10, 2026
Merged

Skills sync revamp#124
shunkakinoki merged 2 commits into
mainfrom
skills-sync-revamp

Conversation

@shunkakinoki

@shunkakinoki shunkakinoki commented Apr 10, 2026

Copy link
Copy Markdown
Owner

Summary by cubic

Revamps skills sync to be incremental and stateful. External skills from SKILLS.txt are reconciled via manifests and cache, cutting unnecessary reinstalls and speeding up make sync.

  • New Features

    • Adds state at ~/.cache/dotagents/skills with per-repo manifests in manifests/*.skills.
    • Introduces make skills-refresh for a clean reinstall and skills-managed-clean for managed cleanup.
    • skills-install normalizes SKILLS.txt, records installs via before/after diffs, and skips when state matches.
    • make sync uses $(MAKE) and no longer performs a blanket skills-clean by default.
    • README updated with make skills-refresh usage.
  • Bug Fixes

    • Validates cache only against ~/.agents/skills (source of truth), avoiding noise from IDE-modified target dirs.
    • Uses diff-based manifest recording to prevent phantom skills from forcing perpetual reinstalls.

Written for commit aee2627. Summary will update on new commits.

- Cache check: only validate ~/.agents/skills/ (source of truth),
  not all target dirs which get modified by IDEs
- Manifest recording: use before/after diff for selective installs
  so phantom skills (requested but nonexistent) don't cause
  perpetual reinstalls
Copilot AI review requested due to automatic review settings April 10, 2026 13:50
@coderabbitai

coderabbitai Bot commented Apr 10, 2026

Copy link
Copy Markdown
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added skill state management and manifest-based tracking for external skills installation and cleanup
    • Introduced new make skills-refresh command for forcing clean reinstalls of external skills
    • Enhanced skill synchronization with intelligent change detection and targeted cleanup
  • Documentation

    • Updated README to document the new skills reconciliation workflow and refresh command

Walkthrough

The Makefile is enhanced with a state and manifest tracking system for external skills management. New directories and files track normalized SKILLS.txt specifications, installed skill manifests, and state information. The skills-install target is rewritten to detect specification changes and perform conditional reinstallation, while new targets skills-managed-clean and skills-refresh provide cleanup and forced-reinstall capabilities.

Changes

Cohort / File(s) Summary
Makefile Skills State & Manifest Infrastructure
Makefile
Added variables: SKILLS_STATE_DIR, SKILLS_MANIFEST_DIR, SKILLS_SPEC_STATE_FILE, SKILLS_EXTERNAL_SOURCE_DIR. Modified sync target to use $(MAKE) instead of make and removed skills-clean dependency. Expanded skills-clean to delete SKILLS_STATE_DIR. Added skills-managed-clean target to remove managed external skills from manifests and clear state. Completely rewrote skills-install with spec normalization, change detection, conditional reinstall, and manifest-based tracking logic. Added skills-refresh target for forced reinstall.
Documentation Updates
README.md
Updated make sync description to document the new managed external skills reconciliation step. Added documentation for new make skills-refresh command for forcing clean reinstall of external skills.

Sequence Diagram(s)

sequenceDiagram
    participant User as User/CI
    participant Make as Makefile
    participant Spec as SKILLS.txt
    participant State as State Files
    participant Cache as Skills Cache<br/>(SKILLS_EXTERNAL_SOURCE_DIR)
    participant Repos as Installation<br/>Targets

    User->>Make: make skills-install
    Make->>Spec: Read and normalize SKILLS.txt
    Make->>State: Generate normalized spec state
    Make->>State: Compare with previous spec state
    
    alt Spec Changed or Force Install
        Make->>Make: Run skills-managed-clean
        Make->>State: Clear SKILLS_STATE_DIR
        Make->>Repos: Clean managed external skills from all targets
        Make->>Cache: Install all skills from SKILLS.txt
        Make->>State: Create/update manifests for installed skills
    else Spec Unchanged
        Make->>State: Check per-repo manifest existence
        alt Manifest Missing or Skills Missing
            Make->>Cache: Targeted skill installation for affected repos
            Make->>State: Update manifest with newly installed skills
        else All Manifests Valid
            Make->>Make: Skip reinstall
        end
    end
    
    Make->>State: Write updated spec state file
    Make->>User: Report: "managed external skills are in sync"
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • #89: Updates SKILLS.txt configuration file that is now consumed by the new normalization and spec-change detection logic in the rewritten skills-install target.
  • #107: Modifies the same skills-install and skills-clean Makefile targets with different install/cleanup strategies, creating potential merge and behavior conflicts.
  • #59: Introduces the foundational SKILLS.txt-based SKILLS_REPOS approach that this PR extends with state tracking, manifest management, and conditional reinstall logic.

Suggested labels

enhancement

Poem

🐰 With whiskers twitching, I hopped through the cache,
Skills now tracked in manifests, no more trash!
Normalized specs and smart reinstalls too,
State-driven workflows—refreshed and brand new! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Skills sync revamp' is concise and directly describes the main change: refactoring the skills synchronization mechanism to be incremental and stateful.
Description check ✅ Passed The description is well-detailed and directly related to the changeset, covering new features, bug fixes, and specific implementation details about the skills sync revamp.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch skills-sync-revamp

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Revamps the make sync workflow’s external skill handling by introducing a cached “managed skills” reconciliation mechanism driven by SKILLS.txt, plus a convenience target to force reinstall.

Changes:

  • Update make sync to install/reconcile external skills without doing a full skills wipe each run.
  • Add managed-skill state/manifest tracking under ~/.cache/dotagents/skills, plus skills-managed-clean and skills-refresh targets.
  • Update README to document the new behavior and the skills-refresh workflow.

Reviewed changes

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

File Description
README.md Documents the new skills reconciliation behavior and how to force a reinstall.
Makefile Implements managed external skills reconciliation with cached state/manifests and adds skills-refresh.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Makefile
manifest_dir="$(SKILLS_MANIFEST_DIR)"; \
spec_state="$(SKILLS_SPEC_STATE_FILE)"; \
external_source="$(SKILLS_EXTERNAL_SOURCE_DIR)"; \
tmp_spec=$$(mktemp); \

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

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

mktemp is invoked without a template (e.g., mktemp). On macOS/BSD mktemp typically requires a template or -t, so this target can fail. Use a portable mktemp invocation (or allow TMPDIR) for the spec temp file.

Suggested change
tmp_spec=$$(mktemp); \
tmp_spec=$$(mktemp "$${TMPDIR:-/tmp}/skills-spec.XXXXXX"); \

Copilot uses AI. Check for mistakes.
Comment thread Makefile
Comment on lines +161 to +162
before_file=$$(mktemp); \
after_file=$$(mktemp); \

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

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

mktemp is invoked without a template for before_file/after_file. This is not portable on macOS/BSD (often errors with “too few X's in template”). Use a portable mktemp pattern for these temp files as well.

Suggested change
before_file=$$(mktemp); \
after_file=$$(mktemp); \
before_file=$$(mktemp "$${TMPDIR:-/tmp}/before_file.XXXXXX"); \
after_file=$$(mktemp "$${TMPDIR:-/tmp}/after_file.XXXXXX"); \

Copilot uses AI. Check for mistakes.
Comment thread Makefile
skills-clean: ## Remove all globally installed skills and cached install state.
@for target in $(SKILLS_TARGET_DIRS); do \
if [ -d "$$target" ]; then \
rm -rf "$$target"/*; \

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

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

The updated skills-clean description says it removes “all” skills, but the implementation uses rm -rf "$target"/*, which won’t remove dotfiles/directories (e.g., .foo). Either adjust the wording or update removal to include hidden entries so behavior matches the description.

Suggested change
rm -rf "$$target"/*; \
find "$$target" -mindepth 1 -maxdepth 1 -exec rm -rf -- {} +; \

Copilot uses AI. Check for mistakes.
Comment thread Makefile
Comment on lines +211 to +214
while IFS='|' read -r repo normalized_skills || [ -n "$$repo$$normalized_skills" ]; do \
manifest_file="$$manifest_dir/$$(printf '%s' "$$repo" | sed 's#[^A-Za-z0-9_.-]#_#g').skills"; \
install_repo "$$repo" "$$normalized_skills" "$$manifest_file" 0; \
done < "$$tmp_spec"; \

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

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

Manifest filenames are derived from a sanitized repo string (sed 's#[^A-Za-z0-9_.-]#_#g'). Different repos can collide to the same manifest name (e.g., a/b vs a_b), causing managed-clean/install state to mix. Consider using a collision-resistant encoding (e.g., include a hash of the repo) for manifest filenames.

Copilot uses AI. Check for mistakes.
Comment thread Makefile

.PHONY: skills-refresh
skills-refresh: ## Force a clean reinstall of external skills and re-sync local repo skills.
@$(MAKE) skills-managed-clean

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

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

skills-refresh runs skills-managed-clean, then forces skills-install with DOTAGENTS_FORCE_SKILLS_INSTALL=1. But skills-install will call skills-managed-clean again when the force flag is set, doing the cleanup twice. Consider removing the first clean or adjusting skills-install to skip the clean when invoked from refresh.

Suggested change
@$(MAKE) skills-managed-clean

Copilot uses AI. Check for mistakes.
Comment thread Makefile
Comment on lines +241 to 247
cp "$$tmp_spec" "$$spec_state"; \
rm -f "$$tmp_spec"; \
if [ "$$failed" = "1" ]; then \
echo "Some skills failed to install (see above)."; \
else \
echo "All external skills installed successfully."; \
echo "Managed external skills are in sync."; \
fi

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

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

skills-install performs critical filesystem state updates (cp to the spec state file) but the recipe doesn’t use set -e or check return codes, so a failure can still print “Managed external skills are in sync.” and leave the cache inconsistent. Consider failing the target on unexpected errors (while still allowing bunx skills add to be non-fatal if that’s intended).

Copilot uses AI. Check for mistakes.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No issues found across 2 files

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
Makefile (1)

208-210: Consider handling skills-managed-clean failure.

If $(MAKE) skills-managed-clean fails (e.g., permission error), the script continues silently and may leave stale skills. Consider adding error handling or at least a warning.

🔧 Optional: Add error handling for clean step
 	if [ "$$spec_changed" = "1" ]; then \
-		$(MAKE) skills-managed-clean; \
+		if ! $(MAKE) skills-managed-clean; then \
+			echo "Warning: skills-managed-clean failed; continuing with reinstall..."; \
+		fi; \
 		mkdir -p "$$state_dir" "$$manifest_dir" "$$external_source"; \
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Makefile` around lines 208 - 210, The Makefile currently runs `$(MAKE)
skills-managed-clean` when `$$spec_changed` is 1 but ignores failures; update
the block so a non-zero exit from the `skills-managed-clean` target is detected
and handled (e.g., emit a clear error via echo/process logger and exit non‑zero
or at least print a warning), before proceeding to `mkdir -p "$$state_dir"
"$$manifest_dir" "$$external_source"`, ensuring `skills-managed-clean` failures
do not allow the script to continue silently and leave stale state.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Makefile`:
- Around line 243-247: The Makefile currently prints "Some skills failed to
install" when the variable failed is set but still exits with status 0, masking
CI failures; modify the recipe that checks the failed variable (the if [
"$$failed" = "1" ]; then ... fi block) to explicitly exit with a non‑zero status
in the failure branch (e.g., add an "exit 1" or "false" immediately after the
failure echo) so the make target (e.g., sync/managed-skills target that sets
failed) propagates failure to the caller; ensure the change remains inside the
same shell invocation (preserve the existing backslash line-continuations or use
.ONESHELL).
- Line 212: The manifest filename generation using manifest_file and sed
's#[^A-Za-z0-9_.-]#_#g' can produce collisions (e.g., owner/foo -> owner_foo);
update the sanitization used when computing manifest_file to make slashes
distinct or otherwise collision-resistant — for example, first map '/' to a
unique token like '__' (apply s#/#__#g before the generic sanitizer) or append a
short hash of $$repo (e.g., use a checksum of $$repo) to the sanitized name;
change the expression that builds manifest_file so it either replaces '/' with
'__' prior to the broad character replacement or includes the hash to ensure
uniqueness.

---

Nitpick comments:
In `@Makefile`:
- Around line 208-210: The Makefile currently runs `$(MAKE)
skills-managed-clean` when `$$spec_changed` is 1 but ignores failures; update
the block so a non-zero exit from the `skills-managed-clean` target is detected
and handled (e.g., emit a clear error via echo/process logger and exit non‑zero
or at least print a warning), before proceeding to `mkdir -p "$$state_dir"
"$$manifest_dir" "$$external_source"`, ensuring `skills-managed-clean` failures
do not allow the script to continue silently and leave stale state.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c0c5b87f-d092-4894-b8ae-a25ac2aeb4a8

📥 Commits

Reviewing files that changed from the base of the PR and between d5dde50 and aee2627.

📒 Files selected for processing (2)
  • Makefile
  • README.md

Comment thread Makefile
$(MAKE) skills-managed-clean; \
mkdir -p "$$state_dir" "$$manifest_dir" "$$external_source"; \
while IFS='|' read -r repo normalized_skills || [ -n "$$repo$$normalized_skills" ]; do \
manifest_file="$$manifest_dir/$$(printf '%s' "$$repo" | sed 's#[^A-Za-z0-9_.-]#_#g').skills"; \

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for potential manifest filename collisions in SKILLS.txt
if [ -f "SKILLS.txt" ]; then
  echo "Repos in SKILLS.txt:"
  grep -v '^#' SKILLS.txt | grep -v '^$' | awk '{print $1}' | while read repo; do
    sanitized=$(echo "$repo" | sed 's#[^A-Za-z0-9_.-]#_#g')
    echo "  $repo -> $sanitized.skills"
  done | sort | uniq -d -f1
else
  echo "SKILLS.txt not found"
fi

Repository: shunkakinoki/dotagents

Length of output: 86


🏁 Script executed:

# Check if SKILLS.txt exists and examine its content
echo "=== Checking SKILLS.txt ==="
if [ -f "SKILLS.txt" ]; then
    echo "SKILLS.txt exists. First 30 lines (non-comment):"
    head -30 SKILLS.txt | grep -v '^#' | grep -v '^$'
    echo ""
    echo "Total non-empty, non-comment lines:"
    grep -v '^#' SKILLS.txt | grep -v '^$' | wc -l
else
    echo "SKILLS.txt not found"
fi

Repository: shunkakinoki/dotagents

Length of output: 1075


🏁 Script executed:

# Examine the Makefile context around line 212
echo "=== Makefile context (lines 200-220) ==="
sed -n '200,220p' Makefile

Repository: shunkakinoki/dotagents

Length of output: 1165


🏁 Script executed:

# Check for any actual collision cases by analyzing all repos
echo "=== Analyzing repos for collisions ==="
if [ -f "SKILLS.txt" ]; then
    grep -v '^#' SKILLS.txt | grep -v '^$' | awk '{print $1}' | while read repo; do
        sanitized=$(echo "$repo" | sed 's#[^A-Za-z0-9_.-]#_#g')
        echo "$sanitized"
    done | sort > /tmp/sanitized_names.txt
    
    # Find duplicates
    echo "Duplicate sanitized names:"
    uniq -d /tmp/sanitized_names.txt
    
    # Show what repos map to each duplicate
    if [ -s /tmp/sanitized_names.txt ]; then
        echo ""
        echo "Full mapping:"
        grep -v '^#' SKILLS.txt | grep -v '^$' | awk '{print $1}' | while read repo; do
            sanitized=$(echo "$repo" | sed 's#[^A-Za-z0-9_.-]#_#g')
            echo "$sanitized <- $repo"
        done | sort
    fi
fi

Repository: shunkakinoki/dotagents

Length of output: 1951


Potential manifest filename collision for different repos is theoretically possible but not present in current SKILLS.txt.

The sanitization sed 's#[^A-Za-z0-9_.-]#_#g' replaces / with _, so repos like owner/foo-bar and owner_foo-bar would produce the same manifest filename. Analysis of the 39 repos currently in SKILLS.txt confirms no collisions exist today; however, this remains a latent collision risk as the file grows.

Consider updating to a more collision-resistant scheme (e.g., double-underscore for slash: s#/#__#g) as a defensive measure.

🔧 Suggested fix
-			manifest_file="$$manifest_dir/$$(printf '%s' "$$repo" | sed 's#[^A-Za-z0-9_.-]#_#g').skills"; \
+			manifest_file="$$manifest_dir/$$(printf '%s' "$$repo" | sed 's#/#__#g; s#[^A-Za-z0-9_.-]#_#g').skills"; \
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
manifest_file="$$manifest_dir/$$(printf '%s' "$$repo" | sed 's#[^A-Za-z0-9_.-]#_#g').skills"; \
manifest_file="$$manifest_dir/$$(printf '%s' "$$repo" | sed 's#/#__#g; s#[^A-Za-z0-9_.-]#_#g').skills"; \
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Makefile` at line 212, The manifest filename generation using manifest_file
and sed 's#[^A-Za-z0-9_.-]#_#g' can produce collisions (e.g., owner/foo ->
owner_foo); update the sanitization used when computing manifest_file to make
slashes distinct or otherwise collision-resistant — for example, first map '/'
to a unique token like '__' (apply s#/#__#g before the generic sanitizer) or
append a short hash of $$repo (e.g., use a checksum of $$repo) to the sanitized
name; change the expression that builds manifest_file so it either replaces '/'
with '__' prior to the broad character replacement or includes the hash to
ensure uniqueness.

Comment thread Makefile
Comment on lines 243 to 247
if [ "$$failed" = "1" ]; then \
echo "Some skills failed to install (see above)."; \
else \
echo "All external skills installed successfully."; \
echo "Managed external skills are in sync."; \
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

The target exits successfully even when installations fail.

When failed=1, the message is printed but the target exits with status 0. This masks failures in CI/CD pipelines and makes make sync report success even when some skills failed to install.

🐛 Proposed fix to propagate failure status
 	if [ "$$failed" = "1" ]; then \
 		echo "Some skills failed to install (see above)."; \
+		exit 1; \
 	else \
 		echo "Managed external skills are in sync."; \
 	fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if [ "$$failed" = "1" ]; then \
echo "Some skills failed to install (see above)."; \
else \
echo "All external skills installed successfully."; \
echo "Managed external skills are in sync."; \
fi
if [ "$$failed" = "1" ]; then \
echo "Some skills failed to install (see above)."; \
exit 1; \
else \
echo "Managed external skills are in sync."; \
fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Makefile` around lines 243 - 247, The Makefile currently prints "Some skills
failed to install" when the variable failed is set but still exits with status
0, masking CI failures; modify the recipe that checks the failed variable (the
if [ "$$failed" = "1" ]; then ... fi block) to explicitly exit with a non‑zero
status in the failure branch (e.g., add an "exit 1" or "false" immediately after
the failure echo) so the make target (e.g., sync/managed-skills target that sets
failed) propagates failure to the caller; ensure the change remains inside the
same shell invocation (preserve the existing backslash line-continuations or use
.ONESHELL).

@shunkakinoki
shunkakinoki merged commit e6abc9b into main Apr 10, 2026
8 checks passed
@shunkakinoki
shunkakinoki deleted the skills-sync-revamp branch April 10, 2026 17:23
@coderabbitai coderabbitai Bot mentioned this pull request May 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants