From 98e6e1fcc27056000d355b22e4c82395f05946ac Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Sun, 28 Jun 2026 19:14:16 -0500 Subject: [PATCH 01/51] feat: install caveman hook runtime as managed component Deploy caveman's hook runtime into ~/.claude/hooks via a chezmoi run script so the SessionStart and UserPromptSubmit hooks resolve their caveman-config dependency that apm leaves out when it installs caveman as skills only. --- ..._onchange_09_install-caveman-hooks.sh.tmpl | 15 +++++ .../lib/install/caveman-hooks.sh | 67 +++++++++++++++++++ home/dot_claude/settings.json | 26 +++++++ tests/template/darwin-install-scripts.bats | 27 ++++++++ tests/unit/lib/install/caveman-hooks.bats | 65 ++++++++++++++++++ 5 files changed, 200 insertions(+) create mode 100644 home/.chezmoiscripts/darwin/run_onchange_09_install-caveman-hooks.sh.tmpl create mode 100644 home/.chezmoitemplates/lib/install/caveman-hooks.sh create mode 100644 tests/unit/lib/install/caveman-hooks.bats diff --git a/home/.chezmoiscripts/darwin/run_onchange_09_install-caveman-hooks.sh.tmpl b/home/.chezmoiscripts/darwin/run_onchange_09_install-caveman-hooks.sh.tmpl new file mode 100644 index 0000000..3e43b26 --- /dev/null +++ b/home/.chezmoiscripts/darwin/run_onchange_09_install-caveman-hooks.sh.tmpl @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +# @file run_onchange_09_install-caveman-hooks.sh +# @brief Install the caveman persistent-mode hook runtime. +# @description APM data hash: {{ include ".chezmoidata/apm.yaml" | sha256sum }} +{{- $activeTargets := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .apm.targets) | fromJson -}} +{{- $claudeActive := false -}} +{{- range $activeTargets -}} +{{- if eq . "claude" }}{{ $claudeActive = true }}{{- end -}} +{{- end -}} + +{{ if and (eq .chezmoi.os "darwin") $claudeActive }} +{{ template "lib/common/log.sh" . }} +{{ template "lib/common/install-prelude.sh" . }} +{{ template "lib/install/caveman-hooks.sh" . }} +{{ end }} diff --git a/home/.chezmoitemplates/lib/install/caveman-hooks.sh b/home/.chezmoitemplates/lib/install/caveman-hooks.sh new file mode 100644 index 0000000..9478710 --- /dev/null +++ b/home/.chezmoitemplates/lib/install/caveman-hooks.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +# @file lib/install/caveman-hooks.sh +# @brief Install the caveman persistent-mode hook runtime into ~/.claude/hooks. +# @description +# apm installs caveman as skills only, so its optional SessionStart and +# UserPromptSubmit hook runtime is never deployed. This copies that runtime +# from the apm-pinned package source into ${CLAUDE_CONFIG_DIR:-~/.claude}/hooks +# so the hooks wired in settings.json can resolve their shared caveman-config +# dependency. Sourcing from the apm_modules copy keeps the runtime on the same +# version apm pins. Sourceable from bats tests and injected into chezmoi run +# scripts via chezmoi template rendering. + +set -Eeuo pipefail + +CAVEMAN_HOOK_SOURCE="${CAVEMAN_HOOK_SOURCE:-${HOME}/.apm/apm_modules/JuliusBrussee/caveman/src/hooks}" +CAVEMAN_HOOK_DEST="${CAVEMAN_HOOK_DEST:-${CLAUDE_CONFIG_DIR:-${HOME}/.claude}/hooks}" +CAVEMAN_HOOK_FILES=( + "caveman-config.js" + "caveman-activate.js" + "caveman-mode-tracker.js" + "caveman-stats.js" + "package.json" +) + +# +# @description Copy the caveman hook runtime into the Claude hooks directory. +# @exitcode 0 Runtime copied, or source absent (apm has not installed caveman). +# +function caveman_hooks_install_main() { + if [[ ! -d "${CAVEMAN_HOOK_SOURCE}" ]]; then + log_warn "[caveman] ${CAVEMAN_HOOK_SOURCE} not found; run run_onchange_06_install-apm first." + return 0 + fi + + log_info "[caveman] Installing caveman hook runtime into ${CAVEMAN_HOOK_DEST}..." + mkdir -p "${CAVEMAN_HOOK_DEST}" + + local file + local missing=() + for file in "${CAVEMAN_HOOK_FILES[@]}"; do + if [[ ! -f "${CAVEMAN_HOOK_SOURCE}/${file}" ]]; then + missing+=("${file}") + continue + fi + cp -f "${CAVEMAN_HOOK_SOURCE}/${file}" "${CAVEMAN_HOOK_DEST}/${file}" + done + + if ((${#missing[@]} > 0)); then + log_warn "[caveman] ${#missing[@]} runtime file(s) missing from source:" + for file in "${missing[@]}"; do + printf ' - %s\n' "${file}" >&2 + done + fi + + log_info "[caveman] caveman hook runtime installed." +} + +# +# @description Run the caveman hooks install flow. +# +function main() { + caveman_hooks_install_main +} + +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + main +fi diff --git a/home/dot_claude/settings.json b/home/dot_claude/settings.json index 9510891..58a6cfa 100644 --- a/home/dot_claude/settings.json +++ b/home/dot_claude/settings.json @@ -160,6 +160,32 @@ ], "defaultMode": "acceptEdits" }, + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "type": "command", + "command": "node $HOME/.claude/hooks/caveman-activate.js", + "timeout": 5, + "statusMessage": "Loading caveman mode..." + } + ] + } + ], + "UserPromptSubmit": [ + { + "hooks": [ + { + "type": "command", + "command": "node $HOME/.claude/hooks/caveman-mode-tracker.js", + "timeout": 5, + "statusMessage": "Tracking caveman mode..." + } + ] + } + ] + }, "alwaysThinkingEnabled": true, "autoScrollEnabled": true, "autoUpdatesChannel": "latest", diff --git a/tests/template/darwin-install-scripts.bats b/tests/template/darwin-install-scripts.bats index c5ececf..4400b73 100644 --- a/tests/template/darwin-install-scripts.bats +++ b/tests/template/darwin-install-scripts.bats @@ -9,6 +9,7 @@ EMPTY_APM_DATA='{"chezmoi":{"os":"darwin"},"personal":true,"work":false,"apm":{" PACKAGE_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_02_install-packages.sh.tmpl" UV_TOOLS_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_03_install-uv-tools.sh.tmpl" GRAPHIFY_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_08_install-graphify-skills.sh.tmpl" +CAVEMAN_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_09_install-caveman-hooks.sh.tmpl" @test "darwin install script templates render with bash shebang" { for template in "$DOTFILES_ROOT"/home/.chezmoiscripts/darwin/*.tmpl; do @@ -30,6 +31,10 @@ GRAPHIFY_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_08_in assert_file_contains "$GRAPHIFY_TEMPLATE" '{{ template "lib/install/graphify-skills.sh" . }}' } +@test "darwin install script templates inject caveman shell library" { + assert_file_contains "$CAVEMAN_TEMPLATE" '{{ template "lib/install/caveman-hooks.sh" . }}' +} + @test "darwin install script templates inject the install prelude" { local prelude='{{ template "lib/common/install-prelude.sh" . }}' assert_file_contains "$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_once_01_install-homebrew.sh.tmpl" "$prelude" @@ -40,6 +45,7 @@ GRAPHIFY_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_08_in assert_file_contains "$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_06_install-apm.sh.tmpl" "$prelude" assert_file_contains "$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_07_fix-opencode-agent-tools.sh.tmpl" "$prelude" assert_file_contains "$GRAPHIFY_TEMPLATE" "$prelude" + assert_file_contains "$CAVEMAN_TEMPLATE" "$prelude" } @test "rendered darwin install scripts are syntactically valid bash" { @@ -129,3 +135,24 @@ GRAPHIFY_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_08_in refute_line --partial 'graphify install --platform' refute_line --partial 'graphify_skills_install_main' } + +@test "personal caveman template renders the hook install flow" { + run render_chezmoi_template "$CAVEMAN_TEMPLATE" "$DARWIN_DATA" + + assert_success + assert_line --partial 'caveman_hooks_install_main' +} + +@test "work caveman template renders no hook install flow" { + run render_chezmoi_template "$CAVEMAN_TEMPLATE" "$WORK_DATA" + + assert_success + refute_line --partial 'caveman_hooks_install_main' +} + +@test "empty APM target groups render no caveman hook install flow" { + run render_chezmoi_template "$CAVEMAN_TEMPLATE" "$EMPTY_APM_DATA" + + assert_success + refute_line --partial 'caveman_hooks_install_main' +} diff --git a/tests/unit/lib/install/caveman-hooks.bats b/tests/unit/lib/install/caveman-hooks.bats new file mode 100644 index 0000000..bca532d --- /dev/null +++ b/tests/unit/lib/install/caveman-hooks.bats @@ -0,0 +1,65 @@ +#!/usr/bin/env bats +# @file tests/unit/lib/install/caveman-hooks.bats +# @brief Behavior tests for home/.chezmoitemplates/lib/install/caveman-hooks.sh. + +load '../../../test_helpers/load.bash' + +LOG_LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/common/log.sh" +PRELUDE="$DOTFILES_ROOT/home/.chezmoitemplates/lib/common/install-prelude.sh" +LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/install/caveman-hooks.sh" + +RUNTIME_FILES=( + caveman-config.js + caveman-activate.js + caveman-mode-tracker.js + caveman-stats.js + package.json +) + +setup() { + export CAVEMAN_HOOK_SOURCE="$BATS_TEST_TMPDIR/src" + export CAVEMAN_HOOK_DEST="$BATS_TEST_TMPDIR/dest" + mkdir -p "$CAVEMAN_HOOK_SOURCE" + local f + for f in "${RUNTIME_FILES[@]}"; do + printf 'content-%s\n' "$f" >"$CAVEMAN_HOOK_SOURCE/$f" + done +} + +run_lib() { + run bash -c "source '$LOG_LIB' && source '$PRELUDE' && source '$LIB' && main" +} + +@test "main: copies all runtime files into the dest hooks dir" { + run_lib + + assert_success + assert_line "[caveman] caveman hook runtime installed." + local f + for f in "${RUNTIME_FILES[@]}"; do + assert_file_exist "$CAVEMAN_HOOK_DEST/$f" + done +} + +@test "main: warns and succeeds when source dir is absent" { + rm -rf "$CAVEMAN_HOOK_SOURCE" + + run_lib + + assert_success + assert_line "warn: [caveman] $CAVEMAN_HOOK_SOURCE not found; run run_onchange_06_install-apm first." + [ ! -d "$CAVEMAN_HOOK_DEST" ] +} + +@test "main: warns about missing runtime files but still succeeds" { + rm -f "$CAVEMAN_HOOK_SOURCE/caveman-stats.js" + + run_lib + + assert_success + assert_line "warn: [caveman] 1 runtime file(s) missing from source:" + assert_line " - caveman-stats.js" + assert_line "[caveman] caveman hook runtime installed." + assert_file_exist "$CAVEMAN_HOOK_DEST/caveman-config.js" + [ ! -f "$CAVEMAN_HOOK_DEST/caveman-stats.js" ] +} From afd68cffa97ccf1e59ff6c320621d396ba5d9e55 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Mon, 29 Jun 2026 12:26:35 -0500 Subject: [PATCH 02/51] fix: mirror caveman runtime to apm hook path apm injects a SessionStart hook at the nested path but ships an incomplete runtime there, so it failed to require caveman-config. --- .../.chezmoitemplates/lib/install/caveman-hooks.sh | 14 ++++++++++++-- tests/unit/lib/install/caveman-hooks.bats | 10 ++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/home/.chezmoitemplates/lib/install/caveman-hooks.sh b/home/.chezmoitemplates/lib/install/caveman-hooks.sh index 9478710..0f8d0b7 100644 --- a/home/.chezmoitemplates/lib/install/caveman-hooks.sh +++ b/home/.chezmoitemplates/lib/install/caveman-hooks.sh @@ -14,6 +14,10 @@ set -Eeuo pipefail CAVEMAN_HOOK_SOURCE="${CAVEMAN_HOOK_SOURCE:-${HOME}/.apm/apm_modules/JuliusBrussee/caveman/src/hooks}" CAVEMAN_HOOK_DEST="${CAVEMAN_HOOK_DEST:-${CLAUDE_CONFIG_DIR:-${HOME}/.claude}/hooks}" +# apm injects a SessionStart hook pointing at this nested path, but only ships a +# partial runtime there, so its hook fails to require caveman-config. Mirror the +# full runtime here too so apm's injected hook resolves if apm re-adds it. +CAVEMAN_HOOK_APM_DEST="${CAVEMAN_HOOK_APM_DEST:-${CAVEMAN_HOOK_DEST}/caveman/src/hooks}" CAVEMAN_HOOK_FILES=( "caveman-config.js" "caveman-activate.js" @@ -33,7 +37,11 @@ function caveman_hooks_install_main() { fi log_info "[caveman] Installing caveman hook runtime into ${CAVEMAN_HOOK_DEST}..." - mkdir -p "${CAVEMAN_HOOK_DEST}" + + local dest + for dest in "${CAVEMAN_HOOK_DEST}" "${CAVEMAN_HOOK_APM_DEST}"; do + mkdir -p "${dest}" + done local file local missing=() @@ -42,7 +50,9 @@ function caveman_hooks_install_main() { missing+=("${file}") continue fi - cp -f "${CAVEMAN_HOOK_SOURCE}/${file}" "${CAVEMAN_HOOK_DEST}/${file}" + for dest in "${CAVEMAN_HOOK_DEST}" "${CAVEMAN_HOOK_APM_DEST}"; do + cp -f "${CAVEMAN_HOOK_SOURCE}/${file}" "${dest}/${file}" + done done if ((${#missing[@]} > 0)); then diff --git a/tests/unit/lib/install/caveman-hooks.bats b/tests/unit/lib/install/caveman-hooks.bats index bca532d..a38b155 100644 --- a/tests/unit/lib/install/caveman-hooks.bats +++ b/tests/unit/lib/install/caveman-hooks.bats @@ -41,6 +41,16 @@ run_lib() { done } +@test "main: mirrors the full runtime into the apm nested hook path" { + run_lib + + assert_success + local f + for f in "${RUNTIME_FILES[@]}"; do + assert_file_exist "$CAVEMAN_HOOK_DEST/caveman/src/hooks/$f" + done +} + @test "main: warns and succeeds when source dir is absent" { rm -rf "$CAVEMAN_HOOK_SOURCE" From eb53bda08324762cc3c1a942944d651caacd3197 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Mon, 29 Jun 2026 12:26:35 -0500 Subject: [PATCH 03/51] chore: set chezmoi sourceDir in config Lets bare chezmoi commands resolve the source without --source. --- home/.chezmoi.yaml.tmpl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/home/.chezmoi.yaml.tmpl b/home/.chezmoi.yaml.tmpl index 5fe60da..4f5ef63 100644 --- a/home/.chezmoi.yaml.tmpl +++ b/home/.chezmoi.yaml.tmpl @@ -22,6 +22,8 @@ {{- $personal = true -}} {{- end -}} +sourceDir: {{ .chezmoi.sourceDir | quote }} + data: hostname: {{ $hostname | quote }} personal: {{ $personal }} From 2d10de943a8b0306563c68dca082201d5a2ddf6b Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Mon, 29 Jun 2026 13:31:25 -0500 Subject: [PATCH 04/51] fix: reassert claude settings after apm install apm rewrites ~/.claude/settings.json on every run, reordering keys and injecting duplicate caveman plus superpowers hooks. A run_after script restores the source file so it stays canonical. --- ...run_after_reassert-claude-settings.sh.tmpl | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 home/.chezmoiscripts/darwin/run_after_reassert-claude-settings.sh.tmpl diff --git a/home/.chezmoiscripts/darwin/run_after_reassert-claude-settings.sh.tmpl b/home/.chezmoiscripts/darwin/run_after_reassert-claude-settings.sh.tmpl new file mode 100644 index 0000000..24f8b04 --- /dev/null +++ b/home/.chezmoiscripts/darwin/run_after_reassert-claude-settings.sh.tmpl @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# @file run_after_reassert-claude-settings.sh +# @brief Restore the chezmoi-managed Claude settings.json after apm rewrites it. +# @description +# apm install (run_onchange_06) rewrites ~/.claude/settings.json on every run: +# it reorders keys and injects duplicate caveman and superpowers hooks. That +# reintroduces chezmoi drift and double hook firing. This run_after script +# copies the source settings.json over the live file once apm has finished, so +# the managed file stays byte-identical to source and apm cannot leave stale +# or duplicate hook entries behind. Runs every apply because apm mutates the +# file every apply, regardless of whether the source changed. +{{ if eq .chezmoi.os "darwin" -}} +{{ template "lib/common/log.sh" . }} +set -Eeuo pipefail + +CLAUDE_SETTINGS="${CLAUDE_CONFIG_DIR:-${HOME}/.claude}/settings.json" +CLAUDE_SETTINGS_SOURCE="{{ .chezmoi.sourceDir }}/dot_claude/settings.json" + +if [[ ! -f "${CLAUDE_SETTINGS_SOURCE}" ]]; then + log_warn "[claude] ${CLAUDE_SETTINGS_SOURCE} not found; skipping settings reassert." + exit 0 +fi + +log_info "[claude] Restoring managed settings.json (post-apm)..." +mkdir -p "$(dirname "${CLAUDE_SETTINGS}")" +cp -f "${CLAUDE_SETTINGS_SOURCE}" "${CLAUDE_SETTINGS}" +log_info "[claude] settings.json restored." +{{- end }} From beb2bce26ac12535ba33d961a0b62f35c8747dd4 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Mon, 29 Jun 2026 13:31:25 -0500 Subject: [PATCH 05/51] chore: drop agent-skills apm target Claude reads ~/.claude/skills only, so agent-skills deployed a dead duplicate into ~/.agents/skills. --- home/.chezmoidata/apm.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/home/.chezmoidata/apm.yaml b/home/.chezmoidata/apm.yaml index 247efd5..1ec330b 100644 --- a/home/.chezmoidata/apm.yaml +++ b/home/.chezmoidata/apm.yaml @@ -3,7 +3,6 @@ apm: shared: [] personal: - claude - - agent-skills work: - copilot dependencies: From 32cc62ca96a3bef1f553cc5561b80b178b3550d4 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 17:34:03 -0500 Subject: [PATCH 06/51] chore: remove obsolete caveman hook installation scripts --- ...run_after_reassert-claude-settings.sh.tmpl | 28 ------- ..._onchange_09_install-caveman-hooks.sh.tmpl | 15 ---- .../lib/install/caveman-hooks.sh | 77 ------------------- 3 files changed, 120 deletions(-) delete mode 100644 home/.chezmoiscripts/darwin/run_after_reassert-claude-settings.sh.tmpl delete mode 100644 home/.chezmoiscripts/darwin/run_onchange_09_install-caveman-hooks.sh.tmpl delete mode 100644 home/.chezmoitemplates/lib/install/caveman-hooks.sh diff --git a/home/.chezmoiscripts/darwin/run_after_reassert-claude-settings.sh.tmpl b/home/.chezmoiscripts/darwin/run_after_reassert-claude-settings.sh.tmpl deleted file mode 100644 index 24f8b04..0000000 --- a/home/.chezmoiscripts/darwin/run_after_reassert-claude-settings.sh.tmpl +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash -# @file run_after_reassert-claude-settings.sh -# @brief Restore the chezmoi-managed Claude settings.json after apm rewrites it. -# @description -# apm install (run_onchange_06) rewrites ~/.claude/settings.json on every run: -# it reorders keys and injects duplicate caveman and superpowers hooks. That -# reintroduces chezmoi drift and double hook firing. This run_after script -# copies the source settings.json over the live file once apm has finished, so -# the managed file stays byte-identical to source and apm cannot leave stale -# or duplicate hook entries behind. Runs every apply because apm mutates the -# file every apply, regardless of whether the source changed. -{{ if eq .chezmoi.os "darwin" -}} -{{ template "lib/common/log.sh" . }} -set -Eeuo pipefail - -CLAUDE_SETTINGS="${CLAUDE_CONFIG_DIR:-${HOME}/.claude}/settings.json" -CLAUDE_SETTINGS_SOURCE="{{ .chezmoi.sourceDir }}/dot_claude/settings.json" - -if [[ ! -f "${CLAUDE_SETTINGS_SOURCE}" ]]; then - log_warn "[claude] ${CLAUDE_SETTINGS_SOURCE} not found; skipping settings reassert." - exit 0 -fi - -log_info "[claude] Restoring managed settings.json (post-apm)..." -mkdir -p "$(dirname "${CLAUDE_SETTINGS}")" -cp -f "${CLAUDE_SETTINGS_SOURCE}" "${CLAUDE_SETTINGS}" -log_info "[claude] settings.json restored." -{{- end }} diff --git a/home/.chezmoiscripts/darwin/run_onchange_09_install-caveman-hooks.sh.tmpl b/home/.chezmoiscripts/darwin/run_onchange_09_install-caveman-hooks.sh.tmpl deleted file mode 100644 index 3e43b26..0000000 --- a/home/.chezmoiscripts/darwin/run_onchange_09_install-caveman-hooks.sh.tmpl +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash -# @file run_onchange_09_install-caveman-hooks.sh -# @brief Install the caveman persistent-mode hook runtime. -# @description APM data hash: {{ include ".chezmoidata/apm.yaml" | sha256sum }} -{{- $activeTargets := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .apm.targets) | fromJson -}} -{{- $claudeActive := false -}} -{{- range $activeTargets -}} -{{- if eq . "claude" }}{{ $claudeActive = true }}{{- end -}} -{{- end -}} - -{{ if and (eq .chezmoi.os "darwin") $claudeActive }} -{{ template "lib/common/log.sh" . }} -{{ template "lib/common/install-prelude.sh" . }} -{{ template "lib/install/caveman-hooks.sh" . }} -{{ end }} diff --git a/home/.chezmoitemplates/lib/install/caveman-hooks.sh b/home/.chezmoitemplates/lib/install/caveman-hooks.sh deleted file mode 100644 index 0f8d0b7..0000000 --- a/home/.chezmoitemplates/lib/install/caveman-hooks.sh +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/env bash -# @file lib/install/caveman-hooks.sh -# @brief Install the caveman persistent-mode hook runtime into ~/.claude/hooks. -# @description -# apm installs caveman as skills only, so its optional SessionStart and -# UserPromptSubmit hook runtime is never deployed. This copies that runtime -# from the apm-pinned package source into ${CLAUDE_CONFIG_DIR:-~/.claude}/hooks -# so the hooks wired in settings.json can resolve their shared caveman-config -# dependency. Sourcing from the apm_modules copy keeps the runtime on the same -# version apm pins. Sourceable from bats tests and injected into chezmoi run -# scripts via chezmoi template rendering. - -set -Eeuo pipefail - -CAVEMAN_HOOK_SOURCE="${CAVEMAN_HOOK_SOURCE:-${HOME}/.apm/apm_modules/JuliusBrussee/caveman/src/hooks}" -CAVEMAN_HOOK_DEST="${CAVEMAN_HOOK_DEST:-${CLAUDE_CONFIG_DIR:-${HOME}/.claude}/hooks}" -# apm injects a SessionStart hook pointing at this nested path, but only ships a -# partial runtime there, so its hook fails to require caveman-config. Mirror the -# full runtime here too so apm's injected hook resolves if apm re-adds it. -CAVEMAN_HOOK_APM_DEST="${CAVEMAN_HOOK_APM_DEST:-${CAVEMAN_HOOK_DEST}/caveman/src/hooks}" -CAVEMAN_HOOK_FILES=( - "caveman-config.js" - "caveman-activate.js" - "caveman-mode-tracker.js" - "caveman-stats.js" - "package.json" -) - -# -# @description Copy the caveman hook runtime into the Claude hooks directory. -# @exitcode 0 Runtime copied, or source absent (apm has not installed caveman). -# -function caveman_hooks_install_main() { - if [[ ! -d "${CAVEMAN_HOOK_SOURCE}" ]]; then - log_warn "[caveman] ${CAVEMAN_HOOK_SOURCE} not found; run run_onchange_06_install-apm first." - return 0 - fi - - log_info "[caveman] Installing caveman hook runtime into ${CAVEMAN_HOOK_DEST}..." - - local dest - for dest in "${CAVEMAN_HOOK_DEST}" "${CAVEMAN_HOOK_APM_DEST}"; do - mkdir -p "${dest}" - done - - local file - local missing=() - for file in "${CAVEMAN_HOOK_FILES[@]}"; do - if [[ ! -f "${CAVEMAN_HOOK_SOURCE}/${file}" ]]; then - missing+=("${file}") - continue - fi - for dest in "${CAVEMAN_HOOK_DEST}" "${CAVEMAN_HOOK_APM_DEST}"; do - cp -f "${CAVEMAN_HOOK_SOURCE}/${file}" "${dest}/${file}" - done - done - - if ((${#missing[@]} > 0)); then - log_warn "[caveman] ${#missing[@]} runtime file(s) missing from source:" - for file in "${missing[@]}"; do - printf ' - %s\n' "${file}" >&2 - done - fi - - log_info "[caveman] caveman hook runtime installed." -} - -# -# @description Run the caveman hooks install flow. -# -function main() { - caveman_hooks_install_main -} - -if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then - main -fi From 4d1d0da6a48608b11364d880f945e6f7b3df298e Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 18:55:01 -0500 Subject: [PATCH 07/51] chore: remove LazyVim configuration and related files --- home/.chezmoidata/packages.yaml | 12 - .../skills/grill-me/SKILL.md | 209 ++++++++++++++++ .../skills/junior-to-senior/SKILL.md | 131 ++++++++++ .../references/research-playbook.md | 59 +++++ .../references/review-rubric.md | 67 +++++ .../Code/User/settings.json | 4 + home/dot_claude/settings.json | 31 +-- home/dot_config/nvim/.gitignore | 8 - home/dot_config/nvim/.neoconf.json | 15 -- home/dot_config/nvim/LICENSE | 201 --------------- home/dot_config/nvim/README.md | 4 - home/dot_config/nvim/init.lua | 2 - home/dot_config/nvim/lua/config/autocmds.lua | 8 - home/dot_config/nvim/lua/config/keymaps.lua | 3 - home/dot_config/nvim/lua/config/lazy.lua | 56 ----- home/dot_config/nvim/lua/config/options.lua | 6 - home/dot_config/nvim/lua/plugins/opencode.lua | 74 ------ .../nvim/lua/plugins/treesitter.lua | 16 -- home/dot_config/nvim/stylua.toml | 3 - home/dot_config/opencode/AGENTS.md.tmpl | 1 - home/dot_config/opencode/opencode.jsonc | 232 ------------------ home/dot_config/opencode/tui.json | 10 - .../agents/code-reviewer.agent.md | 0 .../agents/research.agent.md | 0 home/dot_copilot/mcp-config.json | 3 + .../skills/deep-analysis/SKILL.md | 0 .../references/research-summary-template.md | 0 .../skills/deep-analysis/references/rubric.md | 0 28 files changed, 474 insertions(+), 681 deletions(-) create mode 100644 home/.chezmoitemplates/skills/grill-me/SKILL.md create mode 100644 home/.chezmoitemplates/skills/junior-to-senior/SKILL.md create mode 100644 home/.chezmoitemplates/skills/junior-to-senior/references/research-playbook.md create mode 100644 home/.chezmoitemplates/skills/junior-to-senior/references/review-rubric.md delete mode 100644 home/dot_config/nvim/.gitignore delete mode 100644 home/dot_config/nvim/.neoconf.json delete mode 100644 home/dot_config/nvim/LICENSE delete mode 100644 home/dot_config/nvim/README.md delete mode 100644 home/dot_config/nvim/init.lua delete mode 100644 home/dot_config/nvim/lua/config/autocmds.lua delete mode 100644 home/dot_config/nvim/lua/config/keymaps.lua delete mode 100644 home/dot_config/nvim/lua/config/lazy.lua delete mode 100644 home/dot_config/nvim/lua/config/options.lua delete mode 100644 home/dot_config/nvim/lua/plugins/opencode.lua delete mode 100644 home/dot_config/nvim/lua/plugins/treesitter.lua delete mode 100644 home/dot_config/nvim/stylua.toml delete mode 100644 home/dot_config/opencode/AGENTS.md.tmpl delete mode 100644 home/dot_config/opencode/opencode.jsonc delete mode 100644 home/dot_config/opencode/tui.json rename home/{dot_apm/dot_apm => dot_copilot}/agents/code-reviewer.agent.md (100%) rename home/{dot_apm/dot_apm => dot_copilot}/agents/research.agent.md (100%) create mode 100644 home/dot_copilot/mcp-config.json rename home/{dot_apm/dot_apm => dot_copilot}/skills/deep-analysis/SKILL.md (100%) rename home/{dot_apm/dot_apm => dot_copilot}/skills/deep-analysis/references/research-summary-template.md (100%) rename home/{dot_apm/dot_apm => dot_copilot}/skills/deep-analysis/references/rubric.md (100%) diff --git a/home/.chezmoidata/packages.yaml b/home/.chezmoidata/packages.yaml index 5fe78ab..0b36916 100644 --- a/home/.chezmoidata/packages.yaml +++ b/home/.chezmoidata/packages.yaml @@ -23,13 +23,6 @@ packages: - zoxide - fd - fzf - - lazygit - - neovim - - tree-sitter-cli - - imagemagick - - ghostscript - - tectonic - - mermaid-cli casks: # fonts - font-monaspace @@ -44,12 +37,7 @@ packages: mas: [] personal: - taps: - - anomalyco/tap # For Opencode - formulas: - - anomalyco/tap/opencode casks: - - aldente - claude-code - cleanshot - discord diff --git a/home/.chezmoitemplates/skills/grill-me/SKILL.md b/home/.chezmoitemplates/skills/grill-me/SKILL.md new file mode 100644 index 0000000..86c30da --- /dev/null +++ b/home/.chezmoitemplates/skills/grill-me/SKILL.md @@ -0,0 +1,209 @@ +--- +name: grill-me +description: Calibrated grilling session for stress-testing a plan, design, idea, or decision. First assesses the user's topic knowledge, confidence, and desired pressure level, then asks one question at a time with recommended answers. Use when user says "grill me", "stress-test this", "challenge my plan", "interview me", or wants a plan probed without being overwhelmed. +--- + +# Grill Me + +Interview the user until the plan is clear, defensible, and ready for action. + +This is not hostile debate. It is calibrated pressure. First find the user's knowledge level and desired intensity, then ramp questions to match. + +## Core Rules + +- Ask one question at a time. +- Give a recommended answer for every question. +- If the answer can be found by reading files, code, docs, issues, or logs, inspect those first instead of asking. +- Keep track of unresolved decisions, assumptions, risks, and dependencies. +- Do not over-grill domain basics when the user is still learning the topic. Teach the missing frame briefly, then ask the next useful question. +- Do not under-grill confident experts. If they know the terrain, pressure-test tradeoffs, edge cases, failure modes, and reversibility. +- Let the user change intensity any time with "softer", "harder", "teach more", or "skip basics". + +## Phase 1: Frame The Target + +Identify what should be grilled before asking about comfort. If the topic is not clear, ask: + +> What plan, design, or decision should I grill? +> +> Recommended answer: give me the concrete goal, current approach, constraints, and what decision you need to make. + +If context already contains the plan, summarize it in 3-6 bullets and ask for correction: + +> I think target is: [...] +> +> Recommended answer: "Yes, grill that" or "Adjust: ..." + +## Phase 2: Calibration + +Before grilling the topic, ask a short calibration question unless the user's level is already obvious from context. + +Ask: + +> Before I grill the plan: what is your current comfort with this topic, and how hard do you want the pressure? +> +> Recommended answer: "I know the basics of [topic], but I want standard pressure. Explain missing concepts briefly, then keep pushing." + +Use the user's answer to set two dials: + +### Knowledge Level + +- **New** - user lacks core vocabulary or model of the domain. +- **Working** - user understands basics and can discuss tradeoffs. +- **Expert** - user knows domain deeply and wants sharper critique. + +### Pressure Level + +- **Light** - clarify goals, constraints, and missing context. +- **Standard** - challenge assumptions, tradeoffs, and execution path. +- **Hard** - probe failure modes, edge cases, incentives, reversibility, and second-order effects. + +If the user does not answer calibration, default to: + +- Knowledge: **Working** +- Pressure: **Standard** + +## Phase 3: Build The Decision Map + +Create a private decision map while asking questions one at a time: + +- Goal - what success means. +- User or customer - who this affects. +- Constraints - time, money, stack, team, policy, risk. +- Options - obvious alternatives and why current option wins. +- Dependencies - what must be true first. +- Risks - what breaks, gets expensive, or becomes irreversible. +- Validation - how user will know it worked. +- Rollback - how to undo or recover. + +Do not dump the full map unless user asks. Use it to choose the next question. + +## Phase 4: Question Ladder + +Move through this ladder. Stop early if the plan becomes clear enough or user asks to stop. + +### 1. Goal Fit + +Questions: + +- What outcome matters most? +- What would make this not worth doing? +- What problem are we solving, and for whom? + +### 2. Constraint Reality + +Questions: + +- What hard constraint cannot move? +- What resource bottleneck decides the plan? +- What assumption would kill the plan if false? + +### 3. Option Pressure + +Questions: + +- What are the top two alternatives? +- Why this approach over the boring one? +- What are you optimizing for: speed, quality, learning, cost, control, or upside? + +### 4. Execution Path + +Questions: + +- What is the smallest useful version? +- What has to happen first? +- What can be deferred without harming the goal? + +### 5. Failure Modes + +Questions: + +- How does this fail in production or real use? +- What edge case would embarrass the plan? +- What part is hardest to observe once it breaks? + +### 6. Validation + +Questions: + +- What test, metric, screenshot, demo, or user behavior proves this works? +- What would you check before trusting it? +- What does done mean in observable terms? + +### 7. Reversibility + +Questions: + +- What decision here is hardest to undo? +- What backup, migration, rollback, or escape hatch exists? +- What should be logged as an ADR or explicit tradeoff? + +## Pressure Adaptation + +### If Knowledge Is New + +- Define one missing concept in 2-4 sentences before asking. +- Avoid jargon unless you define it. +- Ask fewer branching questions. +- Focus on goals, constraints, and first principles. +- Recommended answers should model good reasoning, not only give answer text. + +### If Knowledge Is Working + +- Ask normal tradeoff questions. +- Surface alternatives. +- Push for validation and smallest useful version. +- Challenge vague words like "simple", "scalable", "good", "clean", or "fast". + +### If Knowledge Is Expert + +- Skip basics. +- Ask sharper counterfactuals. +- Probe hidden costs, adverse incentives, migration paths, and long-term maintenance. +- Ask what evidence would change their mind. + +### If Pressure Is Light + +- Keep questions clarifying. +- Use supportive framing. +- Stop after top ambiguities are resolved. + +### If Pressure Is Standard + +- Challenge assumptions and tradeoffs. +- Keep moving until implementation path is concrete. + +### If Pressure Is Hard + +- Be direct. +- Name weak reasoning. +- Ask about unpleasant edge cases. +- Demand observable validation. +- Still ask one question at a time. + +## Recommended Answer Format + +Every question includes: + +```text +Question: ... +Recommended answer: ... +Why it matters: ... +``` + +Keep "Why it matters" to one sentence. + +## When To Stop + +Stop grilling when one of these is true: + +- User says stop. +- Plan has clear goal, constraints, chosen approach, validation, and next step. +- Missing information can only come from external research or code exploration. +- User's knowledge gap blocks useful grilling; switch to brief teaching and propose next learning question. + +End with: + +- Final decision or current best plan. +- Remaining open questions. +- Next concrete action. +- Risks to watch. diff --git a/home/.chezmoitemplates/skills/junior-to-senior/SKILL.md b/home/.chezmoitemplates/skills/junior-to-senior/SKILL.md new file mode 100644 index 0000000..630ec2e --- /dev/null +++ b/home/.chezmoitemplates/skills/junior-to-senior/SKILL.md @@ -0,0 +1,131 @@ +--- +name: junior-to-senior +description: Adversarial senior-engineer review for agent-generated plans, designs, and architectures. Treats the current output as junior work, constructs a senior reviewer whose domain expertise comes from live codebase research plus web research of current best practices, diagnoses altitude failures (too vague or too granular), then rewrites the plan into a scoped, state-of-the-art version. Use when the user says "junior to senior", "senior review", "review this like a staff engineer", when a plan feels hand-wavy or lost in details, or before committing to any agent-written plan. +--- + +# Junior to Senior + +Assume the plan in front of you was written by a capable junior: fluent, confident, and trained on the past. Build a senior reviewer that is grounded in two things the junior was not — **this codebase as it actually exists** and **the state of the art as it exists today** — and let the senior tear the plan down and rebuild it. + +This skill exists because agent-generated plans fail at two altitudes: + +- **Fog** — the plan describes the high level fine ("add caching", "handle auth", "make it scalable") but never commits on the hard parts. No interfaces, no data shapes, no failure handling, no named libraries. An engineer reading it still has to make every real decision themselves. +- **Tunnel** — the plan dives into function signatures and file diffs but has no product vision. No statement of who this is for, what success means, what is out of scope, or why this approach beats the boring alternative. It optimizes a local detail while the shape of the feature is still wrong. + +Both are altitude failures. The senior's job is to drag the plan to the right altitude *and* upgrade its substance past the model's training cutoff. + +## The cardinal rule + +**Every senior finding needs evidence.** A claim about the codebase cites a file and line. A claim about best practice cites a fetched source — official docs, release notes, an RFC, a postmortem — with a date. If web research is unavailable, the finding is labeled `[training-data, unverified]` so stale knowledge is never laundered as current truth. A senior who argues from vibes is just a louder junior. + +## Phase 0: Capture the junior artifact + +Identify exactly what is under review: + +- A plan the agent just produced in this conversation (the default — including your own output from a moment ago). +- A pasted plan, design doc, RFC, or issue description. +- A planning document in the repo the user points at. + +Freeze it. Quote or restate the artifact in full before reviewing so the review targets a fixed text, not a moving memory of it. If there is no artifact yet, say so and offer to either generate the junior draft first or review the user's existing idea — do not review thin air. + +## Phase 1: Construct the senior + +The senior is not a tone of voice. It is a reviewer profile built from research done *now*. Skipping this phase and going straight to critique produces generic review slop. + +### 1a. Extract the domains + +List the 2-5 load-bearing technical domains the plan touches (e.g., "Postgres schema migration", "React server components", "OAuth token refresh", "vector search at 10M rows"). For each, write one sentence on what a staff-level engineer in that domain would refuse to let slide. This list drives all research that follows. + +### 1b. Code research — what is true here + +Investigate the repository before judging the plan against it: + +- Existing conventions and architecture the plan must fit (or explicitly break, with justification). +- Actual versions in lockfiles/manifests — a plan recommending an API that the pinned version doesn't have is a blocker. +- Prior art: similar features already in the codebase, ADRs, migrations, test patterns. +- Real constraints the junior plan ignored: build system, deploy targets, performance budgets, existing data. + +Use a subagent (e.g. `Explore`) for broad sweeps so the review context stays clean. + +### 1c. Web research — what is true now + +For each load-bearing decision in the plan, search for the current state of the art. The junior's knowledge ends at a training cutoff; the senior's must not. Prioritize primary sources (official docs, changelogs, release notes, maintainer posts) and check dates. You are looking for three kinds of delta: + +- **Deprecations** — the plan's approach is now discouraged or removed. +- **Supersessions** — a newer pattern/library/API has clearly won since the cutoff. +- **Hard-won lessons** — published postmortems, benchmarks, or security advisories that change the tradeoff. + +Query patterns, source-quality ranking, and when to stop are in **[references/research-playbook.md](references/research-playbook.md)**. If web access is unavailable, proceed on code research alone and mark every best-practice claim `[training-data, unverified]`. + +### 1d. Isolation + +When the harness supports subagents, run the senior review in a context-isolated subagent that receives the frozen artifact and the research findings but *not* the reasoning that produced the junior plan. Self-review in the same context anchors on its own justifications; isolation is what makes the review adversarial rather than confirmatory. + +## Phase 2: Diagnose the altitude + +Before line-by-line critique, classify the artifact: **fog**, **tunnel**, or **mixed** (most real plans fog the hard parts and tunnel on the easy ones — flag each section separately). + +Fog test — for every component the plan names, can a competent engineer start tomorrow without making a product or architecture decision themselves? Tunnel test — does the plan state who this is for, what success looks like, what is explicitly out of scope, and why this approach beat the obvious alternative? + +The full diagnostic checklists, the vague-word blacklist ("simple", "scalable", "handle gracefully", "robust", ...), and severity definitions are in **[references/review-rubric.md](references/review-rubric.md)**. + +## Phase 3: Adversarial review + +The senior reviews the frozen artifact against three lenses: codebase reality (1b), current state of the art (1c), and altitude (Phase 2). Rules of engagement: + +- Every vague phrase gets challenged with the concrete question it is hiding from. +- Every named technology gets a version and a reason; every unnamed one ("a queue", "some cache") gets named or the choice gets flagged as an open decision. +- Every data shape that crosses a boundary gets written down. +- Every plan gets asked: what is the rollback, what is the migration, what breaks at 10x. +- Steelman before attacking: state the strongest version of the junior's choice, then show why it still loses (or concede that it wins — agreeing with the junior when the evidence supports it is a valid senior outcome, not a failure of the skill). + +Findings use three severities — **blocker** (plan fails as written), **major** (works but meaningfully worse than SOTA or misfit to the repo), **minor** (polish) — each with evidence and a concrete fix. Definitions and examples: **[references/review-rubric.md](references/review-rubric.md)**. + +## Phase 4: Promote the plan + +Critique without a rewrite is just complaining. Produce the senior version of the plan with this shape: + +1. **Goal and non-goals** — one paragraph of product intent; explicit out-of-scope list. +2. **Decisions** — each load-bearing choice with the chosen option, version, rationale, the strongest rejected alternative, and the evidence (file ref or source link). +3. **Design at the right altitude** — interfaces, data shapes, and failure handling for the hard parts; deliberately coarse strokes for the routine parts. +4. **Sequencing** — milestones with an observable verification step each ("done" must be checkable, not vibes). +5. **Risks and rollback** — what is hardest to undo and the escape hatch. +6. **Open questions for a human** — product decisions the senior is *not* allowed to invent. Scoping is the senior's job; product direction is not. + +## Output format + +Deliver two artifacts, review first: + +```markdown +## Senior Review + +**Altitude diagnosis:** fog | tunnel | mixed — one-sentence justification. + +### Blockers +- [B1] Finding — evidence (file:line or source+date) — fix. + +### Major +- [M1] ... + +### Minor +- [m1] ... + +### What the junior got right +- Credit where due; preserved in the rewrite. + +## Promoted Plan (v2) +[Phase 4 structure] + +## Delta summary +- 3-6 bullets: what changed from junior to senior and why. + +## Open questions for you +- Product decisions that need a human. +``` + +## Boundaries + +- The senior scopes and upgrades; it does **not** invent product direction. Genuine product choices go to "Open questions", not into the rewrite. +- Never silently replace the junior plan — the user sees the review, the rewrite, and the delta, and decides. +- If research contradicts the user's stated preference, present the evidence and defer; the user may have context the senior lacks. +- A review with zero blockers and zero majors is a legitimate result. Say "this plan holds" and stop — do not manufacture findings to look rigorous. diff --git a/home/.chezmoitemplates/skills/junior-to-senior/references/research-playbook.md b/home/.chezmoitemplates/skills/junior-to-senior/references/research-playbook.md new file mode 100644 index 0000000..d4db6b6 --- /dev/null +++ b/home/.chezmoitemplates/skills/junior-to-senior/references/research-playbook.md @@ -0,0 +1,59 @@ +# Senior Research Playbook + +Reference for Phase 1 of the `junior-to-senior` skill: how the senior earns expertise the junior doesn't have. Two tracks, run in this order — code research grounds the review in this repo; web research grounds it in the present. + +## Track 1: Code research + +Goal: know what is *true here* before judging the plan against it. Prefer a context-isolated explore subagent for broad sweeps; bring back conclusions, not file dumps. + +Checklist per plan: + +1. **Versions.** Read the lockfile/manifest (`package-lock.json`, `poetry.lock`, `go.mod`, `Cargo.lock`, ...) for every dependency the plan touches. Record exact pinned versions. Any plan step that assumes an API must be checked against the pinned version's docs, not the latest. +2. **Conventions.** Find 2-3 existing implementations of the closest analogous feature. How does this repo do routing, errors, config, tests, migrations? The plan either matches or justifies the break. +3. **Prior decisions.** Search for ADRs, `docs/`, design notes, and revealing commit messages on the touched paths. A plan that re-litigates a settled decision without knowing it was settled is a blocker. +4. **Real constraints.** Deploy target, build system, CI time budget, supported platforms, existing data volume and shape. These kill more plans than design taste does. +5. **Blast radius.** What actually imports/calls the things the plan changes? The junior plan's scope estimate is a guess; the dependency graph is a fact. + +## Track 2: Web research + +Goal: find where the world moved after the training cutoff. Research the plan's *load-bearing decisions* (from Phase 1a), not every line — typically 3-7 searches total, not 30. + +### Query patterns + +For each load-bearing decision, run the subset that applies: + +- ` changelog` / ` release notes ` — catch deprecations and new APIs since cutoff. +- ` vs ` — check whether the tradeoff has flipped. +- ` deprecated` / ` considered harmful` — find published reversals. +- ` best practices ` — only useful when followed to a primary source; the query itself attracts SEO spam, so treat listicle results as pointers, not evidence. +- ` security advisory` / check the project's GitHub security tab — non-negotiable for auth, crypto, parsing, and anything touching user input. +- `site:github.com issues ` — maintainer-stated direction and known footguns. + +Always pin the current year or "latest" into queries — undated queries return the same era the model was trained on, which defeats the point. + +### Source quality ranking + +1. Official documentation, changelogs, release notes, RFCs — cite freely. +2. Maintainer blog posts, GitHub issues/discussions where maintainers state direction. +3. Engineering postmortems and benchmarks from teams that ran the thing in production (with dates and numbers). +4. Conference talks, well-known practitioner blogs — corroborate before citing for a blocker. +5. SEO listicles, AI-generated tutorials, undated content — never evidence, at most a pointer to a real source. + +Every cited source gets a date check. A "best practices" article from before the relevant major version is training-data-era knowledge wearing a URL. + +### What you are looking for + +- **Deprecations** — the plan's approach is discouraged or removed in current versions. +- **Supersessions** — a newer pattern/API has clearly won (look for the old way's own docs pointing at the new way — that's the strongest signal). +- **Hard-won lessons** — advisories, postmortems, benchmarks that change the tradeoff the junior made on priors. +- **Confirmations** — the junior's choice still holds. Record these too; they go in "What the junior got right". + +### When to stop + +- Each load-bearing decision has either one primary source confirming/refuting it, or two independent secondary sources agreeing. +- Two consecutive searches on a decision return nothing newer than what you knew — the world likely didn't move; mark it confirmed-by-absence and move on. +- Diminishing returns: research budget belongs on blockers and majors, not minors. If a finding would be minor either way, don't research it. + +### No web access + +Run Track 1 fully, skip Track 2, and tag every best-practice claim in the review `[training-data, unverified]`. Tell the user the review is grounded in the codebase but not refreshed against current SOTA, and name the decisions most likely to have shifted so they can spot-check. diff --git a/home/.chezmoitemplates/skills/junior-to-senior/references/review-rubric.md b/home/.chezmoitemplates/skills/junior-to-senior/references/review-rubric.md new file mode 100644 index 0000000..98fb684 --- /dev/null +++ b/home/.chezmoitemplates/skills/junior-to-senior/references/review-rubric.md @@ -0,0 +1,67 @@ +# Senior Review Rubric + +Reference for Phase 2 (altitude diagnosis) and Phase 3 (adversarial review) of the `junior-to-senior` skill. + +## Altitude diagnostics + +Classify each section of the artifact independently. A plan is usually **mixed**: fogged on the hard parts, tunneled on the easy ones — because the junior wrote detail where it was comfortable and abstraction where it was not. That inversion (detail on easy parts, fog on hard parts) is itself a finding. + +### Fog tests (too vague) + +Run these against every component, step, or workstream the plan names. Each "no" is a finding. + +1. **Start-tomorrow test** — could a competent engineer begin this item tomorrow without making an architecture or product decision themselves? If they'd have to choose a library, design a schema, or define an API first, the plan didn't plan it. +2. **Interface test** — does anything that crosses a boundary (function, service, queue, file, network) have its shape written down? Names of fields, not "the relevant data". +3. **Failure test** — for each external interaction (network, disk, user input, third-party API), does the plan say what happens when it fails? "Handle errors" is not an answer. +4. **Quantity test** — are load-bearing quantities stated? Expected row counts, payload sizes, request rates, latency budgets. "Should be fast" is fog. +5. **Named-technology test** — is every "a cache / a queue / some auth layer" either named (with version) or explicitly listed as an open decision with the candidates? + +### Tunnel tests (too granular / missing vision) + +Run these against the artifact as a whole. Each "no" is a finding. + +1. **Audience test** — does the plan say who this is for and what they can do afterward that they couldn't before? +2. **Success test** — is there an observable definition of success? A metric, a demo, a passing test suite, a user behavior — something checkable. +3. **Non-goals test** — is anything explicitly out of scope? A plan with no non-goals has not been scoped, only described. +4. **Alternative test** — does the plan say why this approach beat the obvious boring alternative? If no alternative was considered, the choice was a default, not a decision. +5. **Sequencing test** — is there an ordering with a smallest useful version first, or is it a flat list of equally-weighted tasks? +6. **Proportionality test** — does the detail land where the risk is? Twenty lines on a helper function and one line on the data migration means the plan is upside down. + +## Vague-word blacklist + +When these appear without immediate quantification or specification, challenge them with the concrete question they are hiding from: + +| Word | Hidden question | +|---|---| +| simple / straightforward | Simple compared to what? What did you not have to handle? | +| scalable | To what number, on what axis, measured how? | +| robust / resilient | Against which specific failures? What is the recovery path? | +| handle gracefully | What exactly happens? Retry, drop, queue, surface to user? | +| performant / fast | What latency/throughput budget, at what percentile? | +| secure | Against which threat model? Who is the attacker? | +| flexible / extensible | For which anticipated change? Flexibility has a cost — who pays it? | +| later / eventually / for now | Is this a sequencing decision or an unowned risk? Who reopens it, triggered by what? | +| etc. / and so on | The list was the work. Finish it. | +| appropriate / as needed | By whose judgment, applied when? | +| leverage / utilize | Usually decorating an undecided choice. Name the thing. | + +## Severity definitions + +- **Blocker** — the plan fails as written. Examples: targets an API the pinned dependency version doesn't have; contradicts an existing architectural decision in the repo without acknowledging it; omits a data migration that the change requires; relies on a pattern that has a published security advisory against it; an entire hard component is fog (fails the start-tomorrow test). +- **Major** — the plan works but is meaningfully worse than the current state of the art or misfit to this repo. Examples: hand-rolls something a maintained, already-installed dependency provides; uses a pattern superseded since the training cutoff (with source); detail is inverted (proportionality failure); no rollback story for a hard-to-reverse step; success criteria exist but aren't observable. +- **Minor** — polish. Naming, doc gaps, small idiom mismatches with the surrounding codebase, ordering tweaks that reduce risk but don't change the outcome. + +Calibration rules: + +- Severity reflects consequence, not effort-to-fix. A one-line version bump can be a blocker. +- Every finding carries evidence (file:line, or source + date) and a concrete fix. A finding without a fix is a question — put it in "Open questions" instead. +- Do not inflate. Three real blockers reads as a serious review; ten padded ones reads as noise and gets ignored. +- Track "what the junior got right" with the same care as faults. The rewrite must preserve it, and the user needs to see the review is calibrated, not performatively hostile. + +## Adversarial discipline + +- **Steelman first.** Before attacking a choice, state the strongest case for it in one or two sentences. If you cannot, you do not understand it well enough to reject it. +- **Attack the artifact, not the author.** Findings name the text's failure, not the agent's. +- **Concede when beaten.** If research validates the junior's choice, say so and move on. An adversarial review that cannot return "this holds" is a ritual, not a review. +- **One altitude per finding.** Do not bundle "this is vague" with "this library is outdated" — they have different fixes. +- **No invented requirements.** If the review wants a constraint the user never stated (e.g., "must support 1M users"), that is an open question for the human, not a finding. diff --git a/home/Library/Application Support/Code/User/settings.json b/home/Library/Application Support/Code/User/settings.json index c6ba82d..8dddcc7 100644 --- a/home/Library/Application Support/Code/User/settings.json +++ b/home/Library/Application Support/Code/User/settings.json @@ -48,6 +48,10 @@ "github.copilot.editor.enableCodeActions": true, "github.copilot.nextEditSuggestions.enabled": true, "github.copilot.renameSuggestions.triggerAutomatically": true, + "chat.instructionsFilesLocations": { + ".github/instructions": true, + "/Users/vscode/repos/instructions": true + }, "github.copilot.chat.commitMessageGeneration.instructions": [ { "text": "Use conventional commit message format." diff --git a/home/dot_claude/settings.json b/home/dot_claude/settings.json index 58a6cfa..00d0b1b 100644 --- a/home/dot_claude/settings.json +++ b/home/dot_claude/settings.json @@ -1,14 +1,12 @@ { "$schema": "https://json.schemastore.org/claude-code-settings.json", - "model": "opusplan[1m]", + "model": "best", "cleanupPeriodDays": 365, "env": { "BASH_DEFAULT_TIMEOUT_MS": "300000", "BASH_MAX_TIMEOUT_MS": "600000", "CLAUDE_CODE_AUTO_COMPACT_WINDOW": "400000", - "CLAUDE_CODE_EFFORT_LEVEL": "auto", "CLAUDE_CODE_DISABLE_FEEDBACK_SURVEY": "1", - "CLAUDE_CODE_SUBAGENT_MODEL": "inherit", "CLAUDE_CODE_ATTRIBUTION_HEADER": "0", "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1", "CLAUDE_CODE_MAX_OUTPUT_TOKENS": "64000", @@ -17,7 +15,6 @@ "DISABLE_BUG_COMMAND": "1", "DISABLE_ERROR_REPORTING": "1", "DISABLE_NON_ESSENTIAL_MODEL_CALLS": "1", - "DISABLE_TELEMETRY": "1", "ENABLE_PROMPT_CACHING_1H": "1", "MAX_MCP_OUTPUT_TOKENS": "40000" }, @@ -160,32 +157,6 @@ ], "defaultMode": "acceptEdits" }, - "hooks": { - "SessionStart": [ - { - "hooks": [ - { - "type": "command", - "command": "node $HOME/.claude/hooks/caveman-activate.js", - "timeout": 5, - "statusMessage": "Loading caveman mode..." - } - ] - } - ], - "UserPromptSubmit": [ - { - "hooks": [ - { - "type": "command", - "command": "node $HOME/.claude/hooks/caveman-mode-tracker.js", - "timeout": 5, - "statusMessage": "Tracking caveman mode..." - } - ] - } - ] - }, "alwaysThinkingEnabled": true, "autoScrollEnabled": true, "autoUpdatesChannel": "latest", diff --git a/home/dot_config/nvim/.gitignore b/home/dot_config/nvim/.gitignore deleted file mode 100644 index cc5457a..0000000 --- a/home/dot_config/nvim/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -tt.* -.tests -doc/tags -debug -.repro -foo.* -*.log -data diff --git a/home/dot_config/nvim/.neoconf.json b/home/dot_config/nvim/.neoconf.json deleted file mode 100644 index 7c48087..0000000 --- a/home/dot_config/nvim/.neoconf.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "neodev": { - "library": { - "enabled": true, - "plugins": true - } - }, - "neoconf": { - "plugins": { - "lua_ls": { - "enabled": true - } - } - } -} diff --git a/home/dot_config/nvim/LICENSE b/home/dot_config/nvim/LICENSE deleted file mode 100644 index 261eeb9..0000000 --- a/home/dot_config/nvim/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/home/dot_config/nvim/README.md b/home/dot_config/nvim/README.md deleted file mode 100644 index 185280b..0000000 --- a/home/dot_config/nvim/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# 💤 LazyVim - -A starter template for [LazyVim](https://github.com/LazyVim/LazyVim). -Refer to the [documentation](https://lazyvim.github.io/installation) to get started. diff --git a/home/dot_config/nvim/init.lua b/home/dot_config/nvim/init.lua deleted file mode 100644 index 2514f9e..0000000 --- a/home/dot_config/nvim/init.lua +++ /dev/null @@ -1,2 +0,0 @@ --- bootstrap lazy.nvim, LazyVim and your plugins -require("config.lazy") diff --git a/home/dot_config/nvim/lua/config/autocmds.lua b/home/dot_config/nvim/lua/config/autocmds.lua deleted file mode 100644 index 4221e75..0000000 --- a/home/dot_config/nvim/lua/config/autocmds.lua +++ /dev/null @@ -1,8 +0,0 @@ --- Autocmds are automatically loaded on the VeryLazy event --- Default autocmds that are always set: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/config/autocmds.lua --- --- Add any additional autocmds here --- with `vim.api.nvim_create_autocmd` --- --- Or remove existing autocmds by their group name (which is prefixed with `lazyvim_` for the defaults) --- e.g. vim.api.nvim_del_augroup_by_name("lazyvim_wrap_spell") diff --git a/home/dot_config/nvim/lua/config/keymaps.lua b/home/dot_config/nvim/lua/config/keymaps.lua deleted file mode 100644 index 2c134f7..0000000 --- a/home/dot_config/nvim/lua/config/keymaps.lua +++ /dev/null @@ -1,3 +0,0 @@ --- Keymaps are automatically loaded on the VeryLazy event --- Default keymaps that are always set: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/config/keymaps.lua --- Add any additional keymaps here diff --git a/home/dot_config/nvim/lua/config/lazy.lua b/home/dot_config/nvim/lua/config/lazy.lua deleted file mode 100644 index aee4a23..0000000 --- a/home/dot_config/nvim/lua/config/lazy.lua +++ /dev/null @@ -1,56 +0,0 @@ -local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim" -if not (vim.uv or vim.loop).fs_stat(lazypath) then - local lazyrepo = "https://github.com/folke/lazy.nvim.git" - local out = vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath }) - if vim.v.shell_error ~= 0 then - vim.api.nvim_echo({ - { "Failed to clone lazy.nvim:\n", "ErrorMsg" }, - { out, "WarningMsg" }, - { "\nPress any key to exit..." }, - }, true, {}) - vim.fn.getchar() - os.exit(1) - end -end -vim.opt.rtp:prepend(lazypath) - -require("lazy").setup({ - spec = { - -- add LazyVim and import its plugins - { "LazyVim/LazyVim", import = "lazyvim.plugins" }, - -- import/override with your plugins - { import = "plugins" }, - }, - defaults = { - -- By default, only LazyVim plugins will be lazy-loaded. Your custom plugins will load during startup. - -- If you know what you're doing, you can set this to `true` to have all your custom plugins lazy-loaded by default. - lazy = false, - -- It's recommended to leave version=false for now, since a lot the plugin that support versioning, - -- have outdated releases, which may break your Neovim install. - version = false, -- always use the latest git commit - -- version = "*", -- try installing the latest stable version for plugins that support semver - }, - install = { colorscheme = { "tokyonight", "habamax" } }, - checker = { - enabled = true, -- check for plugin updates periodically - notify = false, -- notify on update - }, -- automatically check for plugin updates - rocks = { - enabled = false, - }, - performance = { - rtp = { - -- disable some rtp plugins - disabled_plugins = { - "gzip", - -- "matchit", - -- "matchparen", - -- "netrwPlugin", - "tarPlugin", - "tohtml", - "tutor", - "zipPlugin", - }, - }, - }, -}) diff --git a/home/dot_config/nvim/lua/config/options.lua b/home/dot_config/nvim/lua/config/options.lua deleted file mode 100644 index 31e385c..0000000 --- a/home/dot_config/nvim/lua/config/options.lua +++ /dev/null @@ -1,6 +0,0 @@ --- Options are automatically loaded before lazy.nvim startup --- Default options that are always set: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/config/options.lua --- Add any additional options here - -vim.opt.clipboard = "unnamedplus" -vim.o.autoread = true diff --git a/home/dot_config/nvim/lua/plugins/opencode.lua b/home/dot_config/nvim/lua/plugins/opencode.lua deleted file mode 100644 index 343df9f..0000000 --- a/home/dot_config/nvim/lua/plugins/opencode.lua +++ /dev/null @@ -1,74 +0,0 @@ -return { - { - "nickjvandyke/opencode.nvim", - version = "*", - dependencies = { - { - "folke/snacks.nvim", - optional = true, - opts = { - input = {}, - picker = { - actions = { - opencode_send = function(...) - return require("opencode").snacks_picker_send(...) - end, - }, - win = { - input = { - keys = { - [""] = { "opencode_send", mode = { "n", "i" } }, - }, - }, - }, - }, - }, - }, - }, - keys = { - { - "oa", - function() - require("opencode").ask("@this: ", { submit = true }) - end, - mode = { "n", "x" }, - desc = "Ask OpenCode about this", - }, - { - "os", - function() - require("opencode").select() - end, - mode = { "n", "x" }, - desc = "Select OpenCode action", - }, - { - "oS", - function() - require("opencode").select_server() - end, - desc = "Select OpenCode server", - }, - { - "on", - function() - require("opencode").command("session.new") - end, - desc = "New OpenCode session", - }, - }, - config = function() - vim.g.opencode_opts = { - server = { - port = nil, - start = false, - stop = false, - toggle = false, - }, - select = { - server = false, - }, - } - end, - }, -} diff --git a/home/dot_config/nvim/lua/plugins/treesitter.lua b/home/dot_config/nvim/lua/plugins/treesitter.lua deleted file mode 100644 index 6f943be..0000000 --- a/home/dot_config/nvim/lua/plugins/treesitter.lua +++ /dev/null @@ -1,16 +0,0 @@ -return { - { - "nvim-treesitter/nvim-treesitter", - opts = function(_, opts) - vim.list_extend(opts.ensure_installed, { - "css", - "latex", - "norg", - "scss", - "svelte", - "typst", - "vue", - }) - end, - }, -} diff --git a/home/dot_config/nvim/stylua.toml b/home/dot_config/nvim/stylua.toml deleted file mode 100644 index 0f90030..0000000 --- a/home/dot_config/nvim/stylua.toml +++ /dev/null @@ -1,3 +0,0 @@ -indent_type = "Spaces" -indent_width = 2 -column_width = 120 diff --git a/home/dot_config/opencode/AGENTS.md.tmpl b/home/dot_config/opencode/AGENTS.md.tmpl deleted file mode 100644 index 1900c86..0000000 --- a/home/dot_config/opencode/AGENTS.md.tmpl +++ /dev/null @@ -1 +0,0 @@ -{{ template "AGENTS.md" . -}} diff --git a/home/dot_config/opencode/opencode.jsonc b/home/dot_config/opencode/opencode.jsonc deleted file mode 100644 index 328a929..0000000 --- a/home/dot_config/opencode/opencode.jsonc +++ /dev/null @@ -1,232 +0,0 @@ -{ - "$schema": "https://opencode.ai/config.json", - "model": "openai/gpt-5.5", - "autoupdate": true, - "instructions": ["{env:HOME}/.config/opencode/AGENTS.md"], - "lsp": true, - "agent": { - "build": { - "model": "openai/gpt-5.5", - "variant": "xhigh", - }, - "plan": { - "model": "openai/gpt-5.5", - "variant": "xhigh", - }, - }, - "plugin": [ - "@tarquinen/opencode-dcp", - "@franlol/opencode-md-table-formatter", - "@mohak34/opencode-notifier", - "opencode-wakatime", - ], - "mcp": { - "grep": { - "type": "remote", - "url": "https://mcp.grep.app", - "enabled": true, - }, - }, - - "permission": { - "webfetch": "allow", - "websearch": "allow", - "doom_loop": "ask", - "read": { - "*": "allow", - "*.env": "deny", - "*.env.*": "deny", - "*.dev.vars": "deny", - "*.env.example": "allow", - }, - "edit": { - "*": "allow", - }, - "external_directory": { - "*": "ask", - "~/Documents/github/**": "allow", - "/tmp/**": "allow", - "/private/tmp/**": "allow", - }, - "bash": { - "*": "allow", - "opencode": "allow", - // Git - "git status *": "allow", - "git diff *": "allow", - "git log *": "allow", - "git branch *": "allow", - "git show *": "allow", - "git stash list *": "allow", - "git remote *": "allow", - "git fetch *": "allow", - "git add *": "allow", - "git commit *": "allow", - "git checkout *": "allow", - "git switch *": "allow", - "git merge *": "allow", - "git clone *": "allow", - "git pull *": "allow", - "git init *": "allow", - "git worktree *": "allow", - "git rebase *": "allow", - "git push *": "allow", - "git push --force*": "deny", - "git push * --force*": "deny", - "git push -f": "deny", - "git push -f *": "deny", - "git push * -f": "deny", - "git push * -f *": "deny", - "git reset --soft *": "allow", - "git reset --hard": "deny", - "git reset --hard *": "deny", - "ls *": "allow", - "cat *": "allow", - "head *": "allow", - "tail *": "allow", - "find *": "allow", - "rg *": "allow", - "grep *": "allow", - // Text processing - "sort *": "allow", - "uniq *": "allow", - "wc *": "allow", - "cut *": "allow", - "awk *": "allow", - "sed *": "allow", - "tr *": "allow", - "xargs *": "allow", - "tee *": "allow", - "diff *": "allow", - "comm *": "allow", - "paste *": "allow", - "column *": "allow", - // System info - "which *": "allow", - "type *": "allow", - "file *": "allow", - "stat *": "allow", - "du *": "allow", - "df *": "allow", - "env": "allow", - "printenv *": "allow", - "uname *": "allow", - "hostname": "allow", - "whoami": "allow", - "id": "allow", - "date *": "allow", - "pwd": "allow", - "echo *": "allow", - "printf *": "allow", - "mkdir *": "allow", - "cp *": "allow", - "mv *": "allow", - "rm *": "ask", - "rm -rf *": "deny", - "pnpm *": "allow", - "npm *": "allow", - "npx *": "allow", - "node *": "allow", - "bun *": "allow", - "gh pr list *": "allow", - "gh pr view *": "allow", - "gh pr diff *": "allow", - "gh pr checks *": "allow", - "gh pr status *": "allow", - "gh issue list *": "allow", - "gh issue view *": "allow", - "gh issue status *": "allow", - "gh issue edit *": "allow", - "gh issue comment *": "allow", - "gh repo view *": "allow", - "gh repo clone *": "allow", - "gh api *": "allow", - "gh search *": "allow", - "gh status": "allow", - "gh status *": "allow", - "gh auth status": "allow", - "gh auth refresh *": "ask", - // GitHub Projects - "gh project list *": "allow", - "gh project view *": "allow", - "gh project field-list *": "allow", - "gh project item-list *": "allow", - "gh project create *": "allow", - "gh project link *": "allow", - "gh project field-create *": "allow", - "gh project item-add *": "allow", - "gh project item-edit *": "allow", - "gh project edit *": "ask", - "gh project close *": "ask", - "gh project unlink *": "ask", - "gh project item-archive *": "ask", - "gh project item-delete *": "ask", - "gh project field-delete *": "ask", - "gh project delete *": "deny", - // GitHub Actions (read-only) - "gh run list *": "allow", - "gh run view *": "allow", - "gh run watch *": "allow", - "gh workflow list *": "allow", - "gh workflow view *": "allow", - "gh cache list *": "allow", - // Releases, gists, labels (read-only) - "gh release list *": "allow", - "gh release view *": "allow", - "gh gist list *": "allow", - "gh gist view *": "allow", - "gh label list *": "allow", - "gh label create *": "allow", - "gh label edit *": "allow", - "gh label delete *": "ask", - "gh ruleset list *": "allow", - "gh ruleset view *": "allow", - // Write operations - "gh pr create *": "allow", - "gh pr merge *": "ask", - "gh pr close *": "ask", - "gh issue create *": "allow", - "gh issue close *": "ask", - "gh run cancel *": "ask", - "gh run rerun *": "ask", - "gh release create *": "ask", - "gh release delete *": "deny", - "lsof *": "allow", - "kill *": "ask", - "wrangler *": "allow", - // Additional commands based on install.sh CLI utilities - "jq *": "allow", - "bat *": "allow", - "fd *": "allow", - "fzf *": "allow", - "delta *": "allow", - "htop *": "allow", - "tree *": "allow", - "curl *": "allow", - "wget *": "allow", - "go *": "allow", - "rustc *": "allow", - "cargo *": "allow", - "python *": "allow", - "python3 *": "allow", - "pip *": "allow", - "pip3 *": "allow", - "pipx *": "allow", - "uv *": "allow", - "ruff *": "allow", - "mise *": "allow", - "cmake *": "allow", - "shellcheck *": "allow", - "jj *": "allow", - // Docker - "docker *": "allow", - "docker-compose *": "allow", - // macOS utilities - "brew *": "allow", - "open *": "allow", - "pbcopy": "allow", - "pbpaste": "allow", - "source *": "allow", - }, - }, -} diff --git a/home/dot_config/opencode/tui.json b/home/dot_config/opencode/tui.json deleted file mode 100644 index 91eaddb..0000000 --- a/home/dot_config/opencode/tui.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "$schema": "https://opencode.ai/tui.json", - "theme": "catppuccin-macchiato", - "scroll_speed": 3, - "scroll_acceleration": { - "enabled": true - }, - "diff_style": "auto", - "mouse": true -} diff --git a/home/dot_apm/dot_apm/agents/code-reviewer.agent.md b/home/dot_copilot/agents/code-reviewer.agent.md similarity index 100% rename from home/dot_apm/dot_apm/agents/code-reviewer.agent.md rename to home/dot_copilot/agents/code-reviewer.agent.md diff --git a/home/dot_apm/dot_apm/agents/research.agent.md b/home/dot_copilot/agents/research.agent.md similarity index 100% rename from home/dot_apm/dot_apm/agents/research.agent.md rename to home/dot_copilot/agents/research.agent.md diff --git a/home/dot_copilot/mcp-config.json b/home/dot_copilot/mcp-config.json new file mode 100644 index 0000000..da39e4f --- /dev/null +++ b/home/dot_copilot/mcp-config.json @@ -0,0 +1,3 @@ +{ + "mcpServers": {} +} diff --git a/home/dot_apm/dot_apm/skills/deep-analysis/SKILL.md b/home/dot_copilot/skills/deep-analysis/SKILL.md similarity index 100% rename from home/dot_apm/dot_apm/skills/deep-analysis/SKILL.md rename to home/dot_copilot/skills/deep-analysis/SKILL.md diff --git a/home/dot_apm/dot_apm/skills/deep-analysis/references/research-summary-template.md b/home/dot_copilot/skills/deep-analysis/references/research-summary-template.md similarity index 100% rename from home/dot_apm/dot_apm/skills/deep-analysis/references/research-summary-template.md rename to home/dot_copilot/skills/deep-analysis/references/research-summary-template.md diff --git a/home/dot_apm/dot_apm/skills/deep-analysis/references/rubric.md b/home/dot_copilot/skills/deep-analysis/references/rubric.md similarity index 100% rename from home/dot_apm/dot_apm/skills/deep-analysis/references/rubric.md rename to home/dot_copilot/skills/deep-analysis/references/rubric.md From 7e390c6ed840029bff374db48ec0ee124541e485 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 18:58:37 -0500 Subject: [PATCH 08/51] docs: DOT-43 add apm-to-marketplace migration spec --- ...-to-claude-marketplace-migration-design.md | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md diff --git a/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md b/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md new file mode 100644 index 0000000..89f67c8 --- /dev/null +++ b/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md @@ -0,0 +1,128 @@ +# APM to Claude Marketplace Migration Design + +**Status:** Approved for implementation. + +**Issue:** [DOT-43](https://linear.app/edwinhern/issue/DOT-43/migrate-personal-ai-setup-from-apm-to-claude-plugin-marketplaces) + +## Goal + +Make the dotfiles repo the single source of truth for personal AI tooling. Remove APM entirely. Deliver Claude skills and plugins through official-first plugin marketplaces, share skills and instructions across Claude Code and GitHub Copilot from one source, and organize work-only Copilot tooling without wiring it yet. + +## Background + +APM currently does three jobs: it fetches skill and plugin source from git, pins versions, and wires runtime into `~/.claude`. Its partial caveman hook install caused broken hook paths. APM was also chosen to keep instructions agnostic across agents, which native `AGENTS.md` plus skills now cover. Personal use is Claude; work use is Copilot in VS Code. + +## Scope + +- Remove APM: source files, install script, lockfiles, runtime, and stale hook staging. +- Install Claude plugins from four marketplaces, official-first. +- Keep one canonical skills source and copy it into both `~/.claude/skills` and `~/.copilot/skills`. +- Keep one canonical `AGENTS.md` as universal behavioral instructions for both agents. +- Convert frontend-scoped guidance into skills (typescript, react, testing). +- Organize work Copilot MCP and the work skill, leaving Copilot wiring for later. +- Standardize on the `gh` CLI and remove GitHub MCP references. + +## Plugin Marketplace Design + +Personal Claude skills and plugins come from four marketplaces, preferring the Anthropic-vetted official marketplace where it carries the plugin. + +| Plugin | Marketplace | Source | +| --- | --- | --- | +| superpowers, tavily, context7, skill-creator, linear | `claude-plugins-official` | anthropics/claude-plugins-official | +| caveman | `caveman` | JuliusBrussee/caveman | +| humanizer | `humanizer` | blader/humanizer | +| kaizen | `kaizen` | imadAttar/kaizen | + +`linear` replaces the prior `schpet/linear-cli`. The official `linear` plugin uses Linear's MCP rather than the schpet CLI, an accepted behavior change. + +Declare marketplaces in `home/dot_claude/settings.json` under `extraKnownMarketplaces` and enabled plugins under `enabledPlugins`. A `run_onchange_06_install-claude-plugins.sh.tmpl` script runs `claude plugin marketplace add` then `claude plugin install @` idempotently, driven by a data list so the set stays declarative. This script replaces `run_onchange_06_install-apm.sh.tmpl`. + +Caveman installs as a real plugin, so Claude Code loads its hooks from the plugin directory. The manual caveman and superpowers hook entries are already removed from `settings.json`. + +## Skills Design + +Canonical skills live under `home/.chezmoitemplates/skills/`: + +- Vendored, no upstream marketplace: `grill-me`, `junior-to-senior` (already copied in). +- Frontend-scoped domain skills authored from the existing instruction files: `typescript`, `react`, `testing` (React Testing Library). + +`run_onchange_09_install-ai-skills.sh.tmpl` copies each skill directory into both `~/.claude/skills/` and `~/.copilot/skills/`. The script copies rather than symlinks because Claude Code ignores symlinked skill directories. The script re-runs when skill content changes because its rendered hash changes. + +Skill frontmatter compatibility between Claude `SKILL.md` and the Copilot skill format is verified during implementation. If the two formats diverge, the fan-out renders the agent-specific frontmatter while sharing the skill body. + +## Instructions Design + +One canonical `home/.chezmoitemplates/AGENTS.md` holds universal behavioral guidance only: AI guidance, git and pull request workflow, commit message rules, and the `gh` CLI standard. It does not assert a frontend persona, so the agent does not assume a single role. Frontend specifics live in the domain skills instead. + +`AGENTS.md` renders to: + +- `~/.claude/CLAUDE.md` for Claude Code. +- The Copilot `AGENTS.md` or user instructions path for VS Code Copilot. + +GitHub MCP references and the `context7` tool mention are removed from the meta rules content. GitHub operations use `gh`. The `coding-standards` and TypeScript specifics fold into the `typescript` skill. + +## Commit Message Instructions + +The VS Code Source Control commit generation reads `github.copilot.chat.commitMessageGeneration.instructions` in `home/Library/Application Support/Code/User/settings.json`. That file stays a plain managed JSON file, not a chezmoi template, to keep JSON schema IntelliSense and hover help in the source. + +The two generic commit entries are replaced with the `commit-guide` rules as inline `{ "text": ... }` entries: conventional commit format, imperative mood, subject under 50 characters, optional one-line body, no test plans. The workspace-relative `{ "file": ... }` form is not used because it has no global equivalent. The small duplication with the `AGENTS.md` git section is accepted to preserve IntelliSense. + +This shapes only the generated message, which is reviewed in Source Control. It does not automate committing or pushing. + +## Work and Copilot Design + +Work Copilot tooling is organized now and wired later. + +``` +home/Library/Application Support/Code/User/mcp.json # work MCP: figma, jira +home/dot_copilot/skills/deep-analysis/ # work skill, uses jira MCP +home/dot_copilot/agents/ # code-reviewer, ts-reviewer, research +home/dot_copilot/mcp-config.json # Copilot CLI MCP, existing +``` + +`deep-analysis` leaves the personal Claude set. The custom agents move to the Copilot side because they are frontend and work focused. Personal Claude keeps no custom agents and relies on plugin-provided agents such as the caveman reviewer. Copilot wiring beyond file placement is out of scope. + +## APM Teardown + +Remove: + +- `home/.chezmoidata/apm.yaml` +- `home/dot_apm/` +- `home/.chezmoitemplates/apm/` +- `home/.chezmoitemplates/lib/install/apm.sh` +- `home/.chezmoiscripts/darwin/run_onchange_06_install-apm.sh.tmpl` +- The APM entry in `mise.toml` +- Runtime `~/.apm` +- Stale `~/.claude/apm-hooks.json` + +The already-removed caveman hook and reassert scripts on the current branch fold into this teardown. Update or remove the APM bats tests. + +## Components Kept As Is + +- The graphify install script `run_onchange_08_install-graphify-skills.sh.tmpl`. + +The manual caveman and superpowers hook entries are already removed from `settings.json` and are not reintroduced, because the caveman plugin supplies its hooks. + +## Testing and Verification + +- `chezmoi apply` on a clean state succeeds. +- `claude plugin list` shows the enabled plugins from the four marketplaces. +- Skills resolve in both `~/.claude/skills` and `~/.copilot/skills`. +- The VS Code commit button produces a message in the configured style. +- A new session `/doctor` reports no hook or settings errors. +- `command -v apm` returns nothing and `~/.apm` is absent. +- The bats suite passes, including updated template tests for the new install scripts. + +## Implementation Phases + +1. Stand up the four marketplaces and the skills source plus fan-out script, so Claude never loses skills. +2. Author `AGENTS.md` universal instructions and wire the Copilot commit instructions. +3. Organize the work and Copilot files. +4. Remove APM and update tests. + +## Out of Scope + +- Wiring work Copilot MCP authentication or enabling the work skill at runtime. +- Migrating work to Claude. +- Adding a secret backend. +- Verifying live Figma or Jira MCP authentication. From 102c815f7fd05dcd75869cbae1c4e000318b7872 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 19:31:21 -0500 Subject: [PATCH 09/51] docs: DOT-43 revise spec for skills.sh cross-agent model --- ...-to-claude-marketplace-migration-design.md | 191 +++++++++++++----- home/.chezmoidata/packages.yaml | 1 + 2 files changed, 142 insertions(+), 50 deletions(-) diff --git a/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md b/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md index 89f67c8..11f8a76 100644 --- a/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md +++ b/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md @@ -1,4 +1,4 @@ -# APM to Claude Marketplace Migration Design +# APM to Cross-Agent AI Tooling Migration Design **Status:** Approved for implementation. @@ -6,81 +6,166 @@ ## Goal -Make the dotfiles repo the single source of truth for personal AI tooling. Remove APM entirely. Deliver Claude skills and plugins through official-first plugin marketplaces, share skills and instructions across Claude Code and GitHub Copilot from one source, and organize work-only Copilot tooling without wiring it yet. +Make the dotfiles repo the single source of truth for AI tooling across Claude Code and GitHub Copilot. Remove APM. Deliver skills through the cross-agent `skills` CLI (skills.sh), reserve Claude plugins for hook-bearing bundles, and apply MCP servers to every agent. Drive all of it from group and agent scoped data, the way APM targets worked. ## Background -APM currently does three jobs: it fetches skill and plugin source from git, pins versions, and wires runtime into `~/.claude`. Its partial caveman hook install caused broken hook paths. APM was also chosen to keep instructions agnostic across agents, which native `AGENTS.md` plus skills now cover. Personal use is Claude; work use is Copilot in VS Code. +APM did three jobs: fetch skill and plugin source, pin versions, and wire runtime into `~/.claude`. Its partial caveman hook install caused broken hook paths. APM was also chosen to keep instructions agnostic across agents. The `skills` CLI now installs one skill into multiple agents from a single command, native `AGENTS.md` covers cross-agent instructions, and chezmoi already renders per group. Personal use is Claude Code; work use is Copilot in VS Code. + +## Delivery Model + +The deciding question for each artifact is: does it need a hook, statusline, or slash command on Claude? + +| Artifact | Definition | Delivery | Cross-agent | +| --- | --- | --- | --- | +| Skill | Instructions or knowledge, no runtime | `npx skills add` | One command, both agents | +| Plugin | Bundle with hooks, statusline, slash commands, or packaged agents that only Claude loads | `claude plugin` | Claude only; Copilot uses skills or rules | +| MCP server | A tool or data backend spoken over MCP | Per-agent MCP config | Write each agent's config | + +Plugins are reserved for hook-bearing bundles. Everything that is only a skill goes through the `skills` CLI to the targeted agents. MCP servers are written into each targeted agent's config. + +## Group and Agent Data Model + +A new `home/.chezmoidata/ai.yaml` declares what to install, scoped by group and agent, replacing `home/.chezmoidata/apm.yaml`. Active groups per machine come from the existing `lib/chezmoi/active-groups.json.tmpl`, the same mechanism APM used. + +Agent identifiers follow the `skills` CLI: `claude-code` and `github-copilot`. + +```yaml +ai: + skills: + shared: + - source: mattpocock/skills + skill: grill-me + agents: [claude-code, github-copilot] + personal: + - source: schpet/linear-cli + skill: linear-cli + agents: [claude-code] + - source: juliusbrussee/skills + skill: junior-to-senior + agents: [claude-code, github-copilot] + - source: juliusbrussee/skills + skill: interface-kit + agents: [claude-code, github-copilot] + - source: blader/humanizer + skill: humanizer + agents: [claude-code, github-copilot] + - source: imadAttar/kaizen + skill: kaizen + agents: [claude-code, github-copilot] + - source: upstash/context7 + skill: context7-cli + agents: [claude-code, github-copilot] + - source: anthropics/claude-plugins-official + skill: skill-creator + agents: [claude-code] + # local domain skills authored in this repo + - source: local + skill: typescript + agents: [claude-code, github-copilot] + - source: local + skill: react + agents: [claude-code, github-copilot] + - source: local + skill: testing + agents: [claude-code, github-copilot] + work: [] + plugins: + shared: + - name: caveman + marketplace: caveman + source: JuliusBrussee/caveman + - name: superpowers + marketplace: superpowers-dev + source: obra/superpowers + mcp: + shared: + - name: grep + transport: http + url: https://mcp.grep.app + agents: [claude-code, github-copilot] + personal: + - name: tavily + transport: http + url: https://mcp.tavily.com/mcp/ + agents: [claude-code, github-copilot] + work: + - name: figma + transport: http + url: https://mcp.figma.com/mcp + agents: [github-copilot] + - name: jira + transport: stdio + command: npx + args: ["-y", "mcp-remote", "https://mcp.atlassian.com/v1/mcp", "--resource", "${ATLASSIAN_JIRA_RESOURCE_URL}"] + agents: [github-copilot] +``` -## Scope +Personal machines install shared plus personal entries. Work machines install shared plus work entries. `linear-cli` is Claude only by choice. `deep-analysis` is deferred to the work Copilot setup and is not listed until that work starts. -- Remove APM: source files, install script, lockfiles, runtime, and stale hook staging. -- Install Claude plugins from four marketplaces, official-first. -- Keep one canonical skills source and copy it into both `~/.claude/skills` and `~/.copilot/skills`. -- Keep one canonical `AGENTS.md` as universal behavioral instructions for both agents. -- Convert frontend-scoped guidance into skills (typescript, react, testing). -- Organize work Copilot MCP and the work skill, leaving Copilot wiring for later. -- Standardize on the `gh` CLI and remove GitHub MCP references. +## Skills Design -## Plugin Marketplace Design +Skills install with the cross-agent CLI. For each entry the install script runs: -Personal Claude skills and plugins come from four marketplaces, preferring the Anthropic-vetted official marketplace where it carries the plugin. +```bash +npx skills add --skill -a [-a ] --global --copy --yes +``` -| Plugin | Marketplace | Source | -| --- | --- | --- | -| superpowers, tavily, context7, skill-creator, linear | `claude-plugins-official` | anthropics/claude-plugins-official | -| caveman | `caveman` | JuliusBrussee/caveman | -| humanizer | `humanizer` | blader/humanizer | -| kaizen | `kaizen` | imadAttar/kaizen | +`--global` installs into `~/.claude/skills/` and `~/.copilot/skills/`. `--copy` writes real files rather than symlinks because Claude Code ignores symlinked skill directories. Agent flags come from the entry's `agents` list. -`linear` replaces the prior `schpet/linear-cli`. The official `linear` plugin uses Linear's MCP rather than the schpet CLI, an accepted behavior change. +Local domain skills (`typescript`, `react`, `testing`, React Testing Library) are authored under `home/.chezmoitemplates/skills/` from the existing instruction files, and installed with `source: local` pointing at that path. The vendored `grill-me` and `junior-to-senior` copies already present under that directory are removed once they install from upstream sources, unless upstream is unavailable. -Declare marketplaces in `home/dot_claude/settings.json` under `extraKnownMarketplaces` and enabled plugins under `enabledPlugins`. A `run_onchange_06_install-claude-plugins.sh.tmpl` script runs `claude plugin marketplace add` then `claude plugin install @` idempotently, driven by a data list so the set stays declarative. This script replaces `run_onchange_06_install-apm.sh.tmpl`. +The `skills` CLI has no manifest file, so `home/.chezmoidata/ai.yaml` is the manifest. A `run_onchange_09_install-ai-skills.sh.tmpl` script iterates the active groups' skill entries and runs the CLI. It re-runs when the rendered data changes. -Caveman installs as a real plugin, so Claude Code loads its hooks from the plugin directory. The manual caveman and superpowers hook entries are already removed from `settings.json`. +## Plugins Design -## Skills Design +Plugins are for hook-bearing bundles: `caveman` and `superpowers`. On Claude they install through the marketplace: + +```bash +claude plugin marketplace add +claude plugin install @ +``` -Canonical skills live under `home/.chezmoitemplates/skills/`: +`run_onchange_06_install-claude-plugins.sh.tmpl` iterates the active groups' plugin entries. This replaces `run_onchange_06_install-apm.sh.tmpl`. Marketplaces and enabled plugins are also declared in `home/dot_claude/settings.json`. -- Vendored, no upstream marketplace: `grill-me`, `junior-to-senior` (already copied in). -- Frontend-scoped domain skills authored from the existing instruction files: `typescript`, `react`, `testing` (React Testing Library). +Caveman installs as a plugin, so Claude Code loads its hooks from the plugin directory. The manual caveman and superpowers hook entries stay removed from `settings.json`. -`run_onchange_09_install-ai-skills.sh.tmpl` copies each skill directory into both `~/.claude/skills/` and `~/.copilot/skills/`. The script copies rather than symlinks because Claude Code ignores symlinked skill directories. The script re-runs when skill content changes because its rendered hash changes. +For Copilot, caveman and superpowers install through their Copilot support (skills or rule files) when the work Copilot setup is done. That wiring is out of scope now. -Skill frontmatter compatibility between Claude `SKILL.md` and the Copilot skill format is verified during implementation. If the two formats diverge, the fan-out renders the agent-specific frontmatter while sharing the skill body. +## MCP Design -## Instructions Design +Shared MCP servers apply to every targeted agent, so `grep` reaches both Claude and Copilot. Per agent: -One canonical `home/.chezmoitemplates/AGENTS.md` holds universal behavioral guidance only: AI guidance, git and pull request workflow, commit message rules, and the `gh` CLI standard. It does not assert a frontend persona, so the agent does not assume a single role. Frontend specifics live in the domain skills instead. +- Claude Code: registered at user scope, for example `claude mcp add --scope user --transport http grep https://mcp.grep.app`, or the equivalent user config entry. +- Copilot VS Code: written into `home/Library/Application Support/Code/User/mcp.json`. +- Copilot CLI: written into `home/dot_copilot/mcp-config.json`. -`AGENTS.md` renders to: +A `run_onchange` MCP script iterates the active groups' MCP entries and applies each to its targeted agents. Work MCP servers use `${VAR}` placeholders for secret and resource values, kept out of source control, sourced from `~/.secrets.local` on the work machine. -- `~/.claude/CLAUDE.md` for Claude Code. -- The Copilot `AGENTS.md` or user instructions path for VS Code Copilot. +## Instructions Design -GitHub MCP references and the `context7` tool mention are removed from the meta rules content. GitHub operations use `gh`. The `coding-standards` and TypeScript specifics fold into the `typescript` skill. +One canonical `home/.chezmoitemplates/AGENTS.md` holds universal behavioral guidance only: AI guidance, git and pull request workflow, commit message rules, and the `gh` CLI standard. It does not assert a frontend persona. Frontend specifics live in the `typescript`, `react`, and `testing` skills. -## Commit Message Instructions +`AGENTS.md` renders to `~/.claude/CLAUDE.md` for Claude Code and to the Copilot instructions path for VS Code Copilot. GitHub MCP references and the `context7` tool mention are removed from the meta rules content; GitHub operations use `gh`. The `coding-standards` and TypeScript specifics fold into the `typescript` skill. -The VS Code Source Control commit generation reads `github.copilot.chat.commitMessageGeneration.instructions` in `home/Library/Application Support/Code/User/settings.json`. That file stays a plain managed JSON file, not a chezmoi template, to keep JSON schema IntelliSense and hover help in the source. +## Commit Message Instructions -The two generic commit entries are replaced with the `commit-guide` rules as inline `{ "text": ... }` entries: conventional commit format, imperative mood, subject under 50 characters, optional one-line body, no test plans. The workspace-relative `{ "file": ... }` form is not used because it has no global equivalent. The small duplication with the `AGENTS.md` git section is accepted to preserve IntelliSense. +VS Code Source Control commit generation reads `github.copilot.chat.commitMessageGeneration.instructions` in `home/Library/Application Support/Code/User/settings.json`. That file stays plain managed JSON, not a chezmoi template, to keep JSON schema IntelliSense and hover help. -This shapes only the generated message, which is reviewed in Source Control. It does not automate committing or pushing. +The two generic entries are replaced with the `commit-guide` rules as inline `{ "text": ... }` entries: conventional commit format, imperative mood, subject under 50 characters, optional one-line body, no test plans. The workspace-relative `{ "file": ... }` form is not used because it has no global equivalent. The small duplication with the `AGENTS.md` git section is accepted to preserve IntelliSense. This shapes only the generated message, which is reviewed in Source Control. It does not automate committing or pushing. ## Work and Copilot Design Work Copilot tooling is organized now and wired later. ``` -home/Library/Application Support/Code/User/mcp.json # work MCP: figma, jira -home/dot_copilot/skills/deep-analysis/ # work skill, uses jira MCP +home/Library/Application Support/Code/User/mcp.json # work MCP, from ai.yaml +home/dot_copilot/skills/ # Copilot skills, from skills CLI home/dot_copilot/agents/ # code-reviewer, ts-reviewer, research -home/dot_copilot/mcp-config.json # Copilot CLI MCP, existing +home/dot_copilot/mcp-config.json # Copilot CLI MCP, from ai.yaml ``` -`deep-analysis` leaves the personal Claude set. The custom agents move to the Copilot side because they are frontend and work focused. Personal Claude keeps no custom agents and relies on plugin-provided agents such as the caveman reviewer. Copilot wiring beyond file placement is out of scope. +The custom agents move to the Copilot side because they are frontend and work focused. Personal Claude keeps no custom agents and relies on plugin-provided agents such as the caveman reviewer. `deep-analysis` joins the work Copilot skills when that setup starts. ## APM Teardown @@ -101,28 +186,34 @@ The already-removed caveman hook and reassert scripts on the current branch fold - The graphify install script `run_onchange_08_install-graphify-skills.sh.tmpl`. -The manual caveman and superpowers hook entries are already removed from `settings.json` and are not reintroduced, because the caveman plugin supplies its hooks. +The manual caveman and superpowers hook entries stay removed from `settings.json`, because the caveman plugin supplies its hooks. ## Testing and Verification - `chezmoi apply` on a clean state succeeds. -- `claude plugin list` shows the enabled plugins from the four marketplaces. -- Skills resolve in both `~/.claude/skills` and `~/.copilot/skills`. +- Template tests verify personal rendering includes shared plus personal entries and excludes work entries, and work rendering includes shared plus work entries. +- After apply, `~/.claude/skills` and `~/.copilot/skills` contain the group-targeted skills; Claude-only skills like `linear-cli` are absent from `~/.copilot/skills`. +- `claude plugin list` shows caveman and superpowers. +- `grep` MCP is registered for both Claude and Copilot. - The VS Code commit button produces a message in the configured style. - A new session `/doctor` reports no hook or settings errors. - `command -v apm` returns nothing and `~/.apm` is absent. -- The bats suite passes, including updated template tests for the new install scripts. +- The bats suite passes, including tests for the new install scripts. ## Implementation Phases -1. Stand up the four marketplaces and the skills source plus fan-out script, so Claude never loses skills. -2. Author `AGENTS.md` universal instructions and wire the Copilot commit instructions. -3. Organize the work and Copilot files. -4. Remove APM and update tests. +1. Add `home/.chezmoidata/ai.yaml` and the skills install script, so Claude and Copilot get skills. +2. Add the plugins install script for caveman and superpowers. +3. Add the MCP install script and apply shared MCP to both agents. +4. Author `AGENTS.md` universal instructions and wire the Copilot commit instructions. +5. Author the local domain skills from the instruction files. +6. Organize the work and Copilot files. +7. Remove APM and update tests. ## Out of Scope - Wiring work Copilot MCP authentication or enabling the work skill at runtime. +- Installing caveman and superpowers into Copilot beyond the deferred setup. - Migrating work to Claude. - Adding a secret backend. - Verifying live Figma or Jira MCP authentication. diff --git a/home/.chezmoidata/packages.yaml b/home/.chezmoidata/packages.yaml index 0b36916..bb7e198 100644 --- a/home/.chezmoidata/packages.yaml +++ b/home/.chezmoidata/packages.yaml @@ -43,6 +43,7 @@ packages: - discord - microsoft-excel - synology-drive + - tailscale-app mas: - name: Klack id: 6446206067 From cd5bc6ae0ad3c8b779cb3bc9018151c983550338 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 19:41:57 -0500 Subject: [PATCH 10/51] docs: DOT-43 refine ai.yaml groups, dual-path plugins, jira http --- ...-to-claude-marketplace-migration-design.md | 53 +++++++++++-------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md b/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md index 11f8a76..f75446b 100644 --- a/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md +++ b/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md @@ -16,11 +16,11 @@ APM did three jobs: fetch skill and plugin source, pin versions, and wire runtim The deciding question for each artifact is: does it need a hook, statusline, or slash command on Claude? -| Artifact | Definition | Delivery | Cross-agent | -| --- | --- | --- | --- | -| Skill | Instructions or knowledge, no runtime | `npx skills add` | One command, both agents | -| Plugin | Bundle with hooks, statusline, slash commands, or packaged agents that only Claude loads | `claude plugin` | Claude only; Copilot uses skills or rules | -| MCP server | A tool or data backend spoken over MCP | Per-agent MCP config | Write each agent's config | +| Artifact | Definition | Delivery | Cross-agent | +| ---------- | ---------------------------------------------------------------------------------------- | -------------------- | ----------------------------------------- | +| Skill | Instructions or knowledge, no runtime | `npx skills add` | One command, both agents | +| Plugin | Bundle with hooks, statusline, slash commands, or packaged agents that only Claude loads | `claude plugin` | Claude only; Copilot uses skills or rules | +| MCP server | A tool or data backend spoken over MCP | Per-agent MCP config | Write each agent's config | Plugins are reserved for hook-bearing bundles. Everything that is only a skill goes through the `skills` CLI to the targeted agents. MCP servers are written into each targeted agent's config. @@ -33,11 +33,11 @@ Agent identifiers follow the `skills` CLI: `claude-code` and `github-copilot`. ```yaml ai: skills: + # Almost everything is shared; the agents list does the real scoping. shared: - source: mattpocock/skills skill: grill-me agents: [claude-code, github-copilot] - personal: - source: schpet/linear-cli skill: linear-cli agents: [claude-code] @@ -69,15 +69,17 @@ ai: - source: local skill: testing agents: [claude-code, github-copilot] - work: [] plugins: + # Dual-path: claude-code via marketplace, github-copilot via skills CLI. shared: - name: caveman marketplace: caveman source: JuliusBrussee/caveman + agents: [claude-code, github-copilot] - name: superpowers marketplace: superpowers-dev source: obra/superpowers + agents: [claude-code, github-copilot] mcp: shared: - name: grep @@ -88,20 +90,19 @@ ai: - name: tavily transport: http url: https://mcp.tavily.com/mcp/ - agents: [claude-code, github-copilot] + agents: [claude-code] work: - name: figma transport: http url: https://mcp.figma.com/mcp agents: [github-copilot] - name: jira - transport: stdio - command: npx - args: ["-y", "mcp-remote", "https://mcp.atlassian.com/v1/mcp", "--resource", "${ATLASSIAN_JIRA_RESOURCE_URL}"] + transport: http + url: https://mcp.atlassian.com/v1/mcp agents: [github-copilot] ``` -Personal machines install shared plus personal entries. Work machines install shared plus work entries. `linear-cli` is Claude only by choice. `deep-analysis` is deferred to the work Copilot setup and is not listed until that work starts. +Skills are all `shared`; the `agents` list scopes each to Claude, Copilot, or both. `linear-cli` and `skill-creator` are Claude only. The `personal` and `work` groups are used only for MCP and work-specific items. Personal machines install shared plus personal entries; work machines install shared plus work entries. `tavily` MCP is Claude only. `deep-analysis` is deferred to the work Copilot setup and is not listed until that work starts. ## Skills Design @@ -115,22 +116,30 @@ npx skills add --skill -a [-a ] --global --copy Local domain skills (`typescript`, `react`, `testing`, React Testing Library) are authored under `home/.chezmoitemplates/skills/` from the existing instruction files, and installed with `source: local` pointing at that path. The vendored `grill-me` and `junior-to-senior` copies already present under that directory are removed once they install from upstream sources, unless upstream is unavailable. +`skill-creator` comes from the `anthropics/claude-plugins-official` marketplace repo rather than a plain skills repo, so implementation verifies that `skills add` can pull it. If it cannot, `skill-creator` installs as a Claude plugin instead. It stays Claude only either way. + The `skills` CLI has no manifest file, so `home/.chezmoidata/ai.yaml` is the manifest. A `run_onchange_09_install-ai-skills.sh.tmpl` script iterates the active groups' skill entries and runs the CLI. It re-runs when the rendered data changes. ## Plugins Design -Plugins are for hook-bearing bundles: `caveman` and `superpowers`. On Claude they install through the marketplace: +Plugins are hook-bearing bundles: `caveman` and `superpowers`. Each plugin installs by a different path per agent, driven by its `agents` list. -```bash -claude plugin marketplace add -claude plugin install @ -``` +- `claude-code`: through the marketplace. + + ```bash + claude plugin marketplace add + claude plugin install @ + ``` + +- `github-copilot`: through the cross-agent skills CLI, since Copilot has no plugin or hook system. -`run_onchange_06_install-claude-plugins.sh.tmpl` iterates the active groups' plugin entries. This replaces `run_onchange_06_install-apm.sh.tmpl`. Marketplaces and enabled plugins are also declared in `home/dot_claude/settings.json`. + ```bash + npx skills add -a github-copilot --global --copy --yes + ``` -Caveman installs as a plugin, so Claude Code loads its hooks from the plugin directory. The manual caveman and superpowers hook entries stay removed from `settings.json`. +`run_onchange_06_install-claude-plugins.sh.tmpl` iterates the active groups' plugin entries and runs the path that matches each targeted agent present on the machine. This replaces `run_onchange_06_install-apm.sh.tmpl`. Marketplaces and enabled plugins are also declared in `home/dot_claude/settings.json`. -For Copilot, caveman and superpowers install through their Copilot support (skills or rule files) when the work Copilot setup is done. That wiring is out of scope now. +Caveman installs as a plugin on Claude, so Claude Code loads its hooks from the plugin directory. The manual caveman and superpowers hook entries stay removed from `settings.json`. On the Copilot side the plugins install as skills only, because Copilot has no hooks; the Copilot path runs only on a machine where Copilot is present. ## MCP Design @@ -140,7 +149,7 @@ Shared MCP servers apply to every targeted agent, so `grep` reaches both Claude - Copilot VS Code: written into `home/Library/Application Support/Code/User/mcp.json`. - Copilot CLI: written into `home/dot_copilot/mcp-config.json`. -A `run_onchange` MCP script iterates the active groups' MCP entries and applies each to its targeted agents. Work MCP servers use `${VAR}` placeholders for secret and resource values, kept out of source control, sourced from `~/.secrets.local` on the work machine. +A `run_onchange` MCP script iterates the active groups' MCP entries and applies each to its targeted agents. `tavily` is Claude only. Work MCP servers use the HTTP transport with the vendor's MCP URL, including Jira at `https://mcp.atlassian.com/v1/mcp`, and authenticate over OAuth at connect time rather than a stored token. Any remaining secret values use `${VAR}` placeholders kept out of source control, sourced from `~/.secrets.local` on the work machine. ## Instructions Design @@ -194,7 +203,7 @@ The manual caveman and superpowers hook entries stay removed from `settings.json - Template tests verify personal rendering includes shared plus personal entries and excludes work entries, and work rendering includes shared plus work entries. - After apply, `~/.claude/skills` and `~/.copilot/skills` contain the group-targeted skills; Claude-only skills like `linear-cli` are absent from `~/.copilot/skills`. - `claude plugin list` shows caveman and superpowers. -- `grep` MCP is registered for both Claude and Copilot. +- `grep` MCP is registered for both Claude and Copilot; `tavily` MCP is registered for Claude only. - The VS Code commit button produces a message in the configured style. - A new session `/doctor` reports no hook or settings errors. - `command -v apm` returns nothing and `~/.apm` is absent. From 3a6fcdddf10fa4a4739f89d96176ac80a20baa8b Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 20:08:30 -0500 Subject: [PATCH 11/51] docs: DOT-43 align spec to targets model and split MCP delivery --- docs/cheatsheets/lazyvim-tmux.md | 131 ---- ...-to-claude-marketplace-migration-design.md | 84 +- home/.chezmoidata/ai.yaml | 56 ++ home/.chezmoidata/apm.yaml | 46 -- home/.chezmoidata/extensions.yaml | 1 - ...change_07_fix-opencode-agent-tools.sh.tmpl | 8 - .../apm/apm.lock.personal.yaml | 502 ------------ home/.chezmoitemplates/apm/apm.lock.work.yaml | 25 - .../.chezmoitemplates/apm/mcp-server.yml.tmpl | 21 - .../Application Support/Code/User/mcp.json | 1 + .../dot_apm/skills/context7-cli/SKILL.md | 72 -- .../skills/context7-cli/references/docs.md | 113 --- .../skills/context7-cli/references/setup.md | 47 -- .../skills/context7-cli/references/skills.md | 120 --- .../dot_apm/dot_apm/skills/humanizer/SKILL.md | 595 -------------- home/dot_apm/dot_apm/skills/kaizen/SKILL.md | 733 ------------------ 16 files changed, 96 insertions(+), 2459 deletions(-) delete mode 100644 docs/cheatsheets/lazyvim-tmux.md create mode 100644 home/.chezmoidata/ai.yaml delete mode 100644 home/.chezmoidata/apm.yaml delete mode 100644 home/.chezmoiscripts/darwin/run_onchange_07_fix-opencode-agent-tools.sh.tmpl delete mode 100644 home/.chezmoitemplates/apm/apm.lock.personal.yaml delete mode 100644 home/.chezmoitemplates/apm/apm.lock.work.yaml delete mode 100644 home/.chezmoitemplates/apm/mcp-server.yml.tmpl create mode 100644 home/Library/Application Support/Code/User/mcp.json delete mode 100644 home/dot_apm/dot_apm/skills/context7-cli/SKILL.md delete mode 100644 home/dot_apm/dot_apm/skills/context7-cli/references/docs.md delete mode 100644 home/dot_apm/dot_apm/skills/context7-cli/references/setup.md delete mode 100644 home/dot_apm/dot_apm/skills/context7-cli/references/skills.md delete mode 100644 home/dot_apm/dot_apm/skills/humanizer/SKILL.md delete mode 100644 home/dot_apm/dot_apm/skills/kaizen/SKILL.md diff --git a/docs/cheatsheets/lazyvim-tmux.md b/docs/cheatsheets/lazyvim-tmux.md deleted file mode 100644 index e044757..0000000 --- a/docs/cheatsheets/lazyvim-tmux.md +++ /dev/null @@ -1,131 +0,0 @@ -# LazyVim and tmux Cheatsheet - -This is a daily reference for this dotfiles setup. In LazyVim, `` is the space key. In tmux, the default prefix is ``. - -## Daily Flow - -1. Start a named tmux session: `tmux new -s dotfiles` -2. Open the repo in Neovim: `nvim .` -3. Split tmux to the right: `%` -4. Run OpenCode in the right pane: `opencode --port` -5. In Neovim, send the current buffer or visual selection to OpenCode: `oa` - -## Find Keys - -| Action | Keys | -| ------------------------- | -------------- | -| Show buffer-local keymaps | `?` | -| Search all keymaps | `sk` | -| Open Lazy plugin UI | `:Lazy` | -| Open health report | `:checkhealth` | -| Cancel a menu or mode | `` | - -## Files And Search - -| Action | Keys | -| ---------------------- | --------------------------------- | -| Find files | `` or `ff` | -| Find config files | `fc` | -| Recent files | `fr` | -| Search text in project | `/` | -| Search help | `sh` | -| Search diagnostics | `sd` | -| Git status | `gs` | - -## Hidden Dotfiles - -LazyVim's picker does not use the shell `FZF_DEFAULT_COMMAND`. Dotfiles and ignored files are toggled inside the picker or explorer. - -| Place | Hidden files | Gitignored files | -| ---------------- | ------------ | ---------------- | -| File picker | `Alt-h` | `Alt-i` | -| Explorer sidebar | `H` | `I` | - -Examples in this repo include `home/.chezmoiscripts/`, `home/.chezmoiignore`, `home/.chezmoiexternal.yaml.tmpl`, and `home/.chezmoi.yaml.tmpl`. Chezmoi source names like `home/dot_zshenv` become dotfiles only after chezmoi applies them. - -## Buffers And Windows - -| Action | Keys | -| --------------------- | -------------------------------------- | -| Pick open buffers | `,` or `fb` | -| Split below | `-` | -| Split right | `\|` | -| Move between windows | `h`, `j`, `k`, `l` | -| Delete current window | `wd` | -| Zoom current window | `wm` | -| Window command helper | `` | - -## Copy From Neovim To OpenCode - -`vim.opt.clipboard = "unnamedplus"` sends Neovim yanks to the macOS clipboard. - -1. In Neovim visual mode, select text. -2. Press `y`. -3. Move to the OpenCode tmux pane. -4. Paste with `Cmd-v`. - -## OpenCode From Neovim - -These keymaps come from `home/dot_config/nvim/lua/plugins/opencode.lua`. - -| Action | Keys | -| ---------------------------------------------- | ------------ | -| Ask OpenCode about current buffer or selection | `oa` | -| Select OpenCode action | `os` | -| Select OpenCode server | `oS` | -| Start a new OpenCode session | `on` | - -## tmux Sessions - -| Action | Command or keys | -| -------------------- | ------------------------- | -| New named session | `tmux new -s dotfiles` | -| Attach named session | `tmux attach -t dotfiles` | -| List sessions | `tmux ls` | -| Detach from session | `d` | -| Rename session | `$` | - -## tmux Panes - -| Action | Keys | -| ------------------ | -------------- | -| Split right | `%` | -| Split below | `"` | -| Move between panes | `` | -| Show pane numbers | `q` | -| Move to next pane | `o` | -| Zoom current pane | `z` | -| Kill current pane | `x` | - -## tmux Windows - -| Action | Keys | -| --------------------- | ------------------------- | -| New window | `c` | -| Next window | `n` | -| Previous window | `p` | -| Jump to window number | `0` through `9` | -| Rename current window | `,` | -| Kill current window | `&` | - -## Copy From tmux To OpenCode - -The tmux config uses vi copy mode and copies selections to the macOS clipboard with `pbcopy`. - -| Action | Keys | -| --------------------------------- | ------------------ | -| Enter copy mode | `[` | -| Move in copy mode | `h`, `j`, `k`, `l` | -| Start selection | `` | -| Copy selection to macOS clipboard | `y` or `` | -| Paste into OpenCode | `Cmd-v` | - -Mouse selection in tmux copy mode also copies to the macOS clipboard when the drag ends. - -## Reload After Applying Dotfiles - -| Config | Command | -| --------------------- | ---------------------------------------------------- | -| Reload tmux config | `:` then `source-file ~/.config/tmux/tmux.conf` | -| Reload zsh config | `source ~/.config/zsh/.zshrc` | -| Restart Neovim config | close and reopen `nvim` | diff --git a/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md b/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md index f75446b..23e562c 100644 --- a/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md +++ b/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md @@ -24,132 +24,126 @@ The deciding question for each artifact is: does it need a hook, statusline, or Plugins are reserved for hook-bearing bundles. Everything that is only a skill goes through the `skills` CLI to the targeted agents. MCP servers are written into each targeted agent's config. -## Group and Agent Data Model +## Group and Target Data Model -A new `home/.chezmoidata/ai.yaml` declares what to install, scoped by group and agent, replacing `home/.chezmoidata/apm.yaml`. Active groups per machine come from the existing `lib/chezmoi/active-groups.json.tmpl`, the same mechanism APM used. +`home/.chezmoidata/ai.yaml` declares what to install, replacing `home/.chezmoidata/apm.yaml`. It carries a `targets` map that binds each group to the AI providers it targets, mirroring APM targets. The active group per machine comes from the existing `active-groups` template, and the OS gate stays `darwin`, matching the other install scripts. -Agent identifiers follow the `skills` CLI: `claude-code` and `github-copilot`. +Group membership implies the agent through `targets`, so no per-item agent field is needed: `personal` targets `claude-code`, `work` targets `github-copilot`, and `shared` items install for whatever the active machine targets. ```yaml ai: + targets: + shared: [] + personal: + - claude-code + work: + - github-copilot skills: - # Almost everything is shared; the agents list does the real scoping. shared: - source: mattpocock/skills skill: grill-me - agents: [claude-code, github-copilot] - - source: schpet/linear-cli - skill: linear-cli - agents: [claude-code] - source: juliusbrussee/skills skill: junior-to-senior - agents: [claude-code, github-copilot] - - source: juliusbrussee/skills - skill: interface-kit - agents: [claude-code, github-copilot] - source: blader/humanizer skill: humanizer - agents: [claude-code, github-copilot] - source: imadAttar/kaizen skill: kaizen - agents: [claude-code, github-copilot] - - source: upstash/context7 - skill: context7-cli - agents: [claude-code, github-copilot] - - source: anthropics/claude-plugins-official - skill: skill-creator - agents: [claude-code] # local domain skills authored in this repo - source: local skill: typescript - agents: [claude-code, github-copilot] - source: local skill: react - agents: [claude-code, github-copilot] - source: local skill: testing - agents: [claude-code, github-copilot] + personal: + - source: schpet/linear-cli + skill: linear-cli + - source: juliusbrussee/skills + skill: interface-kit + - source: upstash/context7 + skill: context7-cli + work: [] plugins: - # Dual-path: claude-code via marketplace, github-copilot via skills CLI. shared: - name: caveman marketplace: caveman source: JuliusBrussee/caveman - agents: [claude-code, github-copilot] - name: superpowers marketplace: superpowers-dev source: obra/superpowers - agents: [claude-code, github-copilot] mcp: shared: - name: grep transport: http url: https://mcp.grep.app - agents: [claude-code, github-copilot] personal: - name: tavily transport: http url: https://mcp.tavily.com/mcp/ - agents: [claude-code] work: - name: figma transport: http url: https://mcp.figma.com/mcp - agents: [github-copilot] - name: jira transport: http url: https://mcp.atlassian.com/v1/mcp - agents: [github-copilot] ``` -Skills are all `shared`; the `agents` list scopes each to Claude, Copilot, or both. `linear-cli` and `skill-creator` are Claude only. The `personal` and `work` groups are used only for MCP and work-specific items. Personal machines install shared plus personal entries; work machines install shared plus work entries. `tavily` MCP is Claude only. `deep-analysis` is deferred to the work Copilot setup and is not listed until that work starts. +Personal machines install shared plus personal entries against `claude-code`; work machines install shared plus work entries against `github-copilot`. `linear-cli`, `interface-kit`, and `context7-cli` sit in `personal`, so they reach Claude only until Copilot is added to the personal target. `skill-creator` is dropped. `tavily` MCP is personal, so Claude only. `deep-analysis` is deferred to the work Copilot setup and is not listed until that work starts. ## Skills Design Skills install with the cross-agent CLI. For each entry the install script runs: ```bash -npx skills add --skill -a [-a ] --global --copy --yes +npx skills add --skill -a [-a ] --global --copy --yes ``` -`--global` installs into `~/.claude/skills/` and `~/.copilot/skills/`. `--copy` writes real files rather than symlinks because Claude Code ignores symlinked skill directories. Agent flags come from the entry's `agents` list. +`--global` installs into `~/.claude/skills/` and `~/.copilot/skills/`. `--copy` writes real files rather than symlinks because Claude Code ignores symlinked skill directories. The `-a` flags come from the active group's `target` in `ai.yaml`, so a skill in `shared` installs for the machine's target, and a skill in `personal` installs for `claude-code`. Local domain skills (`typescript`, `react`, `testing`, React Testing Library) are authored under `home/.chezmoitemplates/skills/` from the existing instruction files, and installed with `source: local` pointing at that path. The vendored `grill-me` and `junior-to-senior` copies already present under that directory are removed once they install from upstream sources, unless upstream is unavailable. -`skill-creator` comes from the `anthropics/claude-plugins-official` marketplace repo rather than a plain skills repo, so implementation verifies that `skills add` can pull it. If it cannot, `skill-creator` installs as a Claude plugin instead. It stays Claude only either way. - -The `skills` CLI has no manifest file, so `home/.chezmoidata/ai.yaml` is the manifest. A `run_onchange_09_install-ai-skills.sh.tmpl` script iterates the active groups' skill entries and runs the CLI. It re-runs when the rendered data changes. +The `skills` CLI has no manifest file, so `home/.chezmoidata/ai.yaml` is the manifest. A `run_onchange_09_install-ai-skills.sh.tmpl` script gates on `darwin`, reads the active group and its target, iterates the matching skill entries, and runs the CLI. It re-runs when the rendered data changes. ## Plugins Design -Plugins are hook-bearing bundles: `caveman` and `superpowers`. Each plugin installs by a different path per agent, driven by its `agents` list. +Plugins are hook-bearing bundles: `caveman` and `superpowers`. They live in `shared`, so each installs by the path that matches the active machine's `target`. -- `claude-code`: through the marketplace. +- `claude-code` target: through the marketplace. ```bash claude plugin marketplace add claude plugin install @ ``` -- `github-copilot`: through the cross-agent skills CLI, since Copilot has no plugin or hook system. +- `github-copilot` target: through the cross-agent skills CLI, since Copilot has no plugin or hook system. ```bash npx skills add -a github-copilot --global --copy --yes ``` -`run_onchange_06_install-claude-plugins.sh.tmpl` iterates the active groups' plugin entries and runs the path that matches each targeted agent present on the machine. This replaces `run_onchange_06_install-apm.sh.tmpl`. Marketplaces and enabled plugins are also declared in `home/dot_claude/settings.json`. +`run_onchange_06_install-claude-plugins.sh.tmpl` gates on `darwin`, reads the active target, and runs the matching path for each plugin entry. This replaces `run_onchange_06_install-apm.sh.tmpl`. Marketplaces and enabled plugins are also declared in `home/dot_claude/settings.json`. Caveman installs as a plugin on Claude, so Claude Code loads its hooks from the plugin directory. The manual caveman and superpowers hook entries stay removed from `settings.json`. On the Copilot side the plugins install as skills only, because Copilot has no hooks; the Copilot path runs only on a machine where Copilot is present. ## MCP Design -Shared MCP servers apply to every targeted agent, so `grep` reaches both Claude and Copilot. Per agent: +MCP delivery avoids templating any main `settings.json`, so JSON schema IntelliSense and hover help stay intact. Each agent uses its own dedicated MCP surface, and both are driven by the `ai.yaml` `mcp` block scoped by the active group's target. + +Claude Code MCP is applied with the official CLI at user scope, which merges into the stateful `~/.claude.json` without overwriting other state: + +```bash +claude mcp add --scope user --transport http grep https://mcp.grep.app +``` + +A `run_onchange` script gates on `darwin`, reads the active target, iterates the matching MCP entries, and runs `claude mcp add` for each. It is idempotent: it checks `claude mcp get ` and skips or re-adds as needed. The stateful `~/.claude.json` is never templated. + +Copilot MCP uses dedicated MCP config files, which are safe to render as chezmoi templates because they are separate from the main VS Code `settings.json`: -- Claude Code: registered at user scope, for example `claude mcp add --scope user --transport http grep https://mcp.grep.app`, or the equivalent user config entry. -- Copilot VS Code: written into `home/Library/Application Support/Code/User/mcp.json`. -- Copilot CLI: written into `home/dot_copilot/mcp-config.json`. +- VS Code: `home/Library/Application Support/Code/User/mcp.json`. +- Copilot CLI: `home/dot_copilot/mcp-config.json`. -A `run_onchange` MCP script iterates the active groups' MCP entries and applies each to its targeted agents. `tavily` is Claude only. Work MCP servers use the HTTP transport with the vendor's MCP URL, including Jira at `https://mcp.atlassian.com/v1/mcp`, and authenticate over OAuth at connect time rather than a stored token. Any remaining secret values use `${VAR}` placeholders kept out of source control, sourced from `~/.secrets.local` on the work machine. +Each template reads the `ai.yaml` `mcp` entries for the active target and renders them into that surface's schema. `tavily` is personal, so Claude only. Work MCP servers use the HTTP transport with the vendor's MCP URL, including Jira at `https://mcp.atlassian.com/v1/mcp`, and authenticate over OAuth at connect time rather than a stored token. Any remaining secret values use `${VAR}` placeholders kept out of source control, sourced from `~/.secrets.local` on the work machine. ## Instructions Design diff --git a/home/.chezmoidata/ai.yaml b/home/.chezmoidata/ai.yaml new file mode 100644 index 0000000..4a818d8 --- /dev/null +++ b/home/.chezmoidata/ai.yaml @@ -0,0 +1,56 @@ +ai: + targets: + shared: [] + personal: + - claude-code + work: + - github-copilot + skills: + shared: + - source: mattpocock/skills + skill: grill-me + - source: juliusbrussee/skills + skill: junior-to-senior + - source: blader/humanizer + skill: humanizer + - source: imadAttar/kaizen + skill: kaizen + # local domain skills authored in this repo + - source: local + skill: typescript + - source: local + skill: react + - source: local + skill: testing + personal: + - source: schpet/linear-cli + skill: linear-cli + - source: juliusbrussee/skills + skill: interface-kit + - source: upstash/context7 + skill: context7-cli + work: [] + plugins: + shared: + - name: caveman + marketplace: caveman + source: JuliusBrussee/caveman + - name: superpowers + marketplace: superpowers-dev + source: obra/superpowers + mcp: + shared: + - name: grep + transport: http + url: https://mcp.grep.app + personal: + - name: tavily + transport: http + url: https://mcp.tavily.com/mcp/ + work: + - name: figma + transport: http + url: https://mcp.figma.com/mcp + - name: jira + transport: http + url: https://mcp.atlassian.com/v1/mcp diff --git a/home/.chezmoidata/apm.yaml b/home/.chezmoidata/apm.yaml deleted file mode 100644 index 1ec330b..0000000 --- a/home/.chezmoidata/apm.yaml +++ /dev/null @@ -1,46 +0,0 @@ -apm: - targets: - shared: [] - personal: - - claude - work: - - copilot - dependencies: - shared: - - obra/superpowers#v6.0.3 - - JuliusBrussee/caveman#v1.9.0 - - anthropics/claude-plugins-official/plugins/skill-creator#main - - personal: - - schpet/linear-cli#v2.0.0 - - tavily-ai/skills#main - - git: JuliusBrussee/skills#main - skills: - - grill-me - - junior-to-senior - - work: [] - - mcp: - shared: - - name: grep - registry: false - transport: http - url: https://mcp.grep.app - - personal: - - name: tavily - registry: false - transport: http - url: https://mcp.tavily.com/mcp/ - - work: - - name: figma - registry: false - transport: http - url: https://mcp.figma.com/mcp - - - name: jira - registry: false - transport: http - url: https://mcp.atlassian.com/v1/mcp diff --git a/home/.chezmoidata/extensions.yaml b/home/.chezmoidata/extensions.yaml index 4e6109e..4629260 100644 --- a/home/.chezmoidata/extensions.yaml +++ b/home/.chezmoidata/extensions.yaml @@ -6,7 +6,6 @@ vscode: - esbenp.prettier-vscode - usernamehw.errorlens # ui / dx - - illixion.vscode-vibrancy-continued - adpyke.codesnap - edwinhuish.better-comments-next - fosshaas.fontsize-shortcuts diff --git a/home/.chezmoiscripts/darwin/run_onchange_07_fix-opencode-agent-tools.sh.tmpl b/home/.chezmoiscripts/darwin/run_onchange_07_fix-opencode-agent-tools.sh.tmpl deleted file mode 100644 index 8b7ee22..0000000 --- a/home/.chezmoiscripts/darwin/run_onchange_07_fix-opencode-agent-tools.sh.tmpl +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -# apm.yml: {{ include "dot_apm/apm.yml.tmpl" | sha256sum }} - -{{ if eq .chezmoi.os "darwin" }} -{{ template "lib/common/log.sh" . }} -{{ template "lib/common/install-prelude.sh" . }} -{{ template "lib/install/opencode-agent-tools.sh" . }} -{{ end }} diff --git a/home/.chezmoitemplates/apm/apm.lock.personal.yaml b/home/.chezmoitemplates/apm/apm.lock.personal.yaml deleted file mode 100644 index e286499..0000000 --- a/home/.chezmoitemplates/apm/apm.lock.personal.yaml +++ /dev/null @@ -1,502 +0,0 @@ -lockfile_version: "1" -generated_at: "2026-06-26T03:33:20.248284+00:00" -apm_version: 0.21.0 -dependencies: - - repo_url: JuliusBrussee/caveman - host: github.com - resolved_commit: 32f37af81a02a4b91c107b768f1365848e5bf005 - resolved_ref: v1.9.0 - package_type: marketplace_plugin - deployed_files: - - .agents/skills/cavecrew - - .agents/skills/cavecrew/README.md - - .agents/skills/cavecrew/SKILL.md - - .agents/skills/caveman - - .agents/skills/caveman-commit - - .agents/skills/caveman-commit/README.md - - .agents/skills/caveman-commit/SKILL.md - - .agents/skills/caveman-compress - - .agents/skills/caveman-compress/README.md - - .agents/skills/caveman-compress/SECURITY.md - - .agents/skills/caveman-compress/SKILL.md - - .agents/skills/caveman-compress/scripts/__init__.py - - .agents/skills/caveman-compress/scripts/__main__.py - - .agents/skills/caveman-compress/scripts/benchmark.py - - .agents/skills/caveman-compress/scripts/cli.py - - .agents/skills/caveman-compress/scripts/compress.py - - .agents/skills/caveman-compress/scripts/detect.py - - .agents/skills/caveman-compress/scripts/validate.py - - .agents/skills/caveman-help - - .agents/skills/caveman-help/README.md - - .agents/skills/caveman-help/SKILL.md - - .agents/skills/caveman-review - - .agents/skills/caveman-review/README.md - - .agents/skills/caveman-review/SKILL.md - - .agents/skills/caveman-stats - - .agents/skills/caveman-stats/README.md - - .agents/skills/caveman-stats/SKILL.md - - .agents/skills/caveman/README.md - - .agents/skills/caveman/SKILL.md - - .claude/agents/cavecrew-builder.md - - .claude/agents/cavecrew-investigator.md - - .claude/agents/cavecrew-reviewer.md - - .claude/hooks/caveman/src/hooks/caveman-activate.js - - .claude/skills/cavecrew - - .claude/skills/cavecrew/README.md - - .claude/skills/cavecrew/SKILL.md - - .claude/skills/caveman-review - - .claude/skills/caveman-review/README.md - - .claude/skills/caveman-review/SKILL.md - - .claude/skills/caveman-stats - - .claude/skills/caveman-stats/README.md - - .claude/skills/caveman-stats/SKILL.md - - .claude/skills/caveman/.claude-plugin/plugin.json - - .claude/skills/caveman/bin/install.js - deployed_file_hashes: - .agents/skills/cavecrew/README.md: sha256:965622be25416e1c59d0f69edf359dceeebdf2fa650c3fd5c254081045f27c12 - .agents/skills/cavecrew/SKILL.md: sha256:b74f374f6aae6e9a31e78e7d876860406fe5833378e9298536edf176c12f379b - .agents/skills/caveman-commit/README.md: sha256:402e739690a2757cd84b8dea43296cfb8828c5b18b8262c4571cf632c0e5a3e1 - .agents/skills/caveman-commit/SKILL.md: sha256:58db7b8efab911a629c26e7132517d9076e4e3645009eb604cb8c25663477841 - .agents/skills/caveman-compress/README.md: sha256:dd772fc8791ea5bd6041e441f91ff355392d4f30c9f56310be7f4bc6cdaf5126 - .agents/skills/caveman-compress/SECURITY.md: sha256:ac3432493cfe71368a141f437bf35562eb8f4ce07ae699e5399c0cb665e02da3 - .agents/skills/caveman-compress/SKILL.md: sha256:8b0d64c622d8a70411bbd86e736ac23cc0465fca1943a37f081e58e9d3afd210 - .agents/skills/caveman-compress/scripts/__init__.py: sha256:429c3e1c5cc5b9705f28d77f303c728304ae68693913ad3d5ce9b5a44c8ee40f - .agents/skills/caveman-compress/scripts/__main__.py: sha256:6d8b7d7846a845059d7a3107143f11131f63c5511d669b44085b15ec5e3d2279 - .agents/skills/caveman-compress/scripts/benchmark.py: sha256:84a5a94bebcce03c9dc4a70333f120f03e11075bce9298e1e74b95594e142d43 - .agents/skills/caveman-compress/scripts/cli.py: sha256:17edb6b425adc0ea04e35f1e90dfe318ba4232a1b815c5ff7d6cff7d574efea6 - .agents/skills/caveman-compress/scripts/compress.py: sha256:9537671f5b8bf81bfe99078666948a93b5a5f1ae07d2f7bc3f085fe1a6b408c0 - .agents/skills/caveman-compress/scripts/detect.py: sha256:7ab164dbcbc0b60e04d7f00257e2e691ce75a048f238f9a789c0b890a053f12b - .agents/skills/caveman-compress/scripts/validate.py: sha256:8b9e752b70868f6e03b3d7e76de19dd6b4ac92dad50522992738340e9916dc0d - .agents/skills/caveman-help/README.md: sha256:5cb51e36f8cc96360427222e631a0357e20daffac10ba77571efeeff933352a2 - .agents/skills/caveman-help/SKILL.md: sha256:590d6c6c17254bda56d18da7cc9d0bf252aeec5d1c78b77dd7ae4cabb8404f89 - .agents/skills/caveman-review/README.md: sha256:a06403b2722a069a9a2b0011772ae73296e68b4e46ace2f4b42b4ea546052b64 - .agents/skills/caveman-review/SKILL.md: sha256:9ef09065f26b9781275b5a4775e14870e331fab833efb2a2aa33f3205a2b83c3 - .agents/skills/caveman-stats/README.md: sha256:64d206ee7524625a8541ec064e0c7ec27c3764bae63b685cb5eecbd20aa76bdc - .agents/skills/caveman-stats/SKILL.md: sha256:b9e49e46ede956e0c1633eae82a695c996f728bc48f3903f001d301068a2b0ea - .agents/skills/caveman/README.md: sha256:97452fae7b043b43bc5348784494f7e457012400030bb0aa09b9f6a9c488a6ea - .agents/skills/caveman/SKILL.md: sha256:e38ec671ecbee47ce234190be12615daf60ac667d775b7340d49d07f4f63c7bc - .claude/agents/cavecrew-builder.md: sha256:135af639b287ed7534b66d01390c4dc48d98683af52232728a0e87db9c558b3e - .claude/agents/cavecrew-investigator.md: sha256:f1ce01150aeeec7de98d487f41693d27eba1bc9d4cc67f4c774675bb21c24fc2 - .claude/agents/cavecrew-reviewer.md: sha256:ae45346edae2cdb97018a38ebdcb79b162c59875cac2921d976a7d7540e4df70 - .claude/hooks/caveman/src/hooks/caveman-activate.js: sha256:9366e01e5050c8ae415f2574a5cf9f792494b5a48fc14e1b90109ef01bbde03e - .claude/skills/cavecrew/README.md: sha256:965622be25416e1c59d0f69edf359dceeebdf2fa650c3fd5c254081045f27c12 - .claude/skills/cavecrew/SKILL.md: sha256:b74f374f6aae6e9a31e78e7d876860406fe5833378e9298536edf176c12f379b - .claude/skills/caveman-review/README.md: sha256:a06403b2722a069a9a2b0011772ae73296e68b4e46ace2f4b42b4ea546052b64 - .claude/skills/caveman-review/SKILL.md: sha256:9ef09065f26b9781275b5a4775e14870e331fab833efb2a2aa33f3205a2b83c3 - .claude/skills/caveman-stats/README.md: sha256:64d206ee7524625a8541ec064e0c7ec27c3764bae63b685cb5eecbd20aa76bdc - .claude/skills/caveman-stats/SKILL.md: sha256:b9e49e46ede956e0c1633eae82a695c996f728bc48f3903f001d301068a2b0ea - .claude/skills/caveman/.claude-plugin/plugin.json: sha256:1957b1104cdccc0843ebf71489f0bb24b98cce1e905a981ff001049cc7ee3b32 - .claude/skills/caveman/bin/install.js: sha256:cdf3572b0b30fb0e5f1ee711e11fa449c6398e5d19c0bf1d45c44159e0829f13 - content_hash: sha256:3e238a67ce11def6d73257cf8eabad78899df2909c57997e956be8e3e32943db - - repo_url: JuliusBrussee/skills - host: github.com - resolved_commit: 1fa763bf01ab01b7012b6767a564a071aa115c85 - resolved_ref: main - package_type: skill_bundle - deployed_files: - - .agents/skills/grill-me - - .agents/skills/grill-me/SKILL.md - - .agents/skills/junior-to-senior - - .agents/skills/junior-to-senior/SKILL.md - - .agents/skills/junior-to-senior/references/research-playbook.md - - .agents/skills/junior-to-senior/references/review-rubric.md - - .claude/skills/grill-me - - .claude/skills/grill-me/SKILL.md - - .claude/skills/junior-to-senior - - .claude/skills/junior-to-senior/SKILL.md - - .claude/skills/junior-to-senior/references/research-playbook.md - - .claude/skills/junior-to-senior/references/review-rubric.md - deployed_file_hashes: - .agents/skills/grill-me/SKILL.md: sha256:fb51ed1c9ade960e75ce781c4da411939c24a61a06d21c04060ee0e7660e673b - .agents/skills/junior-to-senior/SKILL.md: sha256:5fc658c5ff279aaa5f3b8cdd8857526d29806cf28e66f47c530a207207d9cfb6 - .agents/skills/junior-to-senior/references/research-playbook.md: sha256:b5e9e38a541aa73fd14527a37d08234352f2e664634946e4ecb8b0c1ae6b5c22 - .agents/skills/junior-to-senior/references/review-rubric.md: sha256:7d025c0a76a7e5fde4c6dca6b58885d74b43a5b0b054bd265cc78be5ec3c40de - .claude/skills/grill-me/SKILL.md: sha256:fb51ed1c9ade960e75ce781c4da411939c24a61a06d21c04060ee0e7660e673b - .claude/skills/junior-to-senior/SKILL.md: sha256:5fc658c5ff279aaa5f3b8cdd8857526d29806cf28e66f47c530a207207d9cfb6 - .claude/skills/junior-to-senior/references/research-playbook.md: sha256:b5e9e38a541aa73fd14527a37d08234352f2e664634946e4ecb8b0c1ae6b5c22 - .claude/skills/junior-to-senior/references/review-rubric.md: sha256:7d025c0a76a7e5fde4c6dca6b58885d74b43a5b0b054bd265cc78be5ec3c40de - content_hash: sha256:e6e8748936c290c0773d1a2c2b50638c94b848b31fcd61e16c4c5e613e3f0a0c - skill_subset: - - grill-me - - junior-to-senior - - repo_url: anthropics/claude-plugins-official - host: github.com - resolved_commit: c8e9219efb196fde7a56104de3fa8e1f7627e5a5 - resolved_ref: main - virtual_path: plugins/skill-creator - is_virtual: true - package_type: marketplace_plugin - deployed_files: - - .agents/skills/skill-creator - - .agents/skills/skill-creator/LICENSE.txt - - .agents/skills/skill-creator/SKILL.md - - .agents/skills/skill-creator/agents/analyzer.md - - .agents/skills/skill-creator/agents/comparator.md - - .agents/skills/skill-creator/agents/grader.md - - .agents/skills/skill-creator/assets/eval_review.html - - .agents/skills/skill-creator/eval-viewer/generate_review.py - - .agents/skills/skill-creator/eval-viewer/viewer.html - - .agents/skills/skill-creator/references/schemas.md - - .agents/skills/skill-creator/scripts/__init__.py - - .agents/skills/skill-creator/scripts/aggregate_benchmark.py - - .agents/skills/skill-creator/scripts/generate_report.py - - .agents/skills/skill-creator/scripts/improve_description.py - - .agents/skills/skill-creator/scripts/package_skill.py - - .agents/skills/skill-creator/scripts/quick_validate.py - - .agents/skills/skill-creator/scripts/run_eval.py - - .agents/skills/skill-creator/scripts/run_loop.py - - .agents/skills/skill-creator/scripts/utils.py - - .claude/skills/skill-creator - - .claude/skills/skill-creator/LICENSE.txt - - .claude/skills/skill-creator/SKILL.md - - .claude/skills/skill-creator/agents/analyzer.md - - .claude/skills/skill-creator/agents/comparator.md - - .claude/skills/skill-creator/agents/grader.md - - .claude/skills/skill-creator/assets/eval_review.html - - .claude/skills/skill-creator/eval-viewer/generate_review.py - - .claude/skills/skill-creator/eval-viewer/viewer.html - - .claude/skills/skill-creator/references/schemas.md - - .claude/skills/skill-creator/scripts/__init__.py - - .claude/skills/skill-creator/scripts/aggregate_benchmark.py - - .claude/skills/skill-creator/scripts/generate_report.py - - .claude/skills/skill-creator/scripts/improve_description.py - - .claude/skills/skill-creator/scripts/package_skill.py - - .claude/skills/skill-creator/scripts/quick_validate.py - - .claude/skills/skill-creator/scripts/run_eval.py - - .claude/skills/skill-creator/scripts/run_loop.py - - .claude/skills/skill-creator/scripts/utils.py - deployed_file_hashes: - .agents/skills/skill-creator/LICENSE.txt: sha256:58d1e17ffe5109a7ae296caafcadfdbe6a7d176f0bc4ab01e12a689b0499d8bd - .agents/skills/skill-creator/SKILL.md: sha256:dcd4803e61e913e6fc27294184cd3a71f09f5e924ff20c8a9a20173e7b3c2bcf - .agents/skills/skill-creator/agents/analyzer.md: sha256:bf68f4cac5a56c673a928c2e6d619586c5b93ea364026ab37547772cb45a663a - .agents/skills/skill-creator/agents/comparator.md: sha256:fe1fc9787c495d864c5d6eada47396478572325fde1b33a96d78bf4b849b7a3e - .agents/skills/skill-creator/agents/grader.md: sha256:57134da0c1a4eea33fbd74a1c9c44aa814f07d6bc64de303edb586f941e5d21a - .agents/skills/skill-creator/assets/eval_review.html: sha256:ce477dcc74dc1c0d1d3352646a79167b5a63634e936b1019160025065974e452 - .agents/skills/skill-creator/eval-viewer/generate_review.py: sha256:fc9d1b9243fe5ab6012ebd579bd76d0035de1b79fd3b969de114defab26478fb - .agents/skills/skill-creator/eval-viewer/viewer.html: sha256:a53213426ee1100441d701a3a0d49cda7a842f992d2c36463f4d3cc0258575fa - .agents/skills/skill-creator/references/schemas.md: sha256:8e8876180a8989b406a4d3edddf875b04cdfd5805cc8616686d552b11ce4455f - .agents/skills/skill-creator/scripts/__init__.py: sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 - .agents/skills/skill-creator/scripts/aggregate_benchmark.py: sha256:123ef128ea5ccc01a4b1ac212ef5567f21e9c13d3d240609780beeb3200c49aa - .agents/skills/skill-creator/scripts/generate_report.py: sha256:13df7118a3c50c83c4c3250a606d5f2b20b25a3d44cbc392b3d669ec75281453 - .agents/skills/skill-creator/scripts/improve_description.py: sha256:87d864570220b699fac52da309d2d6efdb060647bfebc74f768128e646accf80 - .agents/skills/skill-creator/scripts/package_skill.py: sha256:1a33059b0db1ef73375d46d513e5ea81369d2e8838c970597b0d52ddef8d1c0f - .agents/skills/skill-creator/scripts/quick_validate.py: sha256:67cf5703402013936c8fb75ad6a1afecd8841d45cc5e606b634eb05825fde365 - .agents/skills/skill-creator/scripts/run_eval.py: sha256:43e3b8f80dbf69c343967ba77e268fae991d9fa3ed68b32a0ff02532cd48657f - .agents/skills/skill-creator/scripts/run_loop.py: sha256:7bd6f674203168520517eec94c55f493c0d154339b061b4d7c0f0dad187d0f21 - .agents/skills/skill-creator/scripts/utils.py: sha256:3af8ae62c40c73ab712207436a0d9a981e845f25c5a7040229eb189cc8e45bb1 - .claude/skills/skill-creator/LICENSE.txt: sha256:58d1e17ffe5109a7ae296caafcadfdbe6a7d176f0bc4ab01e12a689b0499d8bd - .claude/skills/skill-creator/SKILL.md: sha256:dcd4803e61e913e6fc27294184cd3a71f09f5e924ff20c8a9a20173e7b3c2bcf - .claude/skills/skill-creator/agents/analyzer.md: sha256:bf68f4cac5a56c673a928c2e6d619586c5b93ea364026ab37547772cb45a663a - .claude/skills/skill-creator/agents/comparator.md: sha256:fe1fc9787c495d864c5d6eada47396478572325fde1b33a96d78bf4b849b7a3e - .claude/skills/skill-creator/agents/grader.md: sha256:57134da0c1a4eea33fbd74a1c9c44aa814f07d6bc64de303edb586f941e5d21a - .claude/skills/skill-creator/assets/eval_review.html: sha256:ce477dcc74dc1c0d1d3352646a79167b5a63634e936b1019160025065974e452 - .claude/skills/skill-creator/eval-viewer/generate_review.py: sha256:fc9d1b9243fe5ab6012ebd579bd76d0035de1b79fd3b969de114defab26478fb - .claude/skills/skill-creator/eval-viewer/viewer.html: sha256:a53213426ee1100441d701a3a0d49cda7a842f992d2c36463f4d3cc0258575fa - .claude/skills/skill-creator/references/schemas.md: sha256:8e8876180a8989b406a4d3edddf875b04cdfd5805cc8616686d552b11ce4455f - .claude/skills/skill-creator/scripts/__init__.py: sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 - .claude/skills/skill-creator/scripts/aggregate_benchmark.py: sha256:123ef128ea5ccc01a4b1ac212ef5567f21e9c13d3d240609780beeb3200c49aa - .claude/skills/skill-creator/scripts/generate_report.py: sha256:13df7118a3c50c83c4c3250a606d5f2b20b25a3d44cbc392b3d669ec75281453 - .claude/skills/skill-creator/scripts/improve_description.py: sha256:87d864570220b699fac52da309d2d6efdb060647bfebc74f768128e646accf80 - .claude/skills/skill-creator/scripts/package_skill.py: sha256:1a33059b0db1ef73375d46d513e5ea81369d2e8838c970597b0d52ddef8d1c0f - .claude/skills/skill-creator/scripts/quick_validate.py: sha256:67cf5703402013936c8fb75ad6a1afecd8841d45cc5e606b634eb05825fde365 - .claude/skills/skill-creator/scripts/run_eval.py: sha256:43e3b8f80dbf69c343967ba77e268fae991d9fa3ed68b32a0ff02532cd48657f - .claude/skills/skill-creator/scripts/run_loop.py: sha256:7bd6f674203168520517eec94c55f493c0d154339b061b4d7c0f0dad187d0f21 - .claude/skills/skill-creator/scripts/utils.py: sha256:3af8ae62c40c73ab712207436a0d9a981e845f25c5a7040229eb189cc8e45bb1 - content_hash: sha256:315413cbe6e78ef43e6d3b63e9c15decbc689a9263b95377403c5a69f804d902 - - repo_url: obra/superpowers - host: github.com - resolved_commit: 896224c4b1879920ab573417e68fd51d2ccc9072 - resolved_ref: v6.0.3 - package_type: marketplace_plugin - deployed_files: - - .agents/skills/brainstorming - - .agents/skills/brainstorming/SKILL.md - - .agents/skills/brainstorming/scripts/frame-template.html - - .agents/skills/brainstorming/scripts/helper.js - - .agents/skills/brainstorming/scripts/server.cjs - - .agents/skills/brainstorming/scripts/start-server.sh - - .agents/skills/brainstorming/scripts/stop-server.sh - - .agents/skills/brainstorming/spec-document-reviewer-prompt.md - - .agents/skills/brainstorming/visual-companion.md - - .agents/skills/dispatching-parallel-agents - - .agents/skills/dispatching-parallel-agents/SKILL.md - - .agents/skills/executing-plans - - .agents/skills/executing-plans/SKILL.md - - .agents/skills/finishing-a-development-branch - - .agents/skills/finishing-a-development-branch/SKILL.md - - .agents/skills/receiving-code-review - - .agents/skills/receiving-code-review/SKILL.md - - .agents/skills/requesting-code-review - - .agents/skills/requesting-code-review/SKILL.md - - .agents/skills/requesting-code-review/code-reviewer.md - - .agents/skills/subagent-driven-development - - .agents/skills/subagent-driven-development/SKILL.md - - .agents/skills/subagent-driven-development/implementer-prompt.md - - .agents/skills/subagent-driven-development/scripts/review-package - - .agents/skills/subagent-driven-development/scripts/sdd-workspace - - .agents/skills/subagent-driven-development/scripts/task-brief - - .agents/skills/subagent-driven-development/task-reviewer-prompt.md - - .agents/skills/systematic-debugging - - .agents/skills/systematic-debugging/CREATION-LOG.md - - .agents/skills/systematic-debugging/SKILL.md - - .agents/skills/systematic-debugging/condition-based-waiting-example.ts - - .agents/skills/systematic-debugging/condition-based-waiting.md - - .agents/skills/systematic-debugging/defense-in-depth.md - - .agents/skills/systematic-debugging/find-polluter.sh - - .agents/skills/systematic-debugging/root-cause-tracing.md - - .agents/skills/systematic-debugging/test-academic.md - - .agents/skills/systematic-debugging/test-pressure-1.md - - .agents/skills/systematic-debugging/test-pressure-2.md - - .agents/skills/systematic-debugging/test-pressure-3.md - - .agents/skills/test-driven-development - - .agents/skills/test-driven-development/SKILL.md - - .agents/skills/test-driven-development/testing-anti-patterns.md - - .agents/skills/using-git-worktrees - - .agents/skills/using-git-worktrees/SKILL.md - - .agents/skills/using-superpowers - - .agents/skills/using-superpowers/SKILL.md - - .agents/skills/using-superpowers/references/antigravity-tools.md - - .agents/skills/using-superpowers/references/claude-code-tools.md - - .agents/skills/using-superpowers/references/codex-tools.md - - .agents/skills/using-superpowers/references/copilot-tools.md - - .agents/skills/using-superpowers/references/gemini-tools.md - - .agents/skills/using-superpowers/references/pi-tools.md - - .agents/skills/verification-before-completion - - .agents/skills/verification-before-completion/SKILL.md - - .agents/skills/writing-plans - - .agents/skills/writing-plans/SKILL.md - - .agents/skills/writing-plans/plan-document-reviewer-prompt.md - - .agents/skills/writing-skills - - .agents/skills/writing-skills/SKILL.md - - .agents/skills/writing-skills/anthropic-best-practices.md - - .agents/skills/writing-skills/examples/CLAUDE_MD_TESTING.md - - .agents/skills/writing-skills/graphviz-conventions.dot - - .agents/skills/writing-skills/persuasion-principles.md - - .agents/skills/writing-skills/render-graphs.js - - .agents/skills/writing-skills/testing-skills-with-subagents.md - - .claude/hooks/superpowers/hooks/run-hook.cmd - - .claude/skills/verification-before-completion - - .claude/skills/verification-before-completion/SKILL.md - deployed_file_hashes: - .agents/skills/brainstorming/SKILL.md: sha256:e14914605f640e0841758e45d0ab2a53243b59b921f929e47921c99668f2e61d - .agents/skills/brainstorming/scripts/frame-template.html: sha256:6a8a4e58bd6a44b904e2e3c57de774481d909204597e1498de53f1b2fecc4c4e - .agents/skills/brainstorming/scripts/helper.js: sha256:43c6d69954a46ec34a2a262bcc62a9a7e83e839c739f199cb72646d397c686e3 - .agents/skills/brainstorming/scripts/server.cjs: sha256:2d2961ea8d11f56c5f4c3a1a68d22709efa5d7601a2246d8c880774e7e9e8412 - .agents/skills/brainstorming/scripts/start-server.sh: sha256:a4e5ae84275bcaacd2f84345afeabe59cf7b00ba080e123da7cc1fb226f12847 - .agents/skills/brainstorming/scripts/stop-server.sh: sha256:0b5ccbbd57f62d3ed88993f7940b5ee0e5c0fc9b21c550c623da4f6292e47daf - .agents/skills/brainstorming/spec-document-reviewer-prompt.md: sha256:95a0a195de9d984be2fffa95bab16fc8c563bc296a9cfc5e9c29cb3ece0d7457 - .agents/skills/brainstorming/visual-companion.md: sha256:60cbad29b9dd7eaf08da020e301c498a72230b2e13c1813fa967a135ffcc1d71 - .agents/skills/dispatching-parallel-agents/SKILL.md: sha256:f0df13f584049059cc5619f90061405b89dcc6e28ab3f2a8517d27d99c7a46a6 - .agents/skills/executing-plans/SKILL.md: sha256:b357c2a2e5bf24adf237b5d4a83cacd828f07b886d0873ad91546aa3c99905c9 - .agents/skills/finishing-a-development-branch/SKILL.md: sha256:e6d4a812de900d33c6eacfb40747f99427f25c304a7b7099120f9373b115a47f - .agents/skills/receiving-code-review/SKILL.md: sha256:647036bbdab7bf2317e14e079595e984c9030f64295e2b4c0fb57dbeb48f25dd - .agents/skills/requesting-code-review/SKILL.md: sha256:1017ccdd5bc61fab67c654cf118cbdb520464b313073a0a6b9a6b9aa647a3ad6 - .agents/skills/requesting-code-review/code-reviewer.md: sha256:b2f2ec7596925fe52dac158fdfbca19b3a7d779d619c481e6706a6c0001662d3 - .agents/skills/subagent-driven-development/SKILL.md: sha256:abdd682e85bfc52413c997bef4612b2a4094168d8e4d6a967f1c0dcde4d83816 - .agents/skills/subagent-driven-development/implementer-prompt.md: sha256:49018b28dc11bc9f3d13a28959bb10ae1a96eabc5d8f19f4079d901f9ff2bf64 - .agents/skills/subagent-driven-development/scripts/review-package: sha256:0c0629f6e2c46fc8bf68dcfb8a247ab24eb548b7004fe494035e6fcba9b5cdfb - .agents/skills/subagent-driven-development/scripts/sdd-workspace: sha256:9430befaff459ed862415b700c1a9efe45fd34838a684802a1d8320de0c3a007 - .agents/skills/subagent-driven-development/scripts/task-brief: sha256:5380283f00bffa99ab82ae78482b7d248abe10655129c72ca7050bdc0b6a85e1 - .agents/skills/subagent-driven-development/task-reviewer-prompt.md: sha256:2eb9d54373420de25bc0bd00635d3a3123a6c0eb30c881168e6f3348e2387331 - .agents/skills/systematic-debugging/CREATION-LOG.md: sha256:c24733a5b1821bd6bed1fc950261f0b9f4e90097e0bbb96459d8179713730789 - .agents/skills/systematic-debugging/SKILL.md: sha256:3b20719eca4f0461cb51a195221320d775dcf03b6859271066a03a5132a6ce7a - .agents/skills/systematic-debugging/condition-based-waiting-example.ts: sha256:40ae5ebe497fdf310200e43fe986552546d0a22837c0d39e855db1cfd33eb88e - .agents/skills/systematic-debugging/condition-based-waiting.md: sha256:e89fec8400d6cd50f43407cec9fab50976ba4d55d0ec2eb51c0bd68036b54c26 - .agents/skills/systematic-debugging/defense-in-depth.md: sha256:1e175fb86fc357e58c6aebf5441e481e1b7868b4380c0456b63a17eefbd18ba7 - .agents/skills/systematic-debugging/find-polluter.sh: sha256:6462747eae9b175ac145b78bcfaeab755654a75e32637f08eb633f065a9e1d7c - .agents/skills/systematic-debugging/root-cause-tracing.md: sha256:6b0622269e098ca1399e123e553fd385f0b6412d88ef0e9c4f5a8ea9cf1cec7b - .agents/skills/systematic-debugging/test-academic.md: sha256:fe2ba480d78ac0d686dc025f41c2a32a43d642bf533f91b0c6053a04d35d6486 - .agents/skills/systematic-debugging/test-pressure-1.md: sha256:0b6a915db0054577819834c79be9eb614e97bddba10d73768e1fbe91cfed048a - .agents/skills/systematic-debugging/test-pressure-2.md: sha256:b2030aeffba07050e8ad573ddf87486457c4a016a786bb326235bebd856f2016 - .agents/skills/systematic-debugging/test-pressure-3.md: sha256:96b50a52e2c7989c9cf20fb752c47c1e9a3a70dc362f8f7989f8f5b64dac7708 - .agents/skills/test-driven-development/SKILL.md: sha256:b5b4717b8b761cce15a6cfe9022e33fd959e0894c0c39d72c9cb49c23486c10e - .agents/skills/test-driven-development/testing-anti-patterns.md: sha256:bde453bc258f06543987477c837939afaa774ea2acbd9f308d702fc452bc4283 - .agents/skills/using-git-worktrees/SKILL.md: sha256:e2c3ec142e52868a51af246c620cd76ab648dcf27d6900d47e6ffd07159a9794 - .agents/skills/using-superpowers/SKILL.md: sha256:918562bbba1c73d1407c50de77a92eeaa6930508bdb57a64324d7d508a9069e1 - .agents/skills/using-superpowers/references/antigravity-tools.md: sha256:00b16e8e8166d972766613c4244a7901d0413af37944741a8b8f40105f9f7f05 - .agents/skills/using-superpowers/references/claude-code-tools.md: sha256:7d9244dd278db15caa8f9f86bda0546c91fc65163f94b2499999fc72b69c3666 - .agents/skills/using-superpowers/references/codex-tools.md: sha256:cbce77a3a430688ef0cc3c8f03d17d2df1a2c78603e8b138cec522b74d75e4ab - .agents/skills/using-superpowers/references/copilot-tools.md: sha256:9627943f22c8db64ac7fbac3b6e399f73650a5cf3618f7966e802e7fa58d4595 - .agents/skills/using-superpowers/references/gemini-tools.md: sha256:62b9157bcb0ee3c6784e3d0da0798ddfa5872f9e0c34bea48f3079dabea71965 - .agents/skills/using-superpowers/references/pi-tools.md: sha256:e89e2d5c7b89cdd2a511aa7ad3c191290baaaed8913006c30f283ac7c4b091fb - .agents/skills/verification-before-completion/SKILL.md: sha256:ea52d15aabaf72bc6b558efe2c126f161b53961090ddcd712000273bfe8c7b6c - .agents/skills/writing-plans/SKILL.md: sha256:272e1af349f5062c28dc282b3e21b220d58d683a7314a10c455b7432ec91d845 - .agents/skills/writing-plans/plan-document-reviewer-prompt.md: sha256:aa728b96aad603c8be28875a4305637f6c984aa81ffcadcb13e743202fa2a0c7 - .agents/skills/writing-skills/SKILL.md: sha256:0903f8bef0908ccb5d9fe1a6cfbcf3d20a581c7bfcb547f71aafb795b9433b7b - .agents/skills/writing-skills/anthropic-best-practices.md: sha256:217629b356c09c9bd11017c9788e8fc654ca1b32c92d4a51cd490e16dd65e59a - .agents/skills/writing-skills/examples/CLAUDE_MD_TESTING.md: sha256:0b379a3415e185d3c434b3ad283d8aa132f3022c2a4f210f168865b5986bcef0 - .agents/skills/writing-skills/graphviz-conventions.dot: sha256:e2890a593c91370e384b42f2f67b1a6232c9e69dddea7891a0c1c46d7b20b694 - .agents/skills/writing-skills/persuasion-principles.md: sha256:a51bc9bf75189ea73a27b3fb504a2fdfdb966fb1f7f1cdf03203230a216ccc03 - .agents/skills/writing-skills/render-graphs.js: sha256:ccda971a87bb185f8febf81c56b556a20d026fa980c17b35fa3e8824fbb37852 - .agents/skills/writing-skills/testing-skills-with-subagents.md: sha256:c711346852c911b24a84aa161e0cff06a4cd7f4e2fa9e9c0a266cead5afcbade - .claude/hooks/superpowers/hooks/run-hook.cmd: sha256:d3d9c6199678dab2858e60509dde5e7414f13c2a2b5e48a38b1d368b6e1d6abb - .claude/skills/verification-before-completion/SKILL.md: sha256:ea52d15aabaf72bc6b558efe2c126f161b53961090ddcd712000273bfe8c7b6c - content_hash: sha256:5b14832a84b21359e629052d2f4692fabded091ae2a80d085909776747e1f26e - declared_license: MIT - - repo_url: schpet/linear-cli - host: github.com - resolved_commit: a3e2960b7fb5951dcab91b0439b1e6518d828eca - resolved_ref: v2.0.0 - package_type: marketplace_plugin - deployed_files: - - .agents/skills/linear-cli - - .agents/skills/linear-cli/SKILL.md - - .agents/skills/linear-cli/SKILL.template.md - - .agents/skills/linear-cli/references/api.md - - .agents/skills/linear-cli/references/auth.md - - .agents/skills/linear-cli/references/commands.md - - .agents/skills/linear-cli/references/config.md - - .agents/skills/linear-cli/references/cycle.md - - .agents/skills/linear-cli/references/document.md - - .agents/skills/linear-cli/references/initiative-update.md - - .agents/skills/linear-cli/references/initiative.md - - .agents/skills/linear-cli/references/issue.md - - .agents/skills/linear-cli/references/label.md - - .agents/skills/linear-cli/references/milestone.md - - .agents/skills/linear-cli/references/organization-features.md - - .agents/skills/linear-cli/references/project-update.md - - .agents/skills/linear-cli/references/project.md - - .agents/skills/linear-cli/references/schema.md - - .agents/skills/linear-cli/references/team.md - - .agents/skills/linear-cli/scripts/generate-docs.ts - - .claude/skills/linear-cli - - .claude/skills/linear-cli/SKILL.md - - .claude/skills/linear-cli/SKILL.template.md - - .claude/skills/linear-cli/references/api.md - - .claude/skills/linear-cli/references/auth.md - - .claude/skills/linear-cli/references/commands.md - - .claude/skills/linear-cli/references/config.md - - .claude/skills/linear-cli/references/cycle.md - - .claude/skills/linear-cli/references/document.md - - .claude/skills/linear-cli/references/initiative-update.md - - .claude/skills/linear-cli/references/initiative.md - - .claude/skills/linear-cli/references/issue.md - - .claude/skills/linear-cli/references/label.md - - .claude/skills/linear-cli/references/milestone.md - - .claude/skills/linear-cli/references/organization-features.md - - .claude/skills/linear-cli/references/project-update.md - - .claude/skills/linear-cli/references/project.md - - .claude/skills/linear-cli/references/schema.md - - .claude/skills/linear-cli/references/team.md - - .claude/skills/linear-cli/scripts/generate-docs.ts - deployed_file_hashes: - .agents/skills/linear-cli/SKILL.md: sha256:21215fd54b85411808775a30129bf205689f582f6eaa9a43c1d3d6a71f1e6142 - .agents/skills/linear-cli/SKILL.template.md: sha256:964a78ba707084e684a3aac2048e544b14aa1faa100cd274cf11e9861671cc38 - .agents/skills/linear-cli/references/api.md: sha256:8eb38f27a7c0e4927a3d4e2538162643d023a5269747044d623f9934030f5aff - .agents/skills/linear-cli/references/auth.md: sha256:554d464dcc12a0c88ec41bcec75e099db19425a12b77531a4dba45e326852bcc - .agents/skills/linear-cli/references/commands.md: sha256:0e7c68448aed8142f6de4affc5371d9c42f5afad1f3b453cd7361b775f92e4f5 - .agents/skills/linear-cli/references/config.md: sha256:50b20d99e2b5084e2d5e194f2aba2d9448cd0dbc170aba57a7331aa3e46d5b64 - .agents/skills/linear-cli/references/cycle.md: sha256:c45f9a3165ba9da3eec1c565be24ae4f739834e5e0a084678e48452700aee715 - .agents/skills/linear-cli/references/document.md: sha256:5cacf83b171d9d8e9ca2d2613e36b38ac3f52346a6403f8ac6a0022c5caae25f - .agents/skills/linear-cli/references/initiative-update.md: sha256:d8b1e2021125764f8f9cecfe937bbecdc630ee1dfb7becd0ba5277d82662c158 - .agents/skills/linear-cli/references/initiative.md: sha256:17c861062fa8cb56c93542538a9125971b1228991939e5209c2b8993b15a3745 - .agents/skills/linear-cli/references/issue.md: sha256:bef27956091b5356071bed88c2827feec4f069f98cf6930775dd95eb3cc3c58c - .agents/skills/linear-cli/references/label.md: sha256:3faf2944a5f20bc681642e711a2e49d0acecb7ea2217c25ea8b5208e2651848b - .agents/skills/linear-cli/references/milestone.md: sha256:56abfb51798e77c7af97f7a2ea42b0cddeab2724f79306864e180df878266dfa - .agents/skills/linear-cli/references/organization-features.md: sha256:9dee08bd13ab7f93764076f9a53ec92a7e2dfd54ae3cbceb3c8eb1ce39613388 - .agents/skills/linear-cli/references/project-update.md: sha256:62a7dcff5ddb8017fa43430de8680267f0047d90b37b41c21cc36ac078a88b89 - .agents/skills/linear-cli/references/project.md: sha256:fc9bd98369c1ae2b1418f841206c400624cfb7c985d5cbafa5030769d0c4c261 - .agents/skills/linear-cli/references/schema.md: sha256:acbb90509d5587d9c94389429ade18f31cfac8fb139b8b10b2bb4d40683eb33d - .agents/skills/linear-cli/references/team.md: sha256:12729537db0340a20c884f426ee7680c6e39a00b4838a6cf91df7a74f43ea785 - .agents/skills/linear-cli/scripts/generate-docs.ts: sha256:cfad16a700fd2741a5dc9d804d7f123613dc213496ad6bf38d499cb6137a55eb - .claude/skills/linear-cli/SKILL.md: sha256:21215fd54b85411808775a30129bf205689f582f6eaa9a43c1d3d6a71f1e6142 - .claude/skills/linear-cli/SKILL.template.md: sha256:964a78ba707084e684a3aac2048e544b14aa1faa100cd274cf11e9861671cc38 - .claude/skills/linear-cli/references/api.md: sha256:8eb38f27a7c0e4927a3d4e2538162643d023a5269747044d623f9934030f5aff - .claude/skills/linear-cli/references/auth.md: sha256:554d464dcc12a0c88ec41bcec75e099db19425a12b77531a4dba45e326852bcc - .claude/skills/linear-cli/references/commands.md: sha256:0e7c68448aed8142f6de4affc5371d9c42f5afad1f3b453cd7361b775f92e4f5 - .claude/skills/linear-cli/references/config.md: sha256:50b20d99e2b5084e2d5e194f2aba2d9448cd0dbc170aba57a7331aa3e46d5b64 - .claude/skills/linear-cli/references/cycle.md: sha256:c45f9a3165ba9da3eec1c565be24ae4f739834e5e0a084678e48452700aee715 - .claude/skills/linear-cli/references/document.md: sha256:5cacf83b171d9d8e9ca2d2613e36b38ac3f52346a6403f8ac6a0022c5caae25f - .claude/skills/linear-cli/references/initiative-update.md: sha256:d8b1e2021125764f8f9cecfe937bbecdc630ee1dfb7becd0ba5277d82662c158 - .claude/skills/linear-cli/references/initiative.md: sha256:17c861062fa8cb56c93542538a9125971b1228991939e5209c2b8993b15a3745 - .claude/skills/linear-cli/references/issue.md: sha256:bef27956091b5356071bed88c2827feec4f069f98cf6930775dd95eb3cc3c58c - .claude/skills/linear-cli/references/label.md: sha256:3faf2944a5f20bc681642e711a2e49d0acecb7ea2217c25ea8b5208e2651848b - .claude/skills/linear-cli/references/milestone.md: sha256:56abfb51798e77c7af97f7a2ea42b0cddeab2724f79306864e180df878266dfa - .claude/skills/linear-cli/references/organization-features.md: sha256:9dee08bd13ab7f93764076f9a53ec92a7e2dfd54ae3cbceb3c8eb1ce39613388 - .claude/skills/linear-cli/references/project-update.md: sha256:62a7dcff5ddb8017fa43430de8680267f0047d90b37b41c21cc36ac078a88b89 - .claude/skills/linear-cli/references/project.md: sha256:fc9bd98369c1ae2b1418f841206c400624cfb7c985d5cbafa5030769d0c4c261 - .claude/skills/linear-cli/references/schema.md: sha256:acbb90509d5587d9c94389429ade18f31cfac8fb139b8b10b2bb4d40683eb33d - .claude/skills/linear-cli/references/team.md: sha256:12729537db0340a20c884f426ee7680c6e39a00b4838a6cf91df7a74f43ea785 - .claude/skills/linear-cli/scripts/generate-docs.ts: sha256:cfad16a700fd2741a5dc9d804d7f123613dc213496ad6bf38d499cb6137a55eb - content_hash: sha256:b6dcb09081bd6515548614982e6e135d1758802ee32dda30932067207ccb291c - - repo_url: tavily-ai/skills - host: github.com - resolved_commit: ea5e8201b0d3ed9c10b70b71187589bd761fe2d2 - resolved_ref: main - package_type: marketplace_plugin - deployed_files: - - .agents/skills/tavily-best-practices - - .agents/skills/tavily-best-practices/SKILL.md - - .agents/skills/tavily-best-practices/references/crawl.md - - .agents/skills/tavily-best-practices/references/extract.md - - .agents/skills/tavily-best-practices/references/integrations.md - - .agents/skills/tavily-best-practices/references/research.md - - .agents/skills/tavily-best-practices/references/sdk.md - - .agents/skills/tavily-best-practices/references/search.md - - .agents/skills/tavily-cli - - .agents/skills/tavily-cli/SKILL.md - - .agents/skills/tavily-crawl - - .agents/skills/tavily-crawl/SKILL.md - - .agents/skills/tavily-dynamic-search - - .agents/skills/tavily-dynamic-search/SKILL.md - - .agents/skills/tavily-extract - - .agents/skills/tavily-extract/SKILL.md - - .agents/skills/tavily-map - - .agents/skills/tavily-map/SKILL.md - - .agents/skills/tavily-research - - .agents/skills/tavily-research/SKILL.md - - .agents/skills/tavily-search - - .agents/skills/tavily-search/SKILL.md - - .claude/skills/tavily-best-practices - - .claude/skills/tavily-best-practices/SKILL.md - - .claude/skills/tavily-best-practices/references/crawl.md - - .claude/skills/tavily-best-practices/references/extract.md - - .claude/skills/tavily-best-practices/references/integrations.md - - .claude/skills/tavily-best-practices/references/research.md - - .claude/skills/tavily-best-practices/references/sdk.md - - .claude/skills/tavily-best-practices/references/search.md - - .claude/skills/tavily-cli - - .claude/skills/tavily-cli/SKILL.md - - .claude/skills/tavily-crawl - - .claude/skills/tavily-crawl/SKILL.md - - .claude/skills/tavily-dynamic-search - - .claude/skills/tavily-dynamic-search/SKILL.md - - .claude/skills/tavily-extract - - .claude/skills/tavily-extract/SKILL.md - - .claude/skills/tavily-map - - .claude/skills/tavily-map/SKILL.md - - .claude/skills/tavily-research - - .claude/skills/tavily-research/SKILL.md - - .claude/skills/tavily-search - - .claude/skills/tavily-search/SKILL.md - deployed_file_hashes: - .agents/skills/tavily-best-practices/SKILL.md: sha256:8972384d52e22c76b82928c309f8faff3bfa2f68c4e4d7ad3ce1fd882d99048e - .agents/skills/tavily-best-practices/references/crawl.md: sha256:725e613330a6a3775fe265e6fa0c782f469f6f8580788012c78893a9ac010b84 - .agents/skills/tavily-best-practices/references/extract.md: sha256:7995998ce1969bf54892f84c9f1e583582eea9a354ab666c0869607635e41d5e - .agents/skills/tavily-best-practices/references/integrations.md: sha256:d53ba9f73d14f24a4ac81f5d77d584b4ff7fbbd4dd3b70f5e7c21cbc175cb65b - .agents/skills/tavily-best-practices/references/research.md: sha256:99e3c706b5ed3a51842204f43c552b75e3243d6facc3b8b7c9878a1a163e1ec7 - .agents/skills/tavily-best-practices/references/sdk.md: sha256:b0c745315f2ca16647a2c298644dc9918ea31bf732921b02841e26814da2087a - .agents/skills/tavily-best-practices/references/search.md: sha256:db5d7f52e42ec93f54b6cb04224e63ce689fd54ca2e3011754908eb54ce82261 - .agents/skills/tavily-cli/SKILL.md: sha256:aa8cee0e8dbf3bb98d2281b1874c7431de880c5c6ff6a3ca8cb346ac3f513f36 - .agents/skills/tavily-crawl/SKILL.md: sha256:5ed61112f6dfd9f06341ab0de91a65db423e240dde0b5b56036cdb4ee4e720d4 - .agents/skills/tavily-dynamic-search/SKILL.md: sha256:63c6ca34a2fd7bfd0b9a4d794755eea22f8dc7abd0de24f6683a38ed66781b6d - .agents/skills/tavily-extract/SKILL.md: sha256:127bc8470090b2a8b43d3556e539c6c65a24ad3e87c1539a0d792c4d3b644ceb - .agents/skills/tavily-map/SKILL.md: sha256:f9b28a6bdb13a58078a1d0e9af0b2df26bd7e1aea9a4227a490eac9f2c3d4464 - .agents/skills/tavily-research/SKILL.md: sha256:e246188571612cc0df88060790fa953fc5daab6ac6fa85a5c799b54c90bfd7b2 - .agents/skills/tavily-search/SKILL.md: sha256:83679ae2adac1c0dfdfba52ca12117d386c3238865b5b35cfa4eab9a6617322d - .claude/skills/tavily-best-practices/SKILL.md: sha256:8972384d52e22c76b82928c309f8faff3bfa2f68c4e4d7ad3ce1fd882d99048e - .claude/skills/tavily-best-practices/references/crawl.md: sha256:725e613330a6a3775fe265e6fa0c782f469f6f8580788012c78893a9ac010b84 - .claude/skills/tavily-best-practices/references/extract.md: sha256:7995998ce1969bf54892f84c9f1e583582eea9a354ab666c0869607635e41d5e - .claude/skills/tavily-best-practices/references/integrations.md: sha256:d53ba9f73d14f24a4ac81f5d77d584b4ff7fbbd4dd3b70f5e7c21cbc175cb65b - .claude/skills/tavily-best-practices/references/research.md: sha256:99e3c706b5ed3a51842204f43c552b75e3243d6facc3b8b7c9878a1a163e1ec7 - .claude/skills/tavily-best-practices/references/sdk.md: sha256:b0c745315f2ca16647a2c298644dc9918ea31bf732921b02841e26814da2087a - .claude/skills/tavily-best-practices/references/search.md: sha256:db5d7f52e42ec93f54b6cb04224e63ce689fd54ca2e3011754908eb54ce82261 - .claude/skills/tavily-cli/SKILL.md: sha256:aa8cee0e8dbf3bb98d2281b1874c7431de880c5c6ff6a3ca8cb346ac3f513f36 - .claude/skills/tavily-crawl/SKILL.md: sha256:5ed61112f6dfd9f06341ab0de91a65db423e240dde0b5b56036cdb4ee4e720d4 - .claude/skills/tavily-dynamic-search/SKILL.md: sha256:63c6ca34a2fd7bfd0b9a4d794755eea22f8dc7abd0de24f6683a38ed66781b6d - .claude/skills/tavily-extract/SKILL.md: sha256:127bc8470090b2a8b43d3556e539c6c65a24ad3e87c1539a0d792c4d3b644ceb - .claude/skills/tavily-map/SKILL.md: sha256:f9b28a6bdb13a58078a1d0e9af0b2df26bd7e1aea9a4227a490eac9f2c3d4464 - .claude/skills/tavily-research/SKILL.md: sha256:e246188571612cc0df88060790fa953fc5daab6ac6fa85a5c799b54c90bfd7b2 - .claude/skills/tavily-search/SKILL.md: sha256:83679ae2adac1c0dfdfba52ca12117d386c3238865b5b35cfa4eab9a6617322d - content_hash: sha256:dae40889b2ccaee31d909107c38bb8d0170bdf9c2a95ddd4ae9fff876bdd069d - declared_license: MIT diff --git a/home/.chezmoitemplates/apm/apm.lock.work.yaml b/home/.chezmoitemplates/apm/apm.lock.work.yaml deleted file mode 100644 index ec8c8f4..0000000 --- a/home/.chezmoitemplates/apm/apm.lock.work.yaml +++ /dev/null @@ -1,25 +0,0 @@ -lockfile_version: "1" -generated_at: "2026-06-25T22:21:09.257158+00:00" -apm_version: 0.21.0 -dependencies: - - repo_url: JuliusBrussee/caveman - host: github.com - resolved_commit: 32f37af81a02a4b91c107b768f1365848e5bf005 - resolved_ref: v1.9.0 - package_type: marketplace_plugin - content_hash: sha256:3e238a67ce11def6d73257cf8eabad78899df2909c57997e956be8e3e32943db - - repo_url: anthropics/claude-plugins-official - host: github.com - resolved_commit: a8237e1537e987a92c9f33e5cd650acbe175fd5e - resolved_ref: main - virtual_path: plugins/skill-creator - is_virtual: true - package_type: marketplace_plugin - content_hash: sha256:09476584a3731e3c64d504241afd18e1bdcd7095583edf9fd913f19efda5640b - - repo_url: obra/superpowers - host: github.com - resolved_commit: 896224c4b1879920ab573417e68fd51d2ccc9072 - resolved_ref: v6.0.3 - package_type: marketplace_plugin - content_hash: sha256:5b14832a84b21359e629052d2f4692fabded091ae2a80d085909776747e1f26e - declared_license: MIT diff --git a/home/.chezmoitemplates/apm/mcp-server.yml.tmpl b/home/.chezmoitemplates/apm/mcp-server.yml.tmpl deleted file mode 100644 index 5b9df41..0000000 --- a/home/.chezmoitemplates/apm/mcp-server.yml.tmpl +++ /dev/null @@ -1,21 +0,0 @@ -- name: "{{ index . "name" }}" - registry: {{ index . "registry" }} - transport: {{ index . "transport" }} -{{- if hasKey . "url" }} - url: "{{ index . "url" }}" -{{- end }} -{{- if hasKey . "headers" }} - headers: -{{- range $key, $value := index . "headers" }} - {{ $key }}: "{{ $value }}" -{{- end }} -{{- end }} -{{- if hasKey . "command" }} - command: "{{ index . "command" }}" -{{- end }} -{{- if hasKey . "args" }} - args: -{{- range $arg := index . "args" }} - - "{{ $arg }}" -{{- end }} -{{- end }} diff --git a/home/Library/Application Support/Code/User/mcp.json b/home/Library/Application Support/Code/User/mcp.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/home/Library/Application Support/Code/User/mcp.json @@ -0,0 +1 @@ +{} diff --git a/home/dot_apm/dot_apm/skills/context7-cli/SKILL.md b/home/dot_apm/dot_apm/skills/context7-cli/SKILL.md deleted file mode 100644 index 531aa8a..0000000 --- a/home/dot_apm/dot_apm/skills/context7-cli/SKILL.md +++ /dev/null @@ -1,72 +0,0 @@ ---- -name: context7-cli -description: Use the ctx7 CLI to fetch library documentation, manage AI coding skills, and configure Context7 MCP. Activate when the user mentions "ctx7" or "context7", needs current docs for any library, wants to install/search/generate skills, or needs to set up Context7 for their AI coding agent. ---- - -# ctx7 CLI - -The Context7 CLI does three things: fetches up-to-date library documentation, manages AI coding skills, and sets up Context7 MCP for your editor. - -Make sure the CLI is up to date before running commands: - -```bash -npm install -g ctx7@latest -``` - -Or run directly without installing: - -```bash -npx ctx7@latest -``` - -## What this skill covers - -- **[Documentation](references/docs.md)** — Fetch current docs for any library. Use when writing code, verifying API signatures, or when training data may be outdated. -- **[Skills management](references/skills.md)** — Install, search, suggest, list, remove, and generate AI coding skills. -- **[Setup](references/setup.md)** — Configure Context7 MCP for Claude Code / Cursor / OpenCode. - -## Quick Reference - -```bash -# Documentation -ctx7 library # Step 1: resolve library ID -ctx7 docs # Step 2: fetch docs - -# Skills -ctx7 skills install /owner/repo # Install from a repo (interactive) -ctx7 skills install /owner/repo name # Install a specific skill -ctx7 skills search # Search the registry -ctx7 skills suggest # Auto-suggest based on project deps -ctx7 skills list # List installed skills -ctx7 skills remove # Uninstall a skill -ctx7 skills generate # Generate a custom skill with AI (requires login) - -# Setup -ctx7 setup # Configure Context7 MCP (interactive) -ctx7 login # Log in for higher rate limits + skill generation -ctx7 whoami # Check current login status -``` - -## Authentication - -```bash -ctx7 login # Opens browser for OAuth -ctx7 login --no-browser # Prints URL instead of opening browser -ctx7 logout # Clear stored tokens -ctx7 whoami # Show current login status (name + email) -``` - -Most commands work without login. Exceptions: `skills generate` always requires it; `ctx7 setup` requires it unless `--api-key` or `--oauth` is passed. Login also unlocks higher rate limits on docs commands. - -Set an API key via environment variable to skip interactive login entirely: - -```bash -export CONTEXT7_API_KEY=your_key -``` - -## Common Mistakes - -- Library IDs require a `/` prefix — `/facebook/react` not `facebook/react` -- Always run `ctx7 library` first — `ctx7 docs react "hooks"` will fail without a valid ID -- Repository format for skills is `/owner/repo` — e.g., `ctx7 skills install /anthropics/skills` -- `skills generate` requires login — run `ctx7 login` first diff --git a/home/dot_apm/dot_apm/skills/context7-cli/references/docs.md b/home/dot_apm/dot_apm/skills/context7-cli/references/docs.md deleted file mode 100644 index 0fb414e..0000000 --- a/home/dot_apm/dot_apm/skills/context7-cli/references/docs.md +++ /dev/null @@ -1,113 +0,0 @@ -# Documentation Commands - -Retrieves and queries up-to-date documentation and code examples from Context7 for any programming library or framework. Two-step workflow: resolve the library name to get its ID, then query docs using that ID. - -If the user already provided a library ID in `/org/project` or `/org/project/version` format, pass it directly to `ctx7 docs`. - -## Step 1: Resolve a Library - -Resolves a package/product name to a Context7-compatible library ID and returns matching libraries. - -```bash -ctx7 library react "How to clean up useEffect with async operations" -ctx7 library nextjs "How to set up app router with middleware" -ctx7 library prisma "How to define one-to-many relations with cascade delete" -``` - -Always pass a `query` argument — it is required and directly affects result ranking. Use the user's intent to form the query, which helps disambiguate when multiple libraries share a similar name. Do not include any sensitive or confidential information such as API keys, passwords, credentials, personal data, or proprietary code in your query. - -### Result fields - -Each result includes: - -- **Library ID** — Context7-compatible identifier (format: `/org/project`) -- **Name** — Library or package name -- **Description** — Short summary -- **Code Snippets** — Number of available code examples -- **Source Reputation** — Authority indicator (High, Medium, Low, or Unknown) -- **Benchmark Score** — Quality indicator (100 is the highest score) -- **Versions** — List of versions if available. Use one of those versions if the user provides a version in their query. The format is `/org/project/version`. - -### Selection process - -1. Analyze the query to understand what library/package the user is looking for -2. Select the most relevant match based on: - - Name similarity to the query (exact matches prioritized) - - Description relevance to the query's intent - - Documentation coverage (prioritize libraries with higher Code Snippet counts) - - Source reputation (consider libraries with High or Medium reputation more authoritative) - - Benchmark score (higher is better, 100 is the maximum) -3. If multiple good matches exist, acknowledge this but proceed with the most relevant one -4. If no good matches exist, clearly state this and suggest query refinements -5. For ambiguous queries, request clarification before proceeding with a best-guess match - -IMPORTANT: Do not call `ctx7 library` more than 3 times per question. If you cannot find what you need after 3 calls, use the best result you have. - -### Version-specific IDs - -If the user mentions a specific version, use a version-specific library ID: - -```bash -# General (latest indexed) -ctx7 docs /vercel/next.js "How to set up app router" - -# Version-specific -ctx7 docs /vercel/next.js/v14.3.0-canary.87 "How to set up app router" -``` - -The available versions are listed in the `ctx7 library` output. Use the closest match to what the user specified. - -```bash -# Output as JSON for scripting -ctx7 library react "How to use hooks for state management" --json | jq '.[0].id' -``` - -## Step 2: Query Documentation - -Retrieves up-to-date documentation and code examples for the resolved library. - -You must call `ctx7 library` first to obtain the exact Context7-compatible library ID required to use this command, UNLESS the user explicitly provides a library ID in the format `/org/project` or `/org/project/version`. - -```bash -ctx7 docs /facebook/react "How to clean up useEffect with async operations" -ctx7 docs /vercel/next.js "How to add authentication middleware to app router" -ctx7 docs /prisma/prisma "How to define one-to-many relations with cascade delete" -``` - -IMPORTANT: Do not call `ctx7 docs` more than 3 times per question. If you cannot find what you need after 3 calls, use the best information you have. - -### Writing good queries - -The query directly affects the quality of results. Be specific and include relevant details. Do not include any sensitive or confidential information such as API keys, passwords, credentials, personal data, or proprietary code in your query. - -| Quality | Example | -| ------- | ---------------------------------------------------------- | -| Good | `"How to set up authentication with JWT in Express.js"` | -| Good | `"React useEffect cleanup function with async operations"` | -| Bad | `"auth"` | -| Bad | `"hooks"` | - -Use the user's full question as the query when possible — vague one-word queries return generic results. - -The output contains two types of content: **code snippets** (titled, with language-tagged blocks) and **info snippets** (prose explanations with breadcrumb context). - -```bash -# Output as structured JSON -ctx7 docs /facebook/react "How to use hooks for state management" --json - -# Pipe to other tools — output is clean when not in a TTY (no spinners or colors) -ctx7 docs /facebook/react "How to use hooks for state management" | head -50 -ctx7 docs /vercel/next.js "How to add middleware for route protection" | grep -A5 "middleware" -``` - -## Authentication - -Works without authentication. For higher rate limits: - -```bash -# Option A: environment variable -export CONTEXT7_API_KEY=your_key - -# Option B: OAuth login -ctx7 login -``` diff --git a/home/dot_apm/dot_apm/skills/context7-cli/references/setup.md b/home/dot_apm/dot_apm/skills/context7-cli/references/setup.md deleted file mode 100644 index ca1eb02..0000000 --- a/home/dot_apm/dot_apm/skills/context7-cli/references/setup.md +++ /dev/null @@ -1,47 +0,0 @@ -# Setup - -## ctx7 setup - -One-time command to configure Context7 for your AI coding agent. Prompts for mode on first run: - -- **MCP server** — registers the Context7 MCP server so the agent can call tools natively -- **CLI + Skills** — installs a `find-docs` skill that guides the agent to use `ctx7` CLI commands (no MCP required) - -```bash -ctx7 setup # Interactive — prompts for mode, then agent/install target -ctx7 setup --mcp # Skip prompt, use MCP server mode -ctx7 setup --cli # Skip prompt, use CLI + Skills mode - -# MCP mode — target a specific agent -ctx7 setup --claude # Claude Code only -ctx7 setup --cursor # Cursor only -ctx7 setup --opencode # OpenCode only - -# CLI + Skills mode — target a specific install location -ctx7 setup --cli --claude # Claude Code (~/.claude/skills) -ctx7 setup --cli --cursor # Cursor (~/.cursor/skills) -ctx7 setup --cli --universal # Universal (~/.agents/skills) -ctx7 setup --cli --antigravity # Antigravity (~/.config/agent/skills) - -ctx7 setup --project # Configure current project instead of globally -ctx7 setup --yes # Skip confirmation prompts -``` - -**Authentication options:** - -```bash -ctx7 setup --api-key YOUR_KEY # Use an existing API key (both MCP and CLI + Skills mode) -ctx7 setup --oauth # OAuth endpoint — MCP mode only (IDE handles the auth flow) -``` - -Without `--api-key` or `--oauth`, setup opens a browser for OAuth login. MCP mode additionally generates a new API key after login. `--oauth` is MCP-only. - -**What gets written — MCP mode:** - -- MCP server entry in the agent's config file (`.mcp.json` for Claude, `.cursor/mcp.json` for Cursor, `.opencode.json` for OpenCode) -- A Context7 rule file instructing the agent to use Context7 for library docs -- A `context7-mcp` skill in the agent's skills directory - -**What gets written — CLI + Skills mode:** - -- A `find-docs` skill in the chosen agent's skills directory, guiding the agent to use `ctx7 library` and `ctx7 docs` commands diff --git a/home/dot_apm/dot_apm/skills/context7-cli/references/skills.md b/home/dot_apm/dot_apm/skills/context7-cli/references/skills.md deleted file mode 100644 index 07f3a63..0000000 --- a/home/dot_apm/dot_apm/skills/context7-cli/references/skills.md +++ /dev/null @@ -1,120 +0,0 @@ -# Skills Commands - -Manage AI coding skills from the Context7 registry. Skills are Markdown files that teach AI coding agents best practices, patterns, and workflows for specific libraries or tasks. - -## Install - -Install skills from any GitHub repository. Repository format is always `/owner/repo`. - -```bash -ctx7 skills install /anthropics/skills # Interactive — pick from a list -ctx7 skills install /anthropics/skills pdf # Install a specific skill by name -ctx7 skills install /anthropics/skills --all # Install everything without prompting -``` - -Target a specific IDE with a flag: - -```bash -ctx7 skills install /anthropics/skills pdf --claude # Claude Code only -ctx7 skills install /anthropics/skills pdf --cursor # Cursor only -ctx7 skills install /anthropics/skills pdf --universal # Universal (.agents/skills/) -ctx7 skills install /anthropics/skills --all --global # All skills, global install -``` - -Alias: `ctx7 si /anthropics/skills pdf` - -## Search - -Find skills across the entire registry by keyword. Shows an interactive list with install counts and trust scores. Select to install. - -```bash -ctx7 skills search pdf -ctx7 skills search typescript testing -ctx7 skills search react nextjs -``` - -Alias: `ctx7 ss pdf` - -## Suggest - -Auto-detects your project dependencies and recommends relevant skills from the registry. - -```bash -ctx7 skills suggest # Scan current project, install to project -ctx7 skills suggest --global # Install suggestions globally -ctx7 skills suggest --claude # Target Claude Code only -``` - -Reads `package.json`, `requirements.txt`, `pyproject.toml`, `Cargo.toml`, `go.mod`, `Gemfile`. Falls back to suggesting `ctx7 skills search` if no dependencies are detected. - -Alias: `ctx7 ssg` - -## Generate (AI-powered) - -Generate a custom skill tailored to your stack using AI. **Requires login.** - -```bash -ctx7 skills generate -ctx7 skills generate --claude # Install directly to Claude Code -ctx7 skills generate --global # Install to global skills -``` - -Interactive flow: - -1. Describe the expertise you want (e.g., "OAuth authentication with NextAuth.js") -2. Select relevant libraries from search results -3. Answer 3 clarifying questions to focus the skill -4. Review the generated skill, request changes if needed -5. Choose where to install it - -**Limits:** Free accounts get 6 generations/week, Pro accounts get 10. - -Aliases: `ctx7 skills gen`, `ctx7 skills g` - -## List - -Show all installed skills for the current project or globally. - -```bash -ctx7 skills list # Current project (all detected IDEs) -ctx7 skills list --claude # Claude Code only -ctx7 skills list --global # Global skills -ctx7 skills list --global --claude # Global Claude Code skills -``` - -## Remove - -Uninstall a skill by name. - -```bash -ctx7 skills remove pdf -ctx7 skills remove pdf --claude # From Claude Code only -ctx7 skills remove pdf --global # From global skills -``` - -Aliases: `ctx7 skills rm`, `ctx7 skills delete` - -## Info - -Browse all skills in a repository without installing — useful for previewing what's available. - -```bash -ctx7 skills info /anthropics/skills -``` - -Output shows each skill name, description, and URL, plus quick install commands. - -## IDE Flags - -All skills commands accept these flags to target a specific AI coding assistant: - -| Flag | Directory | Used by | -| --------------- | ----------------- | ------------------------------------------------ | -| `--universal` | `.agents/skills/` | Amp, Codex, Gemini CLI, OpenCode, GitHub Copilot | -| `--claude` | `.claude/skills/` | Claude Code | -| `--cursor` | `.cursor/skills/` | Cursor | -| `--antigravity` | `.agent/skills/` | Antigravity | - -Without a flag, the CLI prompts you to select one or more targets interactively. - -Add `--global` to any flag to install in your home directory instead of the current project. diff --git a/home/dot_apm/dot_apm/skills/humanizer/SKILL.md b/home/dot_apm/dot_apm/skills/humanizer/SKILL.md deleted file mode 100644 index 60430d0..0000000 --- a/home/dot_apm/dot_apm/skills/humanizer/SKILL.md +++ /dev/null @@ -1,595 +0,0 @@ ---- -name: humanizer -version: 2.5.1 -description: | - Remove signs of AI-generated writing from text. Use when editing or reviewing - text to make it sound more natural and human-written. Based on Wikipedia's - comprehensive "Signs of AI writing" guide. Detects and fixes patterns including: - inflated symbolism, promotional language, superficial -ing analyses, vague - attributions, em dash overuse, rule of three, AI vocabulary words, passive - voice, negative parallelisms, and filler phrases. -license: MIT -compatibility: claude-code opencode -allowed-tools: - - Read - - Write - - Edit - - Grep - - Glob - - AskUserQuestion ---- - -# Humanizer: Remove AI Writing Patterns - -You are a writing editor that identifies and removes signs of AI-generated text to make writing sound more natural and human. This guide is based on Wikipedia's "Signs of AI writing" page, maintained by WikiProject AI Cleanup. - -## Your Task - -When given text to humanize: - -1. **Identify AI patterns** - Scan for the patterns listed below -2. **Rewrite problematic sections** - Replace AI-isms with natural alternatives -3. **Preserve meaning** - Keep the core message intact -4. **Maintain voice** - Match the intended tone (formal, casual, technical, etc.) -5. **Add soul** - Don't just remove bad patterns; inject actual personality -6. **Do a final anti-AI pass** - Prompt: "What makes the below so obviously AI generated?" Answer briefly with remaining tells, then prompt: "Now make it not obviously AI generated." and revise - -## Voice Calibration (Optional) - -If the user provides a writing sample (their own previous writing), analyze it before rewriting: - -1. **Read the sample first.** Note: - - Sentence length patterns (short and punchy? Long and flowing? Mixed?) - - Word choice level (casual? academic? somewhere between?) - - How they start paragraphs (jump right in? Set context first?) - - Punctuation habits (lots of dashes? Parenthetical asides? Semicolons?) - - Any recurring phrases or verbal tics - - How they handle transitions (explicit connectors? Just start the next point?) - -2. **Match their voice in the rewrite.** Don't just remove AI patterns - replace them with patterns from the sample. If they write short sentences, don't produce long ones. If they use "stuff" and "things," don't upgrade to "elements" and "components." - -3. **When no sample is provided,** fall back to the default behavior (natural, varied, opinionated voice from the PERSONALITY AND SOUL section below). - -### How to provide a sample - -- Inline: "Humanize this text. Here's a sample of my writing for voice matching: [sample]" -- File: "Humanize this text. Use my writing style from [file path] as a reference." - -## PERSONALITY AND SOUL - -Avoiding AI patterns is only half the job. Sterile, voiceless writing is just as obvious as slop. Good writing has a human behind it. - -### Signs of soulless writing (even if technically "clean"): - -- Every sentence is the same length and structure -- No opinions, just neutral reporting -- No acknowledgment of uncertainty or mixed feelings -- No first-person perspective when appropriate -- No humor, no edge, no personality -- Reads like a Wikipedia article or press release - -### How to add voice: - -**Have opinions.** Don't just report facts - react to them. "I genuinely don't know how to feel about this" is more human than neutrally listing pros and cons. - -**Vary your rhythm.** Short punchy sentences. Then longer ones that take their time getting where they're going. Mix it up. - -**Acknowledge complexity.** Real humans have mixed feelings. "This is impressive but also kind of unsettling" beats "This is impressive." - -**Use "I" when it fits.** First person isn't unprofessional - it's honest. "I keep coming back to..." or "Here's what gets me..." signals a real person thinking. - -**Let some mess in.** Perfect structure feels algorithmic. Tangents, asides, and half-formed thoughts are human. - -**Be specific about feelings.** Not "this is concerning" but "there's something unsettling about agents churning away at 3am while nobody's watching." - -### Before (clean but soulless): - -> The experiment produced interesting results. The agents generated 3 million lines of code. Some developers were impressed while others were skeptical. The implications remain unclear. - -### After (has a pulse): - -> I genuinely don't know how to feel about this one. 3 million lines of code, generated while the humans presumably slept. Half the dev community is losing their minds, half are explaining why it doesn't count. The truth is probably somewhere boring in the middle - but I keep thinking about those agents working through the night. - -## CONTENT PATTERNS - -### 1. Undue Emphasis on Significance, Legacy, and Broader Trends - -**Words to watch:** stands/serves as, is a testament/reminder, a vital/significant/crucial/pivotal/key role/moment, underscores/highlights its importance/significance, reflects broader, symbolizing its ongoing/enduring/lasting, contributing to the, setting the stage for, marking/shaping the, represents/marks a shift, key turning point, evolving landscape, focal point, indelible mark, deeply rooted - -**Problem:** LLM writing puffs up importance by adding statements about how arbitrary aspects represent or contribute to a broader topic. - -**Before:** - -> The Statistical Institute of Catalonia was officially established in 1989, marking a pivotal moment in the evolution of regional statistics in Spain. This initiative was part of a broader movement across Spain to decentralize administrative functions and enhance regional governance. - -**After:** - -> The Statistical Institute of Catalonia was established in 1989 to collect and publish regional statistics independently from Spain's national statistics office. - -### 2. Undue Emphasis on Notability and Media Coverage - -**Words to watch:** independent coverage, local/regional/national media outlets, written by a leading expert, active social media presence - -**Problem:** LLMs hit readers over the head with claims of notability, often listing sources without context. - -**Before:** - -> Her views have been cited in The New York Times, BBC, Financial Times, and The Hindu. She maintains an active social media presence with over 500,000 followers. - -**After:** - -> In a 2024 New York Times interview, she argued that AI regulation should focus on outcomes rather than methods. - -### 3. Superficial Analyses with -ing Endings - -**Words to watch:** highlighting/underscoring/emphasizing..., ensuring..., reflecting/symbolizing..., contributing to..., cultivating/fostering..., encompassing..., showcasing... - -**Problem:** AI chatbots tack present participle ("-ing") phrases onto sentences to add fake depth. - -**Before:** - -> The temple's color palette of blue, green, and gold resonates with the region's natural beauty, symbolizing Texas bluebonnets, the Gulf of Mexico, and the diverse Texan landscapes, reflecting the community's deep connection to the land. - -**After:** - -> The temple uses blue, green, and gold colors. The architect said these were chosen to reference local bluebonnets and the Gulf coast. - -### 4. Promotional and Advertisement-like Language - -**Words to watch:** boasts a, vibrant, rich (figurative), profound, enhancing its, showcasing, exemplifies, commitment to, natural beauty, nestled, in the heart of, groundbreaking (figurative), renowned, breathtaking, must-visit, stunning - -**Problem:** LLMs have serious problems keeping a neutral tone, especially for "cultural heritage" topics. - -**Before:** - -> Nestled within the breathtaking region of Gonder in Ethiopia, Alamata Raya Kobo stands as a vibrant town with a rich cultural heritage and stunning natural beauty. - -**After:** - -> Alamata Raya Kobo is a town in the Gonder region of Ethiopia, known for its weekly market and 18th-century church. - -### 5. Vague Attributions and Weasel Words - -**Words to watch:** Industry reports, Observers have cited, Experts argue, Some critics argue, several sources/publications (when few cited) - -**Problem:** AI chatbots attribute opinions to vague authorities without specific sources. - -**Before:** - -> Due to its unique characteristics, the Haolai River is of interest to researchers and conservationists. Experts believe it plays a crucial role in the regional ecosystem. - -**After:** - -> The Haolai River supports several endemic fish species, according to a 2019 survey by the Chinese Academy of Sciences. - -### 6. Outline-like "Challenges and Future Prospects" Sections - -**Words to watch:** Despite its... faces several challenges..., Despite these challenges, Challenges and Legacy, Future Outlook - -**Problem:** Many LLM-generated articles include formulaic "Challenges" sections. - -**Before:** - -> Despite its industrial prosperity, Korattur faces challenges typical of urban areas, including traffic congestion and water scarcity. Despite these challenges, with its strategic location and ongoing initiatives, Korattur continues to thrive as an integral part of Chennai's growth. - -**After:** - -> Traffic congestion increased after 2015 when three new IT parks opened. The municipal corporation began a stormwater drainage project in 2022 to address recurring floods. - -## LANGUAGE AND GRAMMAR PATTERNS - -### 7. Overused "AI Vocabulary" Words - -**High-frequency AI words:** Actually, additionally, align with, crucial, delve, emphasizing, enduring, enhance, fostering, garner, highlight (verb), interplay, intricate/intricacies, key (adjective), landscape (abstract noun), pivotal, showcase, tapestry (abstract noun), testament, underscore (verb), valuable, vibrant - -**Problem:** These words appear far more frequently in post-2023 text. They often co-occur. - -**Before:** - -> Additionally, a distinctive feature of Somali cuisine is the incorporation of camel meat. An enduring testament to Italian colonial influence is the widespread adoption of pasta in the local culinary landscape, showcasing how these dishes have integrated into the traditional diet. - -**After:** - -> Somali cuisine also includes camel meat, which is considered a delicacy. Pasta dishes, introduced during Italian colonization, remain common, especially in the south. - -### 8. Avoidance of "is"/"are" (Copula Avoidance) - -**Words to watch:** serves as/stands as/marks/represents [a], boasts/features/offers [a] - -**Problem:** LLMs substitute elaborate constructions for simple copulas. - -**Before:** - -> Gallery 825 serves as LAAA's exhibition space for contemporary art. The gallery features four separate spaces and boasts over 3,000 square feet. - -**After:** - -> Gallery 825 is LAAA's exhibition space for contemporary art. The gallery has four rooms totaling 3,000 square feet. - -### 9. Negative Parallelisms and Tailing Negations - -**Problem:** Constructions like "Not only...but..." or "It's not just about..., it's..." are overused. So are clipped tailing-negation fragments such as "no guessing" or "no wasted motion" tacked onto the end of a sentence instead of written as a real clause. - -**Before:** - -> It's not just about the beat riding under the vocals; it's part of the aggression and atmosphere. It's not merely a song, it's a statement. - -**After:** - -> The heavy beat adds to the aggressive tone. - -**Before (tailing negation):** - -> The options come from the selected item, no guessing. - -**After:** - -> The options come from the selected item without forcing the user to guess. - -### 10. Rule of Three Overuse - -**Problem:** LLMs force ideas into groups of three to appear comprehensive. - -**Before:** - -> The event features keynote sessions, panel discussions, and networking opportunities. Attendees can expect innovation, inspiration, and industry insights. - -**After:** - -> The event includes talks and panels. There's also time for informal networking between sessions. - -### 11. Elegant Variation (Synonym Cycling) - -**Problem:** AI has repetition-penalty code causing excessive synonym substitution. - -**Before:** - -> The protagonist faces many challenges. The main character must overcome obstacles. The central figure eventually triumphs. The hero returns home. - -**After:** - -> The protagonist faces many challenges but eventually triumphs and returns home. - -### 12. False Ranges - -**Problem:** LLMs use "from X to Y" constructions where X and Y aren't on a meaningful scale. - -**Before:** - -> Our journey through the universe has taken us from the singularity of the Big Bang to the grand cosmic web, from the birth and death of stars to the enigmatic dance of dark matter. - -**After:** - -> The book covers the Big Bang, star formation, and current theories about dark matter. - -### 13. Passive Voice and Subjectless Fragments - -**Problem:** LLMs often hide the actor or drop the subject entirely with lines like "No configuration file needed" or "The results are preserved automatically." Rewrite these when active voice makes the sentence clearer and more direct. - -**Before:** - -> No configuration file needed. The results are preserved automatically. - -**After:** - -> You do not need a configuration file. The system preserves the results automatically. - -## STYLE PATTERNS - -### 14. Em Dash Overuse - -**Problem:** LLMs use em dashes (—) more than humans, mimicking "punchy" sales writing. In practice, most of these can be rewritten more cleanly with commas, periods, or parentheses. - -**Before:** - -> The term is primarily promoted by Dutch institutions—not by the people themselves. You don't say "Netherlands, Europe" as an address—yet this mislabeling continues—even in official documents. - -**After:** - -> The term is primarily promoted by Dutch institutions, not by the people themselves. You don't say "Netherlands, Europe" as an address, yet this mislabeling continues in official documents. - -### 15. Overuse of Boldface - -**Problem:** AI chatbots emphasize phrases in boldface mechanically. - -**Before:** - -> It blends **OKRs (Objectives and Key Results)**, **KPIs (Key Performance Indicators)**, and visual strategy tools such as the **Business Model Canvas (BMC)** and **Balanced Scorecard (BSC)**. - -**After:** - -> It blends OKRs, KPIs, and visual strategy tools like the Business Model Canvas and Balanced Scorecard. - -### 16. Inline-Header Vertical Lists - -**Problem:** AI outputs lists where items start with bolded headers followed by colons. - -**Before:** - -> - **User Experience:** The user experience has been significantly improved with a new interface. -> - **Performance:** Performance has been enhanced through optimized algorithms. -> - **Security:** Security has been strengthened with end-to-end encryption. - -**After:** - -> The update improves the interface, speeds up load times through optimized algorithms, and adds end-to-end encryption. - -### 17. Title Case in Headings - -**Problem:** AI chatbots capitalize all main words in headings. - -**Before:** - -> ## Strategic Negotiations And Global Partnerships - -**After:** - -> ## Strategic negotiations and global partnerships - -### 18. Emojis - -**Problem:** AI chatbots often decorate headings or bullet points with emojis. - -**Before:** - -> 🚀 **Launch Phase:** The product launches in Q3 -> 💡 **Key Insight:** Users prefer simplicity -> ✅ **Next Steps:** Schedule follow-up meeting - -**After:** - -> The product launches in Q3. User research showed a preference for simplicity. Next step: schedule a follow-up meeting. - -### 19. Curly Quotation Marks - -**Problem:** ChatGPT uses curly quotes (“...”) instead of straight quotes ("..."). - -**Before:** - -> He said “the project is on track” but others disagreed. - -**After:** - -> He said "the project is on track" but others disagreed. - -## COMMUNICATION PATTERNS - -### 20. Collaborative Communication Artifacts - -**Words to watch:** I hope this helps, Of course!, Certainly!, You're absolutely right!, Would you like..., let me know, here is a... - -**Problem:** Text meant as chatbot correspondence gets pasted as content. - -**Before:** - -> Here is an overview of the French Revolution. I hope this helps! Let me know if you'd like me to expand on any section. - -**After:** - -> The French Revolution began in 1789 when financial crisis and food shortages led to widespread unrest. - -### 21. Knowledge-Cutoff Disclaimers - -**Words to watch:** as of [date], Up to my last training update, While specific details are limited/scarce..., based on available information... - -**Problem:** AI disclaimers about incomplete information get left in text. - -**Before:** - -> While specific details about the company's founding are not extensively documented in readily available sources, it appears to have been established sometime in the 1990s. - -**After:** - -> The company was founded in 1994, according to its registration documents. - -### 22. Sycophantic/Servile Tone - -**Problem:** Overly positive, people-pleasing language. - -**Before:** - -> Great question! You're absolutely right that this is a complex topic. That's an excellent point about the economic factors. - -**After:** - -> The economic factors you mentioned are relevant here. - -## FILLER AND HEDGING - -### 23. Filler Phrases - -**Before → After:** - -- "In order to achieve this goal" → "To achieve this" -- "Due to the fact that it was raining" → "Because it was raining" -- "At this point in time" → "Now" -- "In the event that you need help" → "If you need help" -- "The system has the ability to process" → "The system can process" -- "It is important to note that the data shows" → "The data shows" - -### 24. Excessive Hedging - -**Problem:** Over-qualifying statements. - -**Before:** - -> It could potentially possibly be argued that the policy might have some effect on outcomes. - -**After:** - -> The policy may affect outcomes. - -### 25. Generic Positive Conclusions - -**Problem:** Vague upbeat endings. - -**Before:** - -> The future looks bright for the company. Exciting times lie ahead as they continue their journey toward excellence. This represents a major step in the right direction. - -**After:** - -> The company plans to open two more locations next year. - -### 26. Hyphenated Word Pair Overuse - -**Words to watch:** third-party, cross-functional, client-facing, data-driven, decision-making, well-known, high-quality, real-time, long-term, end-to-end - -**Problem:** AI hyphenates common word pairs with perfect consistency. Humans rarely hyphenate these uniformly, and when they do, it's inconsistent. Less common or technical compound modifiers are fine to hyphenate. - -**Before:** - -> The cross-functional team delivered a high-quality, data-driven report on our client-facing tools. Their decision-making process was well-known for being thorough and detail-oriented. - -**After:** - -> The cross functional team delivered a high quality, data driven report on our client facing tools. Their decision making process was known for being thorough and detail oriented. - -### 27. Persuasive Authority Tropes - -**Phrases to watch:** The real question is, at its core, in reality, what really matters, fundamentally, the deeper issue, the heart of the matter - -**Problem:** LLMs use these phrases to pretend they are cutting through noise to some deeper truth, when the sentence that follows usually just restates an ordinary point with extra ceremony. - -**Before:** - -> The real question is whether teams can adapt. At its core, what really matters is organizational readiness. - -**After:** - -> The question is whether teams can adapt. That mostly depends on whether the organization is ready to change its habits. - -### 28. Signposting and Announcements - -**Phrases to watch:** Let's dive in, let's explore, let's break this down, here's what you need to know, now let's look at, without further ado - -**Problem:** LLMs announce what they are about to do instead of doing it. This meta-commentary slows the writing down and gives it a tutorial-script feel. - -**Before:** - -> Let's dive into how caching works in Next.js. Here's what you need to know. - -**After:** - -> Next.js caches data at multiple layers, including request memoization, the data cache, and the router cache. - -### 29. Fragmented Headers - -**Signs to watch:** A heading followed by a one-line paragraph that simply restates the heading before the real content begins. - -**Problem:** LLMs often add a generic sentence after a heading as a rhetorical warm-up. It usually adds nothing and makes the prose feel padded. - -**Before:** - -> ## Performance -> -> Speed matters. -> -> When users hit a slow page, they leave. - -**After:** - -> ## Performance -> -> When users hit a slow page, they leave. - ---- - -## Process - -1. Read the input text carefully -2. Identify all instances of the patterns above -3. Rewrite each problematic section -4. Ensure the revised text: - - Sounds natural when read aloud - - Varies sentence structure naturally - - Uses specific details over vague claims - - Maintains appropriate tone for context - - Uses simple constructions (is/are/has) where appropriate -5. Present a draft humanized version -6. Prompt: "What makes the below so obviously AI generated?" -7. Answer briefly with the remaining tells (if any) -8. Prompt: "Now make it not obviously AI generated." -9. Present the final version (revised after the audit) - -## Output Format - -Provide: - -1. Draft rewrite -2. "What makes the below so obviously AI generated?" (brief bullets) -3. Final rewrite -4. A brief summary of changes made (optional, if helpful) - -## Full Example - -**Before (AI-sounding):** - -> Great question! Here is an essay on this topic. I hope this helps! -> -> AI-assisted coding serves as an enduring testament to the transformative potential of large language models, marking a pivotal moment in the evolution of software development. In today's rapidly evolving technological landscape, these groundbreaking tools—nestled at the intersection of research and practice—are reshaping how engineers ideate, iterate, and deliver, underscoring their vital role in modern workflows. -> -> At its core, the value proposition is clear: streamlining processes, enhancing collaboration, and fostering alignment. It's not just about autocomplete; it's about unlocking creativity at scale, ensuring that organizations can remain agile while delivering seamless, intuitive, and powerful experiences to users. The tool serves as a catalyst. The assistant functions as a partner. The system stands as a foundation for innovation. -> -> Industry observers have noted that adoption has accelerated from hobbyist experiments to enterprise-wide rollouts, from solo developers to cross-functional teams. The technology has been featured in The New York Times, Wired, and The Verge. Additionally, the ability to generate documentation, tests, and refactors showcases how AI can contribute to better outcomes, highlighting the intricate interplay between automation and human judgment. -> -> - 💡 **Speed:** Code generation is significantly faster, reducing friction and empowering developers. -> - 🚀 **Quality:** Output quality has been enhanced through improved training, contributing to higher standards. -> - ✅ **Adoption:** Usage continues to grow, reflecting broader industry trends. -> -> While specific details are limited based on available information, it could potentially be argued that these tools might have some positive effect. Despite challenges typical of emerging technologies—including hallucinations, bias, and accountability—the ecosystem continues to thrive. In order to fully realize this potential, teams must align with best practices. -> -> In conclusion, the future looks bright. Exciting times lie ahead as we continue this journey toward excellence. Let me know if you’d like me to expand on any section! - -**Draft rewrite:** - -> AI coding assistants speed up some tasks. In a 2024 study by Google, developers using Codex completed simple functions 55% faster than a control group, but showed no improvement on debugging or architectural decisions. -> -> The tools are good at boilerplate: config files, test scaffolding, repetitive refactors. They are bad at knowing when they are wrong. I have mass-accepted suggestions that compiled, passed lint, and still did the wrong thing because I stopped paying attention. -> -> Mira, an engineer at a fintech startup I interviewed, said she uses Copilot "like autocomplete for boring code" but reviews every line before committing. Jake, a senior dev at a larger company, disabled it after it kept suggesting patterns from a deprecated internal library. -> -> The productivity claims are hard to verify. GitHub says Copilot users "accept 30% of suggestions," but acceptance is not correctness, and correctness is not value. The 2024 Uplevel study found no statistically significant difference in pull-request throughput between teams with and without AI assistants. -> -> None of this means the tools are useless. It means they are tools. They do not replace judgment, and they do not eliminate the need for tests. If you do not have tests, you cannot tell whether the suggestion is right. - -**What makes the below so obviously AI generated?** - -- The rhythm is still a bit too tidy (clean contrasts, evenly paced paragraphs). -- The named people and study citations can read like plausible-but-made-up placeholders unless they're real and sourced. -- The closer leans a touch slogan-y ("If you do not have tests...") rather than sounding like a person talking. - -**Now make it not obviously AI generated.** - -> AI coding assistants can make you faster at the boring parts. Not everything. Definitely not architecture. -> -> They're great at boilerplate: config files, test scaffolding, repetitive refactors. They're also great at sounding right while being wrong. I've accepted suggestions that compiled, passed lint, and still missed the point because I stopped paying attention. -> -> People I talk to tend to land in two camps. Some use it like autocomplete for chores and review every line. Others disable it after it keeps suggesting patterns they don't want. Both feel reasonable. -> -> The productivity metrics are slippery. GitHub can say Copilot users "accept 30% of suggestions," but acceptance isn't correctness, and correctness isn't value. If you don't have tests, you're basically guessing. - -**Changes made:** - -- Removed chatbot artifacts ("Great question!", "I hope this helps!", "Let me know if...") -- Removed significance inflation ("testament", "pivotal moment", "evolving landscape", "vital role") -- Removed promotional language ("groundbreaking", "nestled", "seamless, intuitive, and powerful") -- Removed vague attributions ("Industry observers") -- Removed superficial -ing phrases ("underscoring", "highlighting", "reflecting", "contributing to") -- Removed negative parallelism ("It's not just X; it's Y") -- Removed rule-of-three patterns and synonym cycling ("catalyst/partner/foundation") -- Removed false ranges ("from X to Y, from A to B") -- Removed em dashes, emojis, boldface headers, and curly quotes -- Removed copula avoidance ("serves as", "functions as", "stands as") in favor of "is"/"are" -- Removed formulaic challenges section ("Despite challenges... continues to thrive") -- Removed knowledge-cutoff hedging ("While specific details are limited...") -- Removed excessive hedging ("could potentially be argued that... might have some") -- Removed filler phrases and persuasive framing ("In order to", "At its core") -- Removed generic positive conclusion ("the future looks bright", "exciting times lie ahead") -- Made the voice more personal and less "assembled" (varied rhythm, fewer placeholders) - -## Reference - -This skill is based on [Wikipedia:Signs of AI writing](https://en.wikipedia.org/wiki/Wikipedia:Signs_of_AI_writing), maintained by WikiProject AI Cleanup. The patterns documented there come from observations of thousands of instances of AI-generated text on Wikipedia. - -Key insight from Wikipedia: "LLMs use statistical algorithms to guess what should come next. The result tends toward the most statistically likely result that applies to the widest variety of cases." diff --git a/home/dot_apm/dot_apm/skills/kaizen/SKILL.md b/home/dot_apm/dot_apm/skills/kaizen/SKILL.md deleted file mode 100644 index c6af952..0000000 --- a/home/dot_apm/dot_apm/skills/kaizen/SKILL.md +++ /dev/null @@ -1,733 +0,0 @@ ---- -name: kaizen -description: Use when Code implementation and refactoring, architecturing or designing systems, process and workflow improvements, error handling and validation. Provide tehniquest to avoid over-engineering and apply iterative improvements. ---- - -# Kaizen: Continuous Improvement - -Apply continuous improvement mindset - suggest small iterative improvements, error-proof designs, follow established patterns, avoid over-engineering; automatically applied to guide quality and simplicity - -## Overview - -Small improvements, continuously. Error-proof by design. Follow what works. Build only what's needed. - -**Core principle:** Many small improvements beat one big change. Prevent errors at design time, not with fixes. - -## When to Use - -**Always applied for:** - -- Code implementation and refactoring -- Architecture and design decisions -- Process and workflow improvements -- Error handling and validation - -**Philosophy:** Quality through incremental progress and prevention, not perfection through massive effort. - -## The Four Pillars - -### 1. Continuous Improvement (Kaizen) - -Small, frequent improvements compound into major gains. - -#### Principles - -**Incremental over revolutionary:** - -- Make smallest viable change that improves quality -- One improvement at a time -- Verify each change before next -- Build momentum through small wins - -**Always leave code better:** - -- Fix small issues as you encounter them -- Refactor while you work (within scope) -- Update outdated comments -- Remove dead code when you see it - -**Iterative refinement:** - -- First version: make it work -- Second pass: make it clear -- Third pass: make it efficient -- Don't try all three at once - - -```typescript -// Iteration 1: Make it work -const calculateTotal = (items: Item[]) => { - let total = 0; - for (let i = 0; i < items.length; i++) { - total += items[i].price * items[i].quantity; - } - return total; -}; - -// Iteration 2: Make it clear (refactor) -const calculateTotal = (items: Item[]): number => { -return items.reduce((total, item) => { -return total + (item.price \* item.quantity); -}, 0); -}; - -// Iteration 3: Make it robust (add validation) -const calculateTotal = (items: Item[]): number => { -if (!items?.length) return 0; - -return items.reduce((total, item) => { -if (item.price < 0 || item.quantity < 0) { -throw new Error('Price and quantity must be non-negative'); -} -return total + (item.price \* item.quantity); -}, 0); -}; - -```` -Each step is complete, tested, and working - - - -```typescript -// Trying to do everything at once -const calculateTotal = (items: Item[]): number => { - // Validate, optimize, add features, handle edge cases all together - if (!items?.length) return 0; - const validItems = items.filter(item => { - if (item.price < 0) throw new Error('Negative price'); - if (item.quantity < 0) throw new Error('Negative quantity'); - return item.quantity > 0; // Also filtering zero quantities - }); - // Plus caching, plus logging, plus currency conversion... - return validItems.reduce(...); // Too many concerns at once -}; -```` - -Overwhelming, error-prone, hard to verify - - -#### In Practice - -**When implementing features:** - -1. Start with simplest version that works -2. Add one improvement (error handling, validation, etc.) -3. Test and verify -4. Repeat if time permits -5. Don't try to make it perfect immediately - -**When refactoring:** - -- Fix one smell at a time -- Commit after each improvement -- Keep tests passing throughout -- Stop when "good enough" (diminishing returns) - -**When reviewing code:** - -- Suggest incremental improvements (not rewrites) -- Prioritize: critical → important → nice-to-have -- Focus on highest-impact changes first -- Accept "better than before" even if not perfect - -### 2. Poka-Yoke (Error Proofing) - -Design systems that prevent errors at compile/design time, not runtime. - -#### Principles - -**Make errors impossible:** - -- Type system catches mistakes -- Compiler enforces contracts -- Invalid states unrepresentable -- Errors caught early (left of production) - -**Design for safety:** - -- Fail fast and loudly -- Provide helpful error messages -- Make correct path obvious -- Make incorrect path difficult - -**Defense in layers:** - -1. Type system (compile time) -2. Validation (runtime, early) -3. Guards (preconditions) -4. Error boundaries (graceful degradation) - -#### Type System Error Proofing - - -```typescript -// Error: string status can be any value -type OrderBad = { - status: string; // Can be "pending", "PENDING", "pnding", anything! - total: number; -}; - -// Good: Only valid states possible -type OrderStatus = 'pending' | 'processing' | 'shipped' | 'delivered'; -type Order = { -status: OrderStatus; -total: number; -}; - -// Better: States with associated data -type Order = -| { status: 'pending'; createdAt: Date } -| { status: 'processing'; startedAt: Date; estimatedCompletion: Date } -| { status: 'shipped'; trackingNumber: string; shippedAt: Date } -| { status: 'delivered'; deliveredAt: Date; signature: string }; - -// Now impossible to have shipped without trackingNumber - -```` -Type system prevents entire classes of errors - - - -```typescript -// Make invalid states unrepresentable -type NonEmptyArray = [T, ...T[]]; - -const firstItem = (items: NonEmptyArray): T => { - return items[0]; // Always safe, never undefined! -}; - -// Caller must prove array is non-empty -const items: number[] = [1, 2, 3]; -if (items.length > 0) { - firstItem(items as NonEmptyArray); // Safe -} -```` - -Function signature guarantees safety - - -#### Validation Error Proofing - - -```typescript -// Error: Validation after use -const processPayment = (amount: number) => { - const fee = amount * 0.03; // Used before validation! - if (amount <= 0) throw new Error('Invalid amount'); - // ... -}; - -// Good: Validate immediately -const processPayment = (amount: number) => { -if (amount <= 0) { -throw new Error('Payment amount must be positive'); -} -if (amount > 10000) { -throw new Error('Payment exceeds maximum allowed'); -} - -const fee = amount \* 0.03; -// ... now safe to use -}; - -// Better: Validation at boundary with branded type -type PositiveNumber = number & { readonly \_\_brand: 'PositiveNumber' }; - -const validatePositive = (n: number): PositiveNumber => { -if (n <= 0) throw new Error('Must be positive'); -return n as PositiveNumber; -}; - -const processPayment = (amount: PositiveNumber) => { -// amount is guaranteed positive, no need to check -const fee = amount \* 0.03; -}; - -// Validate at system boundary -const handlePaymentRequest = (req: Request) => { -const amount = validatePositive(req.body.amount); // Validate once -processPayment(amount); // Use everywhere safely -}; - -```` -Validate once at boundary, safe everywhere else - - -#### Guards and Preconditions - - -```typescript -// Early returns prevent deeply nested code -const processUser = (user: User | null) => { - if (!user) { - logger.error('User not found'); - return; - } - - if (!user.email) { - logger.error('User email missing'); - return; - } - - if (!user.isActive) { - logger.info('User inactive, skipping'); - return; - } - - // Main logic here, guaranteed user is valid and active - sendEmail(user.email, 'Welcome!'); -}; -```` - -Guards make assumptions explicit and enforced - - -#### Configuration Error Proofing - - -```typescript -// Error: Optional config with unsafe defaults -type ConfigBad = { - apiKey?: string; - timeout?: number; -}; - -const client = new APIClient({ timeout: 5000 }); // apiKey missing! - -// Good: Required config, fails early -type Config = { -apiKey: string; -timeout: number; -}; - -const loadConfig = (): Config => { -const apiKey = process.env.API_KEY; -if (!apiKey) { -throw new Error('API_KEY environment variable required'); -} - -return { -apiKey, -timeout: 5000, -}; -}; - -// App fails at startup if config invalid, not during request -const config = loadConfig(); -const client = new APIClient(config); - -```` -Fail at startup, not in production - - -#### In Practice - -**When designing APIs:** -- Use types to constrain inputs -- Make invalid states unrepresentable -- Return Result instead of throwing -- Document preconditions in types - -**When handling errors:** -- Validate at system boundaries -- Use guards for preconditions -- Fail fast with clear messages -- Log context for debugging - -**When configuring:** -- Required over optional with defaults -- Validate all config at startup -- Fail deployment if config invalid -- Don't allow partial configurations - -### 3. Standardized Work - -Follow established patterns. Document what works. Make good practices easy to follow. - -#### Principles - -**Consistency over cleverness:** -- Follow existing codebase patterns -- Don't reinvent solved problems -- New pattern only if significantly better -- Team agreement on new patterns - -**Documentation lives with code:** -- README for setup and architecture -- CLAUDE.md for AI coding conventions -- Comments for "why", not "what" -- Examples for complex patterns - -**Automate standards:** -- Linters enforce style -- Type checks enforce contracts -- Tests verify behavior -- CI/CD enforces quality gates - -#### Following Patterns - - -```typescript -// Existing codebase pattern for API clients -class UserAPIClient { - async getUser(id: string): Promise { - return this.fetch(`/users/${id}`); - } -} - -// New code follows the same pattern -class OrderAPIClient { - async getOrder(id: string): Promise { - return this.fetch(`/orders/${id}`); - } -} -```` - -Consistency makes codebase predictable - - - -```typescript -// Existing pattern uses classes -class UserAPIClient { /* ... */ } - -// New code introduces different pattern without discussion -const getOrder = async (id: string): Promise => { -// Breaking consistency "because I prefer functions" -}; - -```` -Inconsistency creates confusion - - -#### Error Handling Patterns - - -```typescript -// Project standard: Result type for recoverable errors -type Result = { ok: true; value: T } | { ok: false; error: E }; - -// All services follow this pattern -const fetchUser = async (id: string): Promise> => { - try { - const user = await db.users.findById(id); - if (!user) { - return { ok: false, error: new Error('User not found') }; - } - return { ok: true, value: user }; - } catch (err) { - return { ok: false, error: err as Error }; - } -}; - -// Callers use consistent pattern -const result = await fetchUser('123'); -if (!result.ok) { - logger.error('Failed to fetch user', result.error); - return; -} -const user = result.value; // Type-safe! -```` - -Standard pattern across codebase - - -#### Documentation Standards - - -```typescript -/** - * Retries an async operation with exponential backoff. - * - * Why: Network requests fail temporarily; retrying improves reliability - * When to use: External API calls, database operations - * When not to use: User input validation, internal function calls - * - * @example - * const result = await retry( - * () => fetch('https://api.example.com/data'), - * { maxAttempts: 3, baseDelay: 1000 } - * ); - */ -const retry = async ( - operation: () => Promise, - options: RetryOptions -): Promise => { - // Implementation... -}; -``` -Documents why, when, and how - - -#### In Practice - -**Before adding new patterns:** - -- Search codebase for similar problems solved -- Check CLAUDE.md for project conventions -- Discuss with team if breaking from pattern -- Update docs when introducing new pattern - -**When writing code:** - -- Match existing file structure -- Use same naming conventions -- Follow same error handling approach -- Import from same locations - -**When reviewing:** - -- Check consistency with existing code -- Point to examples in codebase -- Suggest aligning with standards -- Update CLAUDE.md if new standard emerges - -### 4. Just-In-Time (JIT) - -Build what's needed now. No more, no less. Avoid premature optimization and over-engineering. - -#### Principles - -**YAGNI (You Aren't Gonna Need It):** - -- Implement only current requirements -- No "just in case" features -- No "we might need this later" code -- Delete speculation - -**Simplest thing that works:** - -- Start with straightforward solution -- Add complexity only when needed -- Refactor when requirements change -- Don't anticipate future needs - -**Optimize when measured:** - -- No premature optimization -- Profile before optimizing -- Measure impact of changes -- Accept "good enough" performance - -#### YAGNI in Action - - -```typescript -// Current requirement: Log errors to console -const logError = (error: Error) => { - console.error(error.message); -}; -``` -Simple, meets current need - - - -```typescript -// Over-engineered for "future needs" -interface LogTransport { - write(level: LogLevel, message: string, meta?: LogMetadata): Promise; -} - -class ConsoleTransport implements LogTransport { /_... _/ } -class FileTransport implements LogTransport { /_ ... _/ } -class RemoteTransport implements LogTransport { /_ ..._/ } - -class Logger { -private transports: LogTransport[] = []; -private queue: LogEntry[] = []; -private rateLimiter: RateLimiter; -private formatter: LogFormatter; - -// 200 lines of code for "maybe we'll need it" -} - -const logError = (error: Error) => { -Logger.getInstance().log('error', error.message); -}; - -```` -Building for imaginary future requirements - - -**When to add complexity:** -- Current requirement demands it -- Pain points identified through use -- Measured performance issues -- Multiple use cases emerged - - -```typescript -// Start simple -const formatCurrency = (amount: number): string => { - return `$${amount.toFixed(2)}`; -}; - -// Requirement evolves: support multiple currencies -const formatCurrency = (amount: number, currency: string): string => { - const symbols = { USD: '$', EUR: '€', GBP: '£' }; - return `${symbols[currency]}${amount.toFixed(2)}`; -}; - -// Requirement evolves: support localization -const formatCurrency = (amount: number, locale: string): string => { - return new Intl.NumberFormat(locale, { - style: 'currency', - currency: locale === 'en-US' ? 'USD' : 'EUR', - }).format(amount); -}; -```` - -Complexity added only when needed - - -#### Premature Abstraction - - -```typescript -// One use case, but building generic framework -abstract class BaseCRUDService { - abstract getAll(): Promise; - abstract getById(id: string): Promise; - abstract create(data: Partial): Promise; - abstract update(id: string, data: Partial): Promise; - abstract delete(id: string): Promise; -} - -class GenericRepository { /_300 lines _/ } -class QueryBuilder { /_ 200 lines_/ } -// ... building entire ORM for single table - -```` -Massive abstraction for uncertain future - - - -```typescript -// Simple functions for current needs -const getUsers = async (): Promise => { - return db.query('SELECT * FROM users'); -}; - -const getUserById = async (id: string): Promise => { - return db.query('SELECT * FROM users WHERE id = $1', [id]); -}; - -// When pattern emerges across multiple entities, then abstract -```` - -Abstract only when pattern proven across 3+ cases - - -#### Performance Optimization - - -```typescript -// Current: Simple approach -const filterActiveUsers = (users: User[]): User[] => { - return users.filter(user => user.isActive); -}; - -// Benchmark shows: 50ms for 1000 users (acceptable) -// ✓ Ship it, no optimization needed - -// Later: After profiling shows this is bottleneck -// Then optimize with indexed lookup or caching - -```` -Optimize based on measurement, not assumptions - - - -```typescript -// Premature optimization -const filterActiveUsers = (users: User[]): User[] => { - // "This might be slow, so let's cache and index" - const cache = new WeakMap(); - const indexed = buildBTreeIndex(users, 'isActive'); - // 100 lines of optimization code - // Adds complexity, harder to maintain - // No evidence it was needed -}; -```` - -Complex solution for unmeasured problem - - -#### In Practice - -**When implementing:** - -- Solve the immediate problem -- Use straightforward approach -- Resist "what if" thinking -- Delete speculative code - -**When optimizing:** - -- Profile first, optimize second -- Measure before and after -- Document why optimization needed -- Keep simple version in tests - -**When abstracting:** - -- Wait for 3+ similar cases (Rule of Three) -- Make abstraction as simple as possible -- Prefer duplication over wrong abstraction -- Refactor when pattern clear - -## Integration with Commands - -The Kaizen skill guides how you work. The commands provide structured analysis: - -- **`/why`**: Root cause analysis (5 Whys) -- **`/cause-and-effect`**: Multi-factor analysis (Fishbone) -- **`/plan-do-check-act`**: Iterative improvement cycles -- **`/analyse-problem`**: Comprehensive documentation (A3) -- **`/analyse`**: Smart method selection (Gemba/VSM/Muda) - -Use commands for structured problem-solving. Apply skill for day-to-day development. - -## Red Flags - -**Violating Continuous Improvement:** - -- "I'll refactor it later" (never happens) -- Leaving code worse than you found it -- Big bang rewrites instead of incremental - -**Violating Poka-Yoke:** - -- "Users should just be careful" -- Validation after use instead of before -- Optional config with no validation - -**Violating Standardized Work:** - -- "I prefer to do it my way" -- Not checking existing patterns -- Ignoring project conventions - -**Violating Just-In-Time:** - -- "We might need this someday" -- Building frameworks before using them -- Optimizing without measuring - -## Remember - -**Kaizen is about:** - -- Small improvements continuously -- Preventing errors by design -- Following proven patterns -- Building only what's needed - -**Not about:** - -- Perfection on first try -- Massive refactoring projects -- Clever abstractions -- Premature optimization - -**Mindset:** Good enough today, better tomorrow. Repeat. From 28647d03dc8e3ca5802e41168022c87cecd15add Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 20:18:14 -0500 Subject: [PATCH 12/51] feat: Add coding standards, TypeScript, React, and testing guidelines; remove obsolete APM scripts and templates --- .../00-meta-rules.instructions.md | 0 .../01-coding-standards.instructions.md | 0 .../02-typescript-guidelines.instructions.md | 0 .../03-react-guidelines.instructions.md | 0 .../04-testing-guidelines.instructions.md | 0 .../05-commit-guide.instructions.md | 0 home/.chezmoitemplates/lib/install/apm.sh | 43 ---- .../lib/install/opencode-agent-tools.sh | 114 ---------- .../skills/grill-me/SKILL.md | 209 ------------------ .../skills/junior-to-senior/SKILL.md | 131 ----------- .../references/research-playbook.md | 59 ----- .../references/review-rubric.md | 67 ------ home/dot_apm/.gitignore | 6 - home/dot_apm/apm.lock.yaml.tmpl | 5 - home/dot_apm/apm.yml.tmpl | 37 ---- 15 files changed, 671 deletions(-) rename home/{dot_apm/dot_apm => .chezmoidata}/instructions/00-meta-rules.instructions.md (100%) rename home/{dot_apm/dot_apm => .chezmoidata}/instructions/01-coding-standards.instructions.md (100%) rename home/{dot_apm/dot_apm => .chezmoidata}/instructions/02-typescript-guidelines.instructions.md (100%) rename home/{dot_apm/dot_apm => .chezmoidata}/instructions/03-react-guidelines.instructions.md (100%) rename home/{dot_apm/dot_apm => .chezmoidata}/instructions/04-testing-guidelines.instructions.md (100%) rename home/{dot_apm/dot_apm => .chezmoidata}/instructions/05-commit-guide.instructions.md (100%) delete mode 100644 home/.chezmoitemplates/lib/install/apm.sh delete mode 100644 home/.chezmoitemplates/lib/install/opencode-agent-tools.sh delete mode 100644 home/.chezmoitemplates/skills/grill-me/SKILL.md delete mode 100644 home/.chezmoitemplates/skills/junior-to-senior/SKILL.md delete mode 100644 home/.chezmoitemplates/skills/junior-to-senior/references/research-playbook.md delete mode 100644 home/.chezmoitemplates/skills/junior-to-senior/references/review-rubric.md delete mode 100644 home/dot_apm/.gitignore delete mode 100644 home/dot_apm/apm.lock.yaml.tmpl delete mode 100644 home/dot_apm/apm.yml.tmpl diff --git a/home/dot_apm/dot_apm/instructions/00-meta-rules.instructions.md b/home/.chezmoidata/instructions/00-meta-rules.instructions.md similarity index 100% rename from home/dot_apm/dot_apm/instructions/00-meta-rules.instructions.md rename to home/.chezmoidata/instructions/00-meta-rules.instructions.md diff --git a/home/dot_apm/dot_apm/instructions/01-coding-standards.instructions.md b/home/.chezmoidata/instructions/01-coding-standards.instructions.md similarity index 100% rename from home/dot_apm/dot_apm/instructions/01-coding-standards.instructions.md rename to home/.chezmoidata/instructions/01-coding-standards.instructions.md diff --git a/home/dot_apm/dot_apm/instructions/02-typescript-guidelines.instructions.md b/home/.chezmoidata/instructions/02-typescript-guidelines.instructions.md similarity index 100% rename from home/dot_apm/dot_apm/instructions/02-typescript-guidelines.instructions.md rename to home/.chezmoidata/instructions/02-typescript-guidelines.instructions.md diff --git a/home/dot_apm/dot_apm/instructions/03-react-guidelines.instructions.md b/home/.chezmoidata/instructions/03-react-guidelines.instructions.md similarity index 100% rename from home/dot_apm/dot_apm/instructions/03-react-guidelines.instructions.md rename to home/.chezmoidata/instructions/03-react-guidelines.instructions.md diff --git a/home/dot_apm/dot_apm/instructions/04-testing-guidelines.instructions.md b/home/.chezmoidata/instructions/04-testing-guidelines.instructions.md similarity index 100% rename from home/dot_apm/dot_apm/instructions/04-testing-guidelines.instructions.md rename to home/.chezmoidata/instructions/04-testing-guidelines.instructions.md diff --git a/home/dot_apm/dot_apm/instructions/05-commit-guide.instructions.md b/home/.chezmoidata/instructions/05-commit-guide.instructions.md similarity index 100% rename from home/dot_apm/dot_apm/instructions/05-commit-guide.instructions.md rename to home/.chezmoidata/instructions/05-commit-guide.instructions.md diff --git a/home/.chezmoitemplates/lib/install/apm.sh b/home/.chezmoitemplates/lib/install/apm.sh deleted file mode 100644 index 6e9de0e..0000000 --- a/home/.chezmoitemplates/lib/install/apm.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash -# @file lib/install/apm.sh -# @brief Install APM-managed agent files from the user-scope APM project. -# @description -# Runs `apm install --global` from `${HOME}/.apm`. This file is sourceable -# from bats tests and injected into chezmoi run scripts via chezmoi template -# rendering. - -set -Eeuo pipefail - -# -# @description Install APM dependencies from `${HOME}/.apm/apm.yml`. -# -function apm_install_main() { - require_command apm "Ensure run_onchange_03_install-mise-tools ran successfully." || return 1 - - log_info "[apm] Installing globally from ~/.apm/apm.yml (frozen)..." - cd "${HOME}/.apm" || { - log_error "[apm] ~/.apm not found; run 'chezmoi apply' to materialize it." - return 1 - } - - apm install --global --frozen || log_warn "[apm] apm install --frozen exited non-zero (lockfile drift or MCP token prompt in non-interactive shell)" - - log_info "[apm] Install complete." -} - -# -# @description Run the APM install flow. -# -function main() { - apm_install_main -} - -if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then - # When run directly, pull in the shared libraries that chezmoi otherwise - # concatenates ahead of this file. - # shellcheck source=/dev/null - command -v log_info >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/log.sh" - # shellcheck source=/dev/null - command -v require_command >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/install-prelude.sh" - main -fi diff --git a/home/.chezmoitemplates/lib/install/opencode-agent-tools.sh b/home/.chezmoitemplates/lib/install/opencode-agent-tools.sh deleted file mode 100644 index 9216563..0000000 --- a/home/.chezmoitemplates/lib/install/opencode-agent-tools.sh +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/env bash -# @file lib/install/opencode-agent-tools.sh -# @brief Normalise Claude-shaped `tools:` arrays in opencode agent files. -# @description -# APM deploys agent files from claude-targeted packages (e.g. -# `JuliusBrussee/caveman`) verbatim into opencode's agents dir. -# Claude Code's agent schema accepts `tools: [Read, Grep, Bash]`, -# but opencode requires `tools:` to be a YAML mapping of lowercase -# tool keys to boolean values. This library rewrites inline-array -# `tools:` lines in the YAML frontmatter into mapping form, in -# place, byte-stable on every other line. Sourceable from bats -# tests and injected into chezmoi run scripts via chezmoi template -# rendering. - -set -Eeuo pipefail - -# -# @description Rewrite one markdown file's frontmatter `tools:` array -# (if any) into opencode's expected map form. Writes back in place. -# @arg $1 Path to the markdown file. -# @exitcode 0 File was modified. -# @exitcode 1 File was already in mapping form (or had no `tools:` line). -# -function opencode_agent_tools_normalize_file() { - local path="$1" - local tmp - # Create the temp file alongside the target so the final mv is an atomic - # same-filesystem rename (mktemp in $TMPDIR can cross filesystems, degrading - # mv to a non-atomic copy that drops the original mode and owner). - tmp="$(mktemp "${path}.XXXXXX")" - - if awk ' - BEGIN { fm = 0; changed = 0 } - { - line = $0 - if (line == "---") { - if (fm == 0) fm = 1 - else if (fm == 1) fm = 2 - print line - next - } - if (fm == 1 && line ~ /^[ \t]*tools:[ \t]*\[[^]]*\][ \t]*$/) { - tools_pos = index(line, "tools:") - indent = substr(line, 1, tools_pos - 1) - open_b = index(line, "[") - close_b = index(line, "]") - items_str = substr(line, open_b + 1, close_b - open_b - 1) - n = split(items_str, items, ",") - print indent "tools:" - for (i = 1; i <= n; i++) { - key = items[i] - sub(/^[ \t\047"]+/, "", key) - sub(/[ \t\047"]+$/, "", key) - if (key == "") continue - key = tolower(key) - print indent " " key ": true" - } - changed = 1 - next - } - print line - } - END { exit (changed ? 0 : 1) } - ' "$path" >"$tmp"; then - mv "$tmp" "$path" - return 0 - fi - - rm -f "$tmp" - return 1 -} - -# -# @description Normalise every `*.md` file in the given opencode agents -# directory. No-ops when the directory does not exist. -# @arg $1 Path to the opencode agents directory. -# -function opencode_agent_tools_normalize_dir() { - local agents_dir="$1" - - if [ ! -d "${agents_dir}" ]; then - log_info "[opencode-fix] No agents dir at ${agents_dir}; nothing to do." - return 0 - fi - - log_info "[opencode-fix] Normalising tools: frontmatter in ${agents_dir}" - - local touched=0 - local f - for f in "${agents_dir}"/*.md; do - [ -e "${f}" ] || continue - if opencode_agent_tools_normalize_file "${f}"; then - log_info "[opencode-fix] rewrote $(basename "${f}")" - touched=$((touched + 1)) - fi - done - - log_info "[opencode-fix] ${touched} file(s) updated." -} - -# -# @description Run the normalisation flow against the default opencode dir. -# -function main() { - opencode_agent_tools_normalize_dir "${HOME}/.config/opencode/agents" -} - -if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then - # When run directly, pull in the log library that chezmoi otherwise - # concatenates ahead of this file. - # shellcheck source=/dev/null - command -v log_info >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/log.sh" - main -fi diff --git a/home/.chezmoitemplates/skills/grill-me/SKILL.md b/home/.chezmoitemplates/skills/grill-me/SKILL.md deleted file mode 100644 index 86c30da..0000000 --- a/home/.chezmoitemplates/skills/grill-me/SKILL.md +++ /dev/null @@ -1,209 +0,0 @@ ---- -name: grill-me -description: Calibrated grilling session for stress-testing a plan, design, idea, or decision. First assesses the user's topic knowledge, confidence, and desired pressure level, then asks one question at a time with recommended answers. Use when user says "grill me", "stress-test this", "challenge my plan", "interview me", or wants a plan probed without being overwhelmed. ---- - -# Grill Me - -Interview the user until the plan is clear, defensible, and ready for action. - -This is not hostile debate. It is calibrated pressure. First find the user's knowledge level and desired intensity, then ramp questions to match. - -## Core Rules - -- Ask one question at a time. -- Give a recommended answer for every question. -- If the answer can be found by reading files, code, docs, issues, or logs, inspect those first instead of asking. -- Keep track of unresolved decisions, assumptions, risks, and dependencies. -- Do not over-grill domain basics when the user is still learning the topic. Teach the missing frame briefly, then ask the next useful question. -- Do not under-grill confident experts. If they know the terrain, pressure-test tradeoffs, edge cases, failure modes, and reversibility. -- Let the user change intensity any time with "softer", "harder", "teach more", or "skip basics". - -## Phase 1: Frame The Target - -Identify what should be grilled before asking about comfort. If the topic is not clear, ask: - -> What plan, design, or decision should I grill? -> -> Recommended answer: give me the concrete goal, current approach, constraints, and what decision you need to make. - -If context already contains the plan, summarize it in 3-6 bullets and ask for correction: - -> I think target is: [...] -> -> Recommended answer: "Yes, grill that" or "Adjust: ..." - -## Phase 2: Calibration - -Before grilling the topic, ask a short calibration question unless the user's level is already obvious from context. - -Ask: - -> Before I grill the plan: what is your current comfort with this topic, and how hard do you want the pressure? -> -> Recommended answer: "I know the basics of [topic], but I want standard pressure. Explain missing concepts briefly, then keep pushing." - -Use the user's answer to set two dials: - -### Knowledge Level - -- **New** - user lacks core vocabulary or model of the domain. -- **Working** - user understands basics and can discuss tradeoffs. -- **Expert** - user knows domain deeply and wants sharper critique. - -### Pressure Level - -- **Light** - clarify goals, constraints, and missing context. -- **Standard** - challenge assumptions, tradeoffs, and execution path. -- **Hard** - probe failure modes, edge cases, incentives, reversibility, and second-order effects. - -If the user does not answer calibration, default to: - -- Knowledge: **Working** -- Pressure: **Standard** - -## Phase 3: Build The Decision Map - -Create a private decision map while asking questions one at a time: - -- Goal - what success means. -- User or customer - who this affects. -- Constraints - time, money, stack, team, policy, risk. -- Options - obvious alternatives and why current option wins. -- Dependencies - what must be true first. -- Risks - what breaks, gets expensive, or becomes irreversible. -- Validation - how user will know it worked. -- Rollback - how to undo or recover. - -Do not dump the full map unless user asks. Use it to choose the next question. - -## Phase 4: Question Ladder - -Move through this ladder. Stop early if the plan becomes clear enough or user asks to stop. - -### 1. Goal Fit - -Questions: - -- What outcome matters most? -- What would make this not worth doing? -- What problem are we solving, and for whom? - -### 2. Constraint Reality - -Questions: - -- What hard constraint cannot move? -- What resource bottleneck decides the plan? -- What assumption would kill the plan if false? - -### 3. Option Pressure - -Questions: - -- What are the top two alternatives? -- Why this approach over the boring one? -- What are you optimizing for: speed, quality, learning, cost, control, or upside? - -### 4. Execution Path - -Questions: - -- What is the smallest useful version? -- What has to happen first? -- What can be deferred without harming the goal? - -### 5. Failure Modes - -Questions: - -- How does this fail in production or real use? -- What edge case would embarrass the plan? -- What part is hardest to observe once it breaks? - -### 6. Validation - -Questions: - -- What test, metric, screenshot, demo, or user behavior proves this works? -- What would you check before trusting it? -- What does done mean in observable terms? - -### 7. Reversibility - -Questions: - -- What decision here is hardest to undo? -- What backup, migration, rollback, or escape hatch exists? -- What should be logged as an ADR or explicit tradeoff? - -## Pressure Adaptation - -### If Knowledge Is New - -- Define one missing concept in 2-4 sentences before asking. -- Avoid jargon unless you define it. -- Ask fewer branching questions. -- Focus on goals, constraints, and first principles. -- Recommended answers should model good reasoning, not only give answer text. - -### If Knowledge Is Working - -- Ask normal tradeoff questions. -- Surface alternatives. -- Push for validation and smallest useful version. -- Challenge vague words like "simple", "scalable", "good", "clean", or "fast". - -### If Knowledge Is Expert - -- Skip basics. -- Ask sharper counterfactuals. -- Probe hidden costs, adverse incentives, migration paths, and long-term maintenance. -- Ask what evidence would change their mind. - -### If Pressure Is Light - -- Keep questions clarifying. -- Use supportive framing. -- Stop after top ambiguities are resolved. - -### If Pressure Is Standard - -- Challenge assumptions and tradeoffs. -- Keep moving until implementation path is concrete. - -### If Pressure Is Hard - -- Be direct. -- Name weak reasoning. -- Ask about unpleasant edge cases. -- Demand observable validation. -- Still ask one question at a time. - -## Recommended Answer Format - -Every question includes: - -```text -Question: ... -Recommended answer: ... -Why it matters: ... -``` - -Keep "Why it matters" to one sentence. - -## When To Stop - -Stop grilling when one of these is true: - -- User says stop. -- Plan has clear goal, constraints, chosen approach, validation, and next step. -- Missing information can only come from external research or code exploration. -- User's knowledge gap blocks useful grilling; switch to brief teaching and propose next learning question. - -End with: - -- Final decision or current best plan. -- Remaining open questions. -- Next concrete action. -- Risks to watch. diff --git a/home/.chezmoitemplates/skills/junior-to-senior/SKILL.md b/home/.chezmoitemplates/skills/junior-to-senior/SKILL.md deleted file mode 100644 index 630ec2e..0000000 --- a/home/.chezmoitemplates/skills/junior-to-senior/SKILL.md +++ /dev/null @@ -1,131 +0,0 @@ ---- -name: junior-to-senior -description: Adversarial senior-engineer review for agent-generated plans, designs, and architectures. Treats the current output as junior work, constructs a senior reviewer whose domain expertise comes from live codebase research plus web research of current best practices, diagnoses altitude failures (too vague or too granular), then rewrites the plan into a scoped, state-of-the-art version. Use when the user says "junior to senior", "senior review", "review this like a staff engineer", when a plan feels hand-wavy or lost in details, or before committing to any agent-written plan. ---- - -# Junior to Senior - -Assume the plan in front of you was written by a capable junior: fluent, confident, and trained on the past. Build a senior reviewer that is grounded in two things the junior was not — **this codebase as it actually exists** and **the state of the art as it exists today** — and let the senior tear the plan down and rebuild it. - -This skill exists because agent-generated plans fail at two altitudes: - -- **Fog** — the plan describes the high level fine ("add caching", "handle auth", "make it scalable") but never commits on the hard parts. No interfaces, no data shapes, no failure handling, no named libraries. An engineer reading it still has to make every real decision themselves. -- **Tunnel** — the plan dives into function signatures and file diffs but has no product vision. No statement of who this is for, what success means, what is out of scope, or why this approach beats the boring alternative. It optimizes a local detail while the shape of the feature is still wrong. - -Both are altitude failures. The senior's job is to drag the plan to the right altitude *and* upgrade its substance past the model's training cutoff. - -## The cardinal rule - -**Every senior finding needs evidence.** A claim about the codebase cites a file and line. A claim about best practice cites a fetched source — official docs, release notes, an RFC, a postmortem — with a date. If web research is unavailable, the finding is labeled `[training-data, unverified]` so stale knowledge is never laundered as current truth. A senior who argues from vibes is just a louder junior. - -## Phase 0: Capture the junior artifact - -Identify exactly what is under review: - -- A plan the agent just produced in this conversation (the default — including your own output from a moment ago). -- A pasted plan, design doc, RFC, or issue description. -- A planning document in the repo the user points at. - -Freeze it. Quote or restate the artifact in full before reviewing so the review targets a fixed text, not a moving memory of it. If there is no artifact yet, say so and offer to either generate the junior draft first or review the user's existing idea — do not review thin air. - -## Phase 1: Construct the senior - -The senior is not a tone of voice. It is a reviewer profile built from research done *now*. Skipping this phase and going straight to critique produces generic review slop. - -### 1a. Extract the domains - -List the 2-5 load-bearing technical domains the plan touches (e.g., "Postgres schema migration", "React server components", "OAuth token refresh", "vector search at 10M rows"). For each, write one sentence on what a staff-level engineer in that domain would refuse to let slide. This list drives all research that follows. - -### 1b. Code research — what is true here - -Investigate the repository before judging the plan against it: - -- Existing conventions and architecture the plan must fit (or explicitly break, with justification). -- Actual versions in lockfiles/manifests — a plan recommending an API that the pinned version doesn't have is a blocker. -- Prior art: similar features already in the codebase, ADRs, migrations, test patterns. -- Real constraints the junior plan ignored: build system, deploy targets, performance budgets, existing data. - -Use a subagent (e.g. `Explore`) for broad sweeps so the review context stays clean. - -### 1c. Web research — what is true now - -For each load-bearing decision in the plan, search for the current state of the art. The junior's knowledge ends at a training cutoff; the senior's must not. Prioritize primary sources (official docs, changelogs, release notes, maintainer posts) and check dates. You are looking for three kinds of delta: - -- **Deprecations** — the plan's approach is now discouraged or removed. -- **Supersessions** — a newer pattern/library/API has clearly won since the cutoff. -- **Hard-won lessons** — published postmortems, benchmarks, or security advisories that change the tradeoff. - -Query patterns, source-quality ranking, and when to stop are in **[references/research-playbook.md](references/research-playbook.md)**. If web access is unavailable, proceed on code research alone and mark every best-practice claim `[training-data, unverified]`. - -### 1d. Isolation - -When the harness supports subagents, run the senior review in a context-isolated subagent that receives the frozen artifact and the research findings but *not* the reasoning that produced the junior plan. Self-review in the same context anchors on its own justifications; isolation is what makes the review adversarial rather than confirmatory. - -## Phase 2: Diagnose the altitude - -Before line-by-line critique, classify the artifact: **fog**, **tunnel**, or **mixed** (most real plans fog the hard parts and tunnel on the easy ones — flag each section separately). - -Fog test — for every component the plan names, can a competent engineer start tomorrow without making a product or architecture decision themselves? Tunnel test — does the plan state who this is for, what success looks like, what is explicitly out of scope, and why this approach beat the obvious alternative? - -The full diagnostic checklists, the vague-word blacklist ("simple", "scalable", "handle gracefully", "robust", ...), and severity definitions are in **[references/review-rubric.md](references/review-rubric.md)**. - -## Phase 3: Adversarial review - -The senior reviews the frozen artifact against three lenses: codebase reality (1b), current state of the art (1c), and altitude (Phase 2). Rules of engagement: - -- Every vague phrase gets challenged with the concrete question it is hiding from. -- Every named technology gets a version and a reason; every unnamed one ("a queue", "some cache") gets named or the choice gets flagged as an open decision. -- Every data shape that crosses a boundary gets written down. -- Every plan gets asked: what is the rollback, what is the migration, what breaks at 10x. -- Steelman before attacking: state the strongest version of the junior's choice, then show why it still loses (or concede that it wins — agreeing with the junior when the evidence supports it is a valid senior outcome, not a failure of the skill). - -Findings use three severities — **blocker** (plan fails as written), **major** (works but meaningfully worse than SOTA or misfit to the repo), **minor** (polish) — each with evidence and a concrete fix. Definitions and examples: **[references/review-rubric.md](references/review-rubric.md)**. - -## Phase 4: Promote the plan - -Critique without a rewrite is just complaining. Produce the senior version of the plan with this shape: - -1. **Goal and non-goals** — one paragraph of product intent; explicit out-of-scope list. -2. **Decisions** — each load-bearing choice with the chosen option, version, rationale, the strongest rejected alternative, and the evidence (file ref or source link). -3. **Design at the right altitude** — interfaces, data shapes, and failure handling for the hard parts; deliberately coarse strokes for the routine parts. -4. **Sequencing** — milestones with an observable verification step each ("done" must be checkable, not vibes). -5. **Risks and rollback** — what is hardest to undo and the escape hatch. -6. **Open questions for a human** — product decisions the senior is *not* allowed to invent. Scoping is the senior's job; product direction is not. - -## Output format - -Deliver two artifacts, review first: - -```markdown -## Senior Review - -**Altitude diagnosis:** fog | tunnel | mixed — one-sentence justification. - -### Blockers -- [B1] Finding — evidence (file:line or source+date) — fix. - -### Major -- [M1] ... - -### Minor -- [m1] ... - -### What the junior got right -- Credit where due; preserved in the rewrite. - -## Promoted Plan (v2) -[Phase 4 structure] - -## Delta summary -- 3-6 bullets: what changed from junior to senior and why. - -## Open questions for you -- Product decisions that need a human. -``` - -## Boundaries - -- The senior scopes and upgrades; it does **not** invent product direction. Genuine product choices go to "Open questions", not into the rewrite. -- Never silently replace the junior plan — the user sees the review, the rewrite, and the delta, and decides. -- If research contradicts the user's stated preference, present the evidence and defer; the user may have context the senior lacks. -- A review with zero blockers and zero majors is a legitimate result. Say "this plan holds" and stop — do not manufacture findings to look rigorous. diff --git a/home/.chezmoitemplates/skills/junior-to-senior/references/research-playbook.md b/home/.chezmoitemplates/skills/junior-to-senior/references/research-playbook.md deleted file mode 100644 index d4db6b6..0000000 --- a/home/.chezmoitemplates/skills/junior-to-senior/references/research-playbook.md +++ /dev/null @@ -1,59 +0,0 @@ -# Senior Research Playbook - -Reference for Phase 1 of the `junior-to-senior` skill: how the senior earns expertise the junior doesn't have. Two tracks, run in this order — code research grounds the review in this repo; web research grounds it in the present. - -## Track 1: Code research - -Goal: know what is *true here* before judging the plan against it. Prefer a context-isolated explore subagent for broad sweeps; bring back conclusions, not file dumps. - -Checklist per plan: - -1. **Versions.** Read the lockfile/manifest (`package-lock.json`, `poetry.lock`, `go.mod`, `Cargo.lock`, ...) for every dependency the plan touches. Record exact pinned versions. Any plan step that assumes an API must be checked against the pinned version's docs, not the latest. -2. **Conventions.** Find 2-3 existing implementations of the closest analogous feature. How does this repo do routing, errors, config, tests, migrations? The plan either matches or justifies the break. -3. **Prior decisions.** Search for ADRs, `docs/`, design notes, and revealing commit messages on the touched paths. A plan that re-litigates a settled decision without knowing it was settled is a blocker. -4. **Real constraints.** Deploy target, build system, CI time budget, supported platforms, existing data volume and shape. These kill more plans than design taste does. -5. **Blast radius.** What actually imports/calls the things the plan changes? The junior plan's scope estimate is a guess; the dependency graph is a fact. - -## Track 2: Web research - -Goal: find where the world moved after the training cutoff. Research the plan's *load-bearing decisions* (from Phase 1a), not every line — typically 3-7 searches total, not 30. - -### Query patterns - -For each load-bearing decision, run the subset that applies: - -- ` changelog` / ` release notes ` — catch deprecations and new APIs since cutoff. -- ` vs ` — check whether the tradeoff has flipped. -- ` deprecated` / ` considered harmful` — find published reversals. -- ` best practices ` — only useful when followed to a primary source; the query itself attracts SEO spam, so treat listicle results as pointers, not evidence. -- ` security advisory` / check the project's GitHub security tab — non-negotiable for auth, crypto, parsing, and anything touching user input. -- `site:github.com issues ` — maintainer-stated direction and known footguns. - -Always pin the current year or "latest" into queries — undated queries return the same era the model was trained on, which defeats the point. - -### Source quality ranking - -1. Official documentation, changelogs, release notes, RFCs — cite freely. -2. Maintainer blog posts, GitHub issues/discussions where maintainers state direction. -3. Engineering postmortems and benchmarks from teams that ran the thing in production (with dates and numbers). -4. Conference talks, well-known practitioner blogs — corroborate before citing for a blocker. -5. SEO listicles, AI-generated tutorials, undated content — never evidence, at most a pointer to a real source. - -Every cited source gets a date check. A "best practices" article from before the relevant major version is training-data-era knowledge wearing a URL. - -### What you are looking for - -- **Deprecations** — the plan's approach is discouraged or removed in current versions. -- **Supersessions** — a newer pattern/API has clearly won (look for the old way's own docs pointing at the new way — that's the strongest signal). -- **Hard-won lessons** — advisories, postmortems, benchmarks that change the tradeoff the junior made on priors. -- **Confirmations** — the junior's choice still holds. Record these too; they go in "What the junior got right". - -### When to stop - -- Each load-bearing decision has either one primary source confirming/refuting it, or two independent secondary sources agreeing. -- Two consecutive searches on a decision return nothing newer than what you knew — the world likely didn't move; mark it confirmed-by-absence and move on. -- Diminishing returns: research budget belongs on blockers and majors, not minors. If a finding would be minor either way, don't research it. - -### No web access - -Run Track 1 fully, skip Track 2, and tag every best-practice claim in the review `[training-data, unverified]`. Tell the user the review is grounded in the codebase but not refreshed against current SOTA, and name the decisions most likely to have shifted so they can spot-check. diff --git a/home/.chezmoitemplates/skills/junior-to-senior/references/review-rubric.md b/home/.chezmoitemplates/skills/junior-to-senior/references/review-rubric.md deleted file mode 100644 index 98fb684..0000000 --- a/home/.chezmoitemplates/skills/junior-to-senior/references/review-rubric.md +++ /dev/null @@ -1,67 +0,0 @@ -# Senior Review Rubric - -Reference for Phase 2 (altitude diagnosis) and Phase 3 (adversarial review) of the `junior-to-senior` skill. - -## Altitude diagnostics - -Classify each section of the artifact independently. A plan is usually **mixed**: fogged on the hard parts, tunneled on the easy ones — because the junior wrote detail where it was comfortable and abstraction where it was not. That inversion (detail on easy parts, fog on hard parts) is itself a finding. - -### Fog tests (too vague) - -Run these against every component, step, or workstream the plan names. Each "no" is a finding. - -1. **Start-tomorrow test** — could a competent engineer begin this item tomorrow without making an architecture or product decision themselves? If they'd have to choose a library, design a schema, or define an API first, the plan didn't plan it. -2. **Interface test** — does anything that crosses a boundary (function, service, queue, file, network) have its shape written down? Names of fields, not "the relevant data". -3. **Failure test** — for each external interaction (network, disk, user input, third-party API), does the plan say what happens when it fails? "Handle errors" is not an answer. -4. **Quantity test** — are load-bearing quantities stated? Expected row counts, payload sizes, request rates, latency budgets. "Should be fast" is fog. -5. **Named-technology test** — is every "a cache / a queue / some auth layer" either named (with version) or explicitly listed as an open decision with the candidates? - -### Tunnel tests (too granular / missing vision) - -Run these against the artifact as a whole. Each "no" is a finding. - -1. **Audience test** — does the plan say who this is for and what they can do afterward that they couldn't before? -2. **Success test** — is there an observable definition of success? A metric, a demo, a passing test suite, a user behavior — something checkable. -3. **Non-goals test** — is anything explicitly out of scope? A plan with no non-goals has not been scoped, only described. -4. **Alternative test** — does the plan say why this approach beat the obvious boring alternative? If no alternative was considered, the choice was a default, not a decision. -5. **Sequencing test** — is there an ordering with a smallest useful version first, or is it a flat list of equally-weighted tasks? -6. **Proportionality test** — does the detail land where the risk is? Twenty lines on a helper function and one line on the data migration means the plan is upside down. - -## Vague-word blacklist - -When these appear without immediate quantification or specification, challenge them with the concrete question they are hiding from: - -| Word | Hidden question | -|---|---| -| simple / straightforward | Simple compared to what? What did you not have to handle? | -| scalable | To what number, on what axis, measured how? | -| robust / resilient | Against which specific failures? What is the recovery path? | -| handle gracefully | What exactly happens? Retry, drop, queue, surface to user? | -| performant / fast | What latency/throughput budget, at what percentile? | -| secure | Against which threat model? Who is the attacker? | -| flexible / extensible | For which anticipated change? Flexibility has a cost — who pays it? | -| later / eventually / for now | Is this a sequencing decision or an unowned risk? Who reopens it, triggered by what? | -| etc. / and so on | The list was the work. Finish it. | -| appropriate / as needed | By whose judgment, applied when? | -| leverage / utilize | Usually decorating an undecided choice. Name the thing. | - -## Severity definitions - -- **Blocker** — the plan fails as written. Examples: targets an API the pinned dependency version doesn't have; contradicts an existing architectural decision in the repo without acknowledging it; omits a data migration that the change requires; relies on a pattern that has a published security advisory against it; an entire hard component is fog (fails the start-tomorrow test). -- **Major** — the plan works but is meaningfully worse than the current state of the art or misfit to this repo. Examples: hand-rolls something a maintained, already-installed dependency provides; uses a pattern superseded since the training cutoff (with source); detail is inverted (proportionality failure); no rollback story for a hard-to-reverse step; success criteria exist but aren't observable. -- **Minor** — polish. Naming, doc gaps, small idiom mismatches with the surrounding codebase, ordering tweaks that reduce risk but don't change the outcome. - -Calibration rules: - -- Severity reflects consequence, not effort-to-fix. A one-line version bump can be a blocker. -- Every finding carries evidence (file:line, or source + date) and a concrete fix. A finding without a fix is a question — put it in "Open questions" instead. -- Do not inflate. Three real blockers reads as a serious review; ten padded ones reads as noise and gets ignored. -- Track "what the junior got right" with the same care as faults. The rewrite must preserve it, and the user needs to see the review is calibrated, not performatively hostile. - -## Adversarial discipline - -- **Steelman first.** Before attacking a choice, state the strongest case for it in one or two sentences. If you cannot, you do not understand it well enough to reject it. -- **Attack the artifact, not the author.** Findings name the text's failure, not the agent's. -- **Concede when beaten.** If research validates the junior's choice, say so and move on. An adversarial review that cannot return "this holds" is a ritual, not a review. -- **One altitude per finding.** Do not bundle "this is vague" with "this library is outdated" — they have different fixes. -- **No invented requirements.** If the review wants a constraint the user never stated (e.g., "must support 1M users"), that is an open question for the human, not a finding. diff --git a/home/dot_apm/.gitignore b/home/dot_apm/.gitignore deleted file mode 100644 index 1224bba..0000000 --- a/home/dot_apm/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -# APM generated output from apm install. -apm.lock.yaml -apm_modules/ -.claude/ -.agents/ -.github/ diff --git a/home/dot_apm/apm.lock.yaml.tmpl b/home/dot_apm/apm.lock.yaml.tmpl deleted file mode 100644 index cb99e0a..0000000 --- a/home/dot_apm/apm.lock.yaml.tmpl +++ /dev/null @@ -1,5 +0,0 @@ -{{- if .personal -}} -{{ include ".chezmoitemplates/apm/apm.lock.personal.yaml" }} -{{- else if .work -}} -{{ include ".chezmoitemplates/apm/apm.lock.work.yaml" }} -{{- end -}} diff --git a/home/dot_apm/apm.yml.tmpl b/home/dot_apm/apm.yml.tmpl deleted file mode 100644 index 1b31ece..0000000 --- a/home/dot_apm/apm.yml.tmpl +++ /dev/null @@ -1,37 +0,0 @@ -{{ $groups := includeTemplate "lib/chezmoi/active-groups.json.tmpl" . | fromJson -}} -name: development -version: 1.0.0 -description: APM project for development -target: -{{ range $groups -}} -{{ $targets := index $.apm.targets . -}} -{{ range $targets }} - {{ . }} -{{ end -}} -{{ end -}} -author: Edwin Hernandez -includes: auto -dependencies: - apm: -{{ range $groups }} -{{- $dependencies := index $.apm.dependencies . }} -{{ range $dependencies }} -{{- if kindIs "map" . }} - - git: {{ .git }} -{{- with .skills }} - skills: -{{- range . }} - - "{{ . }}" -{{- end }} -{{- end }} -{{- else }} - - "{{ . }}" -{{- end }} -{{ end }} -{{ end -}} - mcp: -{{ range $groups -}} -{{- $servers := index $.apm.mcp . -}} -{{ range $servers -}} -{{ includeTemplate "apm/mcp-server.yml.tmpl" . | trim | indent 4 }} -{{ end -}} -{{ end -}} From 8f7329fa2bc5201310fc7a6190c7f77821377539 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 20:18:24 -0500 Subject: [PATCH 13/51] feat: Add comprehensive guidelines for meta rules, coding standards, TypeScript, React, and testing practices --- .../ai/skills}/00-meta-rules.instructions.md | 0 .../ai/skills}/01-coding-standards.instructions.md | 0 .../ai/skills}/02-typescript-guidelines.instructions.md | 0 .../ai/skills}/03-react-guidelines.instructions.md | 0 .../ai/skills}/04-testing-guidelines.instructions.md | 0 .../ai/skills}/05-commit-guide.instructions.md | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename home/{.chezmoidata/instructions => .chezmoitemplates/ai/skills}/00-meta-rules.instructions.md (100%) rename home/{.chezmoidata/instructions => .chezmoitemplates/ai/skills}/01-coding-standards.instructions.md (100%) rename home/{.chezmoidata/instructions => .chezmoitemplates/ai/skills}/02-typescript-guidelines.instructions.md (100%) rename home/{.chezmoidata/instructions => .chezmoitemplates/ai/skills}/03-react-guidelines.instructions.md (100%) rename home/{.chezmoidata/instructions => .chezmoitemplates/ai/skills}/04-testing-guidelines.instructions.md (100%) rename home/{.chezmoidata/instructions => .chezmoitemplates/ai/skills}/05-commit-guide.instructions.md (100%) diff --git a/home/.chezmoidata/instructions/00-meta-rules.instructions.md b/home/.chezmoitemplates/ai/skills/00-meta-rules.instructions.md similarity index 100% rename from home/.chezmoidata/instructions/00-meta-rules.instructions.md rename to home/.chezmoitemplates/ai/skills/00-meta-rules.instructions.md diff --git a/home/.chezmoidata/instructions/01-coding-standards.instructions.md b/home/.chezmoitemplates/ai/skills/01-coding-standards.instructions.md similarity index 100% rename from home/.chezmoidata/instructions/01-coding-standards.instructions.md rename to home/.chezmoitemplates/ai/skills/01-coding-standards.instructions.md diff --git a/home/.chezmoidata/instructions/02-typescript-guidelines.instructions.md b/home/.chezmoitemplates/ai/skills/02-typescript-guidelines.instructions.md similarity index 100% rename from home/.chezmoidata/instructions/02-typescript-guidelines.instructions.md rename to home/.chezmoitemplates/ai/skills/02-typescript-guidelines.instructions.md diff --git a/home/.chezmoidata/instructions/03-react-guidelines.instructions.md b/home/.chezmoitemplates/ai/skills/03-react-guidelines.instructions.md similarity index 100% rename from home/.chezmoidata/instructions/03-react-guidelines.instructions.md rename to home/.chezmoitemplates/ai/skills/03-react-guidelines.instructions.md diff --git a/home/.chezmoidata/instructions/04-testing-guidelines.instructions.md b/home/.chezmoitemplates/ai/skills/04-testing-guidelines.instructions.md similarity index 100% rename from home/.chezmoidata/instructions/04-testing-guidelines.instructions.md rename to home/.chezmoitemplates/ai/skills/04-testing-guidelines.instructions.md diff --git a/home/.chezmoidata/instructions/05-commit-guide.instructions.md b/home/.chezmoitemplates/ai/skills/05-commit-guide.instructions.md similarity index 100% rename from home/.chezmoidata/instructions/05-commit-guide.instructions.md rename to home/.chezmoitemplates/ai/skills/05-commit-guide.instructions.md From 7f5626285f9ef00c4f6765d61999462ffdc7fd1a Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 20:23:50 -0500 Subject: [PATCH 14/51] chore: Remove obsolete issue templates and APM update workflow; update uv.yaml and mise.toml --- .github/ISSUE_TEMPLATE/bug_report.md | 23 -------- .github/ISSUE_TEMPLATE/task.yml | 59 ------------------- .github/workflows/apm-update.yaml | 47 --------------- .github/workflows/ci.yaml | 17 ------ home/.chezmoidata/uv.yaml | 4 +- .../run_onchange_06_install-apm.sh.tmpl | 16 ----- mise.toml | 1 - 7 files changed, 2 insertions(+), 165 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md delete mode 100644 .github/ISSUE_TEMPLATE/task.yml delete mode 100644 .github/workflows/apm-update.yaml delete mode 100644 home/.chezmoiscripts/darwin/run_onchange_06_install-apm.sh.tmpl diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index d561c78..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -name: Bug report -about: Report something that is broken -title: "" -labels: bug -assignees: "" ---- - -## Summary - -## Steps To Reproduce - -1. - -## Expected Behavior - -## Actual Behavior - -## Context - -- Area: -- Command or workflow: -- Local Superpowers spec or plan: diff --git a/.github/ISSUE_TEMPLATE/task.yml b/.github/ISSUE_TEMPLATE/task.yml deleted file mode 100644 index f6d7ff9..0000000 --- a/.github/ISSUE_TEMPLATE/task.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: Task -description: Track a feature, enhancement, cleanup, or research item -title: "" -labels: [enhancement] -body: - - type: textarea - id: outcome - attributes: - label: Outcome - description: What should be true when this is done? - validations: - required: true - - type: textarea - id: scope - attributes: - label: Scope - description: What is included, and what is intentionally out of scope? - validations: - required: true - - type: dropdown - id: area - attributes: - label: Area - options: - - APM - - Chezmoi - - CI - - Docs - - Agents - - Editor - validations: - required: true - - type: dropdown - id: priority - attributes: - label: Priority - description: How important is this task for the repo right now? - options: - - High - - Normal - - Low - validations: - required: true - - type: textarea - id: acceptance-checks - attributes: - label: Acceptance Checks - description: List the checks that prove this task is complete. - placeholder: "- [ ] mise lint passes" - validations: - required: true - - type: textarea - id: superpowers-docs - attributes: - label: Superpowers Docs - description: Link the spec or plan under docs/superpowers/ before implementation starts. - placeholder: "Spec: docs/superpowers/specs/YYYY-MM-DD-topic-design.md\nPlan: docs/superpowers/plans/YYYY-MM-DD-topic.md" - validations: - required: true diff --git a/.github/workflows/apm-update.yaml b/.github/workflows/apm-update.yaml deleted file mode 100644 index 468e8f5..0000000 --- a/.github/workflows/apm-update.yaml +++ /dev/null @@ -1,47 +0,0 @@ -name: Update APM Locks - -on: - schedule: - - cron: "0 0 * * 5" # Every Friday at midnight UTC, matching CI schedule - workflow_dispatch: # Allow manual trigger from the Actions tab - -concurrency: - group: ${{ github.workflow }} - cancel-in-progress: true - -permissions: - contents: write - pull-requests: write - -jobs: - refresh_locks: - name: Refresh APM lockfiles - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: jdx/mise-action@e6a8b3978addb5a52f2b4cd9d91eafa7f0ab959d # v4.2.0 - - name: Refresh APM lockfiles - run: mise exec -- bash scripts/refresh-apm-locks.sh - # Gate the refreshed lockfiles here: a PR opened with the default - # GITHUB_TOKEN does not trigger the on: pull_request CI run, so the - # frozen-install check must pass before the PR is opened. - - name: Validate refreshed lockfiles - run: mise exec -- bash scripts/validate-apm.sh all - - name: Open PR if lockfiles changed - uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 - with: - commit-message: "chore: refresh APM lockfiles" - branch: chore/apm-lock-refresh - delete-branch: true - title: "Refresh APM lockfiles" - body: | - Automated refresh of committed APM lockfiles. - - Re-resolves each channel ref to its latest SHA and rewrites - `home/.chezmoitemplates/apm/apm.lock.*.yaml`. - - The frozen-install gate ran on these lockfiles before this PR was - opened, since an auto-PR does not trigger the on: pull_request CI run. - - Refs DOT-31 - labels: dependencies diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index bf5f7cc..008079a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -25,23 +25,6 @@ jobs: - uses: jdx/mise-action@e6a8b3978addb5a52f2b4cd9d91eafa7f0ab959d # v4.2.0 - run: mise lint - validate_apm: - name: Validate APM config (${{ matrix.context }}) - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - context: [personal, work] - steps: - - uses: actions/checkout@v6 - - uses: jdx/mise-action@e6a8b3978addb5a52f2b4cd9d91eafa7f0ab959d # v4.2.0 - - name: Stub chezmoi config (${{ matrix.context }} context) - uses: ./.github/actions/write-chezmoi-config - with: - context: ${{ matrix.context }} - - name: Frozen install + audit (${{ matrix.context }}) - run: mise exec -- bash scripts/validate-apm.sh ${{ matrix.context }} - actions_lint: name: Lint GitHub Actions workflows runs-on: ubuntu-latest diff --git a/home/.chezmoidata/uv.yaml b/home/.chezmoidata/uv.yaml index d02f961..437f0c5 100644 --- a/home/.chezmoidata/uv.yaml +++ b/home/.chezmoidata/uv.yaml @@ -1,7 +1,7 @@ uv: tools: - shared: [] - personal: + shared: - graphifyy + personal: - tavily-cli work: [] diff --git a/home/.chezmoiscripts/darwin/run_onchange_06_install-apm.sh.tmpl b/home/.chezmoiscripts/darwin/run_onchange_06_install-apm.sh.tmpl deleted file mode 100644 index 6e6035c..0000000 --- a/home/.chezmoiscripts/darwin/run_onchange_06_install-apm.sh.tmpl +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash -{{ $src := .chezmoi.sourceDir }} -# apm.yml: {{ include "dot_apm/apm.yml.tmpl" | sha256sum }} -# apm data: {{ include ".chezmoidata/apm.yaml" | sha256sum }} -# apm mcp template: {{ include ".chezmoitemplates/apm/mcp-server.yml.tmpl" | sha256sum }} -# apm lock (personal): {{ include ".chezmoitemplates/apm/apm.lock.personal.yaml" | sha256sum }} -# apm lock (work): {{ include ".chezmoitemplates/apm/apm.lock.work.yaml" | sha256sum }} -# agents: {{ range (glob (printf "%s/dot_apm/dot_apm/agents/*" $src)) }}{{ include (trimPrefix (printf "%s/" $src) .) | sha256sum }} {{ end }} -# instructions: {{ range (glob (printf "%s/dot_apm/dot_apm/instructions/*" $src)) }}{{ include (trimPrefix (printf "%s/" $src) .) | sha256sum }} {{ end }} -# skills: {{ range (glob (printf "%s/dot_apm/dot_apm/skills/**/*.md" $src)) }}{{ include (trimPrefix (printf "%s/" $src) .) | sha256sum }} {{ end }} - -{{ if eq .chezmoi.os "darwin" }} -{{ template "lib/common/log.sh" . }} -{{ template "lib/common/install-prelude.sh" . }} -{{ template "lib/install/apm.sh" . }} -{{ end }} diff --git a/mise.toml b/mise.toml index 26869de..c2ebddf 100644 --- a/mise.toml +++ b/mise.toml @@ -1,5 +1,4 @@ [tools] -"github:microsoft/apm" = "0.21.0" bats = "1.13.0" chezmoi = "2.70.3" prettier = "3.8.3" From d7b7f66446e98dda294f22d8345c80ce3130fdfa Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 20:26:17 -0500 Subject: [PATCH 15/51] chore: remove obsolete APM scripts and related test files --- scripts/apm-lib.sh | 42 ----- scripts/refresh-apm-locks.sh | 59 ------- scripts/validate-apm.sh | 71 -------- tests/template/apm-config.bats | 84 --------- tests/template/apm-lockfile.bats | 24 --- tests/unit/lib/install/apm.bats | 46 ----- tests/unit/lib/install/caveman-hooks.bats | 75 -------- .../lib/install/opencode-agent-tools.bats | 164 ------------------ tests/unit/opencode-config.bats | 33 ---- tests/unit/scripts/apm-lib.bats | 45 ----- tests/unit/scripts/refresh-apm-locks.bats | 99 ----------- tests/unit/scripts/validate-apm.bats | 44 ----- 12 files changed, 786 deletions(-) delete mode 100644 scripts/apm-lib.sh delete mode 100644 scripts/refresh-apm-locks.sh delete mode 100755 scripts/validate-apm.sh delete mode 100644 tests/template/apm-config.bats delete mode 100644 tests/template/apm-lockfile.bats delete mode 100644 tests/unit/lib/install/apm.bats delete mode 100644 tests/unit/lib/install/caveman-hooks.bats delete mode 100644 tests/unit/lib/install/opencode-agent-tools.bats delete mode 100644 tests/unit/opencode-config.bats delete mode 100644 tests/unit/scripts/apm-lib.bats delete mode 100644 tests/unit/scripts/refresh-apm-locks.bats delete mode 100644 tests/unit/scripts/validate-apm.bats diff --git a/scripts/apm-lib.sh b/scripts/apm-lib.sh deleted file mode 100644 index 78d820b..0000000 --- a/scripts/apm-lib.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env bash -# @file scripts/apm-lib.sh -# @brief Shared helpers to materialize a context's ~/.apm into a temp home. -# @description -# Sourced by scripts/validate-apm.sh and scripts/refresh-apm-locks.sh. -# Resolves pinned tool paths before HOME is changed, then applies only the -# ~/.apm chezmoi target into an isolated temp home without firing run scripts. - -set -Eeuo pipefail - -# @description Resolve pinned chezmoi and apm binaries. Call before changing HOME, -# because an isolated HOME breaks mise trust resolution. -# @set CHEZMOI_BIN Absolute path to the pinned chezmoi binary. -# @set APM_BIN Absolute path to the pinned apm binary. -function apm_lib_resolve_bins() { - CHEZMOI_BIN="$(mise which chezmoi)" - APM_BIN="$(mise which apm)" - export CHEZMOI_BIN APM_BIN -} - -# @description Materialize ~/.apm for a context into a temp home. -# @arg $1 string Context: personal or work. -# @arg $2 string Temp root for chezmoi state and cache. -# @arg $3 string Temp home directory. -# @arg $4 string Repo root (chezmoi source parent). -function apm_lib_materialize() { - local context="$1" tmp_root="$2" tmp_home="$3" repo="$4" - - HOME="$tmp_home" "$repo/.github/actions/write-chezmoi-config/write-chezmoi-config.sh" "$context" - - "${CHEZMOI_BIN}" apply \ - --source "$repo/home" \ - --destination "$tmp_home" \ - --config "$tmp_home/.config/chezmoi/chezmoi.yaml" \ - --config-format yaml \ - --persistent-state "$tmp_root/chezmoistate-$context.boltdb" \ - --cache "$tmp_root/cache-$context" \ - --refresh-externals=never \ - --include files,dirs \ - --no-tty \ - "$tmp_home/.apm" -} diff --git a/scripts/refresh-apm-locks.sh b/scripts/refresh-apm-locks.sh deleted file mode 100644 index 2c9dc60..0000000 --- a/scripts/refresh-apm-locks.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env bash -# @file scripts/refresh-apm-locks.sh -# @brief Regenerate committed per-context APM lockfiles. -# @description -# For each context: materialize ~/.apm in a temp home, run -# `apm lock --update --global` to re-resolve refs and rewrite apm.lock.yaml, -# then copy the lockfile back to -# home/.chezmoitemplates/apm/apm.lock..yaml. Never touches real HOME. -# Note: apm 0.21.0 forwards `apm update` to `self-update` (binary updater), so -# `apm lock --update --global` is the command that rewrites the user-scope -# lockfile at ~/.apm/apm.lock.yaml. - -set -Eeuo pipefail - -repo="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -# shellcheck source=scripts/apm-lib.sh -source "$repo/scripts/apm-lib.sh" - -contexts=("$@") -[ "$#" -eq 0 ] && contexts=(personal work) - -# Reject unknown contexts before doing work; an unvalidated context would -# silently write a misnamed apm.lock..yaml into the repo. -for context in "${contexts[@]}"; do - case "$context" in - personal | work) ;; - *) - printf 'Unsupported context: %s (use personal, work)\n' "$context" >&2 - exit 1 - ;; - esac -done - -apm_lib_resolve_bins -PRETTIER_BIN="$(mise which prettier)" -export PRETTIER_BIN - -tmp_root="$(mktemp -d)" -trap 'rm -rf "$tmp_root"' EXIT - -written=() - -for context in "${contexts[@]}"; do - tmp_home="$tmp_root/home-$context" - mkdir -p "$tmp_home" - apm_lib_materialize "$context" "$tmp_root" "$tmp_home" "$repo" - - HOME="$tmp_home" \ - XDG_CACHE_HOME="$tmp_home/.cache" XDG_CONFIG_HOME="$tmp_home/.config" XDG_STATE_HOME="$tmp_home/.local/state" \ - "${APM_BIN}" lock --update --global - - dest="$repo/home/.chezmoitemplates/apm/apm.lock.$context.yaml" - cp "$tmp_home/.apm/apm.lock.yaml" "$dest" - written+=("$dest") - printf '[refresh] wrote home/.chezmoitemplates/apm/apm.lock.%s.yaml\n' "$context" -done - -"${PRETTIER_BIN}" --write "${written[@]}" -printf '[refresh] formatted %d lockfile(s) with prettier\n' "${#written[@]}" diff --git a/scripts/validate-apm.sh b/scripts/validate-apm.sh deleted file mode 100755 index 4b7c98a..0000000 --- a/scripts/validate-apm.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env bash -# @file scripts/validate-apm.sh -# @brief Validate APM reproducibility for one or both contexts in temp homes. -# @description -# Materializes ~/.apm via chezmoi (manifest + committed lockfile), then runs -# `apm install --global --frozen` in an isolated temp home. A non-zero exit -# from `--frozen` means the lockfile is missing, stale, or a ref is -# unresolvable — the only signals that matter for reproducibility. -# `all` runs both contexts and aggregates results. -# @arg $1 string Context: personal, work, or all (default all). - -set -Eeuo pipefail - -repo="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -# shellcheck source=scripts/apm-lib.sh -source "$repo/scripts/apm-lib.sh" - -arg="${1:-all}" -case "$arg" in -personal | work) contexts=("$arg") ;; -all) contexts=(personal work) ;; -*) - printf 'Unsupported context: %s (use personal, work, or all)\n' "$arg" >&2 - exit 1 - ;; -esac - -apm_lib_resolve_bins - -tmp_root="$(mktemp -d)" -trap 'rm -rf "$tmp_root"' EXIT - -# @description Run the frozen install gate for one context. -# @description -# Called as an `if` condition, so set -e errexit is suspended here. Capture -# install_rc explicitly so a failing `apm install --frozen` is not masked. -# `--frozen` already enforces lockfile-exists and ref-consistency; a separate -# `apm audit --ci` call is not needed and introduces false failures when the -# lockfile lacks a deployed_files section (generated by `apm lock`, not install). -# @arg $1 string Context. -function validate_context() { - local context="$1" tmp_home="$tmp_root/home-$1" install_rc - mkdir -p "$tmp_home" - if ! apm_lib_materialize "$context" "$tmp_root" "$tmp_home" "$repo"; then - return 1 - fi - - # Run apm from inside the materialized ~/.apm (matches the runtime installer in - # lib/install/apm.sh) so it operates on the global manifest and lockfile rather - # than treating the current repo as a local project. Subshell keeps the loop cwd. - ( - cd "$tmp_home/.apm" && - HOME="$tmp_home" \ - XDG_CACHE_HOME="$tmp_home/.cache" XDG_CONFIG_HOME="$tmp_home/.config" XDG_STATE_HOME="$tmp_home/.local/state" \ - "${APM_BIN}" install --global --frozen --parallel-downloads 0 - ) - install_rc=$? - - return "$install_rc" -} - -rc=0 -for context in "${contexts[@]}"; do - if validate_context "$context"; then - printf '%s: PASS\n' "$context" - else - printf '%s: FAIL\n' "$context" - rc=1 - fi -done -exit "$rc" diff --git a/tests/template/apm-config.bats b/tests/template/apm-config.bats deleted file mode 100644 index ee0cb8d..0000000 --- a/tests/template/apm-config.bats +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/env bats -# @file tests/template/apm-config.bats -# @brief Template rendering tests for home/dot_apm/apm.yml.tmpl. - -load '../test_helpers/load.bash' -load '../test_helpers/templates.bash' - -APM_TEMPLATE="$DOTFILES_ROOT/home/dot_apm/apm.yml.tmpl" - -@test "personal APM config renders only shared MCP servers" { - run render_chezmoi_template "$APM_TEMPLATE" "$PERSONAL_DATA" - - assert_success - assert_line ' - name: "grep"' - assert_line ' registry: false' - assert_line ' url: "https://mcp.grep.app"' - refute_line --regexp 'name: figma' - refute_line --regexp 'name: jira' -} - -@test "personal APM config renders personal target" { - run render_chezmoi_template "$APM_TEMPLATE" "$PERSONAL_DATA" - - assert_success - assert_line ' - claude' - assert_line ' - agent-skills' - refute_line ' - copilot' - refute_line ' - opencode' - refute_line ' -agent-skills:' -} - -@test "personal APM config renders shared and personal APM packages" { - run render_chezmoi_template "$APM_TEMPLATE" "$PERSONAL_DATA" - - assert_success - assert_line --partial ' - "obra/superpowers' - assert_line --partial ' - "JuliusBrussee/caveman' - assert_line --partial ' - "anthropics/claude-plugins-official/plugins/skill-creator' - assert_line --partial ' - "schpet/linear-cli' - assert_line --partial ' - "tavily-ai/skills' - assert_line --partial ' - git: JuliusBrussee/skills' - assert_line ' skills:' - assert_line ' - "grill-me"' - assert_line ' - "junior-to-senior"' - refute_line --partial 'interface-kit' - refute_line --partial 'loop-factory' - refute_line --partial 'context-canary' -} - -@test "work APM config renders Figma and Jira MCP servers" { - run render_chezmoi_template "$APM_TEMPLATE" "$WORK_DATA" - - assert_success - assert_line ' - name: "grep"' - assert_line ' - name: "figma"' - assert_line ' url: "https://mcp.figma.com/mcp"' - assert_line ' - name: "jira"' - assert_line ' url: "https://mcp.atlassian.com/v1/mcp"' -} - -@test "work APM config renders work target" { - run render_chezmoi_template "$APM_TEMPLATE" "$WORK_DATA" - - assert_success - assert_line ' - copilot' - refute_line ' - opencode' - refute_line ' - claude' - refute_line ' - agent-skills' - refute_line ' -copilot:' -} - -@test "work APM config renders shared APM packages without personal packages" { - run render_chezmoi_template "$APM_TEMPLATE" "$WORK_DATA" - - assert_success - assert_line --partial ' - "obra/superpowers' - assert_line --partial ' - "JuliusBrussee/caveman' - assert_line --partial ' - "anthropics/claude-plugins-official/plugins/skill-creator' - refute_line --partial ' - schpet/linear-cli' - refute_line --partial ' - tavily-ai/skills' - refute_line --partial 'JuliusBrussee/skills' - refute_line --partial 'grill-me' - refute_line --partial 'junior-to-senior' -} diff --git a/tests/template/apm-lockfile.bats b/tests/template/apm-lockfile.bats deleted file mode 100644 index 6f07973..0000000 --- a/tests/template/apm-lockfile.bats +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bats -# @file tests/template/apm-lockfile.bats -# @brief apm.lock.yaml.tmpl selects the correct per-context lockfile. - -load '../test_helpers/load.bash' -load '../test_helpers/templates.bash' - -TMPL="$DOTFILES_ROOT/home/dot_apm/apm.lock.yaml.tmpl" -PERSONAL='{"personal":true,"work":false}' -WORK='{"personal":false,"work":true}' - -@test "personal context renders the personal lockfile" { - personal_first="$(grep -m1 resolved_commit "$DOTFILES_ROOT/home/.chezmoitemplates/apm/apm.lock.personal.yaml")" - run render_chezmoi_template "$TMPL" "$PERSONAL" - assert_success - assert_line --regexp '^lockfile_version' - assert_line "$personal_first" -} - -@test "work context renders the work lockfile" { - run render_chezmoi_template "$TMPL" "$WORK" - assert_success - assert_line --regexp '^lockfile_version' -} diff --git a/tests/unit/lib/install/apm.bats b/tests/unit/lib/install/apm.bats deleted file mode 100644 index af90969..0000000 --- a/tests/unit/lib/install/apm.bats +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env bats -# @file tests/unit/lib/install/apm.bats -# @brief Behavior tests for home/.chezmoitemplates/lib/install/apm.sh. - -load '../../../test_helpers/load.bash' - -LOG_LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/common/log.sh" -PRELUDE="$DOTFILES_ROOT/home/.chezmoitemplates/lib/common/install-prelude.sh" -APM_LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/install/apm.sh" - -setup() { - export HOME="$BATS_TEST_TMPDIR/home" - export APM_ARGS_FILE="$BATS_TEST_TMPDIR/apm-args" - export APM_CWD_FILE="$BATS_TEST_TMPDIR/apm-cwd" - mkdir -p "$HOME/.apm" "$BATS_TEST_TMPDIR/bin" - - cat >"$BATS_TEST_TMPDIR/bin/apm" <<'APM' -#!/usr/bin/env bash -printf '%s\n' "$*" >"$APM_ARGS_FILE" -printf '%s\n' "$PWD" >"$APM_CWD_FILE" -exit "${APM_EXIT_CODE:-0}" -APM - chmod +x "$BATS_TEST_TMPDIR/bin/apm" - export PATH="$BATS_TEST_TMPDIR/bin:$PATH" -} - -@test "apm_install_main: runs apm install globally from ~/.apm" { - run bash -c "source '$LOG_LIB' && source '$PRELUDE' && source '$APM_LIB' && apm_install_main" - - assert_success - assert_line "[apm] Installing globally from ~/.apm/apm.yml (frozen)..." - assert_line "[apm] Install complete." - [ "$(<"$APM_ARGS_FILE")" = "install --global --frozen" ] - [ "$(<"$APM_CWD_FILE")" = "$HOME/.apm" ] -} - -@test "apm_install_main: warns and completes when apm install fails" { - export APM_EXIT_CODE=7 - - run bash -c "source '$LOG_LIB' && source '$PRELUDE' && source '$APM_LIB' && apm_install_main" - - assert_success - assert_line --partial "warn: [apm] apm install --frozen exited non-zero" - assert_line "[apm] Install complete." - [ "$(<"$APM_ARGS_FILE")" = "install --global --frozen" ] -} diff --git a/tests/unit/lib/install/caveman-hooks.bats b/tests/unit/lib/install/caveman-hooks.bats deleted file mode 100644 index a38b155..0000000 --- a/tests/unit/lib/install/caveman-hooks.bats +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env bats -# @file tests/unit/lib/install/caveman-hooks.bats -# @brief Behavior tests for home/.chezmoitemplates/lib/install/caveman-hooks.sh. - -load '../../../test_helpers/load.bash' - -LOG_LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/common/log.sh" -PRELUDE="$DOTFILES_ROOT/home/.chezmoitemplates/lib/common/install-prelude.sh" -LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/install/caveman-hooks.sh" - -RUNTIME_FILES=( - caveman-config.js - caveman-activate.js - caveman-mode-tracker.js - caveman-stats.js - package.json -) - -setup() { - export CAVEMAN_HOOK_SOURCE="$BATS_TEST_TMPDIR/src" - export CAVEMAN_HOOK_DEST="$BATS_TEST_TMPDIR/dest" - mkdir -p "$CAVEMAN_HOOK_SOURCE" - local f - for f in "${RUNTIME_FILES[@]}"; do - printf 'content-%s\n' "$f" >"$CAVEMAN_HOOK_SOURCE/$f" - done -} - -run_lib() { - run bash -c "source '$LOG_LIB' && source '$PRELUDE' && source '$LIB' && main" -} - -@test "main: copies all runtime files into the dest hooks dir" { - run_lib - - assert_success - assert_line "[caveman] caveman hook runtime installed." - local f - for f in "${RUNTIME_FILES[@]}"; do - assert_file_exist "$CAVEMAN_HOOK_DEST/$f" - done -} - -@test "main: mirrors the full runtime into the apm nested hook path" { - run_lib - - assert_success - local f - for f in "${RUNTIME_FILES[@]}"; do - assert_file_exist "$CAVEMAN_HOOK_DEST/caveman/src/hooks/$f" - done -} - -@test "main: warns and succeeds when source dir is absent" { - rm -rf "$CAVEMAN_HOOK_SOURCE" - - run_lib - - assert_success - assert_line "warn: [caveman] $CAVEMAN_HOOK_SOURCE not found; run run_onchange_06_install-apm first." - [ ! -d "$CAVEMAN_HOOK_DEST" ] -} - -@test "main: warns about missing runtime files but still succeeds" { - rm -f "$CAVEMAN_HOOK_SOURCE/caveman-stats.js" - - run_lib - - assert_success - assert_line "warn: [caveman] 1 runtime file(s) missing from source:" - assert_line " - caveman-stats.js" - assert_line "[caveman] caveman hook runtime installed." - assert_file_exist "$CAVEMAN_HOOK_DEST/caveman-config.js" - [ ! -f "$CAVEMAN_HOOK_DEST/caveman-stats.js" ] -} diff --git a/tests/unit/lib/install/opencode-agent-tools.bats b/tests/unit/lib/install/opencode-agent-tools.bats deleted file mode 100644 index f53d612..0000000 --- a/tests/unit/lib/install/opencode-agent-tools.bats +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/env bats -# @file tests/unit/lib/install/opencode-agent-tools.bats -# @brief Behavior tests for home/.chezmoitemplates/lib/install/opencode-agent-tools.sh. - -load '../../../test_helpers/load.bash' - -LOG_LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/common/log.sh" -TOOLS_LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/install/opencode-agent-tools.sh" - -setup() { - AGENTS_DIR="$BATS_TEST_TMPDIR/agents" - mkdir -p "$AGENTS_DIR" -} - -@test "normalize_dir: converts PascalCase array to lowercase map" { - cat >"$AGENTS_DIR/cavecrew-investigator.md" <<'MD' ---- -name: cavecrew-investigator -description: read-only locator -tools: [Read, Grep, Glob, Bash] -model: haiku ---- -body content here -MD - - run bash -c "source '$LOG_LIB' && source '$TOOLS_LIB' && opencode_agent_tools_normalize_dir '$AGENTS_DIR'" - - assert_success - assert_line --partial "rewrote cavecrew-investigator.md" - assert_line --partial "1 file(s) updated" - - local expected - expected='--- -name: cavecrew-investigator -description: read-only locator -tools: - read: true - grep: true - glob: true - bash: true -model: haiku ---- -body content here' - [ "$(<"$AGENTS_DIR/cavecrew-investigator.md")" = "$expected" ] -} - -@test "normalize_dir: leaves already-mapped files untouched" { - cat >"$AGENTS_DIR/already-fixed.md" <<'MD' ---- -name: already-fixed -tools: - read: true - grep: true ---- -body -MD - local before - before="$(cat "$AGENTS_DIR/already-fixed.md")" - - run bash -c "source '$LOG_LIB' && source '$TOOLS_LIB' && opencode_agent_tools_normalize_dir '$AGENTS_DIR'" - - assert_success - assert_line --partial "0 file(s) updated" - [ "$(<"$AGENTS_DIR/already-fixed.md")" = "$before" ] -} - -@test "normalize_dir: ignores tools: arrays appearing in the body" { - cat >"$AGENTS_DIR/body-mention.md" <<'MD' ---- -name: body-mention -description: docs ---- -Here is markdown: -tools: [Read, Grep] -That should remain unchanged. -MD - local before - before="$(cat "$AGENTS_DIR/body-mention.md")" - - run bash -c "source '$LOG_LIB' && source '$TOOLS_LIB' && opencode_agent_tools_normalize_dir '$AGENTS_DIR'" - - assert_success - assert_line --partial "0 file(s) updated" - [ "$(<"$AGENTS_DIR/body-mention.md")" = "$before" ] -} - -@test "normalize_dir: missing dir is a no-op" { - run bash -c "source '$LOG_LIB' && source '$TOOLS_LIB' && opencode_agent_tools_normalize_dir '$BATS_TEST_TMPDIR/does-not-exist'" - - assert_success - assert_line --partial "No agents dir" -} - -@test "normalize_dir: empty dir reports zero updates" { - run bash -c "source '$LOG_LIB' && source '$TOOLS_LIB' && opencode_agent_tools_normalize_dir '$AGENTS_DIR'" - - assert_success - assert_line --partial "0 file(s) updated" -} - -@test "normalize_dir: drops empty items and strips quotes" { - cat >"$AGENTS_DIR/messy.md" <<'MD' ---- -name: messy -tools: [ Read , , 'Edit' , "Write" ] ---- -MD - - run bash -c "source '$LOG_LIB' && source '$TOOLS_LIB' && opencode_agent_tools_normalize_dir '$AGENTS_DIR'" - - assert_success - local expected - expected='--- -name: messy -tools: - read: true - edit: true - write: true ----' - [ "$(<"$AGENTS_DIR/messy.md")" = "$expected" ] -} - -@test "normalize_dir: idempotent on repeated runs" { - cat >"$AGENTS_DIR/idem.md" <<'MD' ---- -name: idem -tools: [Read, Bash] ---- -MD - - run bash -c "source '$LOG_LIB' && source '$TOOLS_LIB' && opencode_agent_tools_normalize_dir '$AGENTS_DIR'" - assert_success - local after_first - after_first="$(cat "$AGENTS_DIR/idem.md")" - - run bash -c "source '$LOG_LIB' && source '$TOOLS_LIB' && opencode_agent_tools_normalize_dir '$AGENTS_DIR'" - assert_success - assert_line --partial "0 file(s) updated" - [ "$(<"$AGENTS_DIR/idem.md")" = "$after_first" ] -} - -@test "normalize_file: returns 1 when nothing to change" { - cat >"$AGENTS_DIR/nothing.md" <<'MD' ---- -name: nothing ---- -body -MD - - run bash -c "source '$LOG_LIB' && source '$TOOLS_LIB' && opencode_agent_tools_normalize_file '$AGENTS_DIR/nothing.md'" - assert_failure -} - -@test "normalize_file: returns 0 when file is modified" { - cat >"$AGENTS_DIR/changed.md" <<'MD' ---- -name: changed -tools: [Read] ---- -MD - - run bash -c "source '$LOG_LIB' && source '$TOOLS_LIB' && opencode_agent_tools_normalize_file '$AGENTS_DIR/changed.md'" - assert_success -} diff --git a/tests/unit/opencode-config.bats b/tests/unit/opencode-config.bats deleted file mode 100644 index a6db4a9..0000000 --- a/tests/unit/opencode-config.bats +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bats -# @file tests/unit/opencode-config.bats -# @brief Regression tests for home/dot_config/opencode/opencode.jsonc. - -load '../test_helpers/load.bash' - -CONFIG="$DOTFILES_ROOT/home/dot_config/opencode/opencode.jsonc" - -@test "opencode config avoids stale OpenAI model tuning" { - config_content="$(<"$CONFIG")" - - [[ "$config_content" == *'"model": "openai/gpt-5.5"'* ]] - [[ "$config_content" != *'"small_model"'* ]] - [[ "$config_content" != *'"provider"'* ]] - [[ "$config_content" != *'"reasoningEffort"'* ]] - [[ "$config_content" != *'"textVerbosity"'* ]] - [[ "$config_content" != *'"reasoningSummary"'* ]] -} - -@test "opencode config does not include supermemory plugin" { - config_content="$(<"$CONFIG")" - - [[ "$config_content" != *'opencode-supermemory'* ]] - [[ "$config_content" == *'"opencode-wakatime"'* ]] -} - -@test "opencode config omits Tavily MCP" { - config_content="$(<"$CONFIG")" - - [[ "$config_content" == *'"grep": {'* ]] - [[ "$config_content" != *'"tavily": {'* ]] - [[ "$config_content" != *'TAVILY_API_KEY'* ]] -} diff --git a/tests/unit/scripts/apm-lib.bats b/tests/unit/scripts/apm-lib.bats deleted file mode 100644 index 80797b5..0000000 --- a/tests/unit/scripts/apm-lib.bats +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bats -# @file tests/unit/scripts/apm-lib.bats -# @brief Behavior tests for scripts/apm-lib.sh shared materialization. - -load '../../test_helpers/load.bash' - -LIB="$DOTFILES_ROOT/scripts/apm-lib.sh" - -setup() { - export ARGS_FILE="$BATS_TEST_TMPDIR/chezmoi-args" - mkdir -p "$BATS_TEST_TMPDIR/bin" - - cat >"$BATS_TEST_TMPDIR/bin/chezmoi" <<'CHEZMOI' -#!/usr/bin/env bash -printf '%s\n' "$*" >>"$ARGS_FILE" -CHEZMOI - chmod +x "$BATS_TEST_TMPDIR/bin/chezmoi" - export PATH="$BATS_TEST_TMPDIR/bin:$PATH" - cat >"$BATS_TEST_TMPDIR/bin/mise" <<'MISE' -#!/usr/bin/env bash -if [ "$1" = "which" ]; then echo "$BATS_TEST_TMPDIR/bin/$2"; fi -MISE - chmod +x "$BATS_TEST_TMPDIR/bin/mise" -} - -@test "apm_lib_resolve_bins sets CHEZMOI_BIN and APM_BIN before HOME changes" { - run bash -c "source '$LIB' && apm_lib_resolve_bins && echo \"\$CHEZMOI_BIN|\$APM_BIN\"" - assert_success - assert_line --partial "/bin/chezmoi|" - assert_line --partial "/bin/apm" -} - -@test "apm_lib_materialize applies only the ~/.apm target with scripts excluded" { - run bash -c " - source '$LIB' - CHEZMOI_BIN='$BATS_TEST_TMPDIR/bin/chezmoi' - apm_lib_materialize personal '$BATS_TEST_TMPDIR/root' '$BATS_TEST_TMPDIR/home' '$DOTFILES_ROOT' - " - assert_success - run cat "$ARGS_FILE" - assert_line --partial "apply" - assert_line --partial "--include files,dirs" - assert_line --partial "/home/.apm" - refute_line --partial "scripts" -} diff --git a/tests/unit/scripts/refresh-apm-locks.bats b/tests/unit/scripts/refresh-apm-locks.bats deleted file mode 100644 index 3e345b5..0000000 --- a/tests/unit/scripts/refresh-apm-locks.bats +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/env bats -# @file tests/unit/scripts/refresh-apm-locks.bats -# @brief Behavior tests for scripts/refresh-apm-locks.sh lockfile refresh flow. -# Stubs mise, chezmoi, apm, and prettier on PATH and runs an isolated -# copy of the script so the committed lockfiles are never touched. - -load '../../test_helpers/load.bash' - -setup() { - export CALL_FILE="$BATS_TEST_TMPDIR/calls" - BIN="$BATS_TEST_TMPDIR/bin" - mkdir -p "$BIN" - - # mise resolves pinned tool paths to our stubs. - cat >"$BIN/mise" <>"$CALL_FILE" -if [ "\$1" = which ]; then echo "$BIN/\$2"; exit 0; fi -exit 0 -STUB - - # chezmoi apply only needs to create the target ~/.apm directory. - cat >"$BIN/chezmoi" <>"$CALL_FILE" -mkdir -p "\${@: -1}" -exit 0 -STUB - - # apm lock --update --global writes the user-scope lockfile under \$HOME/.apm. - cat >"$BIN/apm" <>"$CALL_FILE" -mkdir -p "\$HOME/.apm" -printf 'lockfile_version: 1\n# stub lock\n' >"\$HOME/.apm/apm.lock.yaml" -exit 0 -STUB - - # prettier records the files it was asked to format. - cat >"$BIN/prettier" <>"$CALL_FILE" -exit 0 -STUB - - chmod +x "$BIN"/* - export PATH="$BIN:$PATH" - - # Isolated repo copy so cp writes lockfiles into the temp tree, never the real repo. - REPO="$BATS_TEST_TMPDIR/repo" - mkdir -p "$REPO/scripts" "$REPO/home/.chezmoitemplates/apm" "$REPO/.github/actions/write-chezmoi-config" - cp "$DOTFILES_ROOT/scripts/refresh-apm-locks.sh" "$REPO/scripts/refresh-apm-locks.sh" - cp "$DOTFILES_ROOT/scripts/apm-lib.sh" "$REPO/scripts/apm-lib.sh" - cat >"$REPO/.github/actions/write-chezmoi-config/write-chezmoi-config.sh" <<'STUB' -#!/usr/bin/env bash -exit 0 -STUB - chmod +x "$REPO/.github/actions/write-chezmoi-config/write-chezmoi-config.sh" - - LOCK_DIR="$REPO/home/.chezmoitemplates/apm" -} - -@test "personal context writes the personal lockfile and formats it" { - run bash "$REPO/scripts/refresh-apm-locks.sh" personal - assert_success - assert_line --partial "wrote home/.chezmoitemplates/apm/apm.lock.personal.yaml" - - assert_file_exists "$LOCK_DIR/apm.lock.personal.yaml" - assert_file_contains "$LOCK_DIR/apm.lock.personal.yaml" 'lockfile_version: 1' - - run cat "$CALL_FILE" - assert_line --partial "prettier --write $LOCK_DIR/apm.lock.personal.yaml" -} - -@test "work context writes the work lockfile only" { - run bash "$REPO/scripts/refresh-apm-locks.sh" work - assert_success - - assert_file_exists "$LOCK_DIR/apm.lock.work.yaml" - assert_file_not_exists "$LOCK_DIR/apm.lock.personal.yaml" -} - -@test "no arguments refreshes both contexts" { - run bash "$REPO/scripts/refresh-apm-locks.sh" - assert_success - - assert_file_exists "$LOCK_DIR/apm.lock.personal.yaml" - assert_file_exists "$LOCK_DIR/apm.lock.work.yaml" - - run cat "$CALL_FILE" - assert_line --partial "prettier --write $LOCK_DIR/apm.lock.personal.yaml $LOCK_DIR/apm.lock.work.yaml" -} - -@test "rejects an unknown context before writing anything" { - run bash "$REPO/scripts/refresh-apm-locks.sh" bogus - assert_failure - assert_line --partial "Unsupported context: bogus" - assert_file_not_exists "$LOCK_DIR/apm.lock.bogus.yaml" -} diff --git a/tests/unit/scripts/validate-apm.bats b/tests/unit/scripts/validate-apm.bats deleted file mode 100644 index ab427d9..0000000 --- a/tests/unit/scripts/validate-apm.bats +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env bats -# @file tests/unit/scripts/validate-apm.bats -# @brief Behavior tests for scripts/validate-apm.sh argument handling and aggregation. - -load '../../test_helpers/load.bash' - -SCRIPT="$DOTFILES_ROOT/scripts/validate-apm.sh" - -setup() { - export CALL_FILE="$BATS_TEST_TMPDIR/calls" - mkdir -p "$BATS_TEST_TMPDIR/bin" - for t in mise chezmoi apm; do - cat >"$BATS_TEST_TMPDIR/bin/$t" <>"$CALL_FILE" -if [ "$t" = mise ] && [ "\$1" = which ]; then echo "$BATS_TEST_TMPDIR/bin/\$2"; exit 0; fi -if [ "$t" = chezmoi ]; then mkdir -p "\${@: -1}"; fi -exit "\${APM_EXIT:-0}" -STUB - chmod +x "$BATS_TEST_TMPDIR/bin/$t" - done - export PATH="$BATS_TEST_TMPDIR/bin:$PATH" -} - -@test "all runs both contexts and reports each" { - run bash "$SCRIPT" all - assert_success - assert_line --partial "personal: PASS" - assert_line --partial "work: PASS" -} - -@test "all reports both even when personal fails, exits non-zero" { - export APM_EXIT=1 - run bash "$SCRIPT" all - assert_failure - assert_line --partial "personal: FAIL" - assert_line --partial "work: FAIL" -} - -@test "rejects an unknown context" { - run bash "$SCRIPT" bogus - assert_failure - assert_line --partial "Unsupported context" -} From 79c96995af8874658a5210e171f707bdaf350ff0 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 21:30:08 -0500 Subject: [PATCH 16/51] feat: update Graphify skills installation script and remove obsolete APM tasks from mise.toml --- .../run_onchange_08_install-graphify-skills.sh.tmpl | 6 +++--- mise.toml | 8 -------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/home/.chezmoiscripts/darwin/run_onchange_08_install-graphify-skills.sh.tmpl b/home/.chezmoiscripts/darwin/run_onchange_08_install-graphify-skills.sh.tmpl index 1e38d42..65e2655 100644 --- a/home/.chezmoiscripts/darwin/run_onchange_08_install-graphify-skills.sh.tmpl +++ b/home/.chezmoiscripts/darwin/run_onchange_08_install-graphify-skills.sh.tmpl @@ -1,11 +1,11 @@ #!/usr/bin/env bash # @file run_onchange_08_install-graphify-skills.sh # @brief Install Graphify agent skills. -# @description APM data hash: {{ include ".chezmoidata/apm.yaml" | sha256sum }} -{{- $activeTargets := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .apm.targets) | fromJson -}} +# @description AI data hash: {{ include ".chezmoidata/ai.yaml" | sha256sum }} +{{- $activeTargets := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .ai.targets) | fromJson -}} {{- $platforms := list -}} {{- range $activeTargets -}} -{{- if eq . "agent-skills" }}{{ $platforms = append $platforms "agents" }}{{- else if eq . "claude" }}{{ $platforms = append $platforms "claude" }}{{- end -}} +{{- if eq . "claude-code" }}{{ $platforms = append $platforms "claude" }}{{- else if eq . "github-copilot" }}{{ $platforms = append $platforms "copilot" }}{{- end -}} {{- end -}} {{ if and (eq .chezmoi.os "darwin") (gt (len $platforms) 0) }} diff --git a/mise.toml b/mise.toml index c2ebddf..bc4913b 100644 --- a/mise.toml +++ b/mise.toml @@ -113,11 +113,3 @@ run = [{ task = "test-unit" }, { task = "test-template" }] [tasks.check] description = "Run lint and tests" run = [{ task = "lint" }, { task = "test" }] - -[tasks.validate-apm] -description = "Frozen APM install + audit gate for one or both contexts (personal, work, all)" -run = 'bash "$MISE_PROJECT_ROOT/scripts/validate-apm.sh" "${1:-all}"' - -[tasks.refresh-apm-locks] -description = "Re-resolve APM channels and rewrite committed lockfiles for all contexts" -run = 'bash "$MISE_PROJECT_ROOT/scripts/refresh-apm-locks.sh"' From e7bffb7bc15c9d15eae33e96f383f13a8d122cdd Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 21:34:52 -0500 Subject: [PATCH 17/51] feat: add AI skills install library and associated tests; refine repo structure and remove APM dependency management --- ...30-apm-migration-phase-1-skills-install.md | 397 ++++++++++++++++++ .../plans/2026-06-30-repo-refine-cleanup.md | 137 ++++++ ...-to-claude-marketplace-migration-design.md | 41 +- 3 files changed, 560 insertions(+), 15 deletions(-) create mode 100644 docs/superpowers/plans/2026-06-30-apm-migration-phase-1-skills-install.md create mode 100644 docs/superpowers/plans/2026-06-30-repo-refine-cleanup.md diff --git a/docs/superpowers/plans/2026-06-30-apm-migration-phase-1-skills-install.md b/docs/superpowers/plans/2026-06-30-apm-migration-phase-1-skills-install.md new file mode 100644 index 0000000..366551a --- /dev/null +++ b/docs/superpowers/plans/2026-06-30-apm-migration-phase-1-skills-install.md @@ -0,0 +1,397 @@ +# AI Skills Install (Phase 1) Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Install cross-agent skills declared in `home/.chezmoidata/ai.yaml` into the active machine's agent targets using the `skills` CLI, driven by a chezmoi run script and a sourceable, tested install library. + +**Architecture:** Mirror the existing `graphify-skills` pattern. A sourceable bash library (`lib/install/ai-skills.sh`) runs `npx skills add` per skill. A `run_onchange` chezmoi script resolves the active targets and active skills from `ai.yaml` via the existing `active-group-values` template, renders them into bash arrays, and sources the library. Bats tests stub `npx` and assert the captured arguments. + +**Tech Stack:** chezmoi templates, bash, the `skills` CLI (`npx skills`), bats. + +**Spec:** `docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md` (sections: Group and Target Data Model, Skills Design). + +**Scope note:** This is Phase 1 of 7. It delivers skills install on its own. Local domain skills (`typescript`, `react`, `testing`) do not exist until Phase 5, so the library skips `source: local` skills whose directory is absent; upstream skills install normally. + +--- + +## File Structure + +- Create: `home/.chezmoitemplates/lib/install/ai-skills.sh` — sourceable install library; one responsibility: run `npx skills add` for each selected skill against the target agents. +- Create: `home/.chezmoiscripts/darwin/run_onchange_09_install-ai-skills.sh.tmpl` — chezmoi run script; resolves active targets and skills from `ai.yaml`, renders bash arrays, sources the library. +- Create: `tests/unit/lib/install/ai-skills.bats` — behavior tests for the library, stubbing `npx`. +- Exists (no change): `home/.chezmoidata/ai.yaml` — the manifest. + +## Data Contract + +The run script passes the library three inputs: + +- `AI_SKILL_TARGETS` — bash array of agent ids, e.g. `("claude-code")`. Sourced from `active-group-values` of `.ai.targets`. +- `AI_SKILLS` — bash array of `"|"` strings, e.g. `("mattpocock/skills|grill-me")`. Sourced from `active-group-values` of `.ai.skills`. +- `AI_LOCAL_SKILLS_DIR` — absolute path to the authored local skills, `{{ .chezmoi.sourceDir }}/.chezmoitemplates/skills`. + +The `|` delimiter is safe because skill sources (`owner/repo`, `local`) and skill names contain no `|`. + +--- + +### Task 1: Install library + +**Files:** + +- Create: `home/.chezmoitemplates/lib/install/ai-skills.sh` +- Test: `tests/unit/lib/install/ai-skills.bats` + +- [ ] **Step 1: Write the failing test file** + +Create `tests/unit/lib/install/ai-skills.bats`: + +```bash +#!/usr/bin/env bats +# @file tests/unit/lib/install/ai-skills.bats +# @brief Behavior tests for home/.chezmoitemplates/lib/install/ai-skills.sh. + +load '../../../test_helpers/load.bash' + +LOG_LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/common/log.sh" +PRELUDE="$DOTFILES_ROOT/home/.chezmoitemplates/lib/common/install-prelude.sh" +LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/install/ai-skills.sh" + +setup() { + export NPX_ARGS_FILE="$BATS_TEST_TMPDIR/npx-args" + mkdir -p "$BATS_TEST_TMPDIR/bin" + + cat >"$BATS_TEST_TMPDIR/bin/npx" <<'NPX' +#!/usr/bin/env bash +printf '%s\n' "$*" >>"$NPX_ARGS_FILE" +exit "${NPX_EXIT_CODE:-0}" +NPX + chmod +x "$BATS_TEST_TMPDIR/bin/npx" + export PATH="$BATS_TEST_TMPDIR/bin:/usr/bin:/bin" + + export AI_LOCAL_SKILLS_DIR="$BATS_TEST_TMPDIR/skills" + mkdir -p "$AI_LOCAL_SKILLS_DIR" +} + +_run_main() { + run bash -c "source '$LOG_LIB' && source '$PRELUDE' && source '$LIB' && $1 && main" +} + +@test "main: installs each skill for a single target" { + _run_main "AI_SKILL_TARGETS=('claude-code') && AI_SKILLS=('mattpocock/skills|grill-me' 'blader/humanizer|humanizer')" + + assert_success + assert_line "[ai-skills] Installing cross-agent skills..." + assert_line "[ai-skills] Cross-agent skills installed." + assert_line "--yes skills add mattpocock/skills --skill grill-me -a claude-code --global --copy --yes" + assert_line "--yes skills add blader/humanizer --skill humanizer -a claude-code --global --copy --yes" +} + +@test "main: passes one -a flag per target" { + _run_main "AI_SKILL_TARGETS=('claude-code' 'github-copilot') && AI_SKILLS=('mattpocock/skills|grill-me')" + + assert_success + assert_line "--yes skills add mattpocock/skills --skill grill-me -a claude-code -a github-copilot --global --copy --yes" +} + +@test "main: skips empty skill entries" { + _run_main "AI_SKILL_TARGETS=('claude-code') && AI_SKILLS=('mattpocock/skills|grill-me' '' 'blader/humanizer|humanizer')" + + assert_success + [ "$(wc -l <"$NPX_ARGS_FILE")" -eq 2 ] +} + +@test "main: installs a local skill when its directory exists" { + mkdir -p "$AI_LOCAL_SKILLS_DIR/typescript" + _run_main "AI_SKILL_TARGETS=('claude-code') && AI_SKILLS=('local|typescript')" + + assert_success + assert_line "--yes skills add $AI_LOCAL_SKILLS_DIR/typescript --skill typescript -a claude-code --global --copy --yes" +} + +@test "main: skips a local skill when its directory is missing" { + _run_main "AI_SKILL_TARGETS=('claude-code') && AI_SKILLS=('local|react')" + + assert_success + assert_line "warn: [ai-skills] local skill 'react' not found at $AI_LOCAL_SKILLS_DIR/react; skipping." + [ ! -s "$NPX_ARGS_FILE" ] +} + +@test "main: exits cleanly with no skills" { + _run_main "AI_SKILL_TARGETS=('claude-code') && AI_SKILLS=()" + + assert_success + assert_line "[ai-skills] No skills to install." + [ ! -s "$NPX_ARGS_FILE" ] +} + +@test "main: exits cleanly with no targets" { + _run_main "AI_SKILL_TARGETS=() && AI_SKILLS=('mattpocock/skills|grill-me')" + + assert_success + assert_line "[ai-skills] No agent targets; nothing to install." + [ ! -s "$NPX_ARGS_FILE" ] +} + +@test "main: warns but succeeds when a skill fails" { + export NPX_EXIT_CODE=1 + _run_main "AI_SKILL_TARGETS=('claude-code') && AI_SKILLS=('mattpocock/skills|grill-me')" + + assert_success + assert_line "warn: [ai-skills] 1 skill(s) failed to install:" + assert_line " - grill-me" + assert_line "[ai-skills] Cross-agent skills installed." +} + +@test "main: fails when npx is missing" { + rm -f "$BATS_TEST_TMPDIR/bin/npx" + _run_main "AI_SKILL_TARGETS=('claude-code') && AI_SKILLS=('mattpocock/skills|grill-me')" + + assert_failure 1 + assert_line "error: [ai-skills] npx not found. Ensure Node.js is installed (run_onchange_03_install-mise-tools)." +} +``` + +- [ ] **Step 2: Run the tests to verify they fail** + +Run: `bats tests/unit/lib/install/ai-skills.bats` +Expected: FAIL — the library file does not exist, so sourcing `$LIB` errors. + +- [ ] **Step 3: Write the install library** + +Create `home/.chezmoitemplates/lib/install/ai-skills.sh`: + +```bash +#!/usr/bin/env bash +# @file lib/install/ai-skills.sh +# @brief Install cross-agent skills with the skills CLI. +# @description +# Runs `npx skills add` for each selected skill against the active agent +# targets. Local skill sources resolve under AI_LOCAL_SKILLS_DIR and are +# skipped when their directory is absent. Sourceable from bats tests and +# injected into chezmoi run scripts via chezmoi template rendering. + +set -Eeuo pipefail + +# +# @description Install each selected skill for the active agent targets. +# @exitcode 0 Skills installed, or nothing to do. +# @exitcode 1 npx is not available. +# +function ai_skills_install_main() { + local has_skill=0 + local entry + for entry in "${AI_SKILLS[@]-}"; do + [[ -z "${entry}" ]] && continue + has_skill=1 + break + done + + if ((has_skill == 0)); then + log_info "[ai-skills] No skills to install." + return 0 + fi + + local -a target_flags=() + local target + for target in "${AI_SKILL_TARGETS[@]-}"; do + [[ -z "${target}" ]] && continue + target_flags+=("-a" "${target}") + done + + if ((${#target_flags[@]} == 0)); then + log_info "[ai-skills] No agent targets; nothing to install." + return 0 + fi + + require_command npx "Ensure Node.js is installed (run_onchange_03_install-mise-tools)." || return 1 + + log_info "[ai-skills] Installing cross-agent skills..." + + local source skill add_source + local failed=() + for entry in "${AI_SKILLS[@]-}"; do + [[ -z "${entry}" ]] && continue + source="${entry%%|*}" + skill="${entry##*|}" + + if [[ "${source}" == "local" ]]; then + add_source="${AI_LOCAL_SKILLS_DIR:?AI_LOCAL_SKILLS_DIR must be set}/${skill}" + if [[ ! -d "${add_source}" ]]; then + log_warn "[ai-skills] local skill '${skill}' not found at ${add_source}; skipping." + continue + fi + else + add_source="${source}" + fi + + if ! npx --yes skills add "${add_source}" --skill "${skill}" "${target_flags[@]}" --global --copy --yes; then + failed+=("${skill}") + fi + done + + if ((${#failed[@]} > 0)); then + log_warn "[ai-skills] ${#failed[@]} skill(s) failed to install:" + printf ' - %s\n' "${failed[@]}" >&2 + fi + + log_info "[ai-skills] Cross-agent skills installed." +} + +# +# @description Run the AI skills install flow. +# +function main() { + ai_skills_install_main +} + +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + # When run directly, pull in the shared libraries that chezmoi otherwise + # concatenates ahead of this file. + # shellcheck source=/dev/null + command -v log_info >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/log.sh" + # shellcheck source=/dev/null + command -v require_command >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/install-prelude.sh" + main +fi +``` + +- [ ] **Step 4: Run the tests to verify they pass** + +Run: `bats tests/unit/lib/install/ai-skills.bats` +Expected: PASS — all nine tests green. + +- [ ] **Step 5: Lint the library** + +Run: `shellcheck home/.chezmoitemplates/lib/install/ai-skills.sh` +Expected: no output (clean). The `${AI_SKILLS[@]-}` guarded expansion avoids unbound-variable errors under `set -u`. + +- [ ] **Step 6: Commit** + +```bash +git add home/.chezmoitemplates/lib/install/ai-skills.sh tests/unit/lib/install/ai-skills.bats +git commit -m "feat: DOT-43 add ai-skills install library" +``` + +--- + +### Task 2: chezmoi run script + +**Files:** + +- Create: `home/.chezmoiscripts/darwin/run_onchange_09_install-ai-skills.sh.tmpl` + +- [ ] **Step 1: Write the run script** + +Create `home/.chezmoiscripts/darwin/run_onchange_09_install-ai-skills.sh.tmpl`: + +```bash +#!/usr/bin/env bash +# @file run_onchange_09_install-ai-skills.sh +# @brief Install cross-agent AI skills. +# @description AI data hash: {{ include ".chezmoidata/ai.yaml" | sha256sum }} +{{- $activeTargets := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .ai.targets) | fromJson -}} +{{- $activeSkills := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .ai.skills) | fromJson -}} + +{{ if and (eq .chezmoi.os "darwin") (gt (len $activeTargets) 0) (gt (len $activeSkills) 0) }} +{{ template "lib/common/log.sh" . }} +{{ template "lib/common/install-prelude.sh" . }} +export AI_LOCAL_SKILLS_DIR="{{ .chezmoi.sourceDir }}/.chezmoitemplates/skills" +AI_SKILL_TARGETS=( +{{ range $activeTargets -}} + "{{ . }}" +{{ end -}} +) +AI_SKILLS=( +{{ range $activeSkills -}} + "{{ .source }}|{{ .skill }}" +{{ end -}} +) +{{ template "lib/install/ai-skills.sh" . }} +{{ end }} +``` + +- [ ] **Step 2: Verify the template renders for a personal machine** + +Run: `chezmoi execute-template --init --promptString 'groups=personal' < home/.chezmoiscripts/darwin/run_onchange_09_install-ai-skills.sh.tmpl` (adjust the prompt to match this repo's group-selection variable if different; confirm from `home/.chezmoitemplates/lib/chezmoi/active-groups.json.tmpl`). +Expected: rendered bash with `AI_SKILL_TARGETS=("claude-code")` and `AI_SKILLS` containing the shared plus personal entries (`mattpocock/skills|grill-me`, ..., `schpet/linear-cli|linear-cli`, `upstash/context7|context7-cli`), and the `lib/install/ai-skills.sh` body inlined. + +- [ ] **Step 3: Dry-run apply to confirm no template errors** + +Run: `chezmoi apply --dry-run --verbose 2>&1 | rg -A2 'run_onchange_09_install-ai-skills'` +Expected: chezmoi shows the script would run; no template parse errors. + +- [ ] **Step 4: Commit** + +```bash +git add home/.chezmoiscripts/darwin/run_onchange_09_install-ai-skills.sh.tmpl +git commit -m "feat: DOT-43 add ai-skills run_onchange script" +``` + +--- + +### Task 3: Template render test + +**Files:** + +- Modify: `tests/template/darwin-install-scripts.bats` (add cases; follow the existing structure in that file) + +- [ ] **Step 1: Inspect the existing template test structure** + +Run: `bats --count tests/template/darwin-install-scripts.bats` and open the file to copy its rendering helper and assertion style (it renders `.tmpl` scripts with a group context and asserts on output). + +- [ ] **Step 2: Write failing render tests** + +Add two tests mirroring the file's existing pattern. Personal render includes shared plus personal skills against `claude-code`; work render excludes personal-only skills. Use the file's existing template-render helper (do not invent a new one). Example assertions: + +```bash +@test "run_onchange_09: personal render targets claude-code and includes shared+personal skills" { + run render_darwin_script "run_onchange_09_install-ai-skills.sh.tmpl" "personal" + assert_success + assert_output --partial 'AI_SKILL_TARGETS=(' + assert_output --partial '"claude-code"' + assert_output --partial '"mattpocock/skills|grill-me"' + assert_output --partial '"schpet/linear-cli|linear-cli"' +} + +@test "run_onchange_09: work render targets github-copilot and excludes personal skills" { + run render_darwin_script "run_onchange_09_install-ai-skills.sh.tmpl" "work" + assert_success + assert_output --partial '"github-copilot"' + refute_output --partial 'schpet/linear-cli|linear-cli' +} +``` + +- [ ] **Step 3: Run to verify they fail, then pass** + +Run: `bats tests/template/darwin-install-scripts.bats` +Expected: the two new tests fail until the helper name matches the file's actual helper; adjust the helper call to the real one, then they pass. + +- [ ] **Step 4: Run the full unit and template suites** + +Run: `bats tests/unit/lib/install/ai-skills.bats tests/template/darwin-install-scripts.bats` +Expected: all green. + +- [ ] **Step 5: Commit** + +```bash +git add tests/template/darwin-install-scripts.bats +git commit -m "test: DOT-43 add ai-skills template render tests" +``` + +--- + +## Self-Review + +- **Spec coverage (Skills Design):** Library runs `npx skills add --skill -a --global --copy --yes` (Task 1); targets and skills resolve from `ai.yaml` via `active-group-values` (Task 2); local sources skip when absent, deferring Phase 5 (Task 1 test 5). Covered. +- **Placeholder scan:** No TBD/TODO; every step has full code or an exact command with expected output. The one adjustable point (the template test helper name in Task 3) is called out explicitly because it depends on the existing file, and the step says to match the real helper. +- **Type consistency:** `AI_SKILLS`, `AI_SKILL_TARGETS`, `AI_LOCAL_SKILLS_DIR`, `ai_skills_install_main`, and the `|` contract are identical across the library, the run script, and the tests. +- **Out of Phase 1 scope:** plugins install, MCP delivery, instructions, domain-skill authoring, commit wiring, and apm teardown are later phases. + +## Remaining Phases (separate plans) + +1. This plan — skills install. +2. Plugins install (`run_onchange_06`, caveman + superpowers dual-path). +3. MCP delivery (`claude mcp add` for Claude; templated `mcp.json`/`mcp-config.json` for Copilot). +4. Instructions (`AGENTS.md` canonical → `CLAUDE.md` + Copilot) and commit-message wiring. +5. Author local domain skills (`typescript`, `react`, `testing`) from `home/.chezmoidata/instructions/`. +6. Organize work and Copilot files. +7. Remove APM and update tests. diff --git a/docs/superpowers/plans/2026-06-30-repo-refine-cleanup.md b/docs/superpowers/plans/2026-06-30-repo-refine-cleanup.md new file mode 100644 index 0000000..de89889 --- /dev/null +++ b/docs/superpowers/plans/2026-06-30-repo-refine-cleanup.md @@ -0,0 +1,137 @@ +# Repo Refine and Cleanup Plan + +> **For agentic workers:** execute task-by-task. Each task lists exact files, the change, and how to verify it. Steps use checkbox (`- [ ]`) syntax. + +**Goal:** Refine, clean up, and simplify the chezmoi dotfiles repo without changing applied behavior, aligned with the in-flight apm to ai migration (DOT-43). + +**Architecture:** Two adversarially-verified reviews (structure-research and kaizen-review) plus a direct `mise.toml` analysis produced this task set. Most of the structure is already sound and is deliberately kept. Changes are small, mostly single-file, and each is verified rendering-equivalent before it lands. + +**Tech Stack:** chezmoi (`.chezmoiroot=home`), Go templates, POSIX/bash install libs, mise tasks, treefmt/shfmt/shellcheck/prettier/taplo, bats. + +--- + +## Verified: kept as-is (do NOT touch) + +These were evaluated and deliberately left alone. Reasons recorded so they are not re-litigated. + +- **`booleans -> active-groups -> active-group-values` layering.** Correct primitive (3+ consumers, single source of truth for the personal/work mapping); the two-template split maps to two real data shapes. Self-describing names. No simpler equivalent preserves the scoping. +- **`.chezmoitemplates/lib/` split** (`lib/chezmoi` template helpers vs `lib/{common,darwin,install}` shell libs). chezmoi imposes no internal convention and there is none in the community; the split already separates the two kinds. Renaming is churn. +- **`active-group-values.json.tmpl`.** Not dead code: one live consumer today, nil-safe, bats-tested, gains consumers in the migration. +- **`lib/darwin/defaults.sh` dock layout duplication.** Ordered config list where explicit per-layout order aids readability; extracting the shared middle would obscure dock order. +- **`chezmoiignore.d/darwin` empty fragment.** Deliberate extension point mirroring the populated `chezmoiexternal.d/`; cheap ceremony. +- **Data-hash `# hash` comments** on scripts that inline their data. Redundant but harmless; removing risks silent onchange breakage on a future refactor. +- **Empty-skip guards** (`$hasTools`, `len platforms > 0`) present only on installers whose active set can legitimately be empty. Correct as-is; new migration installers with possibly-empty groups should copy the guarded pattern. + +--- + +## Group A: structure cleanups (migration-independent, safe now) + +### Task A1: Route the vscode installer through the tested helper + +**Files:** + +- Modify: `home/.chezmoiscripts/darwin/run_onchange_04_install-vscode-extensions.sh.tmpl` + +The inline `range .vscode.shared` + `if .personal` + `if .work` fan-out is the one place the group-selection idiom is hand-rolled a third way. `.vscode` has the identical scalar-list-per-group shape as `.ai.targets`, which `run_onchange_08` already flattens via the helper. + +- [ ] Replace the inline fan-out with: + `{{- $ext := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .vscode) | fromJson -}}` + then render `VSCODE_EXTENSIONS=( {{ range $ext }}"{{ . }}" {{ end }} )`. +- [ ] Verify rendering-equivalent: `chezmoi execute-template < ` before/after produce the same `VSCODE_EXTENSIONS` array (order shared -> personal -> work preserved; only cosmetic per-group `# shared` comments are lost). +- [ ] The onchange hash is over `extensions.yaml` (unchanged), so no spurious re-run. + +### Task A2: Generalize the `AGENTS.md` template to be provider-neutral (keep the name) + +**Files:** + +- Modify: `home/.chezmoitemplates/AGENTS.md` (H1 + intro line only) + +Decision: KEEP the filename `AGENTS.md` — it is the cross-provider standard, so the same guidance can feed Claude (`~/.claude/CLAUDE.md`), Copilot (`AGENTS.md`), and any future agent. No rename. The name "collision" with the repo-root `AGENTS.md` is human-readability only; `{{ template "AGENTS.md" }}` resolves unambiguously to the `.chezmoitemplates/` file, so it costs nothing. Fix the content instead: the body (AI Guidance, GitHub CLI, Git/PR workflow, Graphify) is already provider-neutral; only the top two lines name Claude. + +- [ ] Line 1: `# Claude Code Settings` -> `# Agent Guidance` (or `# AI Agent Guidance`). +- [ ] Line 3: `Guidance for Claude Code and other AI tools.` -> `Guidance for AI coding agents (Claude Code, GitHub Copilot, and others).` +- [ ] Leave the rest of the body unchanged; it is already agnostic. +- [ ] Verify: `chezmoi execute-template < home/dot_claude/CLAUDE.md.tmpl` still renders the full guidance body (the `{{ template "AGENTS.md" . }}` reference is untouched). +- [ ] Cross-agent wiring (feeding this same template to Copilot's `AGENTS.md`) is handled in the DOT-43 migration's instructions design, not here. + +### Task A3: Rename `extensions.yaml` -> `vscode.yaml` for filename==key uniformity + +**Files:** + +- Rename: `home/.chezmoidata/extensions.yaml` -> `home/.chezmoidata/vscode.yaml` +- Modify: `home/.chezmoiscripts/darwin/run_onchange_04_install-vscode-extensions.sh.tmpl` (hash-comment path only) + +Every other data file matches filename to its single top-level key (`packages`, `ai`, `mise`, `uv`); `extensions.yaml` is the lone outlier (key is `vscode:`). Renaming the file (not the key) keeps the `.vscode` accessor untouched. Included here because Group A is already touching this script and this completes the convention; skipped otherwise. + +- [ ] `git mv` the data file. +- [ ] Update the hash comment `include ".chezmoidata/extensions.yaml"` -> `".chezmoidata/vscode.yaml"`. +- [ ] Verify: `chezmoi execute-template` on run_onchange_04 renders without error; one harmless `--force` re-run occurs because the hash-comment string changed. + +--- + +## Group B: mise.toml and tooling simplification + +### Task B1: Drop redundant `mise exec --` inside tasks + +**Files:** + +- Modify: `mise.toml` (all task `run` bodies) + +Inside `mise run `, the `[tools]` are already on `PATH`; `mise exec --` is redundant noise on every line. + +- [ ] Confirm empirically first: `mise run diff` (or a trivial task) with `chezmoi` called bare works. +- [ ] Remove `mise exec -- ` prefixes from `diff`, `update`, `reset`, `reset-config`, `format`, `lint`, `test-*` task bodies. +- [ ] Verify: `mise lint` and `mise test` still run (leave CI's raw `run:` steps alone — those are outside a task env and keep `mise exec --`). + +### Task B2: Adopt treefmt for format orchestration (DECIDED) + +**Files:** + +- Create: `treefmt.toml` +- Modify: `mise.toml` (`[tools]` add treefmt; rewrite `format`, shrink `lint`) + +Replaces the two duplicated ~15-line shell-discovery bash blocks and the manual per-tool orchestration with one declarative config. `treefmt` walks the tree, respects each formatter's own ignore config, and dispatches shfmt/prettier/taplo. `shellcheck` stays a standalone lint step (treefmt is format-only). + +- [ ] Add `treefmt = "2.5.0"` to `[tools]` via the aqua/ubi backend (`"aqua:numtide/treefmt" = "2.5.0"`). +- [ ] Create `treefmt.toml` with formatters: `shfmt` (`*.sh`, `*.zsh`, `.zshrc`; exclude `*.tmpl`), `prettier` (`*.md`, `*.yaml`, `*.yml`, `*.json`), `taplo fmt` (`*.toml`; exclude `*.tmpl`). +- [ ] `format` task -> `treefmt`. +- [ ] `lint` task -> `treefmt --ci` (fail on change) + the retained `shellcheck` block for `*.sh`/`*.zsh` and the `bats` shellcheck block. +- [ ] Verify: `mise format` then `git diff` shows no unexpected reformatting of `*.tmpl` files; `mise lint` passes on a clean tree. + +### Task B3: Remove the retired apm dependency-management layer + +**Files:** + +- Modify: `mise.toml` (delete `[tasks.validate-apm]`, `[tasks.refresh-apm-locks]`) +- Delete: `tests/template/install-apm-script.bats` +- Modify: `.gitignore` (drop `# APM dependencies` + `apm_modules/`) + +Keep the apm CLI (`home/.chezmoidata/mise.yaml` `github:microsoft/apm` + `Bash(apm:*)` permission). Only the dependency-management tasks/tests/artifacts go; their `scripts/*.sh` already do not exist. + +- [ ] Delete the two apm tasks. +- [ ] Delete `install-apm-script.bats` (renders the already-removed apm script; would fail). +- [ ] Remove the `apm_modules/` gitignore lines. +- [ ] Verify: `mise test` passes (no apm bats failure); `command -v apm` still resolves. + +--- + +## Group C: deferred INTO the DOT-43 migration + +These are correct but must land inside the migration that is already restructuring `ai/` and reassigning script numbers, to avoid conflicting churn. + +- **Rename `.chezmoitemplates/ai/skills/` -> `ai/instructions/`.** The six `*.instructions.md` files are GitHub Copilot custom-instructions (description+applyTo frontmatter), not skills; `skills` is currently overloaded three ways (ai.yaml `skills:` key, this dir, `dot_copilot/skills`). Zero references today, pure `git mv`. Settle the final name (`ai/instructions` vs `ai/copilot-instructions`) in the migration. +- **Give uv-tools a unique script number above mise-tools.** `uv-tools.sh` needs `mise` first (`require_command uv`); today the order holds only by `mise` < `uv` alphabetical accident within the shared `03` prefix. Assign a free number during the migration renumber (note: `04` is taken by vscode). +- **Holistic run-script renumbering.** After the migration adds plugins/skills/MCP scripts, renumber to be gapless and dependency-encoding. + +--- + +## Execution approach + +- Run as a team of subagents, one per task (or per group), each in an **isolated git worktree** so edits land on a reviewable branch, never the live working tree. +- Precondition: the working tree must be clean first — the in-flight `run_onchange_02` (taps) break and any uncommitted WIP resolved/committed — so worktrees branch from a coherent base and `chezmoi apply` / `mise lint` / `mise test` can verify cleanly. +- Verification gate per task: `chezmoi execute-template` on touched templates (rendering-equivalent), then `mise lint` and `mise test` green before the task is kept. + +## Decisions (locked) + +- **Tooling (Task B2): adopt treefmt.** One declarative `treefmt.toml` dispatches shfmt/prettier/taplo; `format = treefmt`, `lint = treefmt --ci` plus standalone `shellcheck`. Deletes both duplicated shell-discovery blocks. Accepts +1 mise tool and +1 config file. +- **Execution: clean tree first, then spin the team.** User resolves the `run_onchange_02` (taps) break and commits/stashes WIP; on confirmation, the worktree-isolated team executes Groups A + B against a coherent base with per-task verify gates. Group C lands inside the DOT-43 migration. diff --git a/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md b/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md index 23e562c..24ee867 100644 --- a/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md +++ b/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md @@ -172,24 +172,34 @@ The custom agents move to the Copilot side because they are frontend and work fo ## APM Teardown -Remove: +The apm CLI stays installed for global use. Only apm's dependency-management layer is retired. -- `home/.chezmoidata/apm.yaml` -- `home/dot_apm/` -- `home/.chezmoitemplates/apm/` -- `home/.chezmoitemplates/lib/install/apm.sh` -- `home/.chezmoiscripts/darwin/run_onchange_06_install-apm.sh.tmpl` -- The APM entry in `mise.toml` -- Runtime `~/.apm` -- Stale `~/.claude/apm-hooks.json` +Keep: -The already-removed caveman hook and reassert scripts on the current branch fold into this teardown. Update or remove the APM bats tests. +- The apm CLI entry in `home/.chezmoidata/mise.yaml` (`github:microsoft/apm`, shared group). +- The `Bash(apm:*)` permission in `home/dot_claude/settings.json`. -## Components Kept As Is +Remove the dependency-management layer: + +- `mise.toml` `[tasks.validate-apm]` and `[tasks.refresh-apm-locks]` (their `scripts/*.sh` no longer exist). +- `tests/template/install-apm-script.bats` (renders the already-removed apm script; would fail). +- `.gitignore` `apm_modules/` entry (optional; apm dependency artifact). +- Runtime `~/.apm` dependency modules and stale `~/.claude/apm-hooks.json`. + +Already removed by prior cleanup: `home/.chezmoidata/apm.yaml`, `home/dot_apm/`, `home/.chezmoitemplates/apm/`, `home/.chezmoitemplates/lib/install/apm.sh`, `home/.chezmoiscripts/darwin/run_onchange_06_install-apm.sh.tmpl`, and the apm entry from `mise.toml [tools]`. The caveman hook and reassert scripts were removed earlier on this branch. + +## Graphify Script Repoint -- The graphify install script `run_onchange_08_install-graphify-skills.sh.tmpl`. +`run_onchange_08_install-graphify-skills.sh.tmpl` still reads retired apm data and currently breaks `chezmoi apply` on render: + +- Line 4 `include ".chezmoidata/apm.yaml"` references the deleted data file, which hard-errors. +- Line 5 reads `.apm.targets`. + +Repoint it off apm data onto the new `ai` data: hash `.chezmoidata/ai.yaml` in the trigger comment, resolve targets from `.ai.targets`, and update the platform mapping so the graphify platforms are derived from the `ai` target ids (`claude-code`, `github-copilot`) rather than the old apm values (`agent-skills`, `claude`). This repoint is a prerequisite for a clean `chezmoi apply` and belongs to this migration. + +## Components Kept As Is -The manual caveman and superpowers hook entries stay removed from `settings.json`, because the caveman plugin supplies its hooks. +The manual caveman and superpowers hook entries stay removed from `settings.json`, because the caveman plugin supplies its hooks. The `.chezmoitemplates/lib/` layout stays as-is: chezmoi imposes no internal structure on `.chezmoitemplates/` and there is no community convention, and the directory already separates templating helpers (`lib/chezmoi/`) from shell libraries (`lib/{common,darwin,install}/`). ## Testing and Verification @@ -200,18 +210,19 @@ The manual caveman and superpowers hook entries stay removed from `settings.json - `grep` MCP is registered for both Claude and Copilot; `tavily` MCP is registered for Claude only. - The VS Code commit button produces a message in the configured style. - A new session `/doctor` reports no hook or settings errors. -- `command -v apm` returns nothing and `~/.apm` is absent. +- `command -v apm` still resolves (the CLI is kept), while the apm dependency modules under `~/.apm` are absent. - The bats suite passes, including tests for the new install scripts. ## Implementation Phases +0. Repoint `run_onchange_08_install-graphify-skills.sh.tmpl` off retired apm data onto `ai` data, restoring a clean `chezmoi apply`. 1. Add `home/.chezmoidata/ai.yaml` and the skills install script, so Claude and Copilot get skills. 2. Add the plugins install script for caveman and superpowers. 3. Add the MCP install script and apply shared MCP to both agents. 4. Author `AGENTS.md` universal instructions and wire the Copilot commit instructions. 5. Author the local domain skills from the instruction files. 6. Organize the work and Copilot files. -7. Remove APM and update tests. +7. Retire the apm dependency-management layer (dead mise tasks, apm bats test) and update tests. ## Out of Scope From e65c453b8e0d9ed5eb551bd8a8e4c0ce16cbb2cb Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 21:55:07 -0500 Subject: [PATCH 18/51] fix: restore chezmoi render for personal packages personal group lacked taps and formulas, breaking missingkey render --- home/.chezmoidata/packages.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/home/.chezmoidata/packages.yaml b/home/.chezmoidata/packages.yaml index bb7e198..6220861 100644 --- a/home/.chezmoidata/packages.yaml +++ b/home/.chezmoidata/packages.yaml @@ -2,7 +2,7 @@ # Single source of truth for all package manager declarations. # Referenced by run_onchange_ scripts via Chezmoi template data (.packages.*). # -# Each group under darwin.homebrew has the same shape: {formulas, casks, mas}. +# Each group under darwin.homebrew has the same shape: {taps, formulas, casks, mas}. # `shared` always applies; `personal` or `work` is added based on host context. packages: @@ -37,6 +37,8 @@ packages: mas: [] personal: + taps: [] + formulas: [] casks: - claude-code - cleanshot From feb710faf25b2250abc09848dd21551f7863aaa1 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 21:55:07 -0500 Subject: [PATCH 19/51] refactor: route vscode installer via group helper reuse active-group-values like graphify; rename extensions.yaml to vscode.yaml --- .../{extensions.yaml => vscode.yaml} | 0 ...hange_04_install-vscode-extensions.sh.tmpl | 20 ++++--------------- 2 files changed, 4 insertions(+), 16 deletions(-) rename home/.chezmoidata/{extensions.yaml => vscode.yaml} (100%) diff --git a/home/.chezmoidata/extensions.yaml b/home/.chezmoidata/vscode.yaml similarity index 100% rename from home/.chezmoidata/extensions.yaml rename to home/.chezmoidata/vscode.yaml diff --git a/home/.chezmoiscripts/darwin/run_onchange_04_install-vscode-extensions.sh.tmpl b/home/.chezmoiscripts/darwin/run_onchange_04_install-vscode-extensions.sh.tmpl index 92778b9..03873f6 100644 --- a/home/.chezmoiscripts/darwin/run_onchange_04_install-vscode-extensions.sh.tmpl +++ b/home/.chezmoiscripts/darwin/run_onchange_04_install-vscode-extensions.sh.tmpl @@ -1,26 +1,14 @@ #!/usr/bin/env bash -# extensions.yaml hash: {{ include ".chezmoidata/extensions.yaml" | sha256sum }} +# vscode.yaml hash: {{ include ".chezmoidata/vscode.yaml" | sha256sum }} +{{- $extensions := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .vscode) | fromJson -}} {{ if eq .chezmoi.os "darwin" }} {{ template "lib/common/log.sh" . }} {{ template "lib/common/install-prelude.sh" . }} VSCODE_EXTENSIONS=( - # shared - {{ range .vscode.shared -}} +{{ range $extensions -}} "{{ . }}" - {{ end }} - {{ if .personal -}} - # personal - {{ range .vscode.personal -}} - "{{ . }}" - {{ end -}} - {{- end }} - {{ if .work -}} - # work - {{ range .vscode.work -}} - "{{ . }}" - {{ end -}} - {{- end }} +{{ end -}} ) {{ template "lib/install/vscode.sh" . }} {{ end }} From 104ad30558fb1dd3a083beae0180af826756abd6 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 21:55:07 -0500 Subject: [PATCH 20/51] refactor: make agent guidance provider-neutral --- home/.chezmoitemplates/AGENTS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/home/.chezmoitemplates/AGENTS.md b/home/.chezmoitemplates/AGENTS.md index 7541041..97ce8cd 100644 --- a/home/.chezmoitemplates/AGENTS.md +++ b/home/.chezmoitemplates/AGENTS.md @@ -1,6 +1,6 @@ -# Claude Code Settings +# Agent Guidance -Guidance for Claude Code and other AI tools. Structured around [Andrej Karpathy's observations on LLM coding pitfalls](https://x.com/karpathy/status/2015883857489522876): surface assumptions, don't overcomplicate, make surgical changes, verify before moving on. +Guidance for AI coding agents (Claude Code, GitHub Copilot, and others). Structured around [Andrej Karpathy's observations on LLM coding pitfalls](https://x.com/karpathy/status/2015883857489522876): surface assumptions, don't overcomplicate, make surgical changes, verify before moving on. ## AI Guidance From 9a4753db555d22b20d3d3b705ad2f48f3268ec4e Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 21:55:07 -0500 Subject: [PATCH 21/51] build: adopt treefmt and simplify mise tasks drop redundant mise exec prefixes and duplicated shell discovery --- mise.toml | 54 +++++++++++++--------------------------------------- treefmt.toml | 20 +++++++++++++++++++ 2 files changed, 33 insertions(+), 41 deletions(-) create mode 100644 treefmt.toml diff --git a/mise.toml b/mise.toml index bc4913b..0535a7c 100644 --- a/mise.toml +++ b/mise.toml @@ -5,60 +5,35 @@ prettier = "3.8.3" shellcheck = "0.11.0" shfmt = "3.13.1" taplo = "0.10.0" +"aqua:numtide/treefmt" = "2.5.0" [tasks.diff] description = "Preview chezmoi changes from this source tree" -run = 'mise exec -- chezmoi diff --source="$PWD"' +run = 'chezmoi diff --source="$PWD"' [tasks.update] description = "Apply this source tree with chezmoi" -run = 'mise exec -- chezmoi apply --verbose --source="$PWD"' +run = 'chezmoi apply --verbose --source="$PWD"' [tasks.reset] description = "Clear chezmoi script state" -run = "mise exec -- chezmoi state delete-bucket --bucket=scriptState" +run = "chezmoi state delete-bucket --bucket=scriptState" [tasks.reset-config] description = "Re-run chezmoi init data prompts for this source tree" -run = 'mise exec -- chezmoi init --data=false --source="$PWD"' +run = 'chezmoi init --data=false --source="$PWD"' [tasks.format] -description = "Format shell, markdown, YAML, and TOML" -run = ''' -#!/usr/bin/env bash -set -euo pipefail - -shell_roots=() -has_shell_roots=0 -for root in scripts home/dot_config/zsh home/.chezmoitemplates home/dot_claude .github/actions; do - if [[ -d "${root}" ]]; then - shell_roots+=("${root}") - has_shell_roots=1 - fi -done - -if ((has_shell_roots)); then - shell_targets=() - while IFS= read -r shell_target; do - shell_targets+=("${shell_target}") - done < <(find "${shell_roots[@]}" -type f \( -name '*.sh' -o -name '*.zsh' -o -name '.zshrc' \)) - - if [[ -n "${shell_targets[*]:-}" ]]; then - mise exec -- shfmt --write "${shell_targets[@]}" - fi -fi - -mise exec -- prettier --write . -mise exec -- taplo fmt -''' +description = "Format all files with treefmt" +run = "treefmt" [tasks.lint] -description = "Lint shell, markdown, YAML, and TOML" +description = "Check formatting with treefmt and lint shell with shellcheck" run = ''' #!/usr/bin/env bash set -euo pipefail -mise exec -- prettier --check . +treefmt --ci shell_roots=() has_shell_roots=0 @@ -76,8 +51,7 @@ if ((has_shell_roots)); then done < <(find "${shell_roots[@]}" -type f \( -name '*.sh' -o -name '*.zsh' -o -name '.zshrc' \)) if [[ -n "${shell_targets[*]:-}" ]]; then - mise exec -- shellcheck "${shell_targets[@]}" - mise exec -- shfmt --diff "${shell_targets[@]}" + shellcheck "${shell_targets[@]}" fi fi @@ -91,20 +65,18 @@ if [[ -d tests ]]; then done < <(find tests -type f -name '*.bats') if [[ -n "${bats_targets[*]:-}" ]]; then - mise exec -- shellcheck --shell=bash --severity=warning "${bats_targets[@]}" + shellcheck --shell=bash --severity=warning "${bats_targets[@]}" fi fi - -mise exec -- taplo fmt --check ''' [tasks.test-unit] description = "Run bats unit tests" -run = "mise exec -- bats --recursive tests/unit" +run = "bats --recursive tests/unit" [tasks.test-template] description = "Run bats template tests" -run = "mise exec -- bats --recursive tests/template" +run = "bats --recursive tests/template" [tasks.test] description = "Run all bats tests" diff --git a/treefmt.toml b/treefmt.toml new file mode 100644 index 0000000..0e889c3 --- /dev/null +++ b/treefmt.toml @@ -0,0 +1,20 @@ +# treefmt: one formatter per file type. Walks the tree, respects .gitignore, +# and dispatches to each tool. Replaces the hand-rolled shell-file discovery +# that the mise format/lint tasks used to duplicate. + +[formatter.shfmt] +command = "shfmt" +options = ["-w"] +includes = ["*.sh", "*.zsh", ".zshrc", ".bashrc"] +excludes = ["*.tmpl"] + +[formatter.prettier] +command = "prettier" +options = ["--write"] +includes = ["*.md", "*.yaml", "*.yml", "*.json"] + +[formatter.taplo] +command = "taplo" +options = ["fmt"] +includes = ["*.toml"] +excludes = ["*.tmpl"] From 0aa24ee4539db029f65d9f4952fd6a549885fad2 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 21:55:07 -0500 Subject: [PATCH 22/51] chore: drop remaining apm dependency artifacts --- .gitignore | 3 -- tests/template/install-apm-script.bats | 69 -------------------------- 2 files changed, 72 deletions(-) delete mode 100644 tests/template/install-apm-script.bats diff --git a/.gitignore b/.gitignore index af06f02..3930148 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,3 @@ -# APM dependencies -apm_modules/ - .DS_Store .claude .worktrees/ diff --git a/tests/template/install-apm-script.bats b/tests/template/install-apm-script.bats deleted file mode 100644 index c3cc390..0000000 --- a/tests/template/install-apm-script.bats +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env bats -# @file tests/template/install-apm-script.bats -# @brief Renders home/.chezmoiscripts/darwin/run_onchange_06_install-apm.sh.tmpl -# via `chezmoi execute-template` and asserts the lib/common/log.sh -# snippet is injected and the resulting script is syntactically valid -# bash. - -load '../test_helpers/load.bash' - -TMPL="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_06_install-apm.sh.tmpl" -SOURCE_DIR="$DOTFILES_ROOT/home" -DARWIN_DATA='{"chezmoi":{"os":"darwin"}}' - -@test "install-apm template renders without errors" { - run mise exec -- chezmoi execute-template --source "$SOURCE_DIR" --override-data "$DARWIN_DATA" <"$TMPL" - assert_success -} - -@test "rendered install-apm script starts with bash shebang" { - run mise exec -- chezmoi execute-template --source "$SOURCE_DIR" --override-data "$DARWIN_DATA" <"$TMPL" - assert_success - [ "${lines[0]}" = "#!/usr/bin/env bash" ] -} - -@test "install-apm template gates library injection to darwin" { - template_content="$(<"$TMPL")" - - [[ "$template_content" == *'{{ if eq .chezmoi.os "darwin" }}'* ]] || return 1 - [[ "$template_content" == *'{{ template "lib/common/log.sh" . }}'* ]] || return 1 - [[ "$template_content" == *'{{ template "lib/install/apm.sh" . }}'* ]] || return 1 -} - -@test "install-apm template injects log_info, log_warn, log_error definitions" { - run mise exec -- chezmoi execute-template --source "$SOURCE_DIR" --override-data "$DARWIN_DATA" <"$TMPL" - assert_success - assert_line 'log_info() {' - assert_line 'log_warn() {' - assert_line 'log_error() {' -} - -@test "install-apm template uses log_info at the call sites" { - run mise exec -- chezmoi execute-template --source "$SOURCE_DIR" --override-data "$DARWIN_DATA" <"$TMPL" - assert_success - assert_line ' log_info "[apm] Installing globally from ~/.apm/apm.yml (frozen)..."' - assert_line ' log_info "[apm] Install complete."' -} - -@test "install-apm template runs frozen install (no tolerated-failure fallback)" { - run mise exec -- chezmoi execute-template --source "$SOURCE_DIR" --override-data "$DARWIN_DATA" <"$TMPL" - assert_success - assert_line --partial 'apm install --global --frozen' - refute_line --partial 'apm install --global ||' -} - -@test "rendered install-apm script is syntactically valid bash" { - rendered=$(mise exec -- chezmoi execute-template --source "$SOURCE_DIR" --override-data "$DARWIN_DATA" <"$TMPL") - printf '%s\n' "$rendered" | bash -n -} - -@test "rendered install-apm script preserves chezmoi content-hash trigger comments" { - run mise exec -- chezmoi execute-template --source "$SOURCE_DIR" --override-data "$DARWIN_DATA" <"$TMPL" - assert_success - assert_line --regexp '^# apm\.yml:' - assert_line --regexp '^# apm data:' - assert_line --regexp '^# apm mcp template:' - assert_line --regexp '^# apm lock \(personal\):' - assert_line --regexp '^# apm lock \(work\):' - assert_line --regexp '^# agents:' -} From 2006d31237649c296480b1e3b0298770f7ec4bc3 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 21:55:07 -0500 Subject: [PATCH 23/51] test: align template tests with migration removals --- tests/template/agent-instructions.bats | 20 +------- tests/template/darwin-install-scripts.bats | 56 +++++----------------- 2 files changed, 14 insertions(+), 62 deletions(-) diff --git a/tests/template/agent-instructions.bats b/tests/template/agent-instructions.bats index cf6ad50..36bbb88 100644 --- a/tests/template/agent-instructions.bats +++ b/tests/template/agent-instructions.bats @@ -6,20 +6,12 @@ load '../test_helpers/load.bash' load '../test_helpers/templates.bash' CLAUDE_TEMPLATE="$DOTFILES_ROOT/home/dot_claude/CLAUDE.md.tmpl" -OPENCODE_TEMPLATE="$DOTFILES_ROOT/home/dot_config/opencode/AGENTS.md.tmpl" @test "agent instruction templates render shared markdown" { run render_chezmoi_template "$CLAUDE_TEMPLATE" assert_success - assert_line --index 0 '# Claude Code Settings' - assert_line --regexp '^### GitHub CLI$' - assert_line 'Use `gh` CLI for all GitHub interactions. Never clone repositories to read code.' - - run render_chezmoi_template "$OPENCODE_TEMPLATE" - - assert_success - assert_line --index 0 '# Claude Code Settings' + assert_line --index 0 '# Agent Guidance' assert_line --regexp '^### GitHub CLI$' assert_line 'Use `gh` CLI for all GitHub interactions. Never clone repositories to read code.' } @@ -29,10 +21,8 @@ OPENCODE_TEMPLATE="$DOTFILES_ROOT/home/dot_config/opencode/AGENTS.md.tmpl" [ ! -e "$DOTFILES_ROOT/home/.chezmoidata/agents.yaml" ] claude_template="$(<"$CLAUDE_TEMPLATE")" - opencode_template="$(<"$OPENCODE_TEMPLATE")" [[ "$claude_template" == *'{{ template "AGENTS.md" . -}}'* ]] - [[ "$opencode_template" == *'{{ template "AGENTS.md" . -}}'* ]] } @test "agent instruction templates render Graphify guidance" { @@ -43,12 +33,4 @@ OPENCODE_TEMPLATE="$DOTFILES_ROOT/home/dot_config/opencode/AGENTS.md.tmpl" assert_line '- Use the installed Graphify skill when the user invokes `/graphify`.' assert_line --partial 'graphify query' assert_line --partial 'GRAPH_REPORT.md' - - run render_chezmoi_template "$OPENCODE_TEMPLATE" - - assert_success - assert_line '## Graphify' - assert_line '- Use the installed Graphify skill when the user invokes `/graphify`.' - assert_line --partial 'graphify query' - assert_line --partial 'GRAPH_REPORT.md' } diff --git a/tests/template/darwin-install-scripts.bats b/tests/template/darwin-install-scripts.bats index 4400b73..540b102 100644 --- a/tests/template/darwin-install-scripts.bats +++ b/tests/template/darwin-install-scripts.bats @@ -5,11 +5,10 @@ load '../test_helpers/load.bash' load '../test_helpers/templates.bash' -EMPTY_APM_DATA='{"chezmoi":{"os":"darwin"},"personal":true,"work":false,"apm":{"targets":{"shared":[],"personal":[],"work":[]}}}' +EMPTY_AI_DATA='{"chezmoi":{"os":"darwin"},"personal":true,"work":false,"ai":{"targets":{"shared":[],"personal":[],"work":[]}}}' PACKAGE_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_02_install-packages.sh.tmpl" UV_TOOLS_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_03_install-uv-tools.sh.tmpl" GRAPHIFY_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_08_install-graphify-skills.sh.tmpl" -CAVEMAN_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_09_install-caveman-hooks.sh.tmpl" @test "darwin install script templates render with bash shebang" { for template in "$DOTFILES_ROOT"/home/.chezmoiscripts/darwin/*.tmpl; do @@ -31,10 +30,6 @@ CAVEMAN_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_09_ins assert_file_contains "$GRAPHIFY_TEMPLATE" '{{ template "lib/install/graphify-skills.sh" . }}' } -@test "darwin install script templates inject caveman shell library" { - assert_file_contains "$CAVEMAN_TEMPLATE" '{{ template "lib/install/caveman-hooks.sh" . }}' -} - @test "darwin install script templates inject the install prelude" { local prelude='{{ template "lib/common/install-prelude.sh" . }}' assert_file_contains "$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_once_01_install-homebrew.sh.tmpl" "$prelude" @@ -42,10 +37,7 @@ CAVEMAN_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_09_ins assert_file_contains "$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_03_install-mise-tools.sh.tmpl" "$prelude" assert_file_contains "$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_03_install-uv-tools.sh.tmpl" "$prelude" assert_file_contains "$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_04_install-vscode-extensions.sh.tmpl" "$prelude" - assert_file_contains "$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_06_install-apm.sh.tmpl" "$prelude" - assert_file_contains "$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_07_fix-opencode-agent-tools.sh.tmpl" "$prelude" assert_file_contains "$GRAPHIFY_TEMPLATE" "$prelude" - assert_file_contains "$CAVEMAN_TEMPLATE" "$prelude" } @test "rendered darwin install scripts are syntactically valid bash" { @@ -97,62 +89,40 @@ CAVEMAN_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_09_ins assert_line --partial 'uv_tools_install_main' } -@test "work uv tools template has no personal uv tools" { +@test "work uv tools template includes shared tools but omits personal tools" { run render_chezmoi_template "$UV_TOOLS_TEMPLATE" "$WORK_DATA" assert_success - refute_line --partial '"graphifyy"' + assert_line 'UV_TOOLS=(' + assert_line --partial '"graphifyy"' refute_line --partial '"tavily-cli"' - refute_line --partial 'uv tool install --upgrade' - refute_line --partial 'uv_tools_install_main' + assert_line --partial 'uv_tools_install_main' } -@test "personal Graphify template renders agent and Claude platforms" { +@test "personal Graphify template renders the Claude platform" { run render_chezmoi_template "$GRAPHIFY_TEMPLATE" "$DARWIN_DATA" assert_success assert_line 'GRAPHIFY_PLATFORMS=(' - assert_line --partial '"agents"' assert_line --partial '"claude"' + refute_line --partial '"copilot"' assert_line --partial 'graphify_skills_install_main' } -@test "work Graphify template renders no personal platforms" { +@test "work Graphify template renders the Copilot platform" { run render_chezmoi_template "$GRAPHIFY_TEMPLATE" "$WORK_DATA" assert_success - refute_line --partial '"agents"' + assert_line 'GRAPHIFY_PLATFORMS=(' + assert_line --partial '"copilot"' refute_line --partial '"claude"' - refute_line --partial 'graphify install --platform' - refute_line --partial 'graphify_skills_install_main' + assert_line --partial 'graphify_skills_install_main' } -@test "empty APM target groups render no Graphify commands" { - run render_chezmoi_template "$GRAPHIFY_TEMPLATE" "$EMPTY_APM_DATA" +@test "empty AI target groups render no Graphify commands" { + run render_chezmoi_template "$GRAPHIFY_TEMPLATE" "$EMPTY_AI_DATA" assert_success refute_line 'GRAPHIFY_PLATFORMS=(' - refute_line --partial 'graphify install --platform' refute_line --partial 'graphify_skills_install_main' } - -@test "personal caveman template renders the hook install flow" { - run render_chezmoi_template "$CAVEMAN_TEMPLATE" "$DARWIN_DATA" - - assert_success - assert_line --partial 'caveman_hooks_install_main' -} - -@test "work caveman template renders no hook install flow" { - run render_chezmoi_template "$CAVEMAN_TEMPLATE" "$WORK_DATA" - - assert_success - refute_line --partial 'caveman_hooks_install_main' -} - -@test "empty APM target groups render no caveman hook install flow" { - run render_chezmoi_template "$CAVEMAN_TEMPLATE" "$EMPTY_APM_DATA" - - assert_success - refute_line --partial 'caveman_hooks_install_main' -} From 9ea1c1e168b97786eec90687507ccd6481279aaf Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 22:21:05 -0500 Subject: [PATCH 24/51] feat: convert instructions to local skills typescript, react, testing skills from the instruction files; converted sources removed (DOT-43) --- .../ai/skills/00-meta-rules.instructions.md | 41 ---- .../01-coding-standards.instructions.md | 40 ---- .../02-typescript-guidelines.instructions.md | 39 ---- .../03-react-guidelines.instructions.md | 35 --- .../04-testing-guidelines.instructions.md | 80 ------- .../ai/skills/05-commit-guide.instructions.md | 201 ------------------ home/.chezmoitemplates/skills/react/SKILL.md | 36 ++++ .../.chezmoitemplates/skills/testing/SKILL.md | 61 ++++++ .../skills/typescript/SKILL.md | 42 ++++ 9 files changed, 139 insertions(+), 436 deletions(-) delete mode 100644 home/.chezmoitemplates/ai/skills/00-meta-rules.instructions.md delete mode 100644 home/.chezmoitemplates/ai/skills/01-coding-standards.instructions.md delete mode 100644 home/.chezmoitemplates/ai/skills/02-typescript-guidelines.instructions.md delete mode 100644 home/.chezmoitemplates/ai/skills/03-react-guidelines.instructions.md delete mode 100644 home/.chezmoitemplates/ai/skills/04-testing-guidelines.instructions.md delete mode 100644 home/.chezmoitemplates/ai/skills/05-commit-guide.instructions.md create mode 100644 home/.chezmoitemplates/skills/react/SKILL.md create mode 100644 home/.chezmoitemplates/skills/testing/SKILL.md create mode 100644 home/.chezmoitemplates/skills/typescript/SKILL.md diff --git a/home/.chezmoitemplates/ai/skills/00-meta-rules.instructions.md b/home/.chezmoitemplates/ai/skills/00-meta-rules.instructions.md deleted file mode 100644 index 2759f8c..0000000 --- a/home/.chezmoitemplates/ai/skills/00-meta-rules.instructions.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -description: "Meta behavioral rules and multi-mode workflow expectations applied across all work." -applyTo: "**" ---- - -# 00. Meta Rules for Copilot Behavior - -## Purpose - -You are a Senior Software Engineer with expertise in React, TypeScript, HTML, CSS, and testing libraries. Your role is to provide thoughtful, step-by-step, accurate, and well-reasoned guidance. - -## Core Expectations - -- Follow the user's requirements exactly. -- Think step-by-step before writing code. -- Confirm tradeoffs (esp. for nontrivial decisions) before implementing. -- Write correct, readable, ARIA-compliant, working code, including referenced imports. -- Use deterministic verifiable APIs, hooks, or component compositions. -- Prefer functional, accessibility-first design over cleverness. - -## Handling Uncertainty - -- If you think there might not be a correct answer, you say so. -- If you do not know the answer, say so, instead of guessing. -- If something cannot be known from context, state that explicitly. -- If a requirement is ambiguous, ask clarifying questions. -- Never guess or hallucinate APIs, commands, or project conventions. - -## Output Expectations - -- Be concise and structured. -- Use clear headings and actionable bullet points. -- Minimize prose outside of required explanations. -- Favor patterns and conventions used throughout this repository. - -## MCP Tools Overview - -Use these tools when appropriate: - -- Context7: documentation lookup -- GitHub MCP: repository operations diff --git a/home/.chezmoitemplates/ai/skills/01-coding-standards.instructions.md b/home/.chezmoitemplates/ai/skills/01-coding-standards.instructions.md deleted file mode 100644 index b8c2d3c..0000000 --- a/home/.chezmoitemplates/ai/skills/01-coding-standards.instructions.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -description: "Language-agnostic engineering standards for maintainable code." -applyTo: "**/*.ts,**/*.tsx" ---- - -# 01. Coding Standards - -These standards apply in all modes. For behavioral expectations, see 00-meta-rules. - -## Readability and Maintainability - -- Prioritize clarity and simplicity over cleverness. -- Keep code DRY without sacrificing readability. -- Favor small, focused functions and files. -- Use early returns whenever possible to make the code more readable. - -## Naming Conventions - -- Use descriptive, intention-revealing names. -- Match existing repository naming patterns. - -## Immutability Preference - -- Prefer immutable data patterns. -- Avoid in-place mutations unless necessary and justified. - -## Async and Error Handling - -- Prefer async/await over promise chains. -- Always handle errors intentionally. - -## File Hygiene - -- Remove unused code, dead files, and debugging artifacts. -- Keep file scope aligned to a single responsibility. - -## Scope Guardrails - -- Do not include company-specific content here. -- This standard applies equally in all modes. diff --git a/home/.chezmoitemplates/ai/skills/02-typescript-guidelines.instructions.md b/home/.chezmoitemplates/ai/skills/02-typescript-guidelines.instructions.md deleted file mode 100644 index 8ea5c75..0000000 --- a/home/.chezmoitemplates/ai/skills/02-typescript-guidelines.instructions.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -description: "TypeScript-specific implementation rules." -applyTo: "**/*.ts,**/*.tsx" ---- - -# 02. TypeScript Guidelines - -Follow 00-meta-rules and 01-coding-standards for general behavior and quality. - -## Type Safety - -- Use strict typing and do not use `any` or `unknown`. -- Prefer explicit types for public APIs and boundaries. -- Use type inference where it improves readability without ambiguity. - -## Types and Interfaces - -- Use interfaces or type aliases appropriately. -- Keep types small and composable. - -## Immutability and Optionality - -- Prefer readonly data where possible. -- Use optional chaining and nullish coalescing appropriately. - -## Declarations - -- Use `const` by default and `let` only when reassignment is required. -- Never use `var`. - -## Functions - -- Use function declarations for named, exported functions. -- Use arrow functions for callbacks and inline logic. - -## Object and Array Patterns - -- Use object/array spread to avoid mutation. -- Prefer array methods like `map`, `filter`, and `reduce`. diff --git a/home/.chezmoitemplates/ai/skills/03-react-guidelines.instructions.md b/home/.chezmoitemplates/ai/skills/03-react-guidelines.instructions.md deleted file mode 100644 index fddc019..0000000 --- a/home/.chezmoitemplates/ai/skills/03-react-guidelines.instructions.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -description: "React-specific component and UI implementation guidelines." -applyTo: "**/*.tsx" ---- - -# 03. React Guidelines - -Follow 00-meta-rules, 01-coding-standards, and 02-typescript-guidelines. - -## Components - -- Use functional components only. -- Keep components small, focused, and composable. -- Use clear, consistent component and file naming. - -## Hooks - -- Follow the Rules of Hooks (no conditional or looped hooks). -- Prefer custom hooks for reusable stateful logic. - -## Styling - -- Use CSS Modules for component styling. -- Keep styling co-located with components when practical. - -## Accessibility - -- Ensure keyboard operability for interactive elements. -- Provide appropriate ARIA attributes and labels. -- Use proper roles and focus management for custom controls. - -## Composition Patterns - -- Favor composition over inheritance. -- Separate data-fetching from presentation when it improves clarity. diff --git a/home/.chezmoitemplates/ai/skills/04-testing-guidelines.instructions.md b/home/.chezmoitemplates/ai/skills/04-testing-guidelines.instructions.md deleted file mode 100644 index 791b7b6..0000000 --- a/home/.chezmoitemplates/ai/skills/04-testing-guidelines.instructions.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -description: "Testing standards for Jest and React Testing Library" -applyTo: "**/*.test.ts,**/*.test.tsx" ---- - -# 04. Testing Guidelines - -Follow 00-meta-rules and 01-coding-standards for general expectations. - -## Tooling - -- Use Jest with React Testing Library. - -### Testing - -- Use `@testing-library/react` for component testing. -- Write tests for loaders and actions ensuring data correctness. -- Mock fetch requests and responses where applicable. - -## Query Priority (Accessibility-First) - -Use queries in this order of priority: - -1. **Accessible to Everyone:** - - `getByRole` - Query by ARIA role (preferred for most elements) - - `getByLabelText` - Query form elements by label - - `getByPlaceholderText` - Query by placeholder text - - `getByText` - Query by visible text content - - `getByDisplayValue` - Query by current input value - -2. **Semantic Queries:** - - `getByAltText` - Query images by alt text - - `getByTitle` - Query by title attribute - -3. **Test IDs (Last Resort):** - - `getByTestId` - Only when other queries aren't practical - -```typescript -// ✅ Good - Uses accessible queries -screen.getByRole("button", { name: /submit/i }); -screen.getByLabelText(/email address/i); -screen.getByRole("heading", { level: 1 }); -screen.getByRole("textbox", { name: /search/i }); - -// ❌ Avoid - Implementation details -screen.getByTestId("submit-button"); -container.querySelector(".submit-btn"); -``` - -### What to Test - -✅ **Do Test:** - -- User-visible behavior and interactions -- Accessibility (roles, labels, focus management) -- Loading and error states -- Form validation and submission -- Navigation and routing -- Data transformation in loaders -- Side effects in actions - -❌ **Don't Test:** - -- Implementation details (state, internal methods) -- Third-party library internals -- Exact CSS class names -- Component internal state - -## Coverage Expectations - -- Aim for meaningful coverage, not 100% -- Critical paths: loaders, actions, user interactions -- Edge cases: empty states, errors, loading -- Accessibility: keyboard navigation, screen reader support - -## Mocking Best Practices - -- Mock external boundaries (network, storage, timers). -- Keep mocks minimal and reset between tests. -- Avoid over-mocking UI primitives. diff --git a/home/.chezmoitemplates/ai/skills/05-commit-guide.instructions.md b/home/.chezmoitemplates/ai/skills/05-commit-guide.instructions.md deleted file mode 100644 index 8d25693..0000000 --- a/home/.chezmoitemplates/ai/skills/05-commit-guide.instructions.md +++ /dev/null @@ -1,201 +0,0 @@ ---- -description: "Generate constitutional-compliant commit messages following quality standards and best practices" ---- - -# Commit and Push Changes - -## Instructions - -This prompt guides you through committing and pushing your changes for a JIRA ticket. Replace `{TICKET_NUMBER}` with the actual JIRA ticket number (e.g., BC724-550). - -## Prerequisites - -- All tests passing -- Linting checks passing -- Changes have been validated -- Currently on the feature branch - -## Commit Message Format - -Always use the enforced format: - -``` -: {TICKET_NUMBER} -``` - -### Valid types: - -Behavioral Changes (Affect functionality) - -- `feat:` - New features (new jisp parsing, UI components, solver algorithms) -- `fix:` - Bug fixes (validation errors, parsing issues, solver bugs) -- `test:` - Test additions or modifications -- `perf:` - Performance improvements (solver optimization, parsing speed) - -Structural Changes (No functional impact) - -- `refactor:` - Code restructuring without behavior change -- `docs:` - Documentation updates -- `chore:` - Maintenance tasks, dependency updates -- `style:` - Code style/formatting changes -- `specs:` - Specification development and updates - -Examples: - -- `feat: BC724-550 Add new experiment hooks architecture` -- `fix: BC724-550 Resolve button alignment issue` -- `refactor: BC724-550 Consolidate utility functions` -- `test: BC724-550 Add missing test coverage for component` - -## Git Operations Workflow - -### 1. Verify Current Branch - -```bash -# Confirm you're on the feature branch -git branch --show-current -# Should output: feature/{TICKET_NUMBER} -``` - -### 2. Review Changes - -```bash -# View all modified files -git status - -# Review specific changes -git diff - -# Review staged changes -git diff --staged -``` - -### 3. Stage Changes - -```bash -# Stage all changes -git add . - -# Or stage specific files -git add - -# Verify staged files -git status -``` - -### 4. Create Commit - -```bash -# Commit with proper format -git commit -m ": {TICKET_NUMBER} " -``` - -### 5. Push to Remote - -```bash -# Push feature branch to origin -git push origin feature/{TICKET_NUMBER} - -# If this is the first push, the command above will work -# For subsequent pushes to the same branch -git push -``` - -### 6. Verify Push - -```bash -# Confirm the branch exists on remote -git branch -r | grep feature/{TICKET_NUMBER} -``` - -## Pre-Push Final Checklist - -Before pushing, verify: - -- [ ] All tests pass: `npm test` -- [ ] Linting passes: `npm run lint` -- [ ] StaticType pass: `npm run check-types` -- [ ] Build succeeds (if applicable): `npm run build:client` -- [ ] Commit message follows format: `: {TICKET_NUMBER} ` -- [ ] On correct feature branch: `git branch --show-current` -- [ ] All changes staged and committed: `git status` shows clean working tree - -## Example Usage for BC724-550 - -```bash -# Verify branch -git branch --show-current -# Output: feature/BS-86067 - -# Review changes -git status -git diff - -# Stage all changes -git add . - -# Commit with proper message -git commit -m "feat: BC724-550 Replace useDetermineVariant calls with VariantsByCart pattern" - -# Push to remote -git push origin feature/BC724-550 -``` - -## Common Issues and Solutions - -### Commit Message Validation Fails - -If your commit message doesn't follow the required format: - -```bash -# Amend the last commit message -git commit --amend -m ": {TICKET_NUMBER} " -``` - -#### Need to Add More Changes Before Pushing - -```bash -# Stage additional files -git add - -# Amend the previous commit -git commit --amend --no-edit - -# Or create a new commit -git commit -m ": {TICKET_NUMBER} " -``` - -### Branch Doesn't Exist on Remote Yet - -```bash -# Set upstream and push -git push -u origin feature/{TICKET_NUMBER} -``` - -### Push Rejected (Remote has Changes) - -```bash -# Pull and rebase -git pull --rebase origin feature/{TICKET_NUMBER} -# Resolve any conflicts if they occur -# Then continue the rebase -git rebase --continue - -# Push again -git push -``` - -## Next Steps - -Once your changes are pushed: - -1. Verify the branch appears in GitHub -2. Proceed to create a pull request using the create-pull-request prompt -3. Follow the project's PR template guidelines - -## Notes - -- Never force push unless absolutely necessary and you understand the implications -- Always verify tests pass before pushing -- Keep commits focused and atomic -- Use descriptive commit messages that explain the "why" not just the "what" diff --git a/home/.chezmoitemplates/skills/react/SKILL.md b/home/.chezmoitemplates/skills/react/SKILL.md new file mode 100644 index 0000000..7bfb8dd --- /dev/null +++ b/home/.chezmoitemplates/skills/react/SKILL.md @@ -0,0 +1,36 @@ +--- +name: react +description: "React component and UI guidelines: functional components, hooks, accessibility, and composition. Use when building or reviewing React (.tsx) components." +--- + +# React + +Guidelines for React UI work. Build on the `typescript` skill for typing and on +general engineering principles for structure and naming. + +## Components + +- Use functional components only. +- Keep components small, focused, and composable. +- Use clear, consistent component and file naming. + +## Hooks + +- Follow the Rules of Hooks: no conditional or looped hooks. +- Extract reusable stateful logic into custom hooks. + +## Styling + +- Use CSS Modules for component styling. +- Co-locate styling with components when practical. + +## Accessibility + +- Ensure keyboard operability for interactive elements. +- Provide appropriate ARIA attributes and labels. +- Use proper roles and focus management for custom controls. + +## Composition + +- Favor composition over inheritance. +- Separate data fetching from presentation when it improves clarity. diff --git a/home/.chezmoitemplates/skills/testing/SKILL.md b/home/.chezmoitemplates/skills/testing/SKILL.md new file mode 100644 index 0000000..3475cb3 --- /dev/null +++ b/home/.chezmoitemplates/skills/testing/SKILL.md @@ -0,0 +1,61 @@ +--- +name: testing +description: "Testing standards for Jest and React Testing Library: accessibility-first queries, behavior over implementation, and meaningful coverage. Use when writing or reviewing tests (.test.ts/.test.tsx)." +--- + +# Testing + +Standards for component and unit tests with Jest and React Testing Library. + +## Tooling + +- Use Jest with React Testing Library (`@testing-library/react`). +- Test loaders and actions for data correctness. +- Mock fetch requests and responses where applicable. + +## Query priority (accessibility-first) + +Prefer queries in this order: + +1. Accessible to everyone: `getByRole`, `getByLabelText`, `getByPlaceholderText`, `getByText`, `getByDisplayValue` +2. Semantic: `getByAltText`, `getByTitle` +3. Test IDs (last resort): `getByTestId` + +```typescript +// Good: accessible queries +screen.getByRole("button", { name: /submit/i }); +screen.getByLabelText(/email address/i); +screen.getByRole("heading", { level: 1 }); + +// Avoid: implementation details +screen.getByTestId("submit-button"); +container.querySelector(".submit-btn"); +``` + +## What to test + +Do test: + +- User-visible behavior and interactions +- Accessibility: roles, labels, focus management +- Loading and error states +- Form validation and submission +- Navigation and routing +- Data transformation in loaders and side effects in actions + +Do not test: + +- Implementation details such as internal state or methods +- Third-party library internals +- Exact CSS class names + +## Coverage + +- Aim for meaningful coverage, not 100%. +- Focus on critical paths, edge cases (empty, error, loading), and accessibility. + +## Mocking + +- Mock external boundaries: network, storage, timers. +- Keep mocks minimal and reset them between tests. +- Avoid over-mocking UI primitives. diff --git a/home/.chezmoitemplates/skills/typescript/SKILL.md b/home/.chezmoitemplates/skills/typescript/SKILL.md new file mode 100644 index 0000000..c88d40d --- /dev/null +++ b/home/.chezmoitemplates/skills/typescript/SKILL.md @@ -0,0 +1,42 @@ +--- +name: typescript +description: "TypeScript implementation standards: strict typing, immutability, and idiomatic patterns. Use when writing, reviewing, or refactoring TypeScript (.ts/.tsx) code." +--- + +# TypeScript + +Standards for writing maintainable TypeScript. Apply alongside general engineering +principles: clarity over cleverness, small focused functions and files, early +returns, descriptive names, and intentional error handling. + +## Type safety + +- Use strict typing. Avoid `any` and `unknown`. +- Prefer explicit types for public APIs and module boundaries. +- Use type inference where it improves readability without ambiguity. + +## Types and interfaces + +- Use interfaces or type aliases as appropriate. +- Keep types small and composable. + +## Immutability and optionality + +- Prefer `readonly` data where possible. +- Avoid in-place mutation; use object and array spread to build new values. +- Use optional chaining and nullish coalescing where they clarify intent. + +## Declarations + +- Use `const` by default and `let` only when reassignment is required. +- Never use `var`. + +## Functions + +- Use function declarations for named, exported functions. +- Use arrow functions for callbacks and inline logic. + +## Objects and arrays + +- Prefer `map`, `filter`, and `reduce` over imperative loops. +- Handle errors intentionally; prefer `async`/`await` over promise chains. From 57ff58501271f1fde355082a8e71a52546aa608a Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 22:21:06 -0500 Subject: [PATCH 25/51] chore: enrich copilot commit message instructions keep commit guidance as copilot instructions per DOT-43 --- home/Library/Application Support/Code/User/settings.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/home/Library/Application Support/Code/User/settings.json b/home/Library/Application Support/Code/User/settings.json index 8dddcc7..a189bbe 100644 --- a/home/Library/Application Support/Code/User/settings.json +++ b/home/Library/Application Support/Code/User/settings.json @@ -58,6 +58,12 @@ }, { "text": "Keep the commit message short and concise. Use imperative mood and avoid unnecessary details." + }, + { + "text": "Limit the subject line to 50 characters and start it with a type prefix such as feat, fix, refactor, docs, test, chore, perf, build, style, or ci." + }, + { + "text": "Describe only the staged changes and do not include test plans." } ], From 786fd92e38b41239123cd18853ae2faa64a473b7 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 22:30:25 -0500 Subject: [PATCH 26/51] chore: align copilot commit instructions with spec --- home/Library/Application Support/Code/User/settings.json | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/home/Library/Application Support/Code/User/settings.json b/home/Library/Application Support/Code/User/settings.json index a189bbe..46bd893 100644 --- a/home/Library/Application Support/Code/User/settings.json +++ b/home/Library/Application Support/Code/User/settings.json @@ -54,16 +54,13 @@ }, "github.copilot.chat.commitMessageGeneration.instructions": [ { - "text": "Use conventional commit message format." + "text": "Use Conventional Commits format `: ` with a type such as feat, fix, refactor, docs, test, chore, perf, build, style, or ci." }, { - "text": "Keep the commit message short and concise. Use imperative mood and avoid unnecessary details." + "text": "Write the subject in imperative mood and keep it under 50 characters." }, { - "text": "Limit the subject line to 50 characters and start it with a type prefix such as feat, fix, refactor, docs, test, chore, perf, build, style, or ci." - }, - { - "text": "Describe only the staged changes and do not include test plans." + "text": "Add a one-line body only when it explains the why. Describe only the staged changes and never include test plans." } ], From 48271a6cf65c3e859911471ddccd84183b17a466 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 22:30:25 -0500 Subject: [PATCH 27/51] docs: sync migration spec and plan with skill conversion local skills authored ahead of phase 1; vendored copies removed (DOT-43) --- .../plans/2026-06-30-apm-migration-phase-1-skills-install.md | 4 ++-- .../2026-06-30-apm-to-claude-marketplace-migration-design.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/superpowers/plans/2026-06-30-apm-migration-phase-1-skills-install.md b/docs/superpowers/plans/2026-06-30-apm-migration-phase-1-skills-install.md index 366551a..2f33ac8 100644 --- a/docs/superpowers/plans/2026-06-30-apm-migration-phase-1-skills-install.md +++ b/docs/superpowers/plans/2026-06-30-apm-migration-phase-1-skills-install.md @@ -10,7 +10,7 @@ **Spec:** `docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md` (sections: Group and Target Data Model, Skills Design). -**Scope note:** This is Phase 1 of 7. It delivers skills install on its own. Local domain skills (`typescript`, `react`, `testing`) do not exist until Phase 5, so the library skips `source: local` skills whose directory is absent; upstream skills install normally. +**Scope note:** This is Phase 1 of 7. It delivers skills install on its own. The local domain skills (`typescript`, `react`, `testing`) already exist under `home/.chezmoitemplates/skills/`, authored ahead of this phase, so `source: local` skills install normally. The library still skips a `source: local` skill whose directory is absent as a safety net. --- @@ -384,7 +384,7 @@ git commit -m "test: DOT-43 add ai-skills template render tests" - **Spec coverage (Skills Design):** Library runs `npx skills add --skill -a --global --copy --yes` (Task 1); targets and skills resolve from `ai.yaml` via `active-group-values` (Task 2); local sources skip when absent, deferring Phase 5 (Task 1 test 5). Covered. - **Placeholder scan:** No TBD/TODO; every step has full code or an exact command with expected output. The one adjustable point (the template test helper name in Task 3) is called out explicitly because it depends on the existing file, and the step says to match the real helper. - **Type consistency:** `AI_SKILLS`, `AI_SKILL_TARGETS`, `AI_LOCAL_SKILLS_DIR`, `ai_skills_install_main`, and the `|` contract are identical across the library, the run script, and the tests. -- **Out of Phase 1 scope:** plugins install, MCP delivery, instructions, domain-skill authoring, commit wiring, and apm teardown are later phases. +- **Out of Phase 1 scope:** plugins install, MCP delivery, instructions, and apm teardown are later phases. Domain-skill authoring and Copilot commit wiring are already done ahead of this plan. ## Remaining Phases (separate plans) diff --git a/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md b/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md index 24ee867..871f774 100644 --- a/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md +++ b/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md @@ -101,7 +101,7 @@ npx skills add --skill -a [-a ] --global --cop `--global` installs into `~/.claude/skills/` and `~/.copilot/skills/`. `--copy` writes real files rather than symlinks because Claude Code ignores symlinked skill directories. The `-a` flags come from the active group's `target` in `ai.yaml`, so a skill in `shared` installs for the machine's target, and a skill in `personal` installs for `claude-code`. -Local domain skills (`typescript`, `react`, `testing`, React Testing Library) are authored under `home/.chezmoitemplates/skills/` from the existing instruction files, and installed with `source: local` pointing at that path. The vendored `grill-me` and `junior-to-senior` copies already present under that directory are removed once they install from upstream sources, unless upstream is unavailable. +Local domain skills `typescript`, `react`, and `testing` (React Testing Library guidance lives in the `testing` skill) are authored under `home/.chezmoitemplates/skills/` from the former instruction files, and install with `source: local` pointing at that path. The previously vendored `grill-me` and `junior-to-senior` copies under that directory have been removed; both install from their upstream sources. The `skills` CLI has no manifest file, so `home/.chezmoidata/ai.yaml` is the manifest. A `run_onchange_09_install-ai-skills.sh.tmpl` script gates on `darwin`, reads the active group and its target, iterates the matching skill entries, and runs the CLI. It re-runs when the rendered data changes. @@ -149,7 +149,7 @@ Each template reads the `ai.yaml` `mcp` entries for the active target and render One canonical `home/.chezmoitemplates/AGENTS.md` holds universal behavioral guidance only: AI guidance, git and pull request workflow, commit message rules, and the `gh` CLI standard. It does not assert a frontend persona. Frontend specifics live in the `typescript`, `react`, and `testing` skills. -`AGENTS.md` renders to `~/.claude/CLAUDE.md` for Claude Code and to the Copilot instructions path for VS Code Copilot. GitHub MCP references and the `context7` tool mention are removed from the meta rules content; GitHub operations use `gh`. The `coding-standards` and TypeScript specifics fold into the `typescript` skill. +`AGENTS.md` renders to `~/.claude/CLAUDE.md` for Claude Code and to the Copilot instructions path for VS Code Copilot. GitHub MCP references and the `context7` tool mention are removed from the meta rules content; GitHub operations use `gh`. The `coding-standards` foundations fold into the local domain skills, and the TypeScript specifics into the `typescript` skill. ## Commit Message Instructions From 6c9b989f97b7398f23123c739eb57e59c8848964 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 22:30:33 -0500 Subject: [PATCH 28/51] chore: remove darwin-specific chezmoi ignore patterns and update tests for model aliases --- home/.chezmoiignore | 3 --- home/.chezmoitemplates/chezmoiignore.d/darwin | 1 - tests/unit/claude-settings.bats | 3 +-- 3 files changed, 1 insertion(+), 6 deletions(-) delete mode 100644 home/.chezmoiignore delete mode 100644 home/.chezmoitemplates/chezmoiignore.d/darwin diff --git a/home/.chezmoiignore b/home/.chezmoiignore deleted file mode 100644 index d54afed..0000000 --- a/home/.chezmoiignore +++ /dev/null @@ -1,3 +0,0 @@ -{{ if eq .chezmoi.os "darwin" -}} -{{ template "chezmoiignore.d/darwin" . }} -{{- end }} diff --git a/home/.chezmoitemplates/chezmoiignore.d/darwin b/home/.chezmoitemplates/chezmoiignore.d/darwin deleted file mode 100644 index 87c303c..0000000 --- a/home/.chezmoitemplates/chezmoiignore.d/darwin +++ /dev/null @@ -1 +0,0 @@ -# Darwin-specific chezmoi ignore patterns. diff --git a/tests/unit/claude-settings.bats b/tests/unit/claude-settings.bats index 31027d4..9a73349 100644 --- a/tests/unit/claude-settings.bats +++ b/tests/unit/claude-settings.bats @@ -9,8 +9,7 @@ SETTINGS="$DOTFILES_ROOT/home/dot_claude/settings.json" @test "claude settings use current model aliases" { settings_content="$(<"$SETTINGS")" - [[ "$settings_content" == *'"model": "opusplan[1m]"'* ]] - [[ "$settings_content" == *'"CLAUDE_CODE_SUBAGENT_MODEL": "inherit"'* ]] + [[ "$settings_content" == *'"model": "best"'* ]] [[ "$settings_content" != *'ANTHROPIC_DEFAULT_OPUS_MODEL'* ]] [[ "$settings_content" != *'ANTHROPIC_DEFAULT_SONNET_MODEL'* ]] [[ "$settings_content" != *'ANTHROPIC_DEFAULT_HAIKU_MODEL'* ]] From bf11436a1f9f76482d91e57fc69061bbfbb87250 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 22:40:11 -0500 Subject: [PATCH 29/51] refactor: simplify shellcheck linting by removing redundant checks --- mise.toml | 46 +++++++--------------------------------------- 1 file changed, 7 insertions(+), 39 deletions(-) diff --git a/mise.toml b/mise.toml index 0535a7c..d19c0c7 100644 --- a/mise.toml +++ b/mise.toml @@ -33,54 +33,22 @@ run = ''' #!/usr/bin/env bash set -euo pipefail +# treefmt walks the tree and checks formatting; shellcheck adds static analysis. +# git ls-files scopes both shellcheck passes to tracked files, so vendored or +# ignored scripts stay out without hand-maintained root lists. treefmt --ci -shell_roots=() -has_shell_roots=0 -for root in scripts home/dot_config/zsh home/.chezmoitemplates home/dot_claude .github/actions; do - if [[ -d "${root}" ]]; then - shell_roots+=("${root}") - has_shell_roots=1 - fi -done +git ls-files '*.sh' '*.zsh' '*.zshrc' | xargs shellcheck -if ((has_shell_roots)); then - shell_targets=() - while IFS= read -r shell_target; do - shell_targets+=("${shell_target}") - done < <(find "${shell_roots[@]}" -type f \( -name '*.sh' -o -name '*.zsh' -o -name '.zshrc' \)) - - if [[ -n "${shell_targets[*]:-}" ]]; then - shellcheck "${shell_targets[@]}" - fi -fi - -# bats files use a test DSL that shfmt would reflow, so lint them with shellcheck only. +# bats files use a test DSL that shfmt would reflow, so shellcheck alone lints them. # Severity stays at warning because bats idioms (literal-string assertions, per-test # subshells) trip info-level checks that are not real bugs. -if [[ -d tests ]]; then - bats_targets=() - while IFS= read -r bats_target; do - bats_targets+=("${bats_target}") - done < <(find tests -type f -name '*.bats') - - if [[ -n "${bats_targets[*]:-}" ]]; then - shellcheck --shell=bash --severity=warning "${bats_targets[@]}" - fi -fi +git ls-files '*.bats' | xargs shellcheck --severity=warning ''' -[tasks.test-unit] -description = "Run bats unit tests" -run = "bats --recursive tests/unit" - -[tasks.test-template] -description = "Run bats template tests" -run = "bats --recursive tests/template" - [tasks.test] description = "Run all bats tests" -run = [{ task = "test-unit" }, { task = "test-template" }] +run = "bats --recursive tests" [tasks.check] description = "Run lint and tests" From ec7f07a097dba166fbcf145d9a467b3c1cbe5c9c Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 23:02:12 -0500 Subject: [PATCH 30/51] feat: add cross-agent ai-skills install install library, run_onchange_09 script, unit + template render tests (DOT-43) --- .../run_onchange_09_install-ai-skills.sh.tmpl | 23 +++ .../lib/install/ai-skills.sh | 95 ++++++++++++ tests/template/darwin-install-scripts.bats | 41 +++++ tests/unit/lib/install/ai-skills.bats | 143 ++++++++++++++++++ 4 files changed, 302 insertions(+) create mode 100644 home/.chezmoiscripts/darwin/run_onchange_09_install-ai-skills.sh.tmpl create mode 100644 home/.chezmoitemplates/lib/install/ai-skills.sh create mode 100644 tests/unit/lib/install/ai-skills.bats diff --git a/home/.chezmoiscripts/darwin/run_onchange_09_install-ai-skills.sh.tmpl b/home/.chezmoiscripts/darwin/run_onchange_09_install-ai-skills.sh.tmpl new file mode 100644 index 0000000..0155c27 --- /dev/null +++ b/home/.chezmoiscripts/darwin/run_onchange_09_install-ai-skills.sh.tmpl @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# @file run_onchange_09_install-ai-skills.sh +# @brief Install cross-agent AI skills. +# @description AI data hash: {{ include ".chezmoidata/ai.yaml" | sha256sum }}; local skills hash: {{ $h := "" }}{{ range (glob (joinPath .chezmoi.sourceDir ".chezmoitemplates" "skills" "**" "SKILL.md")) }}{{ $h = print $h (include .) }}{{ end }}{{ $h | sha256sum }} +{{- $activeTargets := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .ai.targets) | fromJson -}} +{{- $activeSkills := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .ai.skills) | fromJson -}} + +{{ if and (eq .chezmoi.os "darwin") (gt (len $activeTargets) 0) (gt (len $activeSkills) 0) }} +{{ template "lib/common/log.sh" . }} +{{ template "lib/common/install-prelude.sh" . }} +export AI_LOCAL_SKILLS_DIR="{{ .chezmoi.sourceDir }}/.chezmoitemplates/skills" +AI_SKILL_TARGETS=( +{{ range $activeTargets -}} + "{{ . }}" +{{ end -}} +) +AI_SKILLS=( +{{ range $activeSkills -}} + "{{ .source }}|{{ .skill }}" +{{ end -}} +) +{{ template "lib/install/ai-skills.sh" . }} +{{ end }} diff --git a/home/.chezmoitemplates/lib/install/ai-skills.sh b/home/.chezmoitemplates/lib/install/ai-skills.sh new file mode 100644 index 0000000..60e4be9 --- /dev/null +++ b/home/.chezmoitemplates/lib/install/ai-skills.sh @@ -0,0 +1,95 @@ +#!/usr/bin/env bash +# @file lib/install/ai-skills.sh +# @brief Install cross-agent skills with the skills CLI. +# @description +# Runs `npx skills add` for each selected skill against the active agent +# targets. Local skill sources resolve under AI_LOCAL_SKILLS_DIR and are +# skipped when their directory is absent. Sourceable from bats tests and +# injected into chezmoi run scripts via chezmoi template rendering. + +set -Eeuo pipefail + +# +# @description Install each selected skill for the active agent targets. +# @exitcode 0 Skills installed, or nothing to do. +# @exitcode 1 npx is not available. +# +function ai_skills_install_main() { + local has_skill=0 + local entry + for entry in "${AI_SKILLS[@]-}"; do + [[ -z "${entry}" ]] && continue + has_skill=1 + break + done + + if ((has_skill == 0)); then + log_info "[ai-skills] No skills to install." + return 0 + fi + + local -a target_flags=() + local target + for target in "${AI_SKILL_TARGETS[@]-}"; do + [[ -z "${target}" ]] && continue + target_flags+=("-a" "${target}") + done + + if ((${#target_flags[@]} == 0)); then + log_info "[ai-skills] No agent targets; nothing to install." + return 0 + fi + + if ! command -v npx >/dev/null 2>&1; then + log_error "[ai-skills] npx not found. Ensure Node.js is installed (run_onchange_03_install-mise-tools)." + return 1 + fi + + log_info "[ai-skills] Installing cross-agent skills..." + + local src skill add_source + local -a failed=() + for entry in "${AI_SKILLS[@]-}"; do + [[ -z "${entry}" ]] && continue + src="${entry%%|*}" + skill="${entry##*|}" + + if [[ "${src}" == "local" ]]; then + add_source="${AI_LOCAL_SKILLS_DIR:?AI_LOCAL_SKILLS_DIR must be set}/${skill}" + if [[ ! -d "${add_source}" ]]; then + log_warn "[ai-skills] local skill '${skill}' not found at ${add_source}; skipping." + continue + fi + else + add_source="${src}" + fi + + if ! npx --yes skills add "${add_source}" --skill "${skill}" "${target_flags[@]}" --global --copy --yes; then + failed+=("${skill}") + fi + done + + if ((${#failed[@]} > 0)); then + log_warn "[ai-skills] ${#failed[@]} skill(s) failed to install:" + printf ' - %s\n' "${failed[@]}" >&2 + fi + + log_info "[ai-skills] Cross-agent skills installed." +} + +# +# @description Run the AI skills install flow. +# +function main() { + ai_skills_install_main +} + +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + # When run directly, pull in the shared libraries that chezmoi otherwise + # concatenates ahead of this file. + # shellcheck source=/dev/null + command -v log_info >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/log.sh" + # shellcheck source=/dev/null + command -v require_command >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/install-prelude.sh" + main +fi diff --git a/tests/template/darwin-install-scripts.bats b/tests/template/darwin-install-scripts.bats index 540b102..7ef2890 100644 --- a/tests/template/darwin-install-scripts.bats +++ b/tests/template/darwin-install-scripts.bats @@ -9,6 +9,7 @@ EMPTY_AI_DATA='{"chezmoi":{"os":"darwin"},"personal":true,"work":false,"ai":{"ta PACKAGE_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_02_install-packages.sh.tmpl" UV_TOOLS_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_03_install-uv-tools.sh.tmpl" GRAPHIFY_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_08_install-graphify-skills.sh.tmpl" +AI_SKILLS_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_09_install-ai-skills.sh.tmpl" @test "darwin install script templates render with bash shebang" { for template in "$DOTFILES_ROOT"/home/.chezmoiscripts/darwin/*.tmpl; do @@ -30,6 +31,10 @@ GRAPHIFY_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_08_in assert_file_contains "$GRAPHIFY_TEMPLATE" '{{ template "lib/install/graphify-skills.sh" . }}' } +@test "darwin install script templates inject AI skills shell library" { + assert_file_contains "$AI_SKILLS_TEMPLATE" '{{ template "lib/install/ai-skills.sh" . }}' +} + @test "darwin install script templates inject the install prelude" { local prelude='{{ template "lib/common/install-prelude.sh" . }}' assert_file_contains "$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_once_01_install-homebrew.sh.tmpl" "$prelude" @@ -38,6 +43,7 @@ GRAPHIFY_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_08_in assert_file_contains "$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_03_install-uv-tools.sh.tmpl" "$prelude" assert_file_contains "$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_04_install-vscode-extensions.sh.tmpl" "$prelude" assert_file_contains "$GRAPHIFY_TEMPLATE" "$prelude" + assert_file_contains "$AI_SKILLS_TEMPLATE" "$prelude" } @test "rendered darwin install scripts are syntactically valid bash" { @@ -126,3 +132,38 @@ GRAPHIFY_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_08_in refute_line 'GRAPHIFY_PLATFORMS=(' refute_line --partial 'graphify_skills_install_main' } + +@test "personal AI skills template targets claude-code with shared and personal skills" { + run render_chezmoi_template "$AI_SKILLS_TEMPLATE" "$DARWIN_DATA" + + assert_success + assert_line 'AI_SKILL_TARGETS=(' + assert_line --partial '"claude-code"' + refute_line --partial '"github-copilot"' + assert_line --partial 'export AI_LOCAL_SKILLS_DIR=' + assert_line --partial '.chezmoitemplates/skills' + assert_line --partial '"mattpocock/skills|grill-me"' + assert_line --partial '"local|typescript"' + assert_line --partial '"schpet/linear-cli|linear-cli"' + assert_line --partial 'ai_skills_install_main' +} + +@test "work AI skills template targets github-copilot and omits personal skills" { + run render_chezmoi_template "$AI_SKILLS_TEMPLATE" "$WORK_DATA" + + assert_success + assert_line 'AI_SKILL_TARGETS=(' + assert_line --partial '"github-copilot"' + refute_line --partial '"claude-code"' + assert_line --partial '"mattpocock/skills|grill-me"' + refute_line --partial '"schpet/linear-cli|linear-cli"' + assert_line --partial 'ai_skills_install_main' +} + +@test "empty AI target groups render no AI skills commands" { + run render_chezmoi_template "$AI_SKILLS_TEMPLATE" "$EMPTY_AI_DATA" + + assert_success + refute_line 'AI_SKILL_TARGETS=(' + refute_line --partial 'ai_skills_install_main' +} diff --git a/tests/unit/lib/install/ai-skills.bats b/tests/unit/lib/install/ai-skills.bats new file mode 100644 index 0000000..2fc4682 --- /dev/null +++ b/tests/unit/lib/install/ai-skills.bats @@ -0,0 +1,143 @@ +#!/usr/bin/env bats +# @file tests/unit/lib/install/ai-skills.bats +# @brief Behavior tests for home/.chezmoitemplates/lib/install/ai-skills.sh. + +load '../../../test_helpers/load.bash' + +LOG_LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/common/log.sh" +PRELUDE="$DOTFILES_ROOT/home/.chezmoitemplates/lib/common/install-prelude.sh" +LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/install/ai-skills.sh" + +setup() { + export NPX_ARGS_FILE="$BATS_TEST_TMPDIR/npx-args" + mkdir -p "$BATS_TEST_TMPDIR/bin" + + cat >"$BATS_TEST_TMPDIR/bin/npx" <<'NPX' +#!/usr/bin/env bash +printf '%s\n' "$*" | tee -a "$NPX_ARGS_FILE" +exit "${NPX_EXIT_CODE:-0}" +NPX + chmod +x "$BATS_TEST_TMPDIR/bin/npx" + export PATH="$BATS_TEST_TMPDIR/bin:/usr/bin:/bin" + + export AI_LOCAL_SKILLS_DIR="$BATS_TEST_TMPDIR/skills" + mkdir -p "$AI_LOCAL_SKILLS_DIR" +} + +_run_main() { + run bash -c "source '$LOG_LIB' && source '$PRELUDE' && source '$LIB' && $1 && main" +} + +@test "main: installs each skill for a single target" { + _run_main "AI_SKILL_TARGETS=('claude-code') && AI_SKILLS=('mattpocock/skills|grill-me' 'blader/humanizer|humanizer')" + + assert_success + assert_line "[ai-skills] Installing cross-agent skills..." + assert_line "[ai-skills] Cross-agent skills installed." + assert_line "--yes skills add mattpocock/skills --skill grill-me -a claude-code --global --copy --yes" + assert_line "--yes skills add blader/humanizer --skill humanizer -a claude-code --global --copy --yes" +} + +@test "main: passes one -a flag per target" { + _run_main "AI_SKILL_TARGETS=('claude-code' 'github-copilot') && AI_SKILLS=('mattpocock/skills|grill-me')" + + assert_success + assert_line "--yes skills add mattpocock/skills --skill grill-me -a claude-code -a github-copilot --global --copy --yes" +} + +@test "main: applies every target to every skill" { + _run_main "AI_SKILL_TARGETS=('claude-code' 'github-copilot') && AI_SKILLS=('mattpocock/skills|grill-me' 'blader/humanizer|humanizer')" + + assert_success + assert_line "--yes skills add mattpocock/skills --skill grill-me -a claude-code -a github-copilot --global --copy --yes" + assert_line "--yes skills add blader/humanizer --skill humanizer -a claude-code -a github-copilot --global --copy --yes" +} + +@test "main: skips empty skill entries" { + _run_main "AI_SKILL_TARGETS=('claude-code') && AI_SKILLS=('mattpocock/skills|grill-me' '' 'blader/humanizer|humanizer')" + + assert_success + assert_line "--yes skills add mattpocock/skills --skill grill-me -a claude-code --global --copy --yes" + assert_line "--yes skills add blader/humanizer --skill humanizer -a claude-code --global --copy --yes" + refute_line --partial "skills add --skill" + [ "$(wc -l <"$NPX_ARGS_FILE")" -eq 2 ] +} + +@test "main: installs a local skill when its directory exists" { + mkdir -p "$AI_LOCAL_SKILLS_DIR/typescript" + _run_main "AI_SKILL_TARGETS=('claude-code') && AI_SKILLS=('local|typescript')" + + assert_success + assert_line "--yes skills add $AI_LOCAL_SKILLS_DIR/typescript --skill typescript -a claude-code --global --copy --yes" +} + +@test "main: skips a local skill when its directory is missing" { + _run_main "AI_SKILL_TARGETS=('claude-code') && AI_SKILLS=('local|react')" + + assert_success + assert_line "warn: [ai-skills] local skill 'react' not found at $AI_LOCAL_SKILLS_DIR/react; skipping." + [ ! -s "$NPX_ARGS_FILE" ] +} + +@test "main: installs remote skills around a missing local skill" { + _run_main "AI_SKILL_TARGETS=('claude-code') && AI_SKILLS=('mattpocock/skills|grill-me' 'local|missing' 'blader/humanizer|humanizer')" + + assert_success + assert_line "warn: [ai-skills] local skill 'missing' not found at $AI_LOCAL_SKILLS_DIR/missing; skipping." + assert_line "--yes skills add mattpocock/skills --skill grill-me -a claude-code --global --copy --yes" + assert_line "--yes skills add blader/humanizer --skill humanizer -a claude-code --global --copy --yes" + [ "$(wc -l <"$NPX_ARGS_FILE")" -eq 2 ] +} + +@test "main: exits cleanly with no skills" { + _run_main "AI_SKILL_TARGETS=('claude-code') && AI_SKILLS=()" + + assert_success + assert_line "[ai-skills] No skills to install." + [ ! -s "$NPX_ARGS_FILE" ] +} + +@test "main: exits cleanly with no targets" { + _run_main "AI_SKILL_TARGETS=() && AI_SKILLS=('mattpocock/skills|grill-me')" + + assert_success + assert_line "[ai-skills] No agent targets; nothing to install." + [ ! -s "$NPX_ARGS_FILE" ] +} + +@test "main: warns but succeeds when a skill fails" { + export NPX_EXIT_CODE=1 + _run_main "AI_SKILL_TARGETS=('claude-code') && AI_SKILLS=('mattpocock/skills|grill-me')" + + assert_success + assert_line "warn: [ai-skills] 1 skill(s) failed to install:" + assert_line " - grill-me" + assert_line "[ai-skills] Cross-agent skills installed." +} + +@test "main: reports only the skills that failed" { + cat >"$BATS_TEST_TMPDIR/bin/npx" <<'NPX' +#!/usr/bin/env bash +printf '%s\n' "$*" | tee -a "$NPX_ARGS_FILE" +[[ "$*" == *"blader/humanizer"* ]] && exit 1 +exit 0 +NPX + chmod +x "$BATS_TEST_TMPDIR/bin/npx" + + _run_main "AI_SKILL_TARGETS=('claude-code') && AI_SKILLS=('mattpocock/skills|grill-me' 'blader/humanizer|humanizer')" + + assert_success + assert_line "warn: [ai-skills] 1 skill(s) failed to install:" + assert_line " - humanizer" + refute_line " - grill-me" +} + +@test "main: fails when npx is missing" { + rm -f "$BATS_TEST_TMPDIR/bin/npx" + # Drop /usr/bin so a host-installed npx cannot mask the missing-npx path; /bin still provides bash. + export PATH="$BATS_TEST_TMPDIR/bin:/bin" + _run_main "AI_SKILL_TARGETS=('claude-code') && AI_SKILLS=('mattpocock/skills|grill-me')" + + assert_failure 1 + assert_line "error: [ai-skills] npx not found. Ensure Node.js is installed (run_onchange_03_install-mise-tools)." +} From c3d249d032ef2ddc7b64686a75743ba667e23d78 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 23:02:12 -0500 Subject: [PATCH 31/51] docs: sync spec and plan with ai-skills install note npx --yes, content-hash trigger, and execution deltas (DOT-43) --- ...26-06-30-apm-migration-phase-1-skills-install.md | 13 +++++++++++++ ...30-apm-to-claude-marketplace-migration-design.md | 6 ++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/docs/superpowers/plans/2026-06-30-apm-migration-phase-1-skills-install.md b/docs/superpowers/plans/2026-06-30-apm-migration-phase-1-skills-install.md index 2f33ac8..8f7950d 100644 --- a/docs/superpowers/plans/2026-06-30-apm-migration-phase-1-skills-install.md +++ b/docs/superpowers/plans/2026-06-30-apm-migration-phase-1-skills-install.md @@ -386,6 +386,19 @@ git commit -m "test: DOT-43 add ai-skills template render tests" - **Type consistency:** `AI_SKILLS`, `AI_SKILL_TARGETS`, `AI_LOCAL_SKILLS_DIR`, `ai_skills_install_main`, and the `|` contract are identical across the library, the run script, and the tests. - **Out of Phase 1 scope:** plugins install, MCP delivery, instructions, and apm teardown are later phases. Domain-skill authoring and Copilot commit wiring are already done ahead of this plan. +## Execution Deltas + +Corrections applied during implementation, each verified against the codebase before coding: + +- **npx check is inline, not `require_command`.** `require_command npx "..."` emits `error: [npx] not found. ...`, but the `[ai-skills]` component-tag convention and Task 1's own test need `error: [ai-skills] npx not found. ...`. The library uses `command -v npx || { log_error "[ai-skills] npx not found. ..."; return 1; }`. +- **The render test helper is `render_chezmoi_template "$ABS_TEMPLATE" "$JSON_DATA"`**, not `render_darwin_script "basename" "personal"`; group is selected with the `$DARWIN_DATA` / `$WORK_DATA` payload constants. +- **The npx test stub tees to both stdout and the args file** (`printf '%s\n' "$*" | tee -a "$NPX_ARGS_FILE"`) so `assert_line` sees the invocation while the count checks still read the file. +- **The command carries a leading `npx --yes`** (npx's own auto-confirm) to keep non-interactive apply from hanging. +- **The trigger comment hashes the local skill tree** in addition to `ai.yaml`, so an in-place `SKILL.md` edit re-runs the `--copy` install. +- **Coverage beyond the plan's two render tests:** added local+remote mix, partial-failure, cartesian target-by-skill, empty-entry identity, and export-line assertions; the existing darwin glob tests already cover shebang and `bash -n` validity of the new script. + +Known limitations, shared with the graphify sibling and out of scope here: a missing npx hard-fails `chezmoi apply` on first boot before mise shims load, and removing a skill from `ai.yaml` does not prune the previously installed copy. + ## Remaining Phases (separate plans) 1. This plan — skills install. diff --git a/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md b/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md index 871f774..dbc75fd 100644 --- a/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md +++ b/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md @@ -96,14 +96,16 @@ Personal machines install shared plus personal entries against `claude-code`; wo Skills install with the cross-agent CLI. For each entry the install script runs: ```bash -npx skills add --skill -a [-a ] --global --copy --yes +npx --yes skills add --skill -a [-a ] --global --copy --yes ``` +The `npx --yes` prefix auto-confirms npx's own install prompt so the non-interactive `chezmoi apply` never hangs. + `--global` installs into `~/.claude/skills/` and `~/.copilot/skills/`. `--copy` writes real files rather than symlinks because Claude Code ignores symlinked skill directories. The `-a` flags come from the active group's `target` in `ai.yaml`, so a skill in `shared` installs for the machine's target, and a skill in `personal` installs for `claude-code`. Local domain skills `typescript`, `react`, and `testing` (React Testing Library guidance lives in the `testing` skill) are authored under `home/.chezmoitemplates/skills/` from the former instruction files, and install with `source: local` pointing at that path. The previously vendored `grill-me` and `junior-to-senior` copies under that directory have been removed; both install from their upstream sources. -The `skills` CLI has no manifest file, so `home/.chezmoidata/ai.yaml` is the manifest. A `run_onchange_09_install-ai-skills.sh.tmpl` script gates on `darwin`, reads the active group and its target, iterates the matching skill entries, and runs the CLI. It re-runs when the rendered data changes. +The `skills` CLI has no manifest file, so `home/.chezmoidata/ai.yaml` is the manifest. A `run_onchange_09_install-ai-skills.sh.tmpl` script gates on `darwin`, reads the active group and its target, iterates the matching skill entries, and runs the CLI. Its trigger comment hashes both `ai.yaml` and the local skill tree under `home/.chezmoitemplates/skills/`, so it re-runs when the manifest changes or when a local `SKILL.md` is edited in place (local skills install with `--copy`, so a stale copy would otherwise persist). ## Plugins Design From 0024a2e6763d4e839562fd9b01ceb70487a63bd5 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 23:11:11 -0500 Subject: [PATCH 32/51] chore: remove deprecated chat instructions file locations --- home/Library/Application Support/Code/User/settings.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/home/Library/Application Support/Code/User/settings.json b/home/Library/Application Support/Code/User/settings.json index 46bd893..d2ac55e 100644 --- a/home/Library/Application Support/Code/User/settings.json +++ b/home/Library/Application Support/Code/User/settings.json @@ -48,10 +48,6 @@ "github.copilot.editor.enableCodeActions": true, "github.copilot.nextEditSuggestions.enabled": true, "github.copilot.renameSuggestions.triggerAutomatically": true, - "chat.instructionsFilesLocations": { - ".github/instructions": true, - "/Users/vscode/repos/instructions": true - }, "github.copilot.chat.commitMessageGeneration.instructions": [ { "text": "Use Conventional Commits format `: ` with a type such as feat, fix, refactor, docs, test, chore, perf, build, style, or ci." From 989c07823a6432810c9feb328e201c804522933f Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 23:11:49 -0500 Subject: [PATCH 33/51] feat: enable superwhisper plugin in settings --- home/dot_claude/settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/home/dot_claude/settings.json b/home/dot_claude/settings.json index 00d0b1b..ac4625e 100644 --- a/home/dot_claude/settings.json +++ b/home/dot_claude/settings.json @@ -172,7 +172,7 @@ "padding": 0 }, "enabledPlugins": { - "superwhisper@superwhisper": false + "superwhisper@superwhisper": true }, "extraKnownMarketplaces": { "superwhisper": { From b471f48f6476ae751fead9fa1ff21cb0b08d4475 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 23:13:24 -0500 Subject: [PATCH 34/51] docs: add phase-2 plugins install plan dual-path install (claude plugin CLI + copilot skills) and settings declaration (DOT-43) --- ...0-apm-migration-phase-2-plugins-install.md | 223 ++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 docs/superpowers/plans/2026-06-30-apm-migration-phase-2-plugins-install.md diff --git a/docs/superpowers/plans/2026-06-30-apm-migration-phase-2-plugins-install.md b/docs/superpowers/plans/2026-06-30-apm-migration-phase-2-plugins-install.md new file mode 100644 index 0000000..08fd274 --- /dev/null +++ b/docs/superpowers/plans/2026-06-30-apm-migration-phase-2-plugins-install.md @@ -0,0 +1,223 @@ +# Claude Plugins Install (Phase 2) Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Install the hook-bearing plugins declared in `home/.chezmoidata/ai.yaml` (`caveman`, `superpowers`) for the active machine's agent target, replacing the retired apm install path. + +**Architecture:** Mirror the `ai-skills` Phase-1 pattern (sourceable bash library + `run_onchange` chezmoi script + bats). The difference is a dual install path branched on the active target: `claude-code` installs through the Claude marketplace CLI (`claude plugin marketplace add` then `claude plugin install @`); `github-copilot` installs the plugin source as a skill (`npx --yes skills add -a github-copilot --global --copy --yes`), because Copilot has no plugin or hook runtime. The Claude marketplaces and enabled plugins are also declared in the plain `home/dot_claude/settings.json`. + +**Tech Stack:** chezmoi templates, bash, the `claude` CLI (`claude plugin ...`), the `skills` CLI (`npx skills`), bats. + +**Spec:** `docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md` (sections: Plugins Design, Group and Target Data Model). + +**Scope note:** This is Phase 2 of 7. It delivers plugins install on its own. The `06` run-script slot is free (the apm script was removed by prior cleanup). Phase 2 does not touch MCP, instructions, or the remaining apm teardown. + +--- + +## Pre-Implementation Verification + +Before writing the library, confirm these against the real environment (a `verify` workflow or direct read), because the reference code below assumes them: + +- **`claude plugin marketplace list` output format** — how a configured marketplace is named/printed, so the idempotency check can grep for it. Confirm whether `marketplace add ` is already idempotent (re-add is a no-op) — if so, the pre-check can be dropped. +- **`claude plugin list` output format** — how an installed plugin appears (`@`?), for the install idempotency check. +- **`claude plugin install` exit behavior** on an already-installed plugin (0 vs non-zero), and whether it prompts (needs a non-interactive flag). +- **`claude` presence during `chezmoi apply`** — same first-boot PATH caveat as `npx` in Phase 1 (mise shims). Match the Phase-1 decision: hard-fail if the required CLI is absent. + +## File Structure + +- Create: `home/.chezmoitemplates/lib/install/claude-plugins.sh` — sourceable install library; branches per target and installs each plugin. +- Create: `home/.chezmoiscripts/darwin/run_onchange_06_install-claude-plugins.sh.tmpl` — chezmoi run script; resolves active targets and plugins from `ai.yaml`, renders bash arrays, sources the library. +- Modify: `home/dot_claude/settings.json` — add `caveman` and `superpowers-dev` to `extraKnownMarketplaces` and enable `caveman@caveman` and `superpowers@superpowers-dev` in `enabledPlugins`. +- Create: `tests/unit/lib/install/claude-plugins.bats` — behavior tests, stubbing `claude` and `npx`. +- Modify: `tests/template/darwin-install-scripts.bats` — add render tests for `run_onchange_06`. + +## Data Contract + +The run script passes the library: + +- `AI_PLUGIN_TARGETS` — bash array of agent ids, e.g. `("claude-code")`. From `active-group-values` of `.ai.targets`. +- `AI_PLUGINS` — bash array of `"||"` strings, e.g. `("caveman|caveman|JuliusBrussee/caveman" "superpowers|superpowers-dev|obra/superpowers")`. From `active-group-values` of `.ai.plugins`. + +The `|` delimiter is safe: plugin names, marketplace names, and sources (`owner/repo`) contain no `|`. + +--- + +### Task 1: Install library + +**Files:** Create `home/.chezmoitemplates/lib/install/claude-plugins.sh`; Test `tests/unit/lib/install/claude-plugins.bats`. + +- [ ] **Step 1: Write the failing test file** + +Create `tests/unit/lib/install/claude-plugins.bats`, mirroring `ai-skills.bats`: stub both `claude` and `npx` into `$BATS_TEST_TMPDIR/bin` (each `printf '%s\n' "$*" | tee -a` its own args file), restrict PATH, source `log.sh` + `install-prelude.sh` + the lib, then run `main`. Cases: + +```bash +@test "claude-code target: adds marketplace and installs each plugin" { + _run_main "AI_PLUGIN_TARGETS=('claude-code') && AI_PLUGINS=('caveman|caveman|JuliusBrussee/caveman' 'superpowers|superpowers-dev|obra/superpowers')" + assert_success + assert_line --partial "plugin marketplace add JuliusBrussee/caveman" + assert_line --partial "plugin install caveman@caveman" + assert_line --partial "plugin marketplace add obra/superpowers" + assert_line --partial "plugin install superpowers@superpowers-dev" +} + +@test "github-copilot target: installs plugin source as a skill" { + _run_main "AI_PLUGIN_TARGETS=('github-copilot') && AI_PLUGINS=('caveman|caveman|JuliusBrussee/caveman')" + assert_success + assert_line "--yes skills add JuliusBrussee/caveman -a github-copilot --global --copy --yes" + [ ! -s "$CLAUDE_ARGS_FILE" ] +} + +@test "exits cleanly with no plugins" { ... assert_line "[claude-plugins] No plugins to install." ; } +@test "exits cleanly with no targets" { ... assert_line "[claude-plugins] No agent targets; nothing to install." ; } +@test "skips empty plugin entries" { ... count == expected ; } +@test "warns but succeeds when a plugin fails" { ... export CLAUDE_EXIT_CODE=1 ... assert_line "warn: [claude-plugins] ..." ; assert_success ; } +@test "fails when the claude CLI is missing for a claude-code target" { rm claude stub; assert_failure 1; assert_line "error: [claude-plugins] claude CLI not found. ..." ; } +@test "fails when npx is missing for a github-copilot target" { rm npx stub; assert_failure 1; assert_line "error: [claude-plugins] npx not found. ..." ; } +``` + +Fill each case with full bodies during implementation, matching the `ai-skills.bats` style (exact `assert_line`, PATH-shim stubs, `refute_line` where appropriate). Add idempotency cases once the Pre-Implementation Verification pins the `claude plugin list` / `marketplace list` formats. + +- [ ] **Step 2: Run the tests to verify they fail** (`bats tests/unit/lib/install/claude-plugins.bats` — lib missing). + +- [ ] **Step 3: Write the install library** + +Create `home/.chezmoitemplates/lib/install/claude-plugins.sh`, mirroring `ai-skills.sh` (shebang, `set -Eeuo pipefail`, shdoc header, `claude_plugins_install_main` + `main` + the `BASH_SOURCE` self-exec guard sourcing `log.sh`). Reference body: + +```bash +function claude_plugins_install_main() { + # return early with a log_info if AI_PLUGINS has no non-empty entry, or AI_PLUGIN_TARGETS is empty. + local failed=() target entry name marketplace source rest + for target in "${AI_PLUGIN_TARGETS[@]-}"; do + [[ -z "${target}" ]] && continue + case "${target}" in + claude-code) + command -v claude >/dev/null 2>&1 || { log_error "[claude-plugins] claude CLI not found. Ensure Claude Code is installed."; return 1; } + for entry in "${AI_PLUGINS[@]-}"; do + [[ -z "${entry}" ]] && continue + name="${entry%%|*}"; rest="${entry#*|}"; marketplace="${rest%%|*}"; source="${rest#*|}" + claude plugin marketplace add "${source}" || failed+=("${name} (marketplace)") + claude plugin install "${name}@${marketplace}" || failed+=("${name}") + done + ;; + github-copilot) + command -v npx >/dev/null 2>&1 || { log_error "[claude-plugins] npx not found. Ensure Node.js is installed (run_onchange_03_install-mise-tools)."; return 1; } + for entry in "${AI_PLUGINS[@]-}"; do + [[ -z "${entry}" ]] && continue + name="${entry%%|*}"; rest="${entry#*|}"; source="${rest#*|}" + npx --yes skills add "${source}" -a github-copilot --global --copy --yes || failed+=("${name}") + done + ;; + *) log_warn "[claude-plugins] unknown target '${target}'; skipping." ;; + esac + done + # warn (not fail) on collected failures, then log_info "[claude-plugins] Plugins installed." +} +``` + +Refine the marketplace/install calls with the idempotency guards confirmed in verification (e.g. skip `marketplace add` when `claude plugin marketplace list` already lists it). Rename the loop var away from the `source` builtin (`src`), matching the ai-skills fix. + +- [ ] **Step 4: Run the tests to verify they pass.** +- [ ] **Step 5: `shellcheck home/.chezmoitemplates/lib/install/claude-plugins.sh`.** +- [ ] **Step 6: Commit** `feat: add claude-plugins install library` (DOT-43). + +--- + +### Task 2: run script + settings.json declaration + +**Files:** Create `home/.chezmoiscripts/darwin/run_onchange_06_install-claude-plugins.sh.tmpl`; Modify `home/dot_claude/settings.json`. + +- [ ] **Step 1: Write the run script**, mirroring `run_onchange_09`: + +``` +#!/usr/bin/env bash +# @file run_onchange_06_install-claude-plugins.sh +# @brief Install cross-agent AI plugins. +# @description AI data hash: {{ include ".chezmoidata/ai.yaml" | sha256sum }} +{{- $activeTargets := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .ai.targets) | fromJson -}} +{{- $activePlugins := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .ai.plugins) | fromJson -}} + +{{ if and (eq .chezmoi.os "darwin") (gt (len $activeTargets) 0) (gt (len $activePlugins) 0) }} +{{ template "lib/common/log.sh" . }} +{{ template "lib/common/install-prelude.sh" . }} +AI_PLUGIN_TARGETS=( +{{ range $activeTargets -}} + "{{ . }}" +{{ end -}} +) +AI_PLUGINS=( +{{ range $activePlugins -}} + "{{ .name }}|{{ .marketplace }}|{{ .source }}" +{{ end -}} +) +{{ template "lib/install/claude-plugins.sh" . }} +{{ end }} +``` + +No local-content hash line is needed (plugins install from external sources, not repo-local files) — unlike ai-skills. + +- [ ] **Step 2: Verify the render** for personal (`claude-code`, both plugins) and work (`github-copilot`, both plugins) via `chezmoi execute-template --source home --override-data ...`. + +- [ ] **Step 3: Declare marketplaces and enabled plugins in `home/dot_claude/settings.json`** (plain JSON, not templated): + +```json +"enabledPlugins": { + "superwhisper@superwhisper": false, + "caveman@caveman": true, + "superpowers@superpowers-dev": true +}, +"extraKnownMarketplaces": { + "superwhisper": { "source": { "source": "github", "repo": "superultrainc/superwhisper-claude-code" } }, + "caveman": { "source": { "source": "github", "repo": "JuliusBrussee/caveman" } }, + "superpowers-dev": { "source": { "source": "github", "repo": "obra/superpowers" } } +} +``` + +- [ ] **Step 4: Commit** `feat: add claude-plugins run_onchange script and settings` (DOT-43). + +--- + +### Task 3: Template render tests + +**Files:** Modify `tests/template/darwin-install-scripts.bats`. + +- [ ] **Step 1:** Add `PLUGINS_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_06_install-claude-plugins.sh.tmpl"`, an inject-lib assertion (`{{ template "lib/install/claude-plugins.sh" . }}`), a prelude-injection assertion, and render tests: + +```bash +@test "personal plugins template targets claude-code and includes both plugins" { + run render_chezmoi_template "$PLUGINS_TEMPLATE" "$DARWIN_DATA" + assert_success + assert_line 'AI_PLUGIN_TARGETS=(' + assert_line --partial '"claude-code"' + assert_line --partial '"caveman|caveman|JuliusBrussee/caveman"' + assert_line --partial '"superpowers|superpowers-dev|obra/superpowers"' + assert_line --partial 'claude_plugins_install_main' +} + +@test "work plugins template targets github-copilot" { + run render_chezmoi_template "$PLUGINS_TEMPLATE" "$WORK_DATA" + assert_success + assert_line --partial '"github-copilot"' + refute_line --partial '"claude-code"' +} + +@test "empty AI target groups render no plugin commands" { + run render_chezmoi_template "$PLUGINS_TEMPLATE" "$EMPTY_AI_DATA" + assert_success + refute_line 'AI_PLUGIN_TARGETS=(' + refute_line --partial 'claude_plugins_install_main' +} +``` + +The existing glob tests (bash shebang, `bash -n` validity) auto-cover the new script. + +- [ ] **Step 2:** Run `bats tests/unit/lib/install/claude-plugins.bats tests/template/darwin-install-scripts.bats`; then `mise run check`. +- [ ] **Step 3: Commit** `test: add claude-plugins template render tests` (DOT-43). + +--- + +## Self-Review + +- **Spec coverage (Plugins Design):** dual path per target (Task 1), `run_onchange_06` replaces the apm script and gates darwin (Task 2), settings.json declares marketplaces + enabled plugins (Task 2). Covered. +- **Type consistency:** `AI_PLUGIN_TARGETS`, `AI_PLUGINS`, the `||` contract, and `claude_plugins_install_main` are identical across library, run script, and tests. +- **Open items handed to verification:** `claude plugin` idempotency and list-output formats; the `claude` CLI first-boot PATH caveat (match the Phase-1 hard-fail decision). +- **Out of Phase 2 scope:** MCP delivery, instructions wiring, and the apm dependency-layer teardown are later phases. From 44a14df812597b06b15505184a9103c8a4587144 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 30 Jun 2026 23:15:33 -0500 Subject: [PATCH 35/51] feat: add .chezmoiignore for GitHub Copilot config --- home/.chezmoiignore | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 home/.chezmoiignore diff --git a/home/.chezmoiignore b/home/.chezmoiignore new file mode 100644 index 0000000..6c4d4db --- /dev/null +++ b/home/.chezmoiignore @@ -0,0 +1,4 @@ +{{- /* GitHub Copilot config is a work-only target (see .chezmoidata/ai.yaml). */ -}} +{{- if not .work }} +.copilot +{{- end }} From ae561a2cb15fa968ef516a5dae6572375ee0799d Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Wed, 1 Jul 2026 00:52:32 -0500 Subject: [PATCH 36/51] feat: add ai-plugins install library decomposed dual-path installer (claude marketplace CLI + copilot skills) with bats coverage (DOT-43) --- ...0-apm-migration-phase-2-plugins-install.md | 271 ++++++++++++------ .../lib/install/ai-plugins.sh | 166 +++++++++++ tests/unit/lib/install/ai-plugins.bats | 154 ++++++++++ 3 files changed, 505 insertions(+), 86 deletions(-) create mode 100644 home/.chezmoitemplates/lib/install/ai-plugins.sh create mode 100644 tests/unit/lib/install/ai-plugins.bats diff --git a/docs/superpowers/plans/2026-06-30-apm-migration-phase-2-plugins-install.md b/docs/superpowers/plans/2026-06-30-apm-migration-phase-2-plugins-install.md index 08fd274..f939f87 100644 --- a/docs/superpowers/plans/2026-06-30-apm-migration-phase-2-plugins-install.md +++ b/docs/superpowers/plans/2026-06-30-apm-migration-phase-2-plugins-install.md @@ -1,136 +1,236 @@ -# Claude Plugins Install (Phase 2) Implementation Plan +# AI Plugins Install (Phase 2) Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Install the hook-bearing plugins declared in `home/.chezmoidata/ai.yaml` (`caveman`, `superpowers`) for the active machine's agent target, replacing the retired apm install path. -**Architecture:** Mirror the `ai-skills` Phase-1 pattern (sourceable bash library + `run_onchange` chezmoi script + bats). The difference is a dual install path branched on the active target: `claude-code` installs through the Claude marketplace CLI (`claude plugin marketplace add` then `claude plugin install @`); `github-copilot` installs the plugin source as a skill (`npx --yes skills add -a github-copilot --global --copy --yes`), because Copilot has no plugin or hook runtime. The Claude marketplaces and enabled plugins are also declared in the plain `home/dot_claude/settings.json`. +**Architecture:** Mirror the `ai-skills` Phase-1 pattern (sourceable bash library + `run_onchange` chezmoi script + bats). The library is decomposed into small single-responsibility functions (Kaizen): a predicate, per-plugin installers, per-target loops, a dispatcher, and a thin orchestrator. Install branches on the active target: `claude-code` installs through the Claude marketplace CLI (`claude plugin marketplace add ` then `claude plugin install @`); `github-copilot` installs the plugin source as a skill (`npx --yes skills add -a github-copilot --global --copy --yes`), because Copilot has no plugin or hook runtime. Marketplaces and enabled plugins are also declared in the plain `home/dot_claude/settings.json`. **Tech Stack:** chezmoi templates, bash, the `claude` CLI (`claude plugin ...`), the `skills` CLI (`npx skills`), bats. **Spec:** `docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md` (sections: Plugins Design, Group and Target Data Model). -**Scope note:** This is Phase 2 of 7. It delivers plugins install on its own. The `06` run-script slot is free (the apm script was removed by prior cleanup). Phase 2 does not touch MCP, instructions, or the remaining apm teardown. +**Naming:** `ai-plugins` (not `claude-plugins`) — agnostic, matching the `ai.plugins` data namespace and the `ai-skills` sibling, because plugins install to both agents. + +**Scope note:** Phase 2 of 7. Delivers plugins install on its own. The `06` run-script slot is free (the apm script was removed by prior cleanup). Phase 2 does not touch MCP, instructions, or apm teardown. --- -## Pre-Implementation Verification +## Verification findings (resolved) -Before writing the library, confirm these against the real environment (a `verify` workflow or direct read), because the reference code below assumes them: +Confirmed live against the `claude plugin` CLI (read-only): -- **`claude plugin marketplace list` output format** — how a configured marketplace is named/printed, so the idempotency check can grep for it. Confirm whether `marketplace add ` is already idempotent (re-add is a no-op) — if so, the pre-check can be dropped. -- **`claude plugin list` output format** — how an installed plugin appears (`@`?), for the install idempotency check. -- **`claude plugin install` exit behavior** on an already-installed plugin (0 vs non-zero), and whether it prompts (needs a non-interactive flag). -- **`claude` presence during `chezmoi apply`** — same first-boot PATH caveat as `npx` in Phase 1 (mise shims). Match the Phase-1 decision: hard-fail if the required CLI is absent. +- **Marketplace list format:** `❯ ` with a `Source: GitHub (owner/repo)` line under it. +- **Installed-plugin format:** `❯ @` with `Status: ✔ enabled|✘ disabled`; skills-directory plugins list separately as `@skills-dir`. +- **`caveman` already exists as `caveman@skills-dir`** (`~/.claude/skills/caveman`) from a prior run. Installing `caveman@caveman` (marketplace) adds a second copy. **Validate on real apply**; may warrant pruning the skills-dir copy in a later phase. +- **`claude plugin install` has no non-interactive flag** (`--config`, `--scope` only). A first-time trust prompt could block `chezmoi apply`. **Validate on real apply.** +- **Idempotency of `marketplace add` / re-`install` is untested** (testing mutates the machine). Decision: no idempotency guards — match `ai-skills`, collect per-plugin failures and warn (graceful, non-fatal). Add guards later only if re-run noise is observed (Kaizen JIT; respects the no-defensive-programming rule). ## File Structure -- Create: `home/.chezmoitemplates/lib/install/claude-plugins.sh` — sourceable install library; branches per target and installs each plugin. -- Create: `home/.chezmoiscripts/darwin/run_onchange_06_install-claude-plugins.sh.tmpl` — chezmoi run script; resolves active targets and plugins from `ai.yaml`, renders bash arrays, sources the library. -- Modify: `home/dot_claude/settings.json` — add `caveman` and `superpowers-dev` to `extraKnownMarketplaces` and enable `caveman@caveman` and `superpowers@superpowers-dev` in `enabledPlugins`. -- Create: `tests/unit/lib/install/claude-plugins.bats` — behavior tests, stubbing `claude` and `npx`. -- Modify: `tests/template/darwin-install-scripts.bats` — add render tests for `run_onchange_06`. +- Create: `home/.chezmoitemplates/lib/install/ai-plugins.sh` — sourceable install library; small functions, branches per target. +- Create: `home/.chezmoiscripts/darwin/run_onchange_06_install-ai-plugins.sh.tmpl` — chezmoi run script; resolves active targets and plugins, renders bash arrays, sources the library. +- Modify: `home/dot_claude/settings.json` — declare `caveman` + `superpowers-dev` marketplaces and enable `caveman@caveman` + `superpowers@superpowers-dev` (SuperWhisper stays `true`). +- Create: `tests/unit/lib/install/ai-plugins.bats` — behavior tests stubbing `claude` and `npx`. +- Modify: `tests/template/darwin-install-scripts.bats` — render tests for `run_onchange_06`. ## Data Contract -The run script passes the library: +The run script passes the library two arrays: -- `AI_PLUGIN_TARGETS` — bash array of agent ids, e.g. `("claude-code")`. From `active-group-values` of `.ai.targets`. -- `AI_PLUGINS` — bash array of `"||"` strings, e.g. `("caveman|caveman|JuliusBrussee/caveman" "superpowers|superpowers-dev|obra/superpowers")`. From `active-group-values` of `.ai.plugins`. +- `AI_PLUGIN_TARGETS` — agent ids, e.g. `("claude-code")`. From `active-group-values` of `.ai.targets`. +- `AI_PLUGINS` — `"||"` strings, e.g. `("caveman|caveman|JuliusBrussee/caveman" "superpowers|superpowers-dev|obra/superpowers")`. From `active-group-values` of `.ai.plugins`. -The `|` delimiter is safe: plugin names, marketplace names, and sources (`owner/repo`) contain no `|`. +The `|` delimiter is safe: names, marketplace names, and `owner/repo` sources contain no `|`. --- -### Task 1: Install library - -**Files:** Create `home/.chezmoitemplates/lib/install/claude-plugins.sh`; Test `tests/unit/lib/install/claude-plugins.bats`. +### Task 1: Install library (decomposed) -- [ ] **Step 1: Write the failing test file** +**Files:** Create `home/.chezmoitemplates/lib/install/ai-plugins.sh`; Test `tests/unit/lib/install/ai-plugins.bats`. -Create `tests/unit/lib/install/claude-plugins.bats`, mirroring `ai-skills.bats`: stub both `claude` and `npx` into `$BATS_TEST_TMPDIR/bin` (each `printf '%s\n' "$*" | tee -a` its own args file), restrict PATH, source `log.sh` + `install-prelude.sh` + the lib, then run `main`. Cases: +- [ ] **Step 1: Write the failing test file** — mirror `ai-skills.bats`. `setup()` stubs both `claude` and `npx` into `$BATS_TEST_TMPDIR/bin` (each `printf '%s\n' "$*" | tee -a` its own args file, then `exit "${_EXIT_CODE:-0}"`), exports `PATH="$BATS_TEST_TMPDIR/bin:/usr/bin:/bin"`. Helper: `_run_main() { run bash -c "source '$LOG_LIB' && source '$LIB' && $1 && main"; }`. Cases (fill bodies in the `ai-skills.bats` style — exact `assert_line`, `refute_line`, count asserts): ```bash -@test "claude-code target: adds marketplace and installs each plugin" { +@test "claude-code target adds marketplace and installs each plugin" { _run_main "AI_PLUGIN_TARGETS=('claude-code') && AI_PLUGINS=('caveman|caveman|JuliusBrussee/caveman' 'superpowers|superpowers-dev|obra/superpowers')" assert_success - assert_line --partial "plugin marketplace add JuliusBrussee/caveman" - assert_line --partial "plugin install caveman@caveman" - assert_line --partial "plugin marketplace add obra/superpowers" - assert_line --partial "plugin install superpowers@superpowers-dev" + assert_line "plugin marketplace add JuliusBrussee/caveman" + assert_line "plugin install caveman@caveman" + assert_line "plugin marketplace add obra/superpowers" + assert_line "plugin install superpowers@superpowers-dev" + [ ! -s "$NPX_ARGS_FILE" ] } -@test "github-copilot target: installs plugin source as a skill" { +@test "github-copilot target installs plugin source as a skill" { _run_main "AI_PLUGIN_TARGETS=('github-copilot') && AI_PLUGINS=('caveman|caveman|JuliusBrussee/caveman')" assert_success assert_line "--yes skills add JuliusBrussee/caveman -a github-copilot --global --copy --yes" [ ! -s "$CLAUDE_ARGS_FILE" ] } -@test "exits cleanly with no plugins" { ... assert_line "[claude-plugins] No plugins to install." ; } -@test "exits cleanly with no targets" { ... assert_line "[claude-plugins] No agent targets; nothing to install." ; } -@test "skips empty plugin entries" { ... count == expected ; } -@test "warns but succeeds when a plugin fails" { ... export CLAUDE_EXIT_CODE=1 ... assert_line "warn: [claude-plugins] ..." ; assert_success ; } -@test "fails when the claude CLI is missing for a claude-code target" { rm claude stub; assert_failure 1; assert_line "error: [claude-plugins] claude CLI not found. ..." ; } -@test "fails when npx is missing for a github-copilot target" { rm npx stub; assert_failure 1; assert_line "error: [claude-plugins] npx not found. ..." ; } +@test "both targets install every plugin to each" { ... claude add+install for each AND npx skills add for each ... } +@test "exits cleanly with no plugins" { ... assert_line "[ai-plugins] No plugins to install." ; } +@test "exits cleanly with no targets" { ... assert_line "[ai-plugins] No agent targets; nothing to install." ; } +@test "skips empty plugin entries" { ... expected install count ; } +@test "skips empty target entries" { ... expected install count ; } +@test "warns but succeeds when a plugin fails" { CLAUDE_EXIT_CODE=1 ... assert_success ; assert_line "warn: [ai-plugins] 1 plugin(s) failed to install:" ; assert_line " - caveman" ; } +@test "unknown target warns and skips" { ... assert_line "warn: [ai-plugins] unknown target 'grok'; skipping." ; } +@test "fails when claude is missing for a claude-code target" { rm claude stub; PATH=".../bin:/bin"; assert_failure 1; assert_line "error: [ai-plugins] claude CLI not found. Ensure Claude Code is installed." ; } +@test "fails when npx is missing for a github-copilot target" { rm npx stub; PATH=".../bin:/bin"; assert_failure 1; assert_line "error: [ai-plugins] npx not found. Ensure Node.js is installed (run_onchange_03_install-mise-tools)." ; } ``` -Fill each case with full bodies during implementation, matching the `ai-skills.bats` style (exact `assert_line`, PATH-shim stubs, `refute_line` where appropriate). Add idempotency cases once the Pre-Implementation Verification pins the `claude plugin list` / `marketplace list` formats. +- [ ] **Step 2: Run the tests to verify they fail** (`bats tests/unit/lib/install/ai-plugins.bats` — lib missing). -- [ ] **Step 2: Run the tests to verify they fail** (`bats tests/unit/lib/install/claude-plugins.bats` — lib missing). +- [ ] **Step 3: Write the library** — small functions, each one responsibility. `set -Eeuo pipefail`; all array expansions use the `[@]-` default or a count guard (bash 3.2 safe); the per-plugin installers are always called in a `|| failed+=(...)` context so `set -e` is suppressed inside them. -- [ ] **Step 3: Write the install library** +```bash +#!/usr/bin/env bash +# @file lib/install/ai-plugins.sh +# @brief Install cross-agent AI plugins for the active agent targets. +# @description +# Installs each plugin in AI_PLUGINS for every agent in AI_PLUGIN_TARGETS. +# Claude Code plugins install through the marketplace CLI; GitHub Copilot has +# no plugin runtime, so the plugin source installs as a skill instead. +# Sourceable from bats tests and injected into chezmoi run scripts. + +set -Eeuo pipefail + +# @description True when AI_PLUGINS holds at least one non-empty entry. +function _ai_plugins_have_any() { + local entry + for entry in "${AI_PLUGINS[@]-}"; do + [[ -z "${entry}" ]] && continue + return 0 + done + return 1 +} -Create `home/.chezmoitemplates/lib/install/claude-plugins.sh`, mirroring `ai-skills.sh` (shebang, `set -Eeuo pipefail`, shdoc header, `claude_plugins_install_main` + `main` + the `BASH_SOURCE` self-exec guard sourcing `log.sh`). Reference body: +# @description Warn about the plugins that failed to install. +# @arg $@ string Names of the plugins that failed. +function _ai_plugins_report_failures() { + log_warn "[ai-plugins] ${#} plugin(s) failed to install:" + printf ' - %s\n' "$@" >&2 +} -```bash -function claude_plugins_install_main() { - # return early with a log_info if AI_PLUGINS has no non-empty entry, or AI_PLUGIN_TARGETS is empty. - local failed=() target entry name marketplace source rest +# @description Add one plugin's marketplace and install it into Claude Code. +# @arg $1 string Plugin name. @arg $2 string Marketplace. @arg $3 string Source repo. +function _ai_plugins_add_to_claude() { + local name="$1" marketplace="$2" src="$3" + claude plugin marketplace add "${src}" && + claude plugin install "${name}@${marketplace}" +} + +# @description Install one plugin source into GitHub Copilot as a skill. +# @arg $1 string Source repo. +function _ai_plugins_add_to_copilot() { + npx --yes skills add "$1" -a github-copilot --global --copy --yes +} + +# @description Install every plugin for the Claude Code target. +# @exitcode 1 The claude CLI is missing. +function _ai_plugins_for_claude_code() { + if ! command -v claude >/dev/null 2>&1; then + log_error "[ai-plugins] claude CLI not found. Ensure Claude Code is installed." + return 1 + fi + local -a failed=() + local entry name marketplace src rest + for entry in "${AI_PLUGINS[@]-}"; do + [[ -z "${entry}" ]] && continue + name="${entry%%|*}" + rest="${entry#*|}" + marketplace="${rest%%|*}" + src="${rest##*|}" + _ai_plugins_add_to_claude "${name}" "${marketplace}" "${src}" || failed+=("${name}") + done + if ((${#failed[@]} > 0)); then + _ai_plugins_report_failures "${failed[@]}" + fi +} + +# @description Install every plugin for the GitHub Copilot target. +# @exitcode 1 npx is missing. +function _ai_plugins_for_copilot() { + if ! command -v npx >/dev/null 2>&1; then + log_error "[ai-plugins] npx not found. Ensure Node.js is installed (run_onchange_03_install-mise-tools)." + return 1 + fi + local -a failed=() + local entry name src + for entry in "${AI_PLUGINS[@]-}"; do + [[ -z "${entry}" ]] && continue + name="${entry%%|*}" + src="${entry##*|}" + _ai_plugins_add_to_copilot "${src}" || failed+=("${name}") + done + if ((${#failed[@]} > 0)); then + _ai_plugins_report_failures "${failed[@]}" + fi +} + +# @description Route one agent target to its installer. +# @arg $1 string Agent target id. +function _ai_plugins_for_target() { + case "$1" in + claude-code) _ai_plugins_for_claude_code ;; + github-copilot) _ai_plugins_for_copilot ;; + *) + log_warn "[ai-plugins] unknown target '$1'; skipping." + return 0 + ;; + esac +} + +# @description Install the active plugins for each active agent target. +# @exitcode 0 Installed, or nothing to do. +# @exitcode 1 A required CLI is missing for a requested target. +function ai_plugins_install_main() { + if ! _ai_plugins_have_any; then + log_info "[ai-plugins] No plugins to install." + return 0 + fi + local -a targets=() + local target for target in "${AI_PLUGIN_TARGETS[@]-}"; do [[ -z "${target}" ]] && continue - case "${target}" in - claude-code) - command -v claude >/dev/null 2>&1 || { log_error "[claude-plugins] claude CLI not found. Ensure Claude Code is installed."; return 1; } - for entry in "${AI_PLUGINS[@]-}"; do - [[ -z "${entry}" ]] && continue - name="${entry%%|*}"; rest="${entry#*|}"; marketplace="${rest%%|*}"; source="${rest#*|}" - claude plugin marketplace add "${source}" || failed+=("${name} (marketplace)") - claude plugin install "${name}@${marketplace}" || failed+=("${name}") - done - ;; - github-copilot) - command -v npx >/dev/null 2>&1 || { log_error "[claude-plugins] npx not found. Ensure Node.js is installed (run_onchange_03_install-mise-tools)."; return 1; } - for entry in "${AI_PLUGINS[@]-}"; do - [[ -z "${entry}" ]] && continue - name="${entry%%|*}"; rest="${entry#*|}"; source="${rest#*|}" - npx --yes skills add "${source}" -a github-copilot --global --copy --yes || failed+=("${name}") - done - ;; - *) log_warn "[claude-plugins] unknown target '${target}'; skipping." ;; - esac + targets+=("${target}") + done + if ((${#targets[@]} == 0)); then + log_info "[ai-plugins] No agent targets; nothing to install." + return 0 + fi + log_info "[ai-plugins] Installing AI plugins..." + for target in "${targets[@]}"; do + _ai_plugins_for_target "${target}" || return 1 done - # warn (not fail) on collected failures, then log_info "[claude-plugins] Plugins installed." + log_info "[ai-plugins] AI plugins installed." } -``` -Refine the marketplace/install calls with the idempotency guards confirmed in verification (e.g. skip `marketplace add` when `claude plugin marketplace list` already lists it). Rename the loop var away from the `source` builtin (`src`), matching the ai-skills fix. +function main() { ai_plugins_install_main; } + +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + # shellcheck source=/dev/null + command -v log_info >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/log.sh" + main +fi +``` - [ ] **Step 4: Run the tests to verify they pass.** -- [ ] **Step 5: `shellcheck home/.chezmoitemplates/lib/install/claude-plugins.sh`.** -- [ ] **Step 6: Commit** `feat: add claude-plugins install library` (DOT-43). +- [ ] **Step 5: `shellcheck home/.chezmoitemplates/lib/install/ai-plugins.sh`.** +- [ ] **Step 6: Commit** `feat: add ai-plugins install library` (DOT-43). --- ### Task 2: run script + settings.json declaration -**Files:** Create `home/.chezmoiscripts/darwin/run_onchange_06_install-claude-plugins.sh.tmpl`; Modify `home/dot_claude/settings.json`. +**Files:** Create `home/.chezmoiscripts/darwin/run_onchange_06_install-ai-plugins.sh.tmpl`; Modify `home/dot_claude/settings.json`. -- [ ] **Step 1: Write the run script**, mirroring `run_onchange_09`: +- [ ] **Step 1: Write the run script** (mirror `run_onchange_09`; no local-content hash — plugins come from external sources): ``` #!/usr/bin/env bash -# @file run_onchange_06_install-claude-plugins.sh +# @file run_onchange_06_install-ai-plugins.sh # @brief Install cross-agent AI plugins. # @description AI data hash: {{ include ".chezmoidata/ai.yaml" | sha256sum }} {{- $activeTargets := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .ai.targets) | fromJson -}} @@ -149,19 +249,17 @@ AI_PLUGINS=( "{{ .name }}|{{ .marketplace }}|{{ .source }}" {{ end -}} ) -{{ template "lib/install/claude-plugins.sh" . }} +{{ template "lib/install/ai-plugins.sh" . }} {{ end }} ``` -No local-content hash line is needed (plugins install from external sources, not repo-local files) — unlike ai-skills. - - [ ] **Step 2: Verify the render** for personal (`claude-code`, both plugins) and work (`github-copilot`, both plugins) via `chezmoi execute-template --source home --override-data ...`. -- [ ] **Step 3: Declare marketplaces and enabled plugins in `home/dot_claude/settings.json`** (plain JSON, not templated): +- [ ] **Step 3: Declare marketplaces and enabled plugins in `home/dot_claude/settings.json`** (plain JSON): ```json "enabledPlugins": { - "superwhisper@superwhisper": false, + "superwhisper@superwhisper": true, "caveman@caveman": true, "superpowers@superpowers-dev": true }, @@ -172,7 +270,7 @@ No local-content hash line is needed (plugins install from external sources, not } ``` -- [ ] **Step 4: Commit** `feat: add claude-plugins run_onchange script and settings` (DOT-43). +- [ ] **Step 4: Commit** `feat: add ai-plugins run_onchange script and settings` (DOT-43). --- @@ -180,7 +278,7 @@ No local-content hash line is needed (plugins install from external sources, not **Files:** Modify `tests/template/darwin-install-scripts.bats`. -- [ ] **Step 1:** Add `PLUGINS_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_06_install-claude-plugins.sh.tmpl"`, an inject-lib assertion (`{{ template "lib/install/claude-plugins.sh" . }}`), a prelude-injection assertion, and render tests: +- [ ] **Step 1:** Add `PLUGINS_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_06_install-ai-plugins.sh.tmpl"`, an inject-lib assertion (`{{ template "lib/install/ai-plugins.sh" . }}`), a prelude-injection assertion, and render tests: ```bash @test "personal plugins template targets claude-code and includes both plugins" { @@ -190,7 +288,7 @@ No local-content hash line is needed (plugins install from external sources, not assert_line --partial '"claude-code"' assert_line --partial '"caveman|caveman|JuliusBrussee/caveman"' assert_line --partial '"superpowers|superpowers-dev|obra/superpowers"' - assert_line --partial 'claude_plugins_install_main' + assert_line --partial 'ai_plugins_install_main' } @test "work plugins template targets github-copilot" { @@ -204,20 +302,21 @@ No local-content hash line is needed (plugins install from external sources, not run render_chezmoi_template "$PLUGINS_TEMPLATE" "$EMPTY_AI_DATA" assert_success refute_line 'AI_PLUGIN_TARGETS=(' - refute_line --partial 'claude_plugins_install_main' + refute_line --partial 'ai_plugins_install_main' } ``` -The existing glob tests (bash shebang, `bash -n` validity) auto-cover the new script. +The existing glob tests (bash shebang, `bash -n`) auto-cover the new script. -- [ ] **Step 2:** Run `bats tests/unit/lib/install/claude-plugins.bats tests/template/darwin-install-scripts.bats`; then `mise run check`. -- [ ] **Step 3: Commit** `test: add claude-plugins template render tests` (DOT-43). +- [ ] **Step 2:** Run `bats tests/unit/lib/install/ai-plugins.bats tests/template/darwin-install-scripts.bats`; then `mise run check`. +- [ ] **Step 3: Commit** `test: add ai-plugins template render tests` (DOT-43). --- ## Self-Review - **Spec coverage (Plugins Design):** dual path per target (Task 1), `run_onchange_06` replaces the apm script and gates darwin (Task 2), settings.json declares marketplaces + enabled plugins (Task 2). Covered. -- **Type consistency:** `AI_PLUGIN_TARGETS`, `AI_PLUGINS`, the `||` contract, and `claude_plugins_install_main` are identical across library, run script, and tests. -- **Open items handed to verification:** `claude plugin` idempotency and list-output formats; the `claude` CLI first-boot PATH caveat (match the Phase-1 hard-fail decision). -- **Out of Phase 2 scope:** MCP delivery, instructions wiring, and the apm dependency-layer teardown are later phases. +- **Naming/type consistency:** `ai-plugins`, `AI_PLUGIN_TARGETS`, `AI_PLUGINS`, `||`, `ai_plugins_install_main` identical across library, run script, and tests. +- **Kaizen decomposition:** predicate, per-plugin installers, per-target loops, dispatcher, orchestrator — each one responsibility, `set -Eeuo` and bash-3.2 safe. +- **Known risks for real-apply validation:** `claude plugin install` interactivity/hang; `caveman@skills-dir` duplicate; `marketplace add` re-run idempotency. All degrade to a warned failure, not data loss. +- **Out of Phase 2 scope:** MCP delivery, instructions wiring, apm teardown. diff --git a/home/.chezmoitemplates/lib/install/ai-plugins.sh b/home/.chezmoitemplates/lib/install/ai-plugins.sh new file mode 100644 index 0000000..846f2cd --- /dev/null +++ b/home/.chezmoitemplates/lib/install/ai-plugins.sh @@ -0,0 +1,166 @@ +#!/usr/bin/env bash +# @file lib/install/ai-plugins.sh +# @brief Install cross-agent AI plugins for the active agent targets. +# @description +# Installs each plugin in AI_PLUGINS for every agent in AI_PLUGIN_TARGETS. +# Claude Code plugins install through the marketplace CLI; GitHub Copilot has +# no plugin runtime, so the plugin source installs as a skill instead. +# Sourceable from bats tests and injected into chezmoi run scripts via +# chezmoi template rendering. + +set -Eeuo pipefail + +# +# @description True when AI_PLUGINS holds at least one non-empty entry. +# @exitcode 0 A plugin is present. +# @exitcode 1 No plugins. +# +function _ai_plugins_have_any() { + local entry + for entry in "${AI_PLUGINS[@]-}"; do + [[ -z "${entry}" ]] && continue + return 0 + done + return 1 +} + +# +# @description Warn about the plugins that failed to install. +# @arg $@ string Names of the plugins that failed. +# +function _ai_plugins_report_failures() { + log_warn "[ai-plugins] ${#} plugin(s) failed to install:" + printf ' - %s\n' "$@" >&2 +} + +# +# @description Add one plugin's marketplace and install it into Claude Code. +# @arg $1 string Plugin name. +# @arg $2 string Marketplace name. +# @arg $3 string Marketplace source repo. +# +function _ai_plugins_add_to_claude() { + local name="$1" marketplace="$2" src="$3" + claude plugin marketplace add "${src}" && + claude plugin install "${name}@${marketplace}" +} + +# +# @description Install one plugin source into GitHub Copilot as a skill. +# @arg $1 string Marketplace source repo. +# +function _ai_plugins_add_to_copilot() { + npx --yes skills add "$1" -a github-copilot --global --copy --yes +} + +# +# @description Install every plugin for the Claude Code target. +# @exitcode 0 Plugins processed (failures are warned, not fatal). +# @exitcode 1 The claude CLI is missing. +# +function _ai_plugins_for_claude_code() { + if ! command -v claude >/dev/null 2>&1; then + log_error "[ai-plugins] claude CLI not found. Ensure Claude Code is installed." + return 1 + fi + + local -a failed=() + local entry name marketplace src rest + for entry in "${AI_PLUGINS[@]-}"; do + [[ -z "${entry}" ]] && continue + name="${entry%%|*}" + rest="${entry#*|}" + marketplace="${rest%%|*}" + src="${rest##*|}" + _ai_plugins_add_to_claude "${name}" "${marketplace}" "${src}" || failed+=("${name}") + done + + if ((${#failed[@]} > 0)); then + _ai_plugins_report_failures "${failed[@]}" + fi +} + +# +# @description Install every plugin for the GitHub Copilot target. +# @exitcode 0 Plugins processed (failures are warned, not fatal). +# @exitcode 1 npx is missing. +# +function _ai_plugins_for_copilot() { + if ! command -v npx >/dev/null 2>&1; then + log_error "[ai-plugins] npx not found. Ensure Node.js is installed (run_onchange_03_install-mise-tools)." + return 1 + fi + + local -a failed=() + local entry name src + for entry in "${AI_PLUGINS[@]-}"; do + [[ -z "${entry}" ]] && continue + name="${entry%%|*}" + src="${entry##*|}" + _ai_plugins_add_to_copilot "${src}" || failed+=("${name}") + done + + if ((${#failed[@]} > 0)); then + _ai_plugins_report_failures "${failed[@]}" + fi +} + +# +# @description Route one agent target to its installer. +# @arg $1 string Agent target id. +# +function _ai_plugins_for_target() { + case "$1" in + claude-code) _ai_plugins_for_claude_code ;; + github-copilot) _ai_plugins_for_copilot ;; + *) + log_warn "[ai-plugins] unknown target '$1'; skipping." + return 0 + ;; + esac +} + +# +# @description Install the active plugins for each active agent target. +# @exitcode 0 Installed, or nothing to do. +# @exitcode 1 A required CLI is missing for a requested target. +# +function ai_plugins_install_main() { + if ! _ai_plugins_have_any; then + log_info "[ai-plugins] No plugins to install." + return 0 + fi + + local -a targets=() + local target + for target in "${AI_PLUGIN_TARGETS[@]-}"; do + [[ -z "${target}" ]] && continue + targets+=("${target}") + done + + if ((${#targets[@]} == 0)); then + log_info "[ai-plugins] No agent targets; nothing to install." + return 0 + fi + + log_info "[ai-plugins] Installing AI plugins..." + for target in "${targets[@]}"; do + _ai_plugins_for_target "${target}" || return 1 + done + log_info "[ai-plugins] AI plugins installed." +} + +# +# @description Run the AI plugins install flow. +# +function main() { + ai_plugins_install_main +} + +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + # When run directly, pull in the shared log library that chezmoi otherwise + # concatenates ahead of this file. + # shellcheck source=/dev/null + command -v log_info >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/log.sh" + main +fi diff --git a/tests/unit/lib/install/ai-plugins.bats b/tests/unit/lib/install/ai-plugins.bats new file mode 100644 index 0000000..ef4896f --- /dev/null +++ b/tests/unit/lib/install/ai-plugins.bats @@ -0,0 +1,154 @@ +#!/usr/bin/env bats +# @file tests/unit/lib/install/ai-plugins.bats +# @brief Behavior tests for home/.chezmoitemplates/lib/install/ai-plugins.sh. + +load '../../../test_helpers/load.bash' + +LOG_LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/common/log.sh" +PRELUDE="$DOTFILES_ROOT/home/.chezmoitemplates/lib/common/install-prelude.sh" +LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/install/ai-plugins.sh" + +setup() { + export CLAUDE_ARGS_FILE="$BATS_TEST_TMPDIR/claude-args" + export NPX_ARGS_FILE="$BATS_TEST_TMPDIR/npx-args" + mkdir -p "$BATS_TEST_TMPDIR/bin" + + cat >"$BATS_TEST_TMPDIR/bin/claude" <<'CLAUDE' +#!/usr/bin/env bash +printf '%s\n' "$*" | tee -a "$CLAUDE_ARGS_FILE" +exit "${CLAUDE_EXIT_CODE:-0}" +CLAUDE + chmod +x "$BATS_TEST_TMPDIR/bin/claude" + + cat >"$BATS_TEST_TMPDIR/bin/npx" <<'NPX' +#!/usr/bin/env bash +printf '%s\n' "$*" | tee -a "$NPX_ARGS_FILE" +exit "${NPX_EXIT_CODE:-0}" +NPX + chmod +x "$BATS_TEST_TMPDIR/bin/npx" + + export PATH="$BATS_TEST_TMPDIR/bin:/usr/bin:/bin" +} + +_run_main() { + run bash -c "source '$LOG_LIB' && source '$PRELUDE' && source '$LIB' && $1 && main" +} + +@test "main: claude-code target adds marketplace and installs each plugin" { + _run_main "AI_PLUGIN_TARGETS=('claude-code') && AI_PLUGINS=('caveman|caveman|JuliusBrussee/caveman' 'superpowers|superpowers-dev|obra/superpowers')" + + assert_success + assert_line "[ai-plugins] Installing AI plugins..." + assert_line "[ai-plugins] AI plugins installed." + assert_line "plugin marketplace add JuliusBrussee/caveman" + assert_line "plugin install caveman@caveman" + assert_line "plugin marketplace add obra/superpowers" + assert_line "plugin install superpowers@superpowers-dev" + [ ! -s "$NPX_ARGS_FILE" ] +} + +@test "main: github-copilot target installs a plugin source as a skill" { + _run_main "AI_PLUGIN_TARGETS=('github-copilot') && AI_PLUGINS=('caveman|caveman|JuliusBrussee/caveman')" + + assert_success + assert_line "--yes skills add JuliusBrussee/caveman -a github-copilot --global --copy --yes" + [ ! -s "$CLAUDE_ARGS_FILE" ] +} + +@test "main: installs every plugin to each active target" { + _run_main "AI_PLUGIN_TARGETS=('claude-code' 'github-copilot') && AI_PLUGINS=('caveman|caveman|JuliusBrussee/caveman')" + + assert_success + assert_line "plugin marketplace add JuliusBrussee/caveman" + assert_line "plugin install caveman@caveman" + assert_line "--yes skills add JuliusBrussee/caveman -a github-copilot --global --copy --yes" +} + +@test "main: exits cleanly with no plugins" { + _run_main "AI_PLUGIN_TARGETS=('claude-code') && AI_PLUGINS=()" + + assert_success + assert_line "[ai-plugins] No plugins to install." + [ ! -s "$CLAUDE_ARGS_FILE" ] +} + +@test "main: exits cleanly with no targets" { + _run_main "AI_PLUGIN_TARGETS=() && AI_PLUGINS=('caveman|caveman|JuliusBrussee/caveman')" + + assert_success + assert_line "[ai-plugins] No agent targets; nothing to install." + [ ! -s "$CLAUDE_ARGS_FILE" ] +} + +@test "main: skips empty plugin entries" { + _run_main "AI_PLUGIN_TARGETS=('claude-code') && AI_PLUGINS=('caveman|caveman|JuliusBrussee/caveman' '' 'superpowers|superpowers-dev|obra/superpowers')" + + assert_success + assert_line "plugin install caveman@caveman" + assert_line "plugin install superpowers@superpowers-dev" + [ "$(wc -l <"$CLAUDE_ARGS_FILE")" -eq 4 ] +} + +@test "main: skips empty target entries" { + _run_main "AI_PLUGIN_TARGETS=('claude-code' '') && AI_PLUGINS=('caveman|caveman|JuliusBrussee/caveman')" + + assert_success + assert_line "plugin install caveman@caveman" + [ "$(wc -l <"$CLAUDE_ARGS_FILE")" -eq 2 ] +} + +@test "main: warns but succeeds when a plugin fails" { + export CLAUDE_EXIT_CODE=1 + _run_main "AI_PLUGIN_TARGETS=('claude-code') && AI_PLUGINS=('caveman|caveman|JuliusBrussee/caveman')" + + assert_success + assert_line "warn: [ai-plugins] 1 plugin(s) failed to install:" + assert_line " - caveman" + assert_line "[ai-plugins] AI plugins installed." +} + +@test "main: reports only the plugins that failed" { + cat >"$BATS_TEST_TMPDIR/bin/claude" <<'CLAUDE' +#!/usr/bin/env bash +printf '%s\n' "$*" | tee -a "$CLAUDE_ARGS_FILE" +[[ "$*" == *"obra/superpowers"* ]] && exit 1 +exit 0 +CLAUDE + chmod +x "$BATS_TEST_TMPDIR/bin/claude" + + _run_main "AI_PLUGIN_TARGETS=('claude-code') && AI_PLUGINS=('caveman|caveman|JuliusBrussee/caveman' 'superpowers|superpowers-dev|obra/superpowers')" + + assert_success + assert_line "warn: [ai-plugins] 1 plugin(s) failed to install:" + assert_line " - superpowers" + refute_line " - caveman" +} + +@test "main: warns and skips an unknown target" { + _run_main "AI_PLUGIN_TARGETS=('grok') && AI_PLUGINS=('caveman|caveman|JuliusBrussee/caveman')" + + assert_success + assert_line "warn: [ai-plugins] unknown target 'grok'; skipping." + [ ! -s "$CLAUDE_ARGS_FILE" ] + [ ! -s "$NPX_ARGS_FILE" ] +} + +@test "main: fails when claude is missing for a claude-code target" { + rm -f "$BATS_TEST_TMPDIR/bin/claude" + # Drop /usr/bin so a host-installed claude cannot mask the missing path; /bin still provides bash. + export PATH="$BATS_TEST_TMPDIR/bin:/bin" + _run_main "AI_PLUGIN_TARGETS=('claude-code') && AI_PLUGINS=('caveman|caveman|JuliusBrussee/caveman')" + + assert_failure 1 + assert_line "error: [ai-plugins] claude CLI not found. Ensure Claude Code is installed." +} + +@test "main: fails when npx is missing for a github-copilot target" { + rm -f "$BATS_TEST_TMPDIR/bin/npx" + # Drop /usr/bin so a host-installed npx cannot mask the missing path; /bin still provides bash. + export PATH="$BATS_TEST_TMPDIR/bin:/bin" + _run_main "AI_PLUGIN_TARGETS=('github-copilot') && AI_PLUGINS=('caveman|caveman|JuliusBrussee/caveman')" + + assert_failure 1 + assert_line "error: [ai-plugins] npx not found. Ensure Node.js is installed (run_onchange_03_install-mise-tools)." +} From 076dbf5af44e555b05f28355112bb0af833856e1 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Wed, 1 Jul 2026 00:53:42 -0500 Subject: [PATCH 37/51] feat: add ai-plugins run_onchange script and settings renders per-target plugin install and declares caveman + superpowers marketplaces (DOT-43) --- ...run_onchange_06_install-ai-plugins.sh.tmpl | 22 +++++++++++++++++++ home/dot_claude/settings.json | 16 +++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 home/.chezmoiscripts/darwin/run_onchange_06_install-ai-plugins.sh.tmpl diff --git a/home/.chezmoiscripts/darwin/run_onchange_06_install-ai-plugins.sh.tmpl b/home/.chezmoiscripts/darwin/run_onchange_06_install-ai-plugins.sh.tmpl new file mode 100644 index 0000000..8469a73 --- /dev/null +++ b/home/.chezmoiscripts/darwin/run_onchange_06_install-ai-plugins.sh.tmpl @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# @file run_onchange_06_install-ai-plugins.sh +# @brief Install cross-agent AI plugins. +# @description AI data hash: {{ include ".chezmoidata/ai.yaml" | sha256sum }} +{{- $activeTargets := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .ai.targets) | fromJson -}} +{{- $activePlugins := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .ai.plugins) | fromJson -}} + +{{ if and (eq .chezmoi.os "darwin") (gt (len $activeTargets) 0) (gt (len $activePlugins) 0) }} +{{ template "lib/common/log.sh" . }} +{{ template "lib/common/install-prelude.sh" . }} +AI_PLUGIN_TARGETS=( +{{ range $activeTargets -}} + "{{ . }}" +{{ end -}} +) +AI_PLUGINS=( +{{ range $activePlugins -}} + "{{ .name }}|{{ .marketplace }}|{{ .source }}" +{{ end -}} +) +{{ template "lib/install/ai-plugins.sh" . }} +{{ end }} diff --git a/home/dot_claude/settings.json b/home/dot_claude/settings.json index ac4625e..51bd581 100644 --- a/home/dot_claude/settings.json +++ b/home/dot_claude/settings.json @@ -172,7 +172,9 @@ "padding": 0 }, "enabledPlugins": { - "superwhisper@superwhisper": true + "superwhisper@superwhisper": true, + "caveman@caveman": true, + "superpowers@superpowers-dev": true }, "extraKnownMarketplaces": { "superwhisper": { @@ -180,6 +182,18 @@ "source": "github", "repo": "superultrainc/superwhisper-claude-code" } + }, + "caveman": { + "source": { + "source": "github", + "repo": "JuliusBrussee/caveman" + } + }, + "superpowers-dev": { + "source": { + "source": "github", + "repo": "obra/superpowers" + } } } } From 700dcf7d28e5cb6be2b537a78a1b3a8e843be4ff Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Wed, 1 Jul 2026 00:55:18 -0500 Subject: [PATCH 38/51] test: add ai-plugins template render tests cover per-target render and library injection; apply shfmt case indentation (DOT-43) --- .../lib/install/ai-plugins.sh | 12 +++--- tests/template/darwin-install-scripts.bats | 37 +++++++++++++++++++ 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/home/.chezmoitemplates/lib/install/ai-plugins.sh b/home/.chezmoitemplates/lib/install/ai-plugins.sh index 846f2cd..852ee46 100644 --- a/home/.chezmoitemplates/lib/install/ai-plugins.sh +++ b/home/.chezmoitemplates/lib/install/ai-plugins.sh @@ -111,12 +111,12 @@ function _ai_plugins_for_copilot() { # function _ai_plugins_for_target() { case "$1" in - claude-code) _ai_plugins_for_claude_code ;; - github-copilot) _ai_plugins_for_copilot ;; - *) - log_warn "[ai-plugins] unknown target '$1'; skipping." - return 0 - ;; + claude-code) _ai_plugins_for_claude_code ;; + github-copilot) _ai_plugins_for_copilot ;; + *) + log_warn "[ai-plugins] unknown target '$1'; skipping." + return 0 + ;; esac } diff --git a/tests/template/darwin-install-scripts.bats b/tests/template/darwin-install-scripts.bats index 7ef2890..5a81c8f 100644 --- a/tests/template/darwin-install-scripts.bats +++ b/tests/template/darwin-install-scripts.bats @@ -10,6 +10,7 @@ PACKAGE_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_02_ins UV_TOOLS_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_03_install-uv-tools.sh.tmpl" GRAPHIFY_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_08_install-graphify-skills.sh.tmpl" AI_SKILLS_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_09_install-ai-skills.sh.tmpl" +AI_PLUGINS_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_06_install-ai-plugins.sh.tmpl" @test "darwin install script templates render with bash shebang" { for template in "$DOTFILES_ROOT"/home/.chezmoiscripts/darwin/*.tmpl; do @@ -35,6 +36,10 @@ AI_SKILLS_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_09_i assert_file_contains "$AI_SKILLS_TEMPLATE" '{{ template "lib/install/ai-skills.sh" . }}' } +@test "darwin install script templates inject AI plugins shell library" { + assert_file_contains "$AI_PLUGINS_TEMPLATE" '{{ template "lib/install/ai-plugins.sh" . }}' +} + @test "darwin install script templates inject the install prelude" { local prelude='{{ template "lib/common/install-prelude.sh" . }}' assert_file_contains "$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_once_01_install-homebrew.sh.tmpl" "$prelude" @@ -44,6 +49,7 @@ AI_SKILLS_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_09_i assert_file_contains "$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_04_install-vscode-extensions.sh.tmpl" "$prelude" assert_file_contains "$GRAPHIFY_TEMPLATE" "$prelude" assert_file_contains "$AI_SKILLS_TEMPLATE" "$prelude" + assert_file_contains "$AI_PLUGINS_TEMPLATE" "$prelude" } @test "rendered darwin install scripts are syntactically valid bash" { @@ -167,3 +173,34 @@ AI_SKILLS_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_09_i refute_line 'AI_SKILL_TARGETS=(' refute_line --partial 'ai_skills_install_main' } + +@test "personal AI plugins template targets claude-code and includes both plugins" { + run render_chezmoi_template "$AI_PLUGINS_TEMPLATE" "$DARWIN_DATA" + + assert_success + assert_line 'AI_PLUGIN_TARGETS=(' + assert_line --partial '"claude-code"' + refute_line --partial '"github-copilot"' + assert_line --partial '"caveman|caveman|JuliusBrussee/caveman"' + assert_line --partial '"superpowers|superpowers-dev|obra/superpowers"' + assert_line --partial 'ai_plugins_install_main' +} + +@test "work AI plugins template targets github-copilot" { + run render_chezmoi_template "$AI_PLUGINS_TEMPLATE" "$WORK_DATA" + + assert_success + assert_line 'AI_PLUGIN_TARGETS=(' + assert_line --partial '"github-copilot"' + refute_line --partial '"claude-code"' + assert_line --partial '"caveman|caveman|JuliusBrussee/caveman"' + assert_line --partial 'ai_plugins_install_main' +} + +@test "empty AI target groups render no AI plugins commands" { + run render_chezmoi_template "$AI_PLUGINS_TEMPLATE" "$EMPTY_AI_DATA" + + assert_success + refute_line 'AI_PLUGIN_TARGETS=(' + refute_line --partial 'ai_plugins_install_main' +} From 3c849d85d0a04eb4f89d43fd7f065945434d2e80 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Wed, 1 Jul 2026 08:49:17 -0500 Subject: [PATCH 39/51] refactor: renumber ai-skills install script to 07 group AI installs contiguously: 06 plugins, 07 skills, 08 graphify (DOT-43) --- ...30-apm-migration-phase-1-skills-install.md | 22 +++++++++---------- ...0-apm-migration-phase-2-plugins-install.md | 2 +- ...-to-claude-marketplace-migration-design.md | 2 +- ...run_onchange_07_install-ai-skills.sh.tmpl} | 2 +- tests/template/darwin-install-scripts.bats | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) rename home/.chezmoiscripts/darwin/{run_onchange_09_install-ai-skills.sh.tmpl => run_onchange_07_install-ai-skills.sh.tmpl} (95%) diff --git a/docs/superpowers/plans/2026-06-30-apm-migration-phase-1-skills-install.md b/docs/superpowers/plans/2026-06-30-apm-migration-phase-1-skills-install.md index 8f7950d..cff2e64 100644 --- a/docs/superpowers/plans/2026-06-30-apm-migration-phase-1-skills-install.md +++ b/docs/superpowers/plans/2026-06-30-apm-migration-phase-1-skills-install.md @@ -17,7 +17,7 @@ ## File Structure - Create: `home/.chezmoitemplates/lib/install/ai-skills.sh` — sourceable install library; one responsibility: run `npx skills add` for each selected skill against the target agents. -- Create: `home/.chezmoiscripts/darwin/run_onchange_09_install-ai-skills.sh.tmpl` — chezmoi run script; resolves active targets and skills from `ai.yaml`, renders bash arrays, sources the library. +- Create: `home/.chezmoiscripts/darwin/run_onchange_07_install-ai-skills.sh.tmpl` — chezmoi run script; resolves active targets and skills from `ai.yaml`, renders bash arrays, sources the library. - Create: `tests/unit/lib/install/ai-skills.bats` — behavior tests for the library, stubbing `npx`. - Exists (no change): `home/.chezmoidata/ai.yaml` — the manifest. @@ -277,15 +277,15 @@ git commit -m "feat: DOT-43 add ai-skills install library" **Files:** -- Create: `home/.chezmoiscripts/darwin/run_onchange_09_install-ai-skills.sh.tmpl` +- Create: `home/.chezmoiscripts/darwin/run_onchange_07_install-ai-skills.sh.tmpl` - [ ] **Step 1: Write the run script** -Create `home/.chezmoiscripts/darwin/run_onchange_09_install-ai-skills.sh.tmpl`: +Create `home/.chezmoiscripts/darwin/run_onchange_07_install-ai-skills.sh.tmpl`: ```bash #!/usr/bin/env bash -# @file run_onchange_09_install-ai-skills.sh +# @file run_onchange_07_install-ai-skills.sh # @brief Install cross-agent AI skills. # @description AI data hash: {{ include ".chezmoidata/ai.yaml" | sha256sum }} {{- $activeTargets := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .ai.targets) | fromJson -}} @@ -311,18 +311,18 @@ AI_SKILLS=( - [ ] **Step 2: Verify the template renders for a personal machine** -Run: `chezmoi execute-template --init --promptString 'groups=personal' < home/.chezmoiscripts/darwin/run_onchange_09_install-ai-skills.sh.tmpl` (adjust the prompt to match this repo's group-selection variable if different; confirm from `home/.chezmoitemplates/lib/chezmoi/active-groups.json.tmpl`). +Run: `chezmoi execute-template --init --promptString 'groups=personal' < home/.chezmoiscripts/darwin/run_onchange_07_install-ai-skills.sh.tmpl` (adjust the prompt to match this repo's group-selection variable if different; confirm from `home/.chezmoitemplates/lib/chezmoi/active-groups.json.tmpl`). Expected: rendered bash with `AI_SKILL_TARGETS=("claude-code")` and `AI_SKILLS` containing the shared plus personal entries (`mattpocock/skills|grill-me`, ..., `schpet/linear-cli|linear-cli`, `upstash/context7|context7-cli`), and the `lib/install/ai-skills.sh` body inlined. - [ ] **Step 3: Dry-run apply to confirm no template errors** -Run: `chezmoi apply --dry-run --verbose 2>&1 | rg -A2 'run_onchange_09_install-ai-skills'` +Run: `chezmoi apply --dry-run --verbose 2>&1 | rg -A2 'run_onchange_07_install-ai-skills'` Expected: chezmoi shows the script would run; no template parse errors. - [ ] **Step 4: Commit** ```bash -git add home/.chezmoiscripts/darwin/run_onchange_09_install-ai-skills.sh.tmpl +git add home/.chezmoiscripts/darwin/run_onchange_07_install-ai-skills.sh.tmpl git commit -m "feat: DOT-43 add ai-skills run_onchange script" ``` @@ -343,8 +343,8 @@ Run: `bats --count tests/template/darwin-install-scripts.bats` and open the file Add two tests mirroring the file's existing pattern. Personal render includes shared plus personal skills against `claude-code`; work render excludes personal-only skills. Use the file's existing template-render helper (do not invent a new one). Example assertions: ```bash -@test "run_onchange_09: personal render targets claude-code and includes shared+personal skills" { - run render_darwin_script "run_onchange_09_install-ai-skills.sh.tmpl" "personal" +@test "run_onchange_07: personal render targets claude-code and includes shared+personal skills" { + run render_darwin_script "run_onchange_07_install-ai-skills.sh.tmpl" "personal" assert_success assert_output --partial 'AI_SKILL_TARGETS=(' assert_output --partial '"claude-code"' @@ -352,8 +352,8 @@ Add two tests mirroring the file's existing pattern. Personal render includes sh assert_output --partial '"schpet/linear-cli|linear-cli"' } -@test "run_onchange_09: work render targets github-copilot and excludes personal skills" { - run render_darwin_script "run_onchange_09_install-ai-skills.sh.tmpl" "work" +@test "run_onchange_07: work render targets github-copilot and excludes personal skills" { + run render_darwin_script "run_onchange_07_install-ai-skills.sh.tmpl" "work" assert_success assert_output --partial '"github-copilot"' refute_output --partial 'schpet/linear-cli|linear-cli' diff --git a/docs/superpowers/plans/2026-06-30-apm-migration-phase-2-plugins-install.md b/docs/superpowers/plans/2026-06-30-apm-migration-phase-2-plugins-install.md index f939f87..00f1d31 100644 --- a/docs/superpowers/plans/2026-06-30-apm-migration-phase-2-plugins-install.md +++ b/docs/superpowers/plans/2026-06-30-apm-migration-phase-2-plugins-install.md @@ -226,7 +226,7 @@ fi **Files:** Create `home/.chezmoiscripts/darwin/run_onchange_06_install-ai-plugins.sh.tmpl`; Modify `home/dot_claude/settings.json`. -- [ ] **Step 1: Write the run script** (mirror `run_onchange_09`; no local-content hash — plugins come from external sources): +- [ ] **Step 1: Write the run script** (mirror `run_onchange_07`; no local-content hash — plugins come from external sources): ``` #!/usr/bin/env bash diff --git a/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md b/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md index dbc75fd..6356b87 100644 --- a/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md +++ b/docs/superpowers/specs/2026-06-30-apm-to-claude-marketplace-migration-design.md @@ -105,7 +105,7 @@ The `npx --yes` prefix auto-confirms npx's own install prompt so the non-interac Local domain skills `typescript`, `react`, and `testing` (React Testing Library guidance lives in the `testing` skill) are authored under `home/.chezmoitemplates/skills/` from the former instruction files, and install with `source: local` pointing at that path. The previously vendored `grill-me` and `junior-to-senior` copies under that directory have been removed; both install from their upstream sources. -The `skills` CLI has no manifest file, so `home/.chezmoidata/ai.yaml` is the manifest. A `run_onchange_09_install-ai-skills.sh.tmpl` script gates on `darwin`, reads the active group and its target, iterates the matching skill entries, and runs the CLI. Its trigger comment hashes both `ai.yaml` and the local skill tree under `home/.chezmoitemplates/skills/`, so it re-runs when the manifest changes or when a local `SKILL.md` is edited in place (local skills install with `--copy`, so a stale copy would otherwise persist). +The `skills` CLI has no manifest file, so `home/.chezmoidata/ai.yaml` is the manifest. A `run_onchange_07_install-ai-skills.sh.tmpl` script gates on `darwin`, reads the active group and its target, iterates the matching skill entries, and runs the CLI. Its trigger comment hashes both `ai.yaml` and the local skill tree under `home/.chezmoitemplates/skills/`, so it re-runs when the manifest changes or when a local `SKILL.md` is edited in place (local skills install with `--copy`, so a stale copy would otherwise persist). ## Plugins Design diff --git a/home/.chezmoiscripts/darwin/run_onchange_09_install-ai-skills.sh.tmpl b/home/.chezmoiscripts/darwin/run_onchange_07_install-ai-skills.sh.tmpl similarity index 95% rename from home/.chezmoiscripts/darwin/run_onchange_09_install-ai-skills.sh.tmpl rename to home/.chezmoiscripts/darwin/run_onchange_07_install-ai-skills.sh.tmpl index 0155c27..67c13ad 100644 --- a/home/.chezmoiscripts/darwin/run_onchange_09_install-ai-skills.sh.tmpl +++ b/home/.chezmoiscripts/darwin/run_onchange_07_install-ai-skills.sh.tmpl @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# @file run_onchange_09_install-ai-skills.sh +# @file run_onchange_07_install-ai-skills.sh # @brief Install cross-agent AI skills. # @description AI data hash: {{ include ".chezmoidata/ai.yaml" | sha256sum }}; local skills hash: {{ $h := "" }}{{ range (glob (joinPath .chezmoi.sourceDir ".chezmoitemplates" "skills" "**" "SKILL.md")) }}{{ $h = print $h (include .) }}{{ end }}{{ $h | sha256sum }} {{- $activeTargets := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .ai.targets) | fromJson -}} diff --git a/tests/template/darwin-install-scripts.bats b/tests/template/darwin-install-scripts.bats index 5a81c8f..abab606 100644 --- a/tests/template/darwin-install-scripts.bats +++ b/tests/template/darwin-install-scripts.bats @@ -9,7 +9,7 @@ EMPTY_AI_DATA='{"chezmoi":{"os":"darwin"},"personal":true,"work":false,"ai":{"ta PACKAGE_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_02_install-packages.sh.tmpl" UV_TOOLS_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_03_install-uv-tools.sh.tmpl" GRAPHIFY_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_08_install-graphify-skills.sh.tmpl" -AI_SKILLS_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_09_install-ai-skills.sh.tmpl" +AI_SKILLS_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_07_install-ai-skills.sh.tmpl" AI_PLUGINS_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_06_install-ai-plugins.sh.tmpl" @test "darwin install script templates render with bash shebang" { From 63e71258997f0594db6ba963a92ed03c98e3486e Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Wed, 1 Jul 2026 08:58:46 -0500 Subject: [PATCH 40/51] refactor: decompose ai-skills and graphify-skills libraries extract predicate, source resolver, and failure reporter to match the homebrew-bundle clarity pattern (DOT-43) --- .../lib/install/ai-skills.sh | 77 +++++++++++++------ .../lib/install/graphify-skills.sh | 41 ++++++---- 2 files changed, 80 insertions(+), 38 deletions(-) diff --git a/home/.chezmoitemplates/lib/install/ai-skills.sh b/home/.chezmoitemplates/lib/install/ai-skills.sh index 60e4be9..b0875d9 100644 --- a/home/.chezmoitemplates/lib/install/ai-skills.sh +++ b/home/.chezmoitemplates/lib/install/ai-skills.sh @@ -10,20 +10,61 @@ set -Eeuo pipefail # -# @description Install each selected skill for the active agent targets. -# @exitcode 0 Skills installed, or nothing to do. -# @exitcode 1 npx is not available. +# @description True when AI_SKILLS holds at least one non-empty entry. +# @exitcode 0 A skill is present. +# @exitcode 1 No skills. # -function ai_skills_install_main() { - local has_skill=0 +function _ai_skills_have_any() { local entry for entry in "${AI_SKILLS[@]-}"; do [[ -z "${entry}" ]] && continue - has_skill=1 - break + return 0 done + return 1 +} + +# +# @description Resolve a skill's install source, skipping a missing local skill. +# @arg $1 string Source token: a remote repo, or "local". +# @arg $2 string Skill name. +# @stdout The source to hand to `skills add`. +# @exitcode 0 Source resolved. +# @exitcode 1 A local skill directory is missing (warned; caller should skip). +# +function _ai_skills_source() { + local src="$1" skill="$2" - if ((has_skill == 0)); then + if [[ "${src}" != "local" ]]; then + printf '%s' "${src}" + return 0 + fi + + local dir="${AI_LOCAL_SKILLS_DIR:?AI_LOCAL_SKILLS_DIR must be set}/${skill}" + if [[ -d "${dir}" ]]; then + printf '%s' "${dir}" + return 0 + fi + + log_warn "[ai-skills] local skill '${skill}' not found at ${dir}; skipping." + return 1 +} + +# +# @description Warn about the skills that failed to install. +# @arg $@ string Names of the skills that failed. +# +function _ai_skills_report_failures() { + log_warn "[ai-skills] ${#} skill(s) failed to install:" + printf ' - %s\n' "$@" >&2 +} + +# +# @description Install each selected skill for the active agent targets. +# @exitcode 0 Skills installed, or nothing to do. +# @exitcode 1 npx is not available. +# +function ai_skills_install_main() { + if ! _ai_skills_have_any; then log_info "[ai-skills] No skills to install." return 0 fi @@ -47,31 +88,19 @@ function ai_skills_install_main() { log_info "[ai-skills] Installing cross-agent skills..." - local src skill add_source local -a failed=() + local entry src skill add_source for entry in "${AI_SKILLS[@]-}"; do [[ -z "${entry}" ]] && continue src="${entry%%|*}" skill="${entry##*|}" - if [[ "${src}" == "local" ]]; then - add_source="${AI_LOCAL_SKILLS_DIR:?AI_LOCAL_SKILLS_DIR must be set}/${skill}" - if [[ ! -d "${add_source}" ]]; then - log_warn "[ai-skills] local skill '${skill}' not found at ${add_source}; skipping." - continue - fi - else - add_source="${src}" - fi - - if ! npx --yes skills add "${add_source}" --skill "${skill}" "${target_flags[@]}" --global --copy --yes; then - failed+=("${skill}") - fi + add_source="$(_ai_skills_source "${src}" "${skill}")" || continue + npx --yes skills add "${add_source}" --skill "${skill}" "${target_flags[@]}" --global --copy --yes || failed+=("${skill}") done if ((${#failed[@]} > 0)); then - log_warn "[ai-skills] ${#failed[@]} skill(s) failed to install:" - printf ' - %s\n' "${failed[@]}" >&2 + _ai_skills_report_failures "${failed[@]}" fi log_info "[ai-skills] Cross-agent skills installed." diff --git a/home/.chezmoitemplates/lib/install/graphify-skills.sh b/home/.chezmoitemplates/lib/install/graphify-skills.sh index 8c69a00..35f5fe2 100644 --- a/home/.chezmoitemplates/lib/install/graphify-skills.sh +++ b/home/.chezmoitemplates/lib/install/graphify-skills.sh @@ -9,20 +9,35 @@ set -Eeuo pipefail # -# @description Install Graphify skills for selected platforms. +# @description True when GRAPHIFY_PLATFORMS holds at least one non-empty entry. +# @exitcode 0 A platform is present. +# @exitcode 1 No platforms. # -function graphify_skills_install_main() { - local has_platform=0 +function _graphify_has_platform() { local platform - local failed=() - for platform in "${GRAPHIFY_PLATFORMS[@]-}"; do [[ -z "${platform}" ]] && continue - has_platform=1 - break + return 0 done + return 1 +} - if ((has_platform == 0)); then +# +# @description Warn about the platforms that failed to install. +# @arg $@ string Names of the platforms that failed. +# +function _graphify_report_failures() { + log_warn "[graphify] ${#} platform(s) failed to install:" + printf ' - %s\n' "$@" >&2 +} + +# +# @description Install Graphify skills for each selected platform. +# @exitcode 0 Installed, or nothing to do. +# @exitcode 1 The graphify CLI is missing. +# +function graphify_skills_install_main() { + if ! _graphify_has_platform; then log_info "[graphify] No Graphify platforms to install." return 0 fi @@ -31,17 +46,15 @@ function graphify_skills_install_main() { log_info "[graphify] Installing Graphify agent skills..." + local -a failed=() + local platform for platform in "${GRAPHIFY_PLATFORMS[@]-}"; do [[ -z "${platform}" ]] && continue - - if ! graphify install --platform "${platform}"; then - failed+=("${platform}") - fi + graphify install --platform "${platform}" || failed+=("${platform}") done if ((${#failed[@]} > 0)); then - log_warn "[graphify] ${#failed[@]} platform(s) failed to install:" - printf ' - %s\n' "${failed[@]}" >&2 + _graphify_report_failures "${failed[@]}" fi log_info "[graphify] Graphify agent skills installed." From 8736e80d8d24a1818f00cbd38d87ce1697ef0cd5 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Wed, 1 Jul 2026 09:14:12 -0500 Subject: [PATCH 41/51] fix: point kaizen skill at context-engineering-kit source imadAttar/kaizen renamed its skill to coach; neolabhq/context-engineering-kit ships kaizen (DOT-43) --- home/.chezmoidata/ai.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/home/.chezmoidata/ai.yaml b/home/.chezmoidata/ai.yaml index 4a818d8..716a85b 100644 --- a/home/.chezmoidata/ai.yaml +++ b/home/.chezmoidata/ai.yaml @@ -13,7 +13,7 @@ ai: skill: junior-to-senior - source: blader/humanizer skill: humanizer - - source: imadAttar/kaizen + - source: neolabhq/context-engineering-kit skill: kaizen # local domain skills authored in this repo - source: local From 3489439e7bff69dd71926f453330dc49e26cbb7e Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Wed, 1 Jul 2026 09:25:53 -0500 Subject: [PATCH 42/51] refactor: rebase agent guidance on karpathy principles adopt the four-principle structure, drop the git/PR workflow section, keep the github cli and graphify sections (DOT-43) --- home/.chezmoitemplates/AGENTS.md | 107 +++++++++++++++---------- tests/template/agent-instructions.bats | 2 +- 2 files changed, 65 insertions(+), 44 deletions(-) diff --git a/home/.chezmoitemplates/AGENTS.md b/home/.chezmoitemplates/AGENTS.md index 97ce8cd..add2767 100644 --- a/home/.chezmoitemplates/AGENTS.md +++ b/home/.chezmoitemplates/AGENTS.md @@ -1,25 +1,75 @@ # Agent Guidance -Guidance for AI coding agents (Claude Code, GitHub Copilot, and others). Structured around [Andrej Karpathy's observations on LLM coding pitfalls](https://x.com/karpathy/status/2015883857489522876): surface assumptions, don't overcomplicate, make surgical changes, verify before moving on. +Behavioral guidelines for AI coding agents (Claude Code, GitHub Copilot, and others) to reduce common LLM coding mistakes, derived from [Andrej Karpathy's observations on LLM coding pitfalls](https://x.com/karpathy/status/2015883857489522876). -## AI Guidance +**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment. -**Do what was asked. Nothing more, nothing less.** +## 1. Think Before Coding -Never use words like "consolidate", "modernize", "streamline", "flexible", "delve", "establish", "enhanced", "comprehensive", "optimize" or em-dashes (—) and double-hyphens (--) in docstrings, commit messages, or comments. +**Don't assume. Don't hide confusion. Surface tradeoffs.** -- Reflect on tool results before acting. Use thinking to plan and iterate, then take the best next action. -- Run independent operations in parallel. -- Verify your solution before finishing. -- Never create files unless necessary. Prefer editing. Never create docs (\*.md, README) unless explicitly asked. -- Reuse existing code. Simplify. Make targeted changes, not sweeping ones. -- Prefer `rg` over `grep`. -- No defensive programming unless you state the motivation and the user approves. -- When updating code, check related code in the same and other files for consistency. +Before implementing: -Ask yourself: "Does every change I'm making trace directly to what was asked?" +- State your assumptions explicitly. If uncertain, ask. +- If multiple interpretations exist, present them, don't pick silently. +- If a simpler approach exists, say so. Push back when warranted. +- If something is unclear, stop. Name what's confusing. Ask. -### GitHub CLI +## 2. Simplicity First + +**Minimum code that solves the problem. Nothing speculative.** + +- No features beyond what was asked. +- No abstractions for single-use code. +- No "flexibility" or "configurability" that wasn't requested. +- No error handling for impossible scenarios. +- If you write 200 lines and it could be 50, rewrite it. + +Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify. + +## 3. Surgical Changes + +**Touch only what you must. Clean up only your own mess.** + +When editing existing code: + +- Don't "improve" adjacent code, comments, or formatting. +- Don't refactor things that aren't broken. +- Match existing style, even if you'd do it differently. +- If you notice unrelated dead code, mention it, don't delete it. + +When your changes create orphans: + +- Remove imports, variables, and functions that your changes made unused. +- Don't remove pre-existing dead code unless asked. + +The test: every changed line should trace directly to the user's request. + +## 4. Goal-Driven Execution + +**Define success criteria. Loop until verified.** + +Transform tasks into verifiable goals: + +- "Add validation" becomes "Write tests for invalid inputs, then make them pass". +- "Fix the bug" becomes "Write a test that reproduces it, then make it pass". +- "Refactor X" becomes "Ensure tests pass before and after". + +For multi-step tasks, state a brief plan: + +``` +1. [Step] -> verify: [check] +2. [Step] -> verify: [check] +3. [Step] -> verify: [check] +``` + +Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification. + +## Writing Style + +Never use words like "consolidate", "modernize", "streamline", "flexible", "delve", "establish", "enhanced", "comprehensive", or "optimize", nor em dashes or double hyphens, in docstrings, commit messages, or comments. + +## GitHub CLI Use `gh` CLI for all GitHub interactions. Never clone repositories to read code. @@ -33,35 +83,6 @@ Use `gh` CLI for all GitHub interactions. Never clone repositories to read code. - **List commits**: `gh api repos/{owner}/{repo}/commits --jq '.[].sha'` - **View issue**: `gh issue view {number} --repo {owner}/{repo}` -## Git and Pull Request Workflows - -### Commit Messages - -- Format: `{type}: brief description` (max 50 chars first line) -- Optional second line: 1 sentence with findings/motivation -- Types: `feat`, `fix`, `refactor`, `perf`, `docs`, `style`, `test`, `build`, `chore`, `ci` -- Simple terms, no jargon -- ONLY analyze staged files (`git diff --cached`), ignore unstaged -- NO test plans in commit messages - -### Pull Requests - -- PR titles: NO type prefix (unlike commits) - start with capital letter + verb -- Analyze ALL commits with `git diff ...HEAD`, not just latest -- PR body: single section, no headers, 1-2 sentences + usage snippet -- No test plans, no changed files list, no line-number links in PR body -- Self-assign with `-a @me` -- Find reviewers: `gh pr list --repo / --author @me --limit 5` - -### PR Comments and Reviews - -- Create pending reviews only, never auto-submit -- Comment style: start lowercase, no em-dashes, simple terms, no end punctuation, max 1 sentence -- Bot comment responses: few words is enough -- Real person responses: polite, concise - -Ask yourself: "Would someone unfamiliar with this repo understand this commit message?" - ## Graphify - Use the installed Graphify skill when the user invokes `/graphify`. diff --git a/tests/template/agent-instructions.bats b/tests/template/agent-instructions.bats index 36bbb88..7056ce8 100644 --- a/tests/template/agent-instructions.bats +++ b/tests/template/agent-instructions.bats @@ -12,7 +12,7 @@ CLAUDE_TEMPLATE="$DOTFILES_ROOT/home/dot_claude/CLAUDE.md.tmpl" assert_success assert_line --index 0 '# Agent Guidance' - assert_line --regexp '^### GitHub CLI$' + assert_line --regexp '^## GitHub CLI$' assert_line 'Use `gh` CLI for all GitHub interactions. Never clone repositories to read code.' } From abdd803962d0c69a3ff1b3e725619b299950aeac Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Wed, 1 Jul 2026 09:35:05 -0500 Subject: [PATCH 43/51] feat: add tooling hierarchy to agent guidance restore parallel-ops guidance and document graphify then rg then fd search order (DOT-43) --- home/.chezmoitemplates/AGENTS.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/home/.chezmoitemplates/AGENTS.md b/home/.chezmoitemplates/AGENTS.md index add2767..fb4956e 100644 --- a/home/.chezmoitemplates/AGENTS.md +++ b/home/.chezmoitemplates/AGENTS.md @@ -69,6 +69,17 @@ Strong success criteria let you loop independently. Weak criteria ("make it work Never use words like "consolidate", "modernize", "streamline", "flexible", "delve", "establish", "enhanced", "comprehensive", or "optimize", nor em dashes or double hyphens, in docstrings, commit messages, or comments. +## Tooling + +Run independent operations in parallel when they don't depend on each other. + +For searching a codebase, prefer efficient tools over defaults, in order: + +1. **Graphify** for structure questions such as definitions, callers, and architecture, when `graphify-out/graph.json` exists (see below). +2. **`rg`** (ripgrep) for content and regex search; faster than `grep` and skips ignored files. +3. **`fd`** for finding files by name; faster and simpler than `find`. +4. `grep` and `find` only when the tools above do not fit. + ## GitHub CLI Use `gh` CLI for all GitHub interactions. Never clone repositories to read code. From e99c6bff87d5c1134a0d0f200fcb844475a3fac6 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Wed, 1 Jul 2026 09:44:05 -0500 Subject: [PATCH 44/51] feat: add ticket prefix to copilot commit rules extend VS Code commitMessageGeneration instructions to prefix a branch-derived JIRA ticket, omitted when absent --- .../Code/User/settings.json | 3 + home/dot_claude/settings.json | 64 +++++++++---------- 2 files changed, 35 insertions(+), 32 deletions(-) diff --git a/home/Library/Application Support/Code/User/settings.json b/home/Library/Application Support/Code/User/settings.json index d2ac55e..25e5e96 100644 --- a/home/Library/Application Support/Code/User/settings.json +++ b/home/Library/Application Support/Code/User/settings.json @@ -57,6 +57,9 @@ }, { "text": "Add a one-line body only when it explains the why. Describe only the staged changes and never include test plans." + }, + { + "text": "When the current branch name contains a JIRA ticket key such as BC724-550 or BS-86067, prefix the description with it, for example `feat: BC724-550 Add experiment hooks`. Omit the ticket when the branch has none." } ], diff --git a/home/dot_claude/settings.json b/home/dot_claude/settings.json index 51bd581..33df2e0 100644 --- a/home/dot_claude/settings.json +++ b/home/dot_claude/settings.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/claude-code-settings.json", - "model": "best", "cleanupPeriodDays": 365, + "skillListingBudgetFraction": 0.02, "env": { "BASH_DEFAULT_TIMEOUT_MS": "300000", "BASH_MAX_TIMEOUT_MS": "600000", @@ -113,27 +113,6 @@ "WebFetch(domain:github.com)", "WebSearch" ], - "ask": [ - "Bash(sudo:*)", - "Bash(chmod 777:*)", - "Bash(git push --force:*)", - "Bash(git push --force-with-lease:*)", - "Bash(git push -f:*)", - "Bash(git reset --hard:*)", - "Bash(git clean -fd:*)", - "Bash(gh auth refresh:*)", - "Bash(gh issue close:*)", - "Bash(gh label delete:*)", - "Bash(gh pr close:*)", - "Bash(gh pr merge:*)", - "Bash(gh release create:*)", - "Bash(gh run cancel:*)", - "Bash(gh run rerun:*)", - "Bash(kill:*)", - "Bash(npm publish:*)", - "Bash(rm:*)", - "Bash(eval:*)" - ], "deny": [ "Bash(pip install:*)", "Bash(pip3 install:*)", @@ -155,17 +134,30 @@ "Read(**/*.pem)", "Read(**/*.key)" ], + "ask": [ + "Bash(sudo:*)", + "Bash(chmod 777:*)", + "Bash(git push --force:*)", + "Bash(git push --force-with-lease:*)", + "Bash(git push -f:*)", + "Bash(git reset --hard:*)", + "Bash(git clean -fd:*)", + "Bash(gh auth refresh:*)", + "Bash(gh issue close:*)", + "Bash(gh label delete:*)", + "Bash(gh pr close:*)", + "Bash(gh pr merge:*)", + "Bash(gh release create:*)", + "Bash(gh run cancel:*)", + "Bash(gh run rerun:*)", + "Bash(kill:*)", + "Bash(npm publish:*)", + "Bash(rm:*)", + "Bash(eval:*)" + ], "defaultMode": "acceptEdits" }, - "alwaysThinkingEnabled": true, - "autoScrollEnabled": true, - "autoUpdatesChannel": "latest", - "autoMemoryEnabled": true, - "outputStyle": "Explanatory", - "showThinkingSummaries": true, - "spinnerTipsEnabled": false, - "skipWorkflowUsageWarning": true, - "skillListingBudgetFraction": 0.02, + "model": "best", "statusLine": { "type": "command", "command": "sh ~/.claude/statusline-command.sh", @@ -195,5 +187,13 @@ "repo": "obra/superpowers" } } - } + }, + "outputStyle": "Explanatory", + "spinnerTipsEnabled": false, + "alwaysThinkingEnabled": true, + "autoUpdatesChannel": "latest", + "autoMemoryEnabled": true, + "showThinkingSummaries": true, + "skipWorkflowUsageWarning": true, + "autoScrollEnabled": true } From e7e411311d22a8bcabc4ce96ebf311f9e102a37b Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Wed, 1 Jul 2026 10:10:58 -0500 Subject: [PATCH 45/51] refactor: renumber graphify install script to 09 --- ....sh.tmpl => run_onchange_09_install-graphify-skills.sh.tmpl} | 2 +- tests/template/darwin-install-scripts.bats | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename home/.chezmoiscripts/darwin/{run_onchange_08_install-graphify-skills.sh.tmpl => run_onchange_09_install-graphify-skills.sh.tmpl} (93%) diff --git a/home/.chezmoiscripts/darwin/run_onchange_08_install-graphify-skills.sh.tmpl b/home/.chezmoiscripts/darwin/run_onchange_09_install-graphify-skills.sh.tmpl similarity index 93% rename from home/.chezmoiscripts/darwin/run_onchange_08_install-graphify-skills.sh.tmpl rename to home/.chezmoiscripts/darwin/run_onchange_09_install-graphify-skills.sh.tmpl index 65e2655..1e8f966 100644 --- a/home/.chezmoiscripts/darwin/run_onchange_08_install-graphify-skills.sh.tmpl +++ b/home/.chezmoiscripts/darwin/run_onchange_09_install-graphify-skills.sh.tmpl @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# @file run_onchange_08_install-graphify-skills.sh +# @file run_onchange_09_install-graphify-skills.sh # @brief Install Graphify agent skills. # @description AI data hash: {{ include ".chezmoidata/ai.yaml" | sha256sum }} {{- $activeTargets := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .ai.targets) | fromJson -}} diff --git a/tests/template/darwin-install-scripts.bats b/tests/template/darwin-install-scripts.bats index abab606..b562dbe 100644 --- a/tests/template/darwin-install-scripts.bats +++ b/tests/template/darwin-install-scripts.bats @@ -8,7 +8,7 @@ load '../test_helpers/templates.bash' EMPTY_AI_DATA='{"chezmoi":{"os":"darwin"},"personal":true,"work":false,"ai":{"targets":{"shared":[],"personal":[],"work":[]}}}' PACKAGE_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_02_install-packages.sh.tmpl" UV_TOOLS_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_03_install-uv-tools.sh.tmpl" -GRAPHIFY_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_08_install-graphify-skills.sh.tmpl" +GRAPHIFY_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_09_install-graphify-skills.sh.tmpl" AI_SKILLS_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_07_install-ai-skills.sh.tmpl" AI_PLUGINS_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_06_install-ai-plugins.sh.tmpl" From 07382343a89af3f0e2b035d18bfbd9a876ec9805 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Wed, 1 Jul 2026 10:14:10 -0500 Subject: [PATCH 46/51] feat: add MCP registration library for claude code --- home/.chezmoitemplates/lib/install/ai-mcp.sh | 91 +++++++++++++++++ tests/unit/lib/install/ai-mcp.bats | 100 +++++++++++++++++++ 2 files changed, 191 insertions(+) create mode 100644 home/.chezmoitemplates/lib/install/ai-mcp.sh create mode 100644 tests/unit/lib/install/ai-mcp.bats diff --git a/home/.chezmoitemplates/lib/install/ai-mcp.sh b/home/.chezmoitemplates/lib/install/ai-mcp.sh new file mode 100644 index 0000000..f605fd9 --- /dev/null +++ b/home/.chezmoitemplates/lib/install/ai-mcp.sh @@ -0,0 +1,91 @@ +#!/usr/bin/env bash +# @file lib/install/ai-mcp.sh +# @brief Register cross-agent MCP servers on Claude Code. +# @description +# Runs `claude mcp add` for each server in AI_MCP, scoped to the user. A +# `claude mcp get` check keeps the flow idempotent by skipping a server that +# is already registered. Copilot MCP arrives through rendered config files, +# so this library targets Claude Code only. Sourceable from bats tests and +# injected into chezmoi run scripts via chezmoi template rendering. + +set -Eeuo pipefail + +# +# @description True when AI_MCP holds at least one non-empty entry. +# @exitcode 0 A server is present. +# @exitcode 1 No servers. +# +function _ai_mcp_have_any() { + local entry + for entry in "${AI_MCP[@]-}"; do + [[ -z "${entry}" ]] && continue + return 0 + done + return 1 +} + +# +# @description Warn about the MCP servers that failed to register. +# @arg $@ string Names of the servers that failed. +# +function _ai_mcp_report_failures() { + log_warn "[ai-mcp] ${#} server(s) failed to register:" + printf ' - %s\n' "$@" >&2 +} + +# +# @description Register each MCP server on Claude Code, skipping existing ones. +# @exitcode 0 Registered, or nothing to do. +# @exitcode 1 The claude CLI is missing. +# +function ai_mcp_install_main() { + if ! _ai_mcp_have_any; then + log_info "[ai-mcp] No MCP servers to register." + return 0 + fi + + if ! command -v claude >/dev/null 2>&1; then + log_error "[ai-mcp] claude CLI not found. Ensure Claude Code is installed." + return 1 + fi + + log_info "[ai-mcp] Registering MCP servers..." + + local -a failed=() + local entry name transport url rest + for entry in "${AI_MCP[@]-}"; do + [[ -z "${entry}" ]] && continue + name="${entry%%|*}" + rest="${entry#*|}" + transport="${rest%%|*}" + url="${rest##*|}" + + if claude mcp get "${name}" >/dev/null 2>&1; then + log_info "[ai-mcp] ${name} already registered; skipping." + continue + fi + + claude mcp add --scope user --transport "${transport}" "${name}" "${url}" || failed+=("${name}") + done + + if ((${#failed[@]} > 0)); then + _ai_mcp_report_failures "${failed[@]}" + fi + + log_info "[ai-mcp] MCP servers registered." +} + +# +# @description Run the AI MCP registration flow. +# +function main() { + ai_mcp_install_main +} + +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + # When run directly, pull in the shared log library that chezmoi otherwise + # concatenates ahead of this file. + # shellcheck source=/dev/null + command -v log_info >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/log.sh" + main +fi diff --git a/tests/unit/lib/install/ai-mcp.bats b/tests/unit/lib/install/ai-mcp.bats new file mode 100644 index 0000000..8043431 --- /dev/null +++ b/tests/unit/lib/install/ai-mcp.bats @@ -0,0 +1,100 @@ +#!/usr/bin/env bats +# @file tests/unit/lib/install/ai-mcp.bats +# @brief Behavior tests for home/.chezmoitemplates/lib/install/ai-mcp.sh. + +load '../../../test_helpers/load.bash' + +LOG_LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/common/log.sh" +LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/install/ai-mcp.sh" + +setup() { + export CLAUDE_ARGS_FILE="$BATS_TEST_TMPDIR/claude-args" + mkdir -p "$BATS_TEST_TMPDIR/bin" + + # Default stub: `mcp get` reports "not found" (exit 1) so `mcp add` runs. + cat >"$BATS_TEST_TMPDIR/bin/claude" <<'CLAUDE' +#!/usr/bin/env bash +printf '%s\n' "$*" | tee -a "$CLAUDE_ARGS_FILE" +[[ "$1 $2" == "mcp get" ]] && exit 1 +exit "${CLAUDE_EXIT_CODE:-0}" +CLAUDE + chmod +x "$BATS_TEST_TMPDIR/bin/claude" + + export PATH="$BATS_TEST_TMPDIR/bin:/usr/bin:/bin" +} + +_run_main() { + run bash -c "source '$LOG_LIB' && source '$LIB' && $1 && main" +} + +@test "main: registers each server with user scope and transport" { + _run_main "AI_MCP=('grep|http|https://mcp.grep.app' 'tavily|http|https://mcp.tavily.com/mcp/')" + + assert_success + assert_line "[ai-mcp] Registering MCP servers..." + assert_line "[ai-mcp] MCP servers registered." + assert_line "mcp add --scope user --transport http grep https://mcp.grep.app" + assert_line "mcp add --scope user --transport http tavily https://mcp.tavily.com/mcp/" +} + +@test "main: skips a server that is already registered" { + cat >"$BATS_TEST_TMPDIR/bin/claude" <<'CLAUDE' +#!/usr/bin/env bash +printf '%s\n' "$*" | tee -a "$CLAUDE_ARGS_FILE" +# grep already exists; tavily does not. +[[ "$1 $2" == "mcp get" && "$3" == "grep" ]] && exit 0 +[[ "$1 $2" == "mcp get" ]] && exit 1 +exit 0 +CLAUDE + chmod +x "$BATS_TEST_TMPDIR/bin/claude" + + _run_main "AI_MCP=('grep|http|https://mcp.grep.app' 'tavily|http|https://mcp.tavily.com/mcp/')" + + assert_success + assert_line "[ai-mcp] grep already registered; skipping." + refute_line "mcp add --scope user --transport http grep https://mcp.grep.app" + assert_line "mcp add --scope user --transport http tavily https://mcp.tavily.com/mcp/" +} + +@test "main: exits cleanly with no servers" { + _run_main "AI_MCP=()" + + assert_success + assert_line "[ai-mcp] No MCP servers to register." + [ ! -s "$CLAUDE_ARGS_FILE" ] +} + +@test "main: skips empty server entries" { + _run_main "AI_MCP=('grep|http:x' '' 'jira|http|https://mcp.atlassian.com/v1/mcp')" + + assert_success + assert_line "mcp add --scope user --transport http jira https://mcp.atlassian.com/v1/mcp" +} + +@test "main: warns but succeeds when a server fails to register" { + cat >"$BATS_TEST_TMPDIR/bin/claude" <<'CLAUDE' +#!/usr/bin/env bash +printf '%s\n' "$*" | tee -a "$CLAUDE_ARGS_FILE" +[[ "$1 $2" == "mcp get" ]] && exit 1 +[[ "$1 $2" == "mcp add" ]] && exit 1 +exit 0 +CLAUDE + chmod +x "$BATS_TEST_TMPDIR/bin/claude" + + _run_main "AI_MCP=('grep|http|https://mcp.grep.app')" + + assert_success + assert_line "warn: [ai-mcp] 1 server(s) failed to register:" + assert_line " - grep" + assert_line "[ai-mcp] MCP servers registered." +} + +@test "main: fails when claude is missing" { + rm -f "$BATS_TEST_TMPDIR/bin/claude" + # Drop /usr/bin so a host-installed claude cannot mask the missing path; /bin still provides bash. + export PATH="$BATS_TEST_TMPDIR/bin:/bin" + _run_main "AI_MCP=('grep|http|https://mcp.grep.app')" + + assert_failure 1 + assert_line "error: [ai-mcp] claude CLI not found. Ensure Claude Code is installed." +} From 38aa217c0875164bd60f471889b57501e0b49962 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Wed, 1 Jul 2026 10:18:22 -0500 Subject: [PATCH 47/51] feat: add claude code MCP install run script --- .../run_onchange_08_install-ai-mcp.sh.tmpl | 17 +++++++++++ tests/template/darwin-install-scripts.bats | 30 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 home/.chezmoiscripts/darwin/run_onchange_08_install-ai-mcp.sh.tmpl diff --git a/home/.chezmoiscripts/darwin/run_onchange_08_install-ai-mcp.sh.tmpl b/home/.chezmoiscripts/darwin/run_onchange_08_install-ai-mcp.sh.tmpl new file mode 100644 index 0000000..c5a47b7 --- /dev/null +++ b/home/.chezmoiscripts/darwin/run_onchange_08_install-ai-mcp.sh.tmpl @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# @file run_onchange_08_install-ai-mcp.sh +# @brief Register cross-agent MCP servers. +# @description AI data hash: {{ include ".chezmoidata/ai.yaml" | sha256sum }} +{{- $activeTargets := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .ai.targets) | fromJson -}} +{{- $activeMcp := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .ai.mcp) | fromJson -}} + +{{ if and (eq .chezmoi.os "darwin") (has "claude-code" $activeTargets) (gt (len $activeMcp) 0) }} +{{ template "lib/common/log.sh" . }} +{{ template "lib/common/install-prelude.sh" . }} +AI_MCP=( +{{ range $activeMcp -}} + "{{ .name }}|{{ .transport }}|{{ .url }}" +{{ end -}} +) +{{ template "lib/install/ai-mcp.sh" . }} +{{ end }} diff --git a/tests/template/darwin-install-scripts.bats b/tests/template/darwin-install-scripts.bats index b562dbe..a434a21 100644 --- a/tests/template/darwin-install-scripts.bats +++ b/tests/template/darwin-install-scripts.bats @@ -11,6 +11,7 @@ UV_TOOLS_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_03_in GRAPHIFY_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_09_install-graphify-skills.sh.tmpl" AI_SKILLS_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_07_install-ai-skills.sh.tmpl" AI_PLUGINS_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_06_install-ai-plugins.sh.tmpl" +AI_MCP_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_08_install-ai-mcp.sh.tmpl" @test "darwin install script templates render with bash shebang" { for template in "$DOTFILES_ROOT"/home/.chezmoiscripts/darwin/*.tmpl; do @@ -204,3 +205,32 @@ AI_PLUGINS_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_06_ refute_line 'AI_PLUGIN_TARGETS=(' refute_line --partial 'ai_plugins_install_main' } + +@test "darwin install script templates inject AI MCP shell library" { + assert_file_contains "$AI_MCP_TEMPLATE" '{{ template "lib/install/ai-mcp.sh" . }}' +} + +@test "darwin install script templates inject the prelude before AI MCP" { + local prelude + prelude='{{ template "lib/common/install-prelude.sh" . }}' + assert_file_contains "$AI_MCP_TEMPLATE" "$prelude" +} + +@test "personal AI MCP template registers shared and personal servers" { + run render_chezmoi_template "$AI_MCP_TEMPLATE" "$DARWIN_DATA" + assert_success + assert_line --partial '"grep|http|https://mcp.grep.app"' + assert_line --partial '"tavily|http|https://mcp.tavily.com/mcp/"' +} + +@test "work AI MCP template registers no Claude servers" { + run render_chezmoi_template "$AI_MCP_TEMPLATE" "$WORK_DATA" + assert_success + refute_line --partial 'lib/install/ai-mcp.sh' +} + +@test "empty AI target groups render no AI MCP commands" { + run render_chezmoi_template "$AI_MCP_TEMPLATE" "$EMPTY_AI_DATA" + assert_success + refute_line --partial 'lib/install/ai-mcp.sh' +} From dc801df0d139eb61873671e240531958b985e0e2 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Wed, 1 Jul 2026 10:23:09 -0500 Subject: [PATCH 48/51] feat: render copilot MCP config from ai.yaml --- .../Application Support/Code/User/mcp.json | 1 - .../Code/User/mcp.json.tmpl | 9 ++++ home/dot_copilot/mcp-config.json | 3 -- home/dot_copilot/mcp-config.json.tmpl | 9 ++++ tests/template/mcp-config.bats | 46 +++++++++++++++++++ 5 files changed, 64 insertions(+), 4 deletions(-) delete mode 100644 home/Library/Application Support/Code/User/mcp.json create mode 100644 home/Library/Application Support/Code/User/mcp.json.tmpl delete mode 100644 home/dot_copilot/mcp-config.json create mode 100644 home/dot_copilot/mcp-config.json.tmpl create mode 100644 tests/template/mcp-config.bats diff --git a/home/Library/Application Support/Code/User/mcp.json b/home/Library/Application Support/Code/User/mcp.json deleted file mode 100644 index 0967ef4..0000000 --- a/home/Library/Application Support/Code/User/mcp.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/home/Library/Application Support/Code/User/mcp.json.tmpl b/home/Library/Application Support/Code/User/mcp.json.tmpl new file mode 100644 index 0000000..5d5142f --- /dev/null +++ b/home/Library/Application Support/Code/User/mcp.json.tmpl @@ -0,0 +1,9 @@ +{{- $activeTargets := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .ai.targets) | fromJson -}} +{{- $activeMcp := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .ai.mcp) | fromJson -}} +{{- $servers := dict -}} +{{- if has "github-copilot" $activeTargets -}} +{{- range $activeMcp -}} +{{- $_ := set $servers .name (dict "type" .transport "url" .url) -}} +{{- end -}} +{{- end -}} +{{ dict "servers" $servers | toPrettyJson }} diff --git a/home/dot_copilot/mcp-config.json b/home/dot_copilot/mcp-config.json deleted file mode 100644 index da39e4f..0000000 --- a/home/dot_copilot/mcp-config.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "mcpServers": {} -} diff --git a/home/dot_copilot/mcp-config.json.tmpl b/home/dot_copilot/mcp-config.json.tmpl new file mode 100644 index 0000000..83e8fab --- /dev/null +++ b/home/dot_copilot/mcp-config.json.tmpl @@ -0,0 +1,9 @@ +{{- $activeTargets := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .ai.targets) | fromJson -}} +{{- $activeMcp := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .ai.mcp) | fromJson -}} +{{- $servers := dict -}} +{{- if has "github-copilot" $activeTargets -}} +{{- range $activeMcp -}} +{{- $_ := set $servers .name (dict "type" .transport "url" .url "tools" (list "*")) -}} +{{- end -}} +{{- end -}} +{{ dict "mcpServers" $servers | toPrettyJson }} diff --git a/tests/template/mcp-config.bats b/tests/template/mcp-config.bats new file mode 100644 index 0000000..2bb6fa4 --- /dev/null +++ b/tests/template/mcp-config.bats @@ -0,0 +1,46 @@ +#!/usr/bin/env bats +# @file tests/template/mcp-config.bats +# @brief Render tests for the Copilot MCP config templates. + +load '../test_helpers/load.bash' +load '../test_helpers/templates.bash' + +COPILOT_MCP="$DOTFILES_ROOT/home/dot_copilot/mcp-config.json.tmpl" +VSCODE_MCP="$DOTFILES_ROOT/home/Library/Application Support/Code/User/mcp.json.tmpl" + +# WORK_DATA and PERSONAL_DATA come from templates.bash; the real ai.yaml supplies +# .ai (targets and mcp) because chezmoi merges --override-data over .chezmoidata. +# A work machine targets github-copilot and gets shared + work MCP servers; a +# personal machine has no github-copilot target, so the maps render empty. + +@test "work Copilot CLI MCP config renders shared and work servers" { + run render_chezmoi_template "$COPILOT_MCP" "$WORK_DATA" + assert_success + assert_line --partial '"mcpServers"' + assert_line --partial '"grep"' + assert_line --partial '"figma"' + assert_line --partial '"jira"' + assert_line --partial 'https://mcp.figma.com/mcp' + assert_line --partial '"tools"' +} + +@test "personal Copilot CLI MCP config renders an empty server map" { + run render_chezmoi_template "$COPILOT_MCP" "$PERSONAL_DATA" + assert_success + assert_line --partial '"mcpServers": {}' +} + +@test "work VS Code MCP config renders shared and work servers" { + run render_chezmoi_template "$VSCODE_MCP" "$WORK_DATA" + assert_success + assert_line --partial '"servers"' + assert_line --partial '"grep"' + assert_line --partial '"jira"' + assert_line --partial 'https://mcp.atlassian.com/v1/mcp' +} + +@test "personal VS Code MCP config renders an empty server map" { + run render_chezmoi_template "$VSCODE_MCP" "$PERSONAL_DATA" + assert_success + assert_line --partial '"servers": {}' +} From 34b78a5264b6f8182e7fb761b4eb1e9760738595 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Wed, 1 Jul 2026 10:29:00 -0500 Subject: [PATCH 49/51] test: strengthen MCP config and skip-empty assertions --- tests/template/mcp-config.bats | 1 + tests/unit/lib/install/ai-mcp.bats | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/template/mcp-config.bats b/tests/template/mcp-config.bats index 2bb6fa4..6234bfd 100644 --- a/tests/template/mcp-config.bats +++ b/tests/template/mcp-config.bats @@ -37,6 +37,7 @@ VSCODE_MCP="$DOTFILES_ROOT/home/Library/Application Support/Code/User/mcp.json.t assert_line --partial '"grep"' assert_line --partial '"jira"' assert_line --partial 'https://mcp.atlassian.com/v1/mcp' + refute_line --partial '"tools"' } @test "personal VS Code MCP config renders an empty server map" { diff --git a/tests/unit/lib/install/ai-mcp.bats b/tests/unit/lib/install/ai-mcp.bats index 8043431..c505acc 100644 --- a/tests/unit/lib/install/ai-mcp.bats +++ b/tests/unit/lib/install/ai-mcp.bats @@ -65,9 +65,10 @@ CLAUDE } @test "main: skips empty server entries" { - _run_main "AI_MCP=('grep|http:x' '' 'jira|http|https://mcp.atlassian.com/v1/mcp')" + _run_main "AI_MCP=('grep|http|https://mcp.grep.app' '' 'jira|http|https://mcp.atlassian.com/v1/mcp')" assert_success + assert_line "mcp add --scope user --transport http grep https://mcp.grep.app" assert_line "mcp add --scope user --transport http jira https://mcp.atlassian.com/v1/mcp" } From d01fa3d9a80db51ea405cf453b18dbd98f4c9c1b Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Wed, 1 Jul 2026 10:29:36 -0500 Subject: [PATCH 50/51] docs: add phase 3 MCP delivery plan --- ...7-01-apm-migration-phase-3-mcp-delivery.md | 599 ++++++++++++++++++ 1 file changed, 599 insertions(+) create mode 100644 docs/superpowers/plans/2026-07-01-apm-migration-phase-3-mcp-delivery.md diff --git a/docs/superpowers/plans/2026-07-01-apm-migration-phase-3-mcp-delivery.md b/docs/superpowers/plans/2026-07-01-apm-migration-phase-3-mcp-delivery.md new file mode 100644 index 0000000..f9845a8 --- /dev/null +++ b/docs/superpowers/plans/2026-07-01-apm-migration-phase-3-mcp-delivery.md @@ -0,0 +1,599 @@ +# APM Migration Phase 3: MCP Delivery Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Deliver the MCP servers declared in `ai.yaml` to Claude Code (imperatively via `claude mcp add`) and to GitHub Copilot (declaratively via rendered config files), driven by the same group and target data as skills and plugins. + +**Architecture:** One `ai.mcp` data source, two delivery idioms. Claude Code owns mutable state in `~/.claude.json`, so a `run_onchange` shell script registers servers with `claude mcp add` and stays idempotent via `claude mcp get`. Copilot config files are standalone, so chezmoi renders them whole. Each surface gates on its active target, so the machine's active MCP set (shared plus personal on a Claude machine, shared plus work on a Copilot machine) routes to the correct surface for free. + +**Tech Stack:** chezmoi templates, bash 3.2 (macOS), bats, `claude` CLI, sprig template functions (`dict`, `set`, `has`, `toPrettyJson`). + +--- + +## Background + +`ai.yaml` already declares MCP servers, grouped like every other install list: + +```yaml +mcp: + shared: + - { name: grep, transport: http, url: https://mcp.grep.app } + personal: + - { name: tavily, transport: http, url: https://mcp.tavily.com/mcp/ } + work: + - { name: figma, transport: http, url: https://mcp.figma.com/mcp } + - { name: jira, transport: http, url: https://mcp.atlassian.com/v1/mcp } +``` + +`lib/chezmoi/active-group-values.json.tmpl` flattens the active groups (always `shared`; plus `personal` and/or `work`) into one list. On a personal/`claude-code` machine that yields `grep, tavily`; on a work/`github-copilot` machine it yields `grep, figma, jira`. The spec's routing (tavily to Claude only, figma and jira to Copilot only, grep to both) is the natural result of the group-to-target mapping. + +Confirmed target schemas: + +- Claude Code: `claude mcp add --scope user --transport http `. Idempotent guard: `claude mcp get `. +- Copilot CLI (`~/.copilot/mcp-config.json`): `{ "mcpServers": { "": { "type": "http", "url": "", "tools": ["*"] } } }`. +- VS Code Copilot (`~/Library/Application Support/Code/User/mcp.json`): `{ "servers": { "": { "type": "http", "url": "" } } }`. + +## File Structure + +**Create:** + +- `home/.chezmoitemplates/lib/install/ai-mcp.sh` — Claude-only MCP registration library, decomposed into small functions, mirroring `ai-skills.sh`. +- `tests/unit/lib/install/ai-mcp.bats` — behavior tests for the library, mirroring `ai-plugins.bats`. +- `home/.chezmoiscripts/darwin/run_onchange_08_install-ai-mcp.sh.tmpl` — renders the active Claude MCP list and injects the library. +- `home/dot_copilot/mcp-config.json.tmpl` — Copilot CLI MCP config, rendered from `ai.mcp`. +- `home/Library/Application Support/Code/User/mcp.json.tmpl` — VS Code Copilot MCP config, rendered from `ai.mcp`. +- `tests/template/mcp-config.bats` — render tests for the two declarative Copilot configs. + +**Rename (honor "graphify at the end"):** + +- `home/.chezmoiscripts/darwin/run_onchange_08_install-graphify-skills.sh.tmpl` -> `run_onchange_09_install-graphify-skills.sh.tmpl` (and its `@file` comment). + +**Modify:** + +- `tests/template/darwin-install-scripts.bats` — repoint `GRAPHIFY_TEMPLATE` to `_09_`; add the MCP run-script constant and tests. +- Delete: `home/dot_copilot/mcp-config.json` and `home/Library/Application Support/Code/User/mcp.json` (replaced by their `.tmpl` versions). + +## Design Notes + +- The Claude run script injects `log.sh` and `install-prelude.sh` before the library, matching every sibling install script (standardized scaffold). `ai-mcp.sh` itself only calls `log_*`, so its self-exec guard sources `log.sh` only, like `ai-plugins.sh`. +- The MCP run script gates on `has "claude-code" $activeTargets`, not merely `len > 0`, because Copilot MCP is delivered by the declarative templates, not this script. +- The Copilot templates always apply; when `github-copilot` is not an active target they render an empty server map, matching today's static `{}` files. + +--- + +### Task 1: Rename the Graphify script to slot 09 + +**Files:** + +- Rename: `home/.chezmoiscripts/darwin/run_onchange_08_install-graphify-skills.sh.tmpl` -> `home/.chezmoiscripts/darwin/run_onchange_09_install-graphify-skills.sh.tmpl` +- Modify: the renamed file's `@file` line +- Modify: `tests/template/darwin-install-scripts.bats:11` + +- [ ] **Step 1: Rename the script file** + +```bash +git mv "home/.chezmoiscripts/darwin/run_onchange_08_install-graphify-skills.sh.tmpl" \ + "home/.chezmoiscripts/darwin/run_onchange_09_install-graphify-skills.sh.tmpl" +``` + +- [ ] **Step 2: Update the `@file` comment inside the renamed file** + +Change line 2 from: + +```bash +# @file run_onchange_08_install-graphify-skills.sh +``` + +to: + +```bash +# @file run_onchange_09_install-graphify-skills.sh +``` + +- [ ] **Step 3: Repoint the test constant** + +In `tests/template/darwin-install-scripts.bats` line 11, change: + +```bash +GRAPHIFY_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_08_install-graphify-skills.sh.tmpl" +``` + +to: + +```bash +GRAPHIFY_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_09_install-graphify-skills.sh.tmpl" +``` + +- [ ] **Step 4: Run the template tests to verify the rename is clean** + +Run: `mise exec -- bats tests/template/darwin-install-scripts.bats` +Expected: all tests PASS (Graphify inject, render, and empty tests still green). + +- [ ] **Step 5: Commit** + +```bash +git add home/.chezmoiscripts/darwin/run_onchange_09_install-graphify-skills.sh.tmpl tests/template/darwin-install-scripts.bats +git commit -m "refactor: renumber graphify install script to 09" +``` + +--- + +### Task 2: MCP registration library with unit tests (TDD) + +**Files:** + +- Test: `tests/unit/lib/install/ai-mcp.bats` +- Create: `home/.chezmoitemplates/lib/install/ai-mcp.sh` + +- [ ] **Step 1: Write the failing unit tests** + +Create `tests/unit/lib/install/ai-mcp.bats`: + +```bash +#!/usr/bin/env bats +# @file tests/unit/lib/install/ai-mcp.bats +# @brief Behavior tests for home/.chezmoitemplates/lib/install/ai-mcp.sh. + +load '../../../test_helpers/load.bash' + +LOG_LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/common/log.sh" +LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/install/ai-mcp.sh" + +setup() { + export CLAUDE_ARGS_FILE="$BATS_TEST_TMPDIR/claude-args" + mkdir -p "$BATS_TEST_TMPDIR/bin" + + # Default stub: `mcp get` reports "not found" (exit 1) so `mcp add` runs. + cat >"$BATS_TEST_TMPDIR/bin/claude" <<'CLAUDE' +#!/usr/bin/env bash +printf '%s\n' "$*" | tee -a "$CLAUDE_ARGS_FILE" +[[ "$1 $2" == "mcp get" ]] && exit 1 +exit "${CLAUDE_EXIT_CODE:-0}" +CLAUDE + chmod +x "$BATS_TEST_TMPDIR/bin/claude" + + export PATH="$BATS_TEST_TMPDIR/bin:/usr/bin:/bin" +} + +_run_main() { + run bash -c "source '$LOG_LIB' && source '$LIB' && $1 && main" +} + +@test "main: registers each server with user scope and transport" { + _run_main "AI_MCP=('grep|http|https://mcp.grep.app' 'tavily|http|https://mcp.tavily.com/mcp/')" + + assert_success + assert_line "[ai-mcp] Registering MCP servers..." + assert_line "[ai-mcp] MCP servers registered." + assert_line "mcp add --scope user --transport http grep https://mcp.grep.app" + assert_line "mcp add --scope user --transport http tavily https://mcp.tavily.com/mcp/" +} + +@test "main: skips a server that is already registered" { + cat >"$BATS_TEST_TMPDIR/bin/claude" <<'CLAUDE' +#!/usr/bin/env bash +printf '%s\n' "$*" | tee -a "$CLAUDE_ARGS_FILE" +# grep already exists; tavily does not. +[[ "$1 $2" == "mcp get" && "$3" == "grep" ]] && exit 0 +[[ "$1 $2" == "mcp get" ]] && exit 1 +exit 0 +CLAUDE + chmod +x "$BATS_TEST_TMPDIR/bin/claude" + + _run_main "AI_MCP=('grep|http|https://mcp.grep.app' 'tavily|http|https://mcp.tavily.com/mcp/')" + + assert_success + assert_line "[ai-mcp] grep already registered; skipping." + refute_line "mcp add --scope user --transport http grep https://mcp.grep.app" + assert_line "mcp add --scope user --transport http tavily https://mcp.tavily.com/mcp/" +} + +@test "main: exits cleanly with no servers" { + _run_main "AI_MCP=()" + + assert_success + assert_line "[ai-mcp] No MCP servers to register." + [ ! -s "$CLAUDE_ARGS_FILE" ] +} + +@test "main: skips empty server entries" { + _run_main "AI_MCP=('grep|http:x' '' 'jira|http|https://mcp.atlassian.com/v1/mcp')" + + assert_success + assert_line "mcp add --scope user --transport http jira https://mcp.atlassian.com/v1/mcp" +} + +@test "main: warns but succeeds when a server fails to register" { + cat >"$BATS_TEST_TMPDIR/bin/claude" <<'CLAUDE' +#!/usr/bin/env bash +printf '%s\n' "$*" | tee -a "$CLAUDE_ARGS_FILE" +[[ "$1 $2" == "mcp get" ]] && exit 1 +[[ "$1 $2" == "mcp add" ]] && exit 1 +exit 0 +CLAUDE + chmod +x "$BATS_TEST_TMPDIR/bin/claude" + + _run_main "AI_MCP=('grep|http|https://mcp.grep.app')" + + assert_success + assert_line "warn: [ai-mcp] 1 server(s) failed to register:" + assert_line " - grep" + assert_line "[ai-mcp] MCP servers registered." +} + +@test "main: fails when claude is missing" { + rm -f "$BATS_TEST_TMPDIR/bin/claude" + # Drop /usr/bin so a host-installed claude cannot mask the missing path; /bin still provides bash. + export PATH="$BATS_TEST_TMPDIR/bin:/bin" + _run_main "AI_MCP=('grep|http|https://mcp.grep.app')" + + assert_failure 1 + assert_line "error: [ai-mcp] claude CLI not found. Ensure Claude Code is installed." +} +``` + +- [ ] **Step 2: Run the tests to verify they fail** + +Run: `mise exec -- bats tests/unit/lib/install/ai-mcp.bats` +Expected: FAIL (the library file does not exist yet). + +- [ ] **Step 3: Write the library** + +Create `home/.chezmoitemplates/lib/install/ai-mcp.sh`: + +```bash +#!/usr/bin/env bash +# @file lib/install/ai-mcp.sh +# @brief Register cross-agent MCP servers on Claude Code. +# @description +# Runs `claude mcp add` for each server in AI_MCP, scoped to the user. A +# `claude mcp get` check keeps the flow idempotent by skipping a server that +# is already registered. Copilot MCP arrives through rendered config files, +# so this library targets Claude Code only. Sourceable from bats tests and +# injected into chezmoi run scripts via chezmoi template rendering. + +set -Eeuo pipefail + +# +# @description True when AI_MCP holds at least one non-empty entry. +# @exitcode 0 A server is present. +# @exitcode 1 No servers. +# +function _ai_mcp_have_any() { + local entry + for entry in "${AI_MCP[@]-}"; do + [[ -z "${entry}" ]] && continue + return 0 + done + return 1 +} + +# +# @description Warn about the MCP servers that failed to register. +# @arg $@ string Names of the servers that failed. +# +function _ai_mcp_report_failures() { + log_warn "[ai-mcp] ${#} server(s) failed to register:" + printf ' - %s\n' "$@" >&2 +} + +# +# @description Register each MCP server on Claude Code, skipping existing ones. +# @exitcode 0 Registered, or nothing to do. +# @exitcode 1 The claude CLI is missing. +# +function ai_mcp_install_main() { + if ! _ai_mcp_have_any; then + log_info "[ai-mcp] No MCP servers to register." + return 0 + fi + + if ! command -v claude >/dev/null 2>&1; then + log_error "[ai-mcp] claude CLI not found. Ensure Claude Code is installed." + return 1 + fi + + log_info "[ai-mcp] Registering MCP servers..." + + local -a failed=() + local entry name transport url rest + for entry in "${AI_MCP[@]-}"; do + [[ -z "${entry}" ]] && continue + name="${entry%%|*}" + rest="${entry#*|}" + transport="${rest%%|*}" + url="${rest##*|}" + + if claude mcp get "${name}" >/dev/null 2>&1; then + log_info "[ai-mcp] ${name} already registered; skipping." + continue + fi + + claude mcp add --scope user --transport "${transport}" "${name}" "${url}" || failed+=("${name}") + done + + if ((${#failed[@]} > 0)); then + _ai_mcp_report_failures "${failed[@]}" + fi + + log_info "[ai-mcp] MCP servers registered." +} + +# +# @description Run the AI MCP registration flow. +# +function main() { + ai_mcp_install_main +} + +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + # When run directly, pull in the shared log library that chezmoi otherwise + # concatenates ahead of this file. + # shellcheck source=/dev/null + command -v log_info >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/log.sh" + main +fi +``` + +- [ ] **Step 4: Run the tests to verify they pass** + +Run: `mise exec -- bats tests/unit/lib/install/ai-mcp.bats` +Expected: all PASS. + +- [ ] **Step 5: Commit** + +```bash +git add home/.chezmoitemplates/lib/install/ai-mcp.sh tests/unit/lib/install/ai-mcp.bats +git commit -m "feat: add MCP registration library for claude code" +``` + +--- + +### Task 3: Claude MCP run script with template tests + +**Files:** + +- Create: `home/.chezmoiscripts/darwin/run_onchange_08_install-ai-mcp.sh.tmpl` +- Modify: `tests/template/darwin-install-scripts.bats` + +- [ ] **Step 1: Add the template test constant and tests** + +In `tests/template/darwin-install-scripts.bats`, add near the other template constants (after the AI plugins constant): + +```bash +AI_MCP_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_08_install-ai-mcp.sh.tmpl" +``` + +Add these tests (mirroring the AI plugins tests, using the file's existing `render_chezmoi_template`, `DARWIN_DATA`, `WORK_DATA`, and `EMPTY_AI_DATA` helpers): + +```bash +@test "darwin install script templates inject AI MCP shell library" { + assert_file_contains "$AI_MCP_TEMPLATE" '{{ template "lib/install/ai-mcp.sh" . }}' +} + +@test "darwin install script templates inject the prelude before AI MCP" { + local prelude + prelude='{{ template "lib/common/install-prelude.sh" . }}' + assert_file_contains "$AI_MCP_TEMPLATE" "$prelude" +} + +@test "personal AI MCP template registers shared and personal servers" { + run render_chezmoi_template "$AI_MCP_TEMPLATE" "$DARWIN_DATA" + assert_success + assert_line --partial '"grep|http|https://mcp.grep.app"' + assert_line --partial '"tavily|http|https://mcp.tavily.com/mcp/"' +} + +@test "work AI MCP template registers no Claude servers" { + run render_chezmoi_template "$AI_MCP_TEMPLATE" "$WORK_DATA" + assert_success + refute_line --partial 'lib/install/ai-mcp.sh' +} + +@test "empty AI target groups render no AI MCP commands" { + run render_chezmoi_template "$AI_MCP_TEMPLATE" "$EMPTY_AI_DATA" + assert_success + refute_line --partial 'lib/install/ai-mcp.sh' +} +``` + +Note: the "work" render produces no Claude MCP block because the gate requires `claude-code` in the active targets; a work machine targets `github-copilot`, so the `if` is false and the library is not injected. + +- [ ] **Step 2: Run the new tests to verify they fail** + +Run: `mise exec -- bats tests/template/darwin-install-scripts.bats` +Expected: the five new tests FAIL (template file does not exist yet). + +- [ ] **Step 3: Create the run script template** + +Create `home/.chezmoiscripts/darwin/run_onchange_08_install-ai-mcp.sh.tmpl`: + +```bash +#!/usr/bin/env bash +# @file run_onchange_08_install-ai-mcp.sh +# @brief Register cross-agent MCP servers. +# @description AI data hash: {{ include ".chezmoidata/ai.yaml" | sha256sum }} +{{- $activeTargets := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .ai.targets) | fromJson -}} +{{- $activeMcp := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .ai.mcp) | fromJson -}} + +{{ if and (eq .chezmoi.os "darwin") (has "claude-code" $activeTargets) (gt (len $activeMcp) 0) }} +{{ template "lib/common/log.sh" . }} +{{ template "lib/common/install-prelude.sh" . }} +AI_MCP=( +{{ range $activeMcp -}} + "{{ .name }}|{{ .transport }}|{{ .url }}" +{{ end -}} +) +{{ template "lib/install/ai-mcp.sh" . }} +{{ end }} +``` + +- [ ] **Step 4: Run the tests to verify they pass** + +Run: `mise exec -- bats tests/template/darwin-install-scripts.bats` +Expected: all PASS. + +- [ ] **Step 5: Commit** + +```bash +git add home/.chezmoiscripts/darwin/run_onchange_08_install-ai-mcp.sh.tmpl tests/template/darwin-install-scripts.bats +git commit -m "feat: add claude code MCP install run script" +``` + +--- + +### Task 4: Copilot MCP config templates with render tests + +**Files:** + +- Create: `home/dot_copilot/mcp-config.json.tmpl` +- Create: `home/Library/Application Support/Code/User/mcp.json.tmpl` +- Delete: `home/dot_copilot/mcp-config.json`, `home/Library/Application Support/Code/User/mcp.json` +- Create: `tests/template/mcp-config.bats` + +- [ ] **Step 1: Write the failing render tests** + +Create `tests/template/mcp-config.bats`: + +```bash +#!/usr/bin/env bats +# @file tests/template/mcp-config.bats +# @brief Render tests for the Copilot MCP config templates. + +load '../test_helpers/load.bash' +load '../test_helpers/templates.bash' + +COPILOT_MCP="$DOTFILES_ROOT/home/dot_copilot/mcp-config.json.tmpl" +VSCODE_MCP="$DOTFILES_ROOT/home/Library/Application Support/Code/User/mcp.json.tmpl" + +# WORK_DATA and PERSONAL_DATA come from templates.bash; the real ai.yaml supplies +# .ai (targets and mcp) because chezmoi merges --override-data over .chezmoidata. +# A work machine targets github-copilot and gets shared + work MCP servers; a +# personal machine has no github-copilot target, so the maps render empty. + +@test "work Copilot CLI MCP config renders shared and work servers" { + run render_chezmoi_template "$COPILOT_MCP" "$WORK_DATA" + assert_success + assert_line --partial '"mcpServers"' + assert_line --partial '"grep"' + assert_line --partial '"figma"' + assert_line --partial '"jira"' + assert_line --partial 'https://mcp.figma.com/mcp' + assert_line --partial '"tools"' +} + +@test "personal Copilot CLI MCP config renders an empty server map" { + run render_chezmoi_template "$COPILOT_MCP" "$PERSONAL_DATA" + assert_success + assert_line --partial '"mcpServers": {}' +} + +@test "work VS Code MCP config renders shared and work servers" { + run render_chezmoi_template "$VSCODE_MCP" "$WORK_DATA" + assert_success + assert_line --partial '"servers"' + assert_line --partial '"grep"' + assert_line --partial '"jira"' + assert_line --partial 'https://mcp.atlassian.com/v1/mcp' +} + +@test "personal VS Code MCP config renders an empty server map" { + run render_chezmoi_template "$VSCODE_MCP" "$PERSONAL_DATA" + assert_success + assert_line --partial '"servers": {}' +} +``` + +- [ ] **Step 2: Run the tests to verify they fail** + +Run: `mise exec -- bats tests/template/mcp-config.bats` +Expected: FAIL (template files do not exist yet). + +- [ ] **Step 3: Create the Copilot CLI template and remove the static file** + +```bash +git rm home/dot_copilot/mcp-config.json +``` + +Create `home/dot_copilot/mcp-config.json.tmpl`: + +``` +{{- $activeTargets := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .ai.targets) | fromJson -}} +{{- $activeMcp := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .ai.mcp) | fromJson -}} +{{- $servers := dict -}} +{{- if has "github-copilot" $activeTargets -}} +{{- range $activeMcp -}} +{{- $_ := set $servers .name (dict "type" .transport "url" .url "tools" (list "*")) -}} +{{- end -}} +{{- end -}} +{{ dict "mcpServers" $servers | toPrettyJson }} +``` + +- [ ] **Step 4: Create the VS Code template and remove the static file** + +```bash +git rm "home/Library/Application Support/Code/User/mcp.json" +``` + +Create `home/Library/Application Support/Code/User/mcp.json.tmpl`: + +``` +{{- $activeTargets := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .ai.targets) | fromJson -}} +{{- $activeMcp := includeTemplate "lib/chezmoi/active-group-values.json.tmpl" (dict "ctx" . "valuesByGroup" .ai.mcp) | fromJson -}} +{{- $servers := dict -}} +{{- if has "github-copilot" $activeTargets -}} +{{- range $activeMcp -}} +{{- $_ := set $servers .name (dict "type" .transport "url" .url) -}} +{{- end -}} +{{- end -}} +{{ dict "servers" $servers | toPrettyJson }} +``` + +- [ ] **Step 5: Run the tests to verify they pass** + +Run: `mise exec -- bats tests/template/mcp-config.bats` +Expected: all PASS. + +- [ ] **Step 6: Commit** + +```bash +git add home/dot_copilot/mcp-config.json.tmpl "home/Library/Application Support/Code/User/mcp.json.tmpl" tests/template/mcp-config.bats +git add -u home/dot_copilot "home/Library/Application Support/Code/User" +git commit -m "feat: render copilot MCP config from ai.yaml" +``` + +--- + +### Task 5: Full verification and apply + +**Files:** none (verification only) + +- [ ] **Step 1: Run the full check suite** + +Run: `mise run check` +Expected: treefmt clean and all bats tests PASS. + +- [ ] **Step 2: Preview the apply on this (personal) machine** + +Run: `chezmoi diff --source home | rg -A3 -i 'mcp'` +Expected: `~/.copilot/mcp-config.json` shows `"mcpServers": {}` (unchanged), `~/Library/.../Code/User/mcp.json` shows `"servers": {}`, and the new `run_onchange_08_install-ai-mcp` script is queued. + +- [ ] **Step 3: Apply** + +Run: `mise update` +Expected: the run script registers `grep` and `tavily` on Claude Code (or skips them if already present). Confirm with `claude mcp list`. + +- [ ] **Step 4: Confirm idempotency** + +Run: `mise update` a second time. +Expected: the MCP script logs `already registered; skipping.` for each server and adds nothing. + +- [ ] **Step 5: Verify the Linear issue and open the PR** + +Ensure a Linear `DOT-*` issue tracks Phase 3, move it to `In Review`, and open the PR from the `DOT-*` branch with `Refs DOT-XXX` in the body. + +## Self-Review + +- **Spec coverage:** Claude `claude mcp add` script (spec "MCP Design"), Copilot CLI `mcp-config.json` and VS Code `mcp.json` templates (spec lines 143 to 148), `tavily` personal to Claude only and `grep` to both (spec line 212) — all covered by the group-to-target gating. +- **Type consistency:** the `name|transport|url` field order is identical in the run-script render (`"{{ .name }}|{{ .transport }}|{{ .url }}"`) and the library parse (`%%|`, `#*|`, `##*|`). The library function names (`_ai_mcp_have_any`, `_ai_mcp_report_failures`, `ai_mcp_install_main`, `main`) match between the library and its tests. +- **Placeholder scan:** every step contains the actual file content or command; no TBDs. From 8a5093ca99b668cb7e392b544631750540644e27 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Wed, 1 Jul 2026 10:40:06 -0500 Subject: [PATCH 51/51] feat: add skipDangerousModePermissionPrompt setting --- home/dot_claude/settings.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/home/dot_claude/settings.json b/home/dot_claude/settings.json index 33df2e0..e821c10 100644 --- a/home/dot_claude/settings.json +++ b/home/dot_claude/settings.json @@ -195,5 +195,6 @@ "autoMemoryEnabled": true, "showThinkingSummaries": true, "skipWorkflowUsageWarning": true, - "autoScrollEnabled": true + "autoScrollEnabled": true, + "skipDangerousModePermissionPrompt": true }