@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.
Files changed (37) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +11 -42
  3. package/install/index.mjs +9 -101
  4. package/package.json +1 -1
  5. package/pipeline/agents/android-architect.md +3 -3
  6. package/pipeline/agents/backend-architect.md +3 -3
  7. package/pipeline/agents/code-reviewer.md +3 -3
  8. package/pipeline/agents/ios-architect.md +3 -3
  9. package/pipeline/commands/multi-agent/finish.md +4 -4
  10. package/pipeline/commands/multi-agent/refs/features/model-fallback.md +18 -10
  11. package/pipeline/commands/multi-agent/refs/phases/phase-4-review.md +1 -11
  12. package/pipeline/commands/multi-agent/setup.md +18 -1
  13. package/pipeline/commands/multi-agent/stack.md +1 -1
  14. package/pipeline/commands/multi-agent/sync.md +5 -74
  15. package/pipeline/commands/multi-agent/update.md +9 -0
  16. package/pipeline/scripts/README.md +0 -1
  17. package/pipeline/scripts/build-stack-plugins.mjs +2 -2
  18. package/pipeline/scripts/smoke-cross-cli-behavior.sh +0 -7
  19. package/pipeline/scripts/smoke-install-layout.sh +1 -2
  20. package/pipeline/scripts/smoke-model-fallback.sh +11 -10
  21. package/pipeline/skills/shared/core/multi-agent-finish/SKILL.md +1 -1
  22. package/pipeline/skills/shared/core/multi-agent-stack/SKILL.md +1 -1
  23. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +1 -1
  24. package/install/_adapters.mjs +0 -73
  25. package/pipeline/adapters/_base.mjs +0 -640
  26. package/pipeline/adapters/antigravity.mjs +0 -140
  27. package/pipeline/adapters/codex.mjs +0 -159
  28. package/pipeline/adapters/copilot-chat-orchestration.mjs +0 -148
  29. package/pipeline/adapters/copilot-chat.mjs +0 -124
  30. package/pipeline/adapters/cursor-orchestration.mjs +0 -152
  31. package/pipeline/adapters/cursor.mjs +0 -146
  32. package/pipeline/scripts/smoke-adapters.sh +0 -276
  33. package/pipeline/scripts/smoke-shared-runtime.sh +0 -108
  34. package/pipeline/scripts/smoke-stack-swap.sh +0 -132
  35. package/pipeline/scripts/smoke-sync-adapters.sh +0 -113
  36. package/pipeline/scripts/stack-swap.sh +0 -182
  37. 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)\"}"