diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a1f8045..dc1752e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -149,7 +149,7 @@ repos: description: Ensure SKILL.md files have required frontmatter fields entry: bash language: system - files: 'SKILL\.md$' + files: '^plugins/[^/]+/\.apm/skills/[^/]+/SKILL\.md$' args: - -c - | diff --git a/scripts/sync-plugin-content.sh b/scripts/sync-plugin-content.sh index aad8a54..0db3c9b 100755 --- a/scripts/sync-plugin-content.sh +++ b/scripts/sync-plugin-content.sh @@ -117,12 +117,17 @@ sync_hooks_json() { printf '%s\n' "$(cat "$src")" >"$dst" } +# Runs entirely inside a backgrounded subshell (see the dispatch loop below), so +# FAIL here is that subshell's own copy -- it never touches the parent's FAIL +# and must be handed back via status_file instead. sync_one() { - local plugin_dir="${1%/}" + local plugin_dir="${1%/}" status_file="$2" local apm_dir="$plugin_dir/.apm" + FAIL=0 if [[ ! -d "$apm_dir" ]]; then echo "SKIP $plugin_dir: no .apm/ directory" >&2 + echo "$FAIL" >"$status_file" return 0 fi @@ -140,6 +145,7 @@ sync_one() { sed 's/^/ /' "$pack_log" >&2 rm -f "$pack_log" FAIL=1 + echo "$FAIL" >"$status_file" return 0 fi rm -f "$pack_log" @@ -148,6 +154,7 @@ sync_one() { if [[ -z "$bundle_dir" ]]; then echo "FAIL $plugin_dir: apm pack produced no bundle directory under $scratch" >&2 FAIL=1 + echo "$FAIL" >"$status_file" return 0 fi @@ -156,10 +163,26 @@ sync_one() { sync_dir "$plugin_dir" "$bundle_dir" "$d" done sync_hooks_json "$plugin_dir" "$bundle_dir" + echo "$FAIL" >"$status_file" } -for plugin_dir in "$@"; do - sync_one "$plugin_dir" +# Each plugin's `apm pack` is an independent CLI invocation dominated by fixed +# process-startup cost, not by per-plugin work -- run them concurrently rather +# than paying that startup cost N times serially. Output is buffered per plugin +# (not streamed) so concurrent DRIFT/FAIL messages from different plugins never +# interleave; it's flushed in stable $@ order once every job has finished. +declare -a plugin_dirs=("$@") +for plugin_dir in "${plugin_dirs[@]}"; do + name="$(basename "${plugin_dir%/}")" + (sync_one "$plugin_dir" "$SCRATCH_ROOT/$name.status") >"$SCRATCH_ROOT/$name.log" 2>&1 & +done +wait + +for plugin_dir in "${plugin_dirs[@]}"; do + name="$(basename "${plugin_dir%/}")" + cat "$SCRATCH_ROOT/$name.log" >&2 + status="$(cat "$SCRATCH_ROOT/$name.status" 2>/dev/null || echo 1)" + [[ "$status" -ne 0 ]] && FAIL=1 done if [[ "$FAIL" -ne 0 ]]; then