@mmerterden/multi-agent-pipeline 10.5.0 → 10.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +55 -0
- package/README.md +11 -42
- package/install/index.mjs +9 -101
- package/package.json +1 -1
- package/pipeline/agents/android-architect.md +3 -3
- package/pipeline/agents/backend-architect.md +3 -3
- package/pipeline/agents/code-reviewer.md +3 -3
- package/pipeline/agents/ios-architect.md +3 -3
- package/pipeline/commands/multi-agent/finish.md +4 -4
- package/pipeline/commands/multi-agent/refs/features/model-fallback.md +18 -10
- package/pipeline/commands/multi-agent/refs/phases/phase-4-review.md +1 -11
- package/pipeline/commands/multi-agent/setup.md +18 -1
- package/pipeline/commands/multi-agent/stack.md +1 -1
- package/pipeline/commands/multi-agent/sync.md +5 -74
- package/pipeline/commands/multi-agent/update.md +9 -0
- package/pipeline/scripts/README.md +0 -1
- package/pipeline/scripts/build-stack-plugins.mjs +2 -2
- package/pipeline/scripts/smoke-cross-cli-behavior.sh +0 -7
- package/pipeline/scripts/smoke-install-layout.sh +1 -2
- package/pipeline/scripts/smoke-model-fallback.sh +11 -10
- package/pipeline/skills/shared/core/multi-agent-finish/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-stack/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +1 -1
- package/install/_adapters.mjs +0 -73
- package/pipeline/adapters/_base.mjs +0 -640
- package/pipeline/adapters/antigravity.mjs +0 -140
- package/pipeline/adapters/codex.mjs +0 -159
- package/pipeline/adapters/copilot-chat-orchestration.mjs +0 -148
- package/pipeline/adapters/copilot-chat.mjs +0 -124
- package/pipeline/adapters/cursor-orchestration.mjs +0 -152
- package/pipeline/adapters/cursor.mjs +0 -146
- package/pipeline/scripts/smoke-adapters.sh +0 -276
- package/pipeline/scripts/smoke-shared-runtime.sh +0 -108
- package/pipeline/scripts/smoke-stack-swap.sh +0 -132
- package/pipeline/scripts/smoke-sync-adapters.sh +0 -113
- package/pipeline/scripts/stack-swap.sh +0 -182
- package/pipeline/scripts/sync-adapters.mjs +0 -183
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# smoke-shared-runtime.sh - the 3 adapter platforms (Cursor, Antigravity, VS
|
|
3
|
-
# Code Copilot Chat) must install the gate scripts to a shared ~/.multi-agent/
|
|
4
|
-
# runtime AND rewrite their emitted agent/command references to that absolute
|
|
5
|
-
# path, so the deterministic gates can actually execute (not just be referenced).
|
|
6
|
-
#
|
|
7
|
-
# Runs in a sandbox HOME so the developer's real ~/.multi-agent is untouched.
|
|
8
|
-
# Exit 0 = all pass, 1 = any failure.
|
|
9
|
-
|
|
10
|
-
set -uo pipefail
|
|
11
|
-
|
|
12
|
-
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
13
|
-
PASS=0; FAIL=0
|
|
14
|
-
pass() { PASS=$((PASS+1)); echo " ✓ $1"; }
|
|
15
|
-
fail() { FAIL=$((FAIL+1)); echo " ✗ $1"; }
|
|
16
|
-
command -v node >/dev/null 2>&1 || { echo "error: node required" >&2; exit 127; }
|
|
17
|
-
|
|
18
|
-
run_adapter() {
|
|
19
|
-
# $1 = adapter module path, $2 = sandbox HOME, $3 = target
|
|
20
|
-
HOME="$2" TARGET="$3" node -e '
|
|
21
|
-
import(process.argv[1]).then(m => {
|
|
22
|
-
m.default.install({ pipelineSrc: process.cwd()+"/pipeline", target: process.env.TARGET, platformFilter: "all" });
|
|
23
|
-
}).catch(e => { console.error(e.message); process.exit(1); });
|
|
24
|
-
' "$1" >/dev/null 2>&1
|
|
25
|
-
}
|
|
26
|
-
uninstall_adapter() {
|
|
27
|
-
HOME="$2" TARGET="$3" node -e '
|
|
28
|
-
import(process.argv[1]).then(m => { m.default.uninstall({ target: process.env.TARGET }); }).catch(()=>process.exit(1));
|
|
29
|
-
' "$1" >/dev/null 2>&1
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
for adapter in cursor antigravity copilot-chat; do
|
|
33
|
-
echo "→ $adapter"
|
|
34
|
-
SBHOME=$(mktemp -d); TGT=$(mktemp -d); git init -q "$TGT" 2>/dev/null
|
|
35
|
-
run_adapter "$ROOT/pipeline/adapters/$adapter.mjs" "$SBHOME" "$TGT"
|
|
36
|
-
|
|
37
|
-
# 1. shared runtime populated with the gate scripts + lib + schemas
|
|
38
|
-
if [ -f "$SBHOME/.multi-agent/scripts/evidence-gate.mjs" ] \
|
|
39
|
-
&& [ -f "$SBHOME/.multi-agent/scripts/learnings-ledger.mjs" ] \
|
|
40
|
-
&& [ -f "$SBHOME/.multi-agent/lib/classify-intent.sh" ] \
|
|
41
|
-
&& [ -f "$SBHOME/.multi-agent/schemas/triage-output.schema.json" ]; then
|
|
42
|
-
pass "$adapter: gate scripts installed to shared runtime"
|
|
43
|
-
else
|
|
44
|
-
fail "$adapter: shared runtime missing gate scripts"
|
|
45
|
-
fi
|
|
46
|
-
|
|
47
|
-
# 2. dev-only / PII files NOT copied into the shared runtime
|
|
48
|
-
if [ -f "$SBHOME/.multi-agent/scripts/figma-placeholder-map.json" ] \
|
|
49
|
-
|| [ -f "$SBHOME/.multi-agent/scripts/smoke-personal-data.sh" ]; then
|
|
50
|
-
fail "$adapter: dev-only/PII file leaked into shared runtime"
|
|
51
|
-
else
|
|
52
|
-
pass "$adapter: dev-only/PII files excluded from shared runtime"
|
|
53
|
-
fi
|
|
54
|
-
|
|
55
|
-
# 3. The ORCHESTRATION files (agents + command/prompt/workflow - NOT the
|
|
56
|
-
# knowledge-layer rules, which legitimately mention pipeline/scripts in
|
|
57
|
-
# prose) reference the absolute runtime path and carry no bare,
|
|
58
|
-
# non-resolving pipeline/scripts invocation.
|
|
59
|
-
case "$adapter" in
|
|
60
|
-
cursor) orch=$(ls "$TGT/.cursor/agents/"*.md "$TGT/.cursor/commands/multi-agent.md" 2>/dev/null) ;;
|
|
61
|
-
antigravity) orch=$(ls "$TGT/.agent/workflows/multi-agent.md" 2>/dev/null) ;;
|
|
62
|
-
copilot-chat) orch=$(ls "$TGT/.github/agents/"*.agent.md "$TGT/.github/prompts/multi-agent.prompt.md" 2>/dev/null) ;;
|
|
63
|
-
esac
|
|
64
|
-
if [ -n "$orch" ]; then
|
|
65
|
-
if grep -qF '$HOME/.multi-agent/' $orch 2>/dev/null; then
|
|
66
|
-
pass "$adapter: orchestration files reference the shared runtime path"
|
|
67
|
-
else
|
|
68
|
-
fail "$adapter: no shared-runtime reference in orchestration files"
|
|
69
|
-
fi
|
|
70
|
-
if grep -qE '[^.a-z/]pipeline/scripts/' $orch 2>/dev/null; then
|
|
71
|
-
fail "$adapter: a bare pipeline/scripts/ invocation survived (won't resolve)"
|
|
72
|
-
else
|
|
73
|
-
pass "$adapter: no unresolved bare pipeline/scripts/ invocation"
|
|
74
|
-
fi
|
|
75
|
-
else
|
|
76
|
-
fail "$adapter: no orchestration files found"
|
|
77
|
-
fi
|
|
78
|
-
|
|
79
|
-
# 3b. cross-vendor 2-model review: a second reviewer pinned to a different
|
|
80
|
-
# model (cursor + copilot-chat; antigravity is dropdown-selected so it
|
|
81
|
-
# documents the pair in the workflow instead).
|
|
82
|
-
case "$adapter" in
|
|
83
|
-
cursor)
|
|
84
|
-
r1=$(grep '^model:' "$TGT/.cursor/agents/ma-code-reviewer.md" 2>/dev/null)
|
|
85
|
-
r2=$(grep '^model:' "$TGT/.cursor/agents/ma-code-reviewer-x.md" 2>/dev/null)
|
|
86
|
-
if [ -n "$r2" ] && [ "$r1" != "$r2" ]; then pass "cursor: two reviewers on distinct models ($r1 vs $r2)"; else fail "cursor: missing distinct cross-model reviewer"; fi ;;
|
|
87
|
-
copilot-chat)
|
|
88
|
-
r1=$(grep '^model:' "$TGT/.github/agents/ma-code-reviewer.agent.md" 2>/dev/null)
|
|
89
|
-
r2=$(grep '^model:' "$TGT/.github/agents/ma-code-reviewer-x.agent.md" 2>/dev/null)
|
|
90
|
-
if [ -n "$r2" ] && [ "$r1" != "$r2" ]; then pass "copilot-chat: two reviewers on distinct models"; else fail "copilot-chat: missing distinct cross-model reviewer"; fi ;;
|
|
91
|
-
antigravity)
|
|
92
|
-
if grep -qi "different model" "$TGT/.agent/workflows/multi-agent.md" 2>/dev/null; then pass "antigravity: workflow documents diverse-model parallel reviewers"; else fail "antigravity: workflow missing diverse-model instruction"; fi ;;
|
|
93
|
-
esac
|
|
94
|
-
|
|
95
|
-
# 4. uninstall removes the shared runtime
|
|
96
|
-
uninstall_adapter "$ROOT/pipeline/adapters/$adapter.mjs" "$SBHOME" "$TGT"
|
|
97
|
-
if [ -d "$SBHOME/.multi-agent" ]; then
|
|
98
|
-
fail "$adapter: uninstall left the shared runtime behind"
|
|
99
|
-
else
|
|
100
|
-
pass "$adapter: uninstall removed the shared runtime"
|
|
101
|
-
fi
|
|
102
|
-
|
|
103
|
-
rm -rf "$SBHOME" "$TGT"
|
|
104
|
-
done
|
|
105
|
-
|
|
106
|
-
echo ""
|
|
107
|
-
echo "══ shared-runtime smoke: $PASS passed, $FAIL failed ══"
|
|
108
|
-
[ "$FAIL" -eq 0 ]
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# smoke-stack-swap.sh - verify stack-swap.sh argument dispatch and skill movement.
|
|
3
|
-
# Bootstraps a temp HOME with fixture skills, runs the script in forced mode for
|
|
4
|
-
# each supported stack, asserts skills move between active and inactive
|
|
5
|
-
# directories, and validates the JSON systemMessage output contract.
|
|
6
|
-
#
|
|
7
|
-
# Covers v5.3.4+ /multi-agent:stack command backend: ios, android, mobile,
|
|
8
|
-
# backend, frontend, fullstack, all, status, and unknown-arg rejection.
|
|
9
|
-
|
|
10
|
-
set -uo pipefail
|
|
11
|
-
|
|
12
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
13
|
-
STACK_SWAP="$SCRIPT_DIR/stack-swap.sh"
|
|
14
|
-
|
|
15
|
-
if [ ! -x "$STACK_SWAP" ]; then
|
|
16
|
-
echo "FAIL: stack-swap.sh not found or not executable at $STACK_SWAP" >&2
|
|
17
|
-
exit 1
|
|
18
|
-
fi
|
|
19
|
-
|
|
20
|
-
PASS=0
|
|
21
|
-
FAIL=0
|
|
22
|
-
pass() { PASS=$((PASS+1)); echo " ✓ $1"; }
|
|
23
|
-
fail() { FAIL=$((FAIL+1)); echo " ✗ $1"; }
|
|
24
|
-
|
|
25
|
-
TMPHOME=$(mktemp -d) || { echo "FAIL: mktemp" >&2; exit 1; }
|
|
26
|
-
trap 'rm -rf "$TMPHOME"' EXIT
|
|
27
|
-
|
|
28
|
-
export HOME="$TMPHOME"
|
|
29
|
-
SKILLS="$TMPHOME/.claude/skills"
|
|
30
|
-
INACTIVE="$TMPHOME/.claude/skills-inactive"
|
|
31
|
-
mkdir -p "$SKILLS" "$INACTIVE"
|
|
32
|
-
|
|
33
|
-
# Fixture skill dirs - one matching each stack's pattern catalog.
|
|
34
|
-
bootstrap_fixtures() {
|
|
35
|
-
rm -rf "$SKILLS" "$INACTIVE"
|
|
36
|
-
mkdir -p "$SKILLS" "$INACTIVE"
|
|
37
|
-
# iOS pattern hits
|
|
38
|
-
mkdir -p "$SKILLS/swiftui-patterns" "$SKILLS/ios-networking" "$SKILLS/hig-foundations"
|
|
39
|
-
# Android pattern hits
|
|
40
|
-
mkdir -p "$SKILLS/android-security" "$SKILLS/compose-navigation" "$SKILLS/kotlin-coroutines-expert"
|
|
41
|
-
# Backend pattern hits
|
|
42
|
-
mkdir -p "$SKILLS/fastapi-patterns" "$SKILLS/nodejs-tips"
|
|
43
|
-
# Frontend pattern hits
|
|
44
|
-
mkdir -p "$SKILLS/react-best-practices" "$SKILLS/nextjs-app-router"
|
|
45
|
-
# Unrelated skill that should never move
|
|
46
|
-
mkdir -p "$SKILLS/general-writing"
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
echo "→ 1. status mode prints JSON with active+inactive counts, no file movement"
|
|
50
|
-
bootstrap_fixtures
|
|
51
|
-
COUNT_BEFORE=$(ls "$SKILLS" | wc -l | tr -d ' ')
|
|
52
|
-
OUT=$("$STACK_SWAP" status 2>&1)
|
|
53
|
-
if echo "$OUT" | grep -q '"systemMessage"'; then
|
|
54
|
-
pass "status emits {\"systemMessage\": ...} JSON"
|
|
55
|
-
else
|
|
56
|
-
fail "status output missing systemMessage field: $OUT"
|
|
57
|
-
fi
|
|
58
|
-
COUNT_AFTER=$(ls "$SKILLS" | wc -l | tr -d ' ')
|
|
59
|
-
if [ "$COUNT_BEFORE" = "$COUNT_AFTER" ]; then
|
|
60
|
-
pass "status does not move files (before=$COUNT_BEFORE after=$COUNT_AFTER)"
|
|
61
|
-
else
|
|
62
|
-
fail "status moved files (before=$COUNT_BEFORE after=$COUNT_AFTER)"
|
|
63
|
-
fi
|
|
64
|
-
|
|
65
|
-
echo ""
|
|
66
|
-
echo "→ 2. ios mode keeps iOS skills active, deactivates Android/backend/frontend"
|
|
67
|
-
bootstrap_fixtures
|
|
68
|
-
"$STACK_SWAP" ios >/dev/null 2>&1
|
|
69
|
-
[ -d "$SKILLS/swiftui-patterns" ] && pass "iOS skill stays active: swiftui-patterns" || fail "swiftui-patterns missing from active"
|
|
70
|
-
[ -d "$SKILLS/ios-networking" ] && pass "iOS skill stays active: ios-networking" || fail "ios-networking missing from active"
|
|
71
|
-
[ -d "$INACTIVE/android-security" ] && pass "Android skill moved to inactive: android-security" || fail "android-security not in inactive"
|
|
72
|
-
[ -d "$INACTIVE/react-best-practices" ] && pass "Frontend skill moved to inactive: react-best-practices" || fail "react-best-practices not in inactive"
|
|
73
|
-
[ -d "$SKILLS/general-writing" ] && pass "Unrelated skill untouched: general-writing" || fail "general-writing was moved"
|
|
74
|
-
|
|
75
|
-
echo ""
|
|
76
|
-
echo "→ 3. android mode keeps Android skills active, deactivates iOS"
|
|
77
|
-
bootstrap_fixtures
|
|
78
|
-
"$STACK_SWAP" android >/dev/null 2>&1
|
|
79
|
-
[ -d "$SKILLS/android-security" ] && pass "Android skill stays active" || fail "android-security missing from active"
|
|
80
|
-
[ -d "$SKILLS/compose-navigation" ] && pass "Compose skill stays active" || fail "compose-navigation missing from active"
|
|
81
|
-
[ -d "$INACTIVE/swiftui-patterns" ] && pass "iOS skill moved to inactive" || fail "swiftui-patterns not in inactive"
|
|
82
|
-
|
|
83
|
-
echo ""
|
|
84
|
-
echo "→ 4. mobile mode keeps iOS AND Android active, deactivates backend"
|
|
85
|
-
bootstrap_fixtures
|
|
86
|
-
"$STACK_SWAP" mobile >/dev/null 2>&1
|
|
87
|
-
[ -d "$SKILLS/swiftui-patterns" ] && pass "mobile keeps iOS active" || fail "iOS deactivated by mobile"
|
|
88
|
-
[ -d "$SKILLS/android-security" ] && pass "mobile keeps Android active" || fail "Android deactivated by mobile"
|
|
89
|
-
[ -d "$INACTIVE/fastapi-patterns" ] && pass "backend moved to inactive under mobile" || fail "backend stayed active under mobile"
|
|
90
|
-
|
|
91
|
-
echo ""
|
|
92
|
-
echo "→ 5. all mode keeps every stack active"
|
|
93
|
-
bootstrap_fixtures
|
|
94
|
-
# Pre-deactivate something so "all" has work to do
|
|
95
|
-
mv "$SKILLS/fastapi-patterns" "$INACTIVE/fastapi-patterns"
|
|
96
|
-
"$STACK_SWAP" all >/dev/null 2>&1
|
|
97
|
-
[ -d "$SKILLS/swiftui-patterns" ] && pass "all keeps iOS" || fail "all dropped iOS"
|
|
98
|
-
[ -d "$SKILLS/android-security" ] && pass "all keeps Android" || fail "all dropped Android"
|
|
99
|
-
[ -d "$SKILLS/fastapi-patterns" ] && pass "all re-activates backend" || fail "all did not re-activate backend"
|
|
100
|
-
|
|
101
|
-
echo ""
|
|
102
|
-
echo "→ 6. unknown arg returns exit 1 with error message"
|
|
103
|
-
bootstrap_fixtures
|
|
104
|
-
OUT=$("$STACK_SWAP" badstack 2>&1)
|
|
105
|
-
RC=$?
|
|
106
|
-
if [ "$RC" -eq 1 ]; then
|
|
107
|
-
pass "unknown arg exits 1"
|
|
108
|
-
else
|
|
109
|
-
fail "unknown arg exit code was $RC, expected 1"
|
|
110
|
-
fi
|
|
111
|
-
if echo "$OUT" | grep -qi "unknown"; then
|
|
112
|
-
pass "unknown arg error message mentions 'unknown'"
|
|
113
|
-
else
|
|
114
|
-
fail "unknown arg message lacks expected text: $OUT"
|
|
115
|
-
fi
|
|
116
|
-
|
|
117
|
-
echo ""
|
|
118
|
-
echo "→ 7. exit code 0 on every valid mode"
|
|
119
|
-
bootstrap_fixtures
|
|
120
|
-
for arg in status ios android mobile backend frontend all; do
|
|
121
|
-
"$STACK_SWAP" "$arg" >/dev/null 2>&1
|
|
122
|
-
RC=$?
|
|
123
|
-
if [ "$RC" -eq 0 ]; then
|
|
124
|
-
pass "$arg exits 0"
|
|
125
|
-
else
|
|
126
|
-
fail "$arg exit code was $RC"
|
|
127
|
-
fi
|
|
128
|
-
done
|
|
129
|
-
|
|
130
|
-
echo ""
|
|
131
|
-
echo "══ stack-swap smoke: $PASS passed, $FAIL failed ══"
|
|
132
|
-
[ "$FAIL" -eq 0 ] || exit 1
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# smoke-sync-adapters.sh
|
|
3
|
-
#
|
|
4
|
-
# Verifies the per-project adapter sync runner:
|
|
5
|
-
# 1. pipeline/scripts/sync-adapters.mjs exists and is executable
|
|
6
|
-
# 2. node sync-adapters.mjs --help exits 0
|
|
7
|
-
# 3. --doctor on empty prefs reports a clean no-op
|
|
8
|
-
# 4. --target=<empty-dir> reports [skip] (no adapter markers)
|
|
9
|
-
# 5. --target=. on the pipeline repo itself triggers cursor adapter
|
|
10
|
-
# (pipeline + .git makes cwd the canonical cursor consumer)
|
|
11
|
-
# 6. After running on pipeline repo, .cursor/rules/ exists and has
|
|
12
|
-
# >100 .mdc files
|
|
13
|
-
# 7. .cursorrules legacy digest exists
|
|
14
|
-
# 8. Unknown arg exits non-zero
|
|
15
|
-
# 9. sync.md references sync-adapters.mjs (so the docs and the runtime agree)
|
|
16
|
-
#
|
|
17
|
-
# Exit 0 = all pass, 1 = any failure.
|
|
18
|
-
|
|
19
|
-
set -uo pipefail
|
|
20
|
-
|
|
21
|
-
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
22
|
-
RUNNER="$ROOT/pipeline/scripts/sync-adapters.mjs"
|
|
23
|
-
SYNC_MD="$ROOT/pipeline/commands/multi-agent/sync.md"
|
|
24
|
-
|
|
25
|
-
pass=0
|
|
26
|
-
fail=0
|
|
27
|
-
failures=()
|
|
28
|
-
record_pass() { pass=$((pass + 1)); printf ' \033[0;32mPASS\033[0m %s\n' "$1"; }
|
|
29
|
-
record_fail() { fail=$((fail + 1)); failures+=("$1"); printf ' \033[0;31mFAIL\033[0m %s\n' "$1"; }
|
|
30
|
-
|
|
31
|
-
printf '→ smoke-sync-adapters: per-project adapter sync runner contract\n'
|
|
32
|
-
|
|
33
|
-
# 1. Runner exists + executable
|
|
34
|
-
if [ ! -f "$RUNNER" ]; then
|
|
35
|
-
record_fail "pipeline/scripts/sync-adapters.mjs missing"
|
|
36
|
-
elif [ ! -x "$RUNNER" ]; then
|
|
37
|
-
record_fail "sync-adapters.mjs not executable"
|
|
38
|
-
else
|
|
39
|
-
record_pass "sync-adapters.mjs exists and is executable"
|
|
40
|
-
fi
|
|
41
|
-
|
|
42
|
-
# 2. --help exits 0
|
|
43
|
-
if node "$RUNNER" --help >/dev/null 2>&1; then
|
|
44
|
-
record_pass "--help exits 0"
|
|
45
|
-
else
|
|
46
|
-
record_fail "--help should exit 0"
|
|
47
|
-
fi
|
|
48
|
-
|
|
49
|
-
# 3. --doctor on empty env runs without error
|
|
50
|
-
if node "$RUNNER" --doctor 2>&1 | grep -qE "projectsTouched is empty|sync-adapters: 0 ok"; then
|
|
51
|
-
record_pass "--doctor reports clean state when projectsTouched is empty"
|
|
52
|
-
else
|
|
53
|
-
record_pass "--doctor exits cleanly" # doctor on a populated env is also fine
|
|
54
|
-
fi
|
|
55
|
-
|
|
56
|
-
# 4. --target=<empty-dir>
|
|
57
|
-
EMPTY=$(mktemp -d)
|
|
58
|
-
out=$(node "$RUNNER" --target="$EMPTY" 2>&1)
|
|
59
|
-
if echo "$out" | grep -q "\[skip\]"; then
|
|
60
|
-
record_pass "empty target reports [skip]"
|
|
61
|
-
else
|
|
62
|
-
record_fail "empty target should report [skip] (got: $out)"
|
|
63
|
-
fi
|
|
64
|
-
rm -rf "$EMPTY"
|
|
65
|
-
|
|
66
|
-
# 5. Pipeline repo cursor sync - must produce output mentioning cursor
|
|
67
|
-
out=$(node "$RUNNER" --target="$ROOT" 2>&1)
|
|
68
|
-
if echo "$out" | grep -qE "\[cursor\]"; then
|
|
69
|
-
record_pass "pipeline repo target triggers cursor adapter"
|
|
70
|
-
else
|
|
71
|
-
record_fail "pipeline repo target did NOT trigger cursor adapter (got: $out)"
|
|
72
|
-
fi
|
|
73
|
-
|
|
74
|
-
# 6. .cursor/rules/ exists with >100 mdc
|
|
75
|
-
if [ -d "$ROOT/.cursor/rules" ]; then
|
|
76
|
-
count=$(ls "$ROOT/.cursor/rules"/*.mdc 2>/dev/null | wc -l | tr -d ' ')
|
|
77
|
-
if [ "$count" -gt 100 ]; then
|
|
78
|
-
record_pass ".cursor/rules contains $count .mdc files (>100 expected)"
|
|
79
|
-
else
|
|
80
|
-
record_fail ".cursor/rules has only $count files (expected >100)"
|
|
81
|
-
fi
|
|
82
|
-
else
|
|
83
|
-
record_fail ".cursor/rules dir missing"
|
|
84
|
-
fi
|
|
85
|
-
|
|
86
|
-
# 7. Legacy .cursorrules
|
|
87
|
-
if [ -f "$ROOT/.cursorrules" ]; then
|
|
88
|
-
record_pass ".cursorrules legacy digest exists"
|
|
89
|
-
else
|
|
90
|
-
record_fail ".cursorrules legacy digest missing"
|
|
91
|
-
fi
|
|
92
|
-
|
|
93
|
-
# 8. Unknown arg
|
|
94
|
-
if node "$RUNNER" --garbage >/dev/null 2>&1; then
|
|
95
|
-
record_fail "unknown arg should exit non-zero"
|
|
96
|
-
else
|
|
97
|
-
record_pass "unknown arg rejected"
|
|
98
|
-
fi
|
|
99
|
-
|
|
100
|
-
# 9. sync.md references the runner
|
|
101
|
-
if grep -qF "sync-adapters.mjs" "$SYNC_MD"; then
|
|
102
|
-
record_pass "sync.md references sync-adapters.mjs"
|
|
103
|
-
else
|
|
104
|
-
record_fail "sync.md missing sync-adapters.mjs reference"
|
|
105
|
-
fi
|
|
106
|
-
|
|
107
|
-
printf '\n══ sync-adapters smoke: %d passed, %d failed ══\n' "$pass" "$fail"
|
|
108
|
-
if [ "$fail" -gt 0 ]; then
|
|
109
|
-
printf '\nFailures:\n'
|
|
110
|
-
for msg in "${failures[@]}"; do printf ' - %s\n' "$msg"; done
|
|
111
|
-
exit 1
|
|
112
|
-
fi
|
|
113
|
-
exit 0
|
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# stack-swap.sh - Auto-detect project stack and swap skills
|
|
3
|
-
# Called by SessionStart hook
|
|
4
|
-
# Default: iOS. Swaps Android/backend in only when detected.
|
|
5
|
-
|
|
6
|
-
set -uo pipefail
|
|
7
|
-
|
|
8
|
-
SKILLS="$HOME/.claude/skills"
|
|
9
|
-
INACTIVE="$HOME/.claude/skills-inactive"
|
|
10
|
-
mkdir -p "$INACTIVE"
|
|
11
|
-
|
|
12
|
-
# Skill group definitions (folder name prefixes/patterns)
|
|
13
|
-
IOS_PATTERNS="swiftui|swift-|ios-|hig-|apple-|storekit|widgetkit|healthkit|homekit|mapkit|musickit|passkit|pencilkit|realitykit|weatherkit|alarmkit|callkit|cloudkit|coreml|core-|eventkit|energykit|permissionkit|tipkit|shareplay|live-activities|background-processing|app-store|app-clips|app-intents|authentication|contacts-framework|device-integrity|macos-|natural-language|photos-camera|push-notifications|speech-recognition|swiftdata|vision-framework|debugging-instruments|help-skills"
|
|
14
|
-
ANDROID_PATTERNS="android|compose-|kotlin-|room-database|retrofit-|gradle-|play-store"
|
|
15
|
-
BACKEND_PATTERNS="fastapi|nodejs|docker|api-pattern|api-security|github-actions|observability|architecture|monorepo|clean-code|debugging-strategies|agentflow|closed-loop|context-compression|python-patterns|database-patterns|rest-api-design|testing-backend|ci-cd-pipelines"
|
|
16
|
-
FRONTEND_PATTERNS="react-|nextjs-|typescript-|tailwind-|vue-|web-accessibility|web-performance|css-modern|web-testing|html-semantic"
|
|
17
|
-
|
|
18
|
-
activate_stack() {
|
|
19
|
-
local patterns="$1"
|
|
20
|
-
# Move matching skills from inactive to active
|
|
21
|
-
for dir in "$INACTIVE"/*/; do
|
|
22
|
-
[ -d "$dir" ] || continue
|
|
23
|
-
name=$(basename "$dir")
|
|
24
|
-
if echo "$name" | grep -qiE "$patterns"; then
|
|
25
|
-
mv "$dir" "$SKILLS/$name" 2>/dev/null
|
|
26
|
-
fi
|
|
27
|
-
done
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
deactivate_stack() {
|
|
31
|
-
local patterns="$1"
|
|
32
|
-
# Move matching skills from active to inactive
|
|
33
|
-
for dir in "$SKILLS"/*/; do
|
|
34
|
-
[ -d "$dir" ] || continue
|
|
35
|
-
name=$(basename "$dir")
|
|
36
|
-
if echo "$name" | grep -qiE "$patterns"; then
|
|
37
|
-
mv "$dir" "$INACTIVE/$name" 2>/dev/null
|
|
38
|
-
fi
|
|
39
|
-
done
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
# Manual mode: if argument provided, force that stack
|
|
43
|
-
FORCE_STACK="${1:-}"
|
|
44
|
-
|
|
45
|
-
if [ -n "$FORCE_STACK" ]; then
|
|
46
|
-
case "$FORCE_STACK" in
|
|
47
|
-
ios)
|
|
48
|
-
activate_stack "$IOS_PATTERNS"
|
|
49
|
-
deactivate_stack "$ANDROID_PATTERNS"
|
|
50
|
-
deactivate_stack "$BACKEND_PATTERNS"
|
|
51
|
-
deactivate_stack "$FRONTEND_PATTERNS"
|
|
52
|
-
;;
|
|
53
|
-
android)
|
|
54
|
-
deactivate_stack "$IOS_PATTERNS"
|
|
55
|
-
activate_stack "$ANDROID_PATTERNS"
|
|
56
|
-
deactivate_stack "$BACKEND_PATTERNS"
|
|
57
|
-
deactivate_stack "$FRONTEND_PATTERNS"
|
|
58
|
-
;;
|
|
59
|
-
mobile)
|
|
60
|
-
activate_stack "$IOS_PATTERNS"
|
|
61
|
-
activate_stack "$ANDROID_PATTERNS"
|
|
62
|
-
deactivate_stack "$BACKEND_PATTERNS"
|
|
63
|
-
deactivate_stack "$FRONTEND_PATTERNS"
|
|
64
|
-
;;
|
|
65
|
-
backend)
|
|
66
|
-
deactivate_stack "$IOS_PATTERNS"
|
|
67
|
-
deactivate_stack "$ANDROID_PATTERNS"
|
|
68
|
-
activate_stack "$BACKEND_PATTERNS"
|
|
69
|
-
deactivate_stack "$FRONTEND_PATTERNS"
|
|
70
|
-
;;
|
|
71
|
-
frontend)
|
|
72
|
-
deactivate_stack "$IOS_PATTERNS"
|
|
73
|
-
deactivate_stack "$ANDROID_PATTERNS"
|
|
74
|
-
deactivate_stack "$BACKEND_PATTERNS"
|
|
75
|
-
activate_stack "$FRONTEND_PATTERNS"
|
|
76
|
-
;;
|
|
77
|
-
fullstack)
|
|
78
|
-
deactivate_stack "$IOS_PATTERNS"
|
|
79
|
-
deactivate_stack "$ANDROID_PATTERNS"
|
|
80
|
-
activate_stack "$BACKEND_PATTERNS"
|
|
81
|
-
activate_stack "$FRONTEND_PATTERNS"
|
|
82
|
-
;;
|
|
83
|
-
all)
|
|
84
|
-
activate_stack "$IOS_PATTERNS"
|
|
85
|
-
activate_stack "$ANDROID_PATTERNS"
|
|
86
|
-
activate_stack "$BACKEND_PATTERNS"
|
|
87
|
-
activate_stack "$FRONTEND_PATTERNS"
|
|
88
|
-
;;
|
|
89
|
-
status)
|
|
90
|
-
ACTIVE=$(ls "$SKILLS" 2>/dev/null | wc -l | tr -d ' ')
|
|
91
|
-
INACTIVE_COUNT=$(ls "$INACTIVE" 2>/dev/null | wc -l | tr -d ' ')
|
|
92
|
-
echo "{\"systemMessage\": \"Active: $ACTIVE | Inactive: $INACTIVE_COUNT\"}"
|
|
93
|
-
exit 0
|
|
94
|
-
;;
|
|
95
|
-
*)
|
|
96
|
-
echo "{\"systemMessage\": \"Unknown stack: $FORCE_STACK. Use: ios, android, mobile, backend, frontend, fullstack, all, status\"}"
|
|
97
|
-
exit 1
|
|
98
|
-
;;
|
|
99
|
-
esac
|
|
100
|
-
ACTIVE=$(ls "$SKILLS" 2>/dev/null | wc -l | tr -d ' ')
|
|
101
|
-
echo "{\"systemMessage\": \"Stack switched to $FORCE_STACK ($ACTIVE skills active). Restart conversation for full effect.\"}"
|
|
102
|
-
exit 0
|
|
103
|
-
fi
|
|
104
|
-
|
|
105
|
-
# Auto-detect mode (SessionStart hook)
|
|
106
|
-
CWD=$(pwd)
|
|
107
|
-
|
|
108
|
-
# If cwd is $HOME, skip detection - keep default (iOS)
|
|
109
|
-
if [ "$CWD" = "$HOME" ]; then
|
|
110
|
-
activate_stack "$IOS_PATTERNS"
|
|
111
|
-
ACTIVE=$(ls "$SKILLS" 2>/dev/null | wc -l | tr -d ' ')
|
|
112
|
-
echo "{\"systemMessage\": \"Stack: iOS - default ($ACTIVE skills active)\"}"
|
|
113
|
-
exit 0
|
|
114
|
-
fi
|
|
115
|
-
|
|
116
|
-
HAS_IOS=$(find "$CWD" -maxdepth 2 \( -name "*.xcodeproj" -o -name "Package.swift" \) -not -path "*/.claude/*" -not -path "*/node_modules/*" 2>/dev/null | head -1)
|
|
117
|
-
HAS_ANDROID=$(find "$CWD" -maxdepth 2 \( -name "build.gradle" -o -name "build.gradle.kts" \) -not -path "*/.claude/*" 2>/dev/null | head -1)
|
|
118
|
-
HAS_FRONTEND=$(find "$CWD" -maxdepth 2 \( -name "next.config.*" -o -name "vite.config.*" -o -name "nuxt.config.*" -o -name "tailwind.config.*" -o -name "tsconfig.json" \) -not -path "*/.claude/*" -not -path "*/node_modules/*" 2>/dev/null | head -1)
|
|
119
|
-
HAS_BACKEND=$(find "$CWD" -maxdepth 2 \( -name "requirements.txt" -o -name "pyproject.toml" -o -name "Pipfile" -o -name "Dockerfile" \) -not -path "*/.claude/*" -not -path "*/node_modules/*" 2>/dev/null | head -1)
|
|
120
|
-
|
|
121
|
-
# Determine stack
|
|
122
|
-
if [ -n "$HAS_IOS" ] && [ -n "$HAS_ANDROID" ]; then
|
|
123
|
-
STACK="mobile"
|
|
124
|
-
elif [ -n "$HAS_ANDROID" ]; then
|
|
125
|
-
STACK="android"
|
|
126
|
-
elif [ -n "$HAS_IOS" ]; then
|
|
127
|
-
STACK="ios"
|
|
128
|
-
elif [ -n "$HAS_FRONTEND" ] && [ -n "$HAS_BACKEND" ]; then
|
|
129
|
-
STACK="fullstack"
|
|
130
|
-
elif [ -n "$HAS_FRONTEND" ]; then
|
|
131
|
-
STACK="frontend"
|
|
132
|
-
elif [ -n "$HAS_BACKEND" ]; then
|
|
133
|
-
STACK="backend"
|
|
134
|
-
else
|
|
135
|
-
STACK="ios" # default
|
|
136
|
-
fi
|
|
137
|
-
|
|
138
|
-
# Apply swap
|
|
139
|
-
case "$STACK" in
|
|
140
|
-
ios)
|
|
141
|
-
activate_stack "$IOS_PATTERNS"
|
|
142
|
-
deactivate_stack "$ANDROID_PATTERNS"
|
|
143
|
-
deactivate_stack "$BACKEND_PATTERNS"
|
|
144
|
-
MSG="iOS"
|
|
145
|
-
;;
|
|
146
|
-
android)
|
|
147
|
-
deactivate_stack "$IOS_PATTERNS"
|
|
148
|
-
activate_stack "$ANDROID_PATTERNS"
|
|
149
|
-
deactivate_stack "$BACKEND_PATTERNS"
|
|
150
|
-
MSG="Android"
|
|
151
|
-
;;
|
|
152
|
-
mobile)
|
|
153
|
-
activate_stack "$IOS_PATTERNS"
|
|
154
|
-
activate_stack "$ANDROID_PATTERNS"
|
|
155
|
-
deactivate_stack "$BACKEND_PATTERNS"
|
|
156
|
-
MSG="Mobile (iOS + Android)"
|
|
157
|
-
;;
|
|
158
|
-
frontend)
|
|
159
|
-
deactivate_stack "$IOS_PATTERNS"
|
|
160
|
-
deactivate_stack "$ANDROID_PATTERNS"
|
|
161
|
-
deactivate_stack "$BACKEND_PATTERNS"
|
|
162
|
-
activate_stack "$FRONTEND_PATTERNS"
|
|
163
|
-
MSG="Frontend"
|
|
164
|
-
;;
|
|
165
|
-
fullstack)
|
|
166
|
-
deactivate_stack "$IOS_PATTERNS"
|
|
167
|
-
deactivate_stack "$ANDROID_PATTERNS"
|
|
168
|
-
activate_stack "$BACKEND_PATTERNS"
|
|
169
|
-
activate_stack "$FRONTEND_PATTERNS"
|
|
170
|
-
MSG="Fullstack (Frontend + Backend)"
|
|
171
|
-
;;
|
|
172
|
-
backend)
|
|
173
|
-
deactivate_stack "$IOS_PATTERNS"
|
|
174
|
-
deactivate_stack "$ANDROID_PATTERNS"
|
|
175
|
-
activate_stack "$BACKEND_PATTERNS"
|
|
176
|
-
deactivate_stack "$FRONTEND_PATTERNS"
|
|
177
|
-
MSG="Backend"
|
|
178
|
-
;;
|
|
179
|
-
esac
|
|
180
|
-
|
|
181
|
-
ACTIVE=$(ls "$SKILLS" 2>/dev/null | wc -l | tr -d ' ')
|
|
182
|
-
echo "{\"systemMessage\": \"Stack: $MSG ($ACTIVE skills active)\"}"
|