shimwrappercheck 0.2.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 (48) hide show
  1. package/AGENTS.md +21 -0
  2. package/README.md +286 -0
  3. package/bin/git +5 -0
  4. package/bin/shim +5 -0
  5. package/bin/shimwrappercheck +2 -0
  6. package/bin/supabase +5 -0
  7. package/dashboard/.next/cache/config.json +7 -0
  8. package/dashboard/.next/package.json +1 -0
  9. package/dashboard/.next/routes-manifest.json +1 -0
  10. package/dashboard/.next/trace +1 -0
  11. package/dashboard/README.md +32 -0
  12. package/dashboard/app/agents/page.tsx +88 -0
  13. package/dashboard/app/api/agents-md/route.ts +68 -0
  14. package/dashboard/app/api/config/route.ts +51 -0
  15. package/dashboard/app/api/run-checks/route.ts +54 -0
  16. package/dashboard/app/api/settings/route.ts +126 -0
  17. package/dashboard/app/api/status/route.ts +38 -0
  18. package/dashboard/app/config/page.tsx +77 -0
  19. package/dashboard/app/globals.css +20 -0
  20. package/dashboard/app/layout.tsx +23 -0
  21. package/dashboard/app/page.tsx +122 -0
  22. package/dashboard/app/settings/page.tsx +422 -0
  23. package/dashboard/components/Nav.tsx +33 -0
  24. package/dashboard/components/StatusCard.tsx +27 -0
  25. package/dashboard/lib/presets.ts +97 -0
  26. package/dashboard/lib/projectRoot.ts +25 -0
  27. package/dashboard/next.config.js +6 -0
  28. package/dashboard/package-lock.json +5307 -0
  29. package/dashboard/package.json +28 -0
  30. package/dashboard/postcss.config.js +6 -0
  31. package/dashboard/tailwind.config.js +14 -0
  32. package/dashboard/tsconfig.json +20 -0
  33. package/docs/SHIM_WRAPPER_CONCEPT.md +79 -0
  34. package/docs/WORKFLOW_AND_GAP_ANALYSIS.md +159 -0
  35. package/package.json +24 -0
  36. package/scripts/cli-checked.sh +307 -0
  37. package/scripts/cli.js +23 -0
  38. package/scripts/fetch-edge-logs.sh +96 -0
  39. package/scripts/git-checked.sh +194 -0
  40. package/scripts/init.js +341 -0
  41. package/scripts/install.js +303 -0
  42. package/scripts/ping-edge-health.sh +113 -0
  43. package/scripts/setup.js +55 -0
  44. package/scripts/supabase-checked.sh +330 -0
  45. package/templates/ai-code-review.sh +217 -0
  46. package/templates/git-pre-push +41 -0
  47. package/templates/husky-pre-push +46 -0
  48. package/templates/run-checks.sh +67 -0
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env bash
2
+ # Fetch recent Edge Function logs after deploy (optional).
3
+ # Runs: supabase functions logs <function> --limit <n>
4
+ set -euo pipefail
5
+
6
+ resolve_project_root() {
7
+ if [[ -n "${SHIM_PROJECT_ROOT:-}" ]]; then
8
+ echo "$SHIM_PROJECT_ROOT"
9
+ return
10
+ fi
11
+ if command -v git >/dev/null 2>&1; then
12
+ local root
13
+ root="$(git rev-parse --show-toplevel 2>/dev/null || true)"
14
+ if [[ -n "$root" ]]; then
15
+ echo "$root"
16
+ return
17
+ fi
18
+ fi
19
+ pwd
20
+ }
21
+
22
+ PROJECT_ROOT="$(resolve_project_root)"
23
+ cd "$PROJECT_ROOT"
24
+
25
+ ARGS=("$@")
26
+ ARGS_TEXT=" ${*:-} "
27
+
28
+ # Only fetch logs when we just deployed functions
29
+ if [[ "$ARGS_TEXT" != *" functions "* ]]; then
30
+ exit 0
31
+ fi
32
+
33
+ function_names=()
34
+ if [[ "$ARGS_TEXT" == *" deploy "* ]]; then
35
+ for i in "${!ARGS[@]}"; do
36
+ if [[ "${ARGS[$i]}" == "deploy" ]]; then
37
+ next_index=$((i + 1))
38
+ if [[ $next_index -lt ${#ARGS[@]} ]]; then
39
+ candidate="${ARGS[$next_index]}"
40
+ if [[ -n "$candidate" ]] && [[ "$candidate" != -* ]]; then
41
+ function_names+=("$candidate")
42
+ fi
43
+ fi
44
+ break
45
+ fi
46
+ done
47
+ fi
48
+
49
+ if [[ -n "${SHIM_LOG_FUNCTIONS:-}" ]]; then
50
+ IFS=',' read -r -a extra_names <<< "${SHIM_LOG_FUNCTIONS}"
51
+ for name in "${extra_names[@]}"; do
52
+ name="$(echo "$name" | xargs)"
53
+ [[ -n "$name" ]] && function_names+=("$name")
54
+ done
55
+ fi
56
+
57
+ if [[ "${#function_names[@]}" -eq 0 ]]; then
58
+ if [[ -z "${SHIM_DEFAULT_FUNCTION+x}" ]]; then
59
+ default_fn="server"
60
+ else
61
+ default_fn="${SHIM_DEFAULT_FUNCTION}"
62
+ fi
63
+ if [[ -n "$default_fn" ]]; then
64
+ function_names+=("$default_fn")
65
+ else
66
+ echo "Edge logs: skipped (no function name detected; set SHIM_LOG_FUNCTIONS)"
67
+ exit 0
68
+ fi
69
+ fi
70
+
71
+ REAL_BIN="${SHIM_SUPABASE_BIN:-${SUPABASE_REAL_BIN:-}}"
72
+ if [[ -z "$REAL_BIN" ]] && [[ -f "$HOME/.supabase-real-bin" ]]; then
73
+ REAL_BIN="$(cat "$HOME/.supabase-real-bin")"
74
+ fi
75
+ if [[ -z "$REAL_BIN" ]]; then
76
+ REAL_BIN="$(command -v supabase || true)"
77
+ fi
78
+ if [[ -n "$REAL_BIN" ]] && { [[ "$REAL_BIN" == *"node_modules"* ]] || [[ "$REAL_BIN" == "$PROJECT_ROOT"* ]]; }; then
79
+ REAL_BIN=""
80
+ fi
81
+
82
+ log_limit="${SHIM_LOG_LIMIT:-30}"
83
+ if ! [[ "$log_limit" =~ ^[0-9]+$ ]]; then
84
+ log_limit=30
85
+ fi
86
+
87
+ for fn in "${function_names[@]}"; do
88
+ echo "Edge logs ($fn):"
89
+ if [[ -n "$REAL_BIN" ]] && [[ -x "$REAL_BIN" ]]; then
90
+ "$REAL_BIN" functions logs "$fn" --limit "$log_limit" 2>/dev/null || true
91
+ else
92
+ npx --yes --package supabase supabase functions logs "$fn" --limit "$log_limit" 2>/dev/null || true
93
+ fi
94
+ done
95
+
96
+ exit 0
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env bash
2
+ # Shim wrapper for Git: run checks (optional), then call real git.
3
+ set -euo pipefail
4
+
5
+ WRAPPER_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
6
+
7
+ resolve_project_root() {
8
+ if [[ -n "${SHIM_PROJECT_ROOT:-}" ]]; then
9
+ echo "$SHIM_PROJECT_ROOT"
10
+ return
11
+ fi
12
+ if command -v git >/dev/null 2>&1; then
13
+ local root
14
+ root="$(git rev-parse --show-toplevel 2>/dev/null || true)"
15
+ if [[ -n "$root" ]]; then
16
+ echo "$root"
17
+ return
18
+ fi
19
+ fi
20
+ pwd
21
+ }
22
+
23
+ PROJECT_ROOT="$(resolve_project_root)"
24
+ cd "$PROJECT_ROOT"
25
+
26
+ CONFIG_FILE="${SHIM_CONFIG_FILE:-$PROJECT_ROOT/.shimwrappercheckrc}"
27
+ if [[ -f "$CONFIG_FILE" ]]; then
28
+ # shellcheck disable=SC1090
29
+ source "$CONFIG_FILE"
30
+ fi
31
+
32
+ ARGS_IN=("$@")
33
+ ARGS_TEXT_RAW=" ${*:-} "
34
+ GIT_ARGS=()
35
+ CHECKS_PASSTHROUGH=()
36
+
37
+ RUN_CHECKS=true
38
+ CHECKS_ONLY=false
39
+
40
+ matches_command_list() {
41
+ local list="$1"
42
+ local text="$2"
43
+
44
+ list="$(echo "$list" | tr '[:upper:]' '[:lower:]')"
45
+ text="$(echo "$text" | tr '[:upper:]' '[:lower:]')"
46
+
47
+ if [[ -z "$list" ]] || [[ "$list" == "all" ]]; then
48
+ return 0
49
+ fi
50
+ if [[ "$list" == "none" ]]; then
51
+ return 1
52
+ fi
53
+
54
+ IFS=',' read -r -a items <<< "$list"
55
+ for item in "${items[@]}"; do
56
+ item="$(echo "$item" | xargs)"
57
+ [[ -z "$item" ]] && continue
58
+ if [[ "$text" == *" $item "* ]]; then
59
+ return 0
60
+ fi
61
+ done
62
+ return 1
63
+ }
64
+
65
+ for arg in "${ARGS_IN[@]}"; do
66
+ case "$arg" in
67
+ --no-checks) RUN_CHECKS=false ;;
68
+ --checks-only) CHECKS_ONLY=true ;;
69
+ --no-ai-review|--ai-review) CHECKS_PASSTHROUGH+=("$arg") ;;
70
+ *) GIT_ARGS+=("$arg") ;;
71
+ esac
72
+ done
73
+
74
+ [[ -n "${SHIM_DISABLE_CHECKS:-}" ]] && RUN_CHECKS=false
75
+
76
+ if [[ "${#GIT_ARGS[@]}" -eq 0 ]] && [[ "$CHECKS_ONLY" != true ]]; then
77
+ echo "No git command provided. Usage: git [shim flags] <git args>" >&2
78
+ echo "Shim flags: --no-checks --checks-only --no-ai-review" >&2
79
+ exit 1
80
+ fi
81
+
82
+ ARGS_TEXT=" ${GIT_ARGS[*]:-} "
83
+ if [[ "$CHECKS_ONLY" != true ]]; then
84
+ enforce_list="${SHIM_GIT_ENFORCE_COMMANDS:-push}"
85
+ if ! matches_command_list "$enforce_list" "$ARGS_TEXT"; then
86
+ RUN_CHECKS=false
87
+ fi
88
+ fi
89
+
90
+ resolve_checks_script() {
91
+ local script="${SHIM_GIT_CHECKS_SCRIPT:-}"
92
+ if [[ -n "$script" ]]; then
93
+ if [[ "$script" != /* ]]; then
94
+ script="$PROJECT_ROOT/$script"
95
+ fi
96
+ echo "$script"
97
+ return
98
+ fi
99
+ script="${SHIM_CHECKS_SCRIPT:-}"
100
+ if [[ -n "$script" ]]; then
101
+ if [[ "$script" != /* ]]; then
102
+ script="$PROJECT_ROOT/$script"
103
+ fi
104
+ echo "$script"
105
+ return
106
+ fi
107
+ local candidates=("scripts/run-checks.sh" "scripts/shim-checks.sh")
108
+ for candidate in "${candidates[@]}"; do
109
+ if [[ -f "$PROJECT_ROOT/$candidate" ]]; then
110
+ echo "$PROJECT_ROOT/$candidate"
111
+ return
112
+ fi
113
+ done
114
+ echo ""
115
+ }
116
+
117
+ if [[ "$RUN_CHECKS" = true ]]; then
118
+ run_frontend=false
119
+ run_backend=false
120
+ run_ai_review=true
121
+
122
+ changed_files=""
123
+ if command -v git >/dev/null 2>&1; then
124
+ if git rev-parse --abbrev-ref --symbolic-full-name @{u} >/dev/null 2>&1; then
125
+ RANGE="@{u}...HEAD"
126
+ changed_files="$(git diff --name-only --diff-filter=ACMR "$RANGE" || true)"
127
+ else
128
+ if git rev-parse --verify HEAD~1 >/dev/null 2>&1; then
129
+ changed_files="$(git diff --name-only --diff-filter=ACMR HEAD~1...HEAD || true)"
130
+ else
131
+ changed_files="$(git diff --name-only --diff-filter=ACMR --root HEAD || true)"
132
+ fi
133
+ fi
134
+ fi
135
+
136
+ if [[ -n "$changed_files" ]]; then
137
+ echo "$changed_files" | grep -q '^src/' && run_frontend=true
138
+ echo "$changed_files" | grep -q '^supabase/functions/' && run_backend=true
139
+ fi
140
+
141
+ if [[ "$ARGS_TEXT_RAW" == *" --no-ai-review "* ]]; then
142
+ run_ai_review=false
143
+ fi
144
+ if [[ -n "${SKIP_AI_REVIEW:-}" ]]; then
145
+ run_ai_review=false
146
+ fi
147
+
148
+ if [[ "$run_frontend" = true ]] || [[ "$run_backend" = true ]]; then
149
+ CHECKS_SCRIPT="$(resolve_checks_script)"
150
+ if [[ -n "$CHECKS_SCRIPT" ]]; then
151
+ CHECKS_ARGS=()
152
+ if [[ -n "${SHIM_GIT_CHECKS_ARGS:-}" ]]; then
153
+ read -r -a CHECKS_ARGS <<< "${SHIM_GIT_CHECKS_ARGS}"
154
+ elif [[ -n "${SHIM_CHECKS_ARGS:-}" ]]; then
155
+ read -r -a CHECKS_ARGS <<< "${SHIM_CHECKS_ARGS}"
156
+ fi
157
+ [[ "$run_frontend" = true ]] && CHECKS_ARGS+=(--frontend)
158
+ [[ "$run_backend" = true ]] && CHECKS_ARGS+=(--backend)
159
+ [[ "$run_ai_review" = false ]] && CHECKS_ARGS+=(--no-ai-review)
160
+ CHECKS_ARGS+=("${CHECKS_PASSTHROUGH[@]}")
161
+ bash "$CHECKS_SCRIPT" "${CHECKS_ARGS[@]}"
162
+ else
163
+ echo "Git shim checks: no checks script found; skipping." >&2
164
+ fi
165
+ fi
166
+ fi
167
+
168
+ if [[ "$CHECKS_ONLY" = true ]]; then
169
+ exit 0
170
+ fi
171
+
172
+ REAL_BIN="${SHIM_GIT_REAL_BIN:-${GIT_REAL_BIN:-}}"
173
+ if [[ -z "$REAL_BIN" ]]; then
174
+ REAL_BIN="$(command -v git || true)"
175
+ fi
176
+ if [[ -n "$REAL_BIN" ]] && { [[ "$REAL_BIN" == *"node_modules"* ]] || [[ "$REAL_BIN" == "$WRAPPER_DIR"* ]]; }; then
177
+ REAL_BIN=""
178
+ fi
179
+
180
+ if [[ -z "$REAL_BIN" ]]; then
181
+ for candidate in /usr/bin/git /usr/local/bin/git /opt/homebrew/bin/git; do
182
+ if [[ -x "$candidate" ]]; then
183
+ REAL_BIN="$candidate"
184
+ break
185
+ fi
186
+ done
187
+ fi
188
+
189
+ if [[ -z "$REAL_BIN" ]]; then
190
+ echo "Real git binary not found. Set SHIM_GIT_REAL_BIN." >&2
191
+ exit 1
192
+ fi
193
+
194
+ exec "$REAL_BIN" "${GIT_ARGS[@]}"
@@ -0,0 +1,341 @@
1
+ #!/usr/bin/env node
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const cp = require('child_process');
5
+ const readline = require('readline');
6
+
7
+ const projectRoot = process.cwd();
8
+ const pkgRoot = path.join(__dirname, '..');
9
+ const templatesDir = path.join(pkgRoot, 'templates');
10
+ const pkgJson = require('../package.json');
11
+
12
+ function exists(p) {
13
+ try {
14
+ return fs.existsSync(p);
15
+ } catch {
16
+ return false;
17
+ }
18
+ }
19
+
20
+ function readJson(filePath) {
21
+ try {
22
+ const raw = fs.readFileSync(filePath, 'utf8');
23
+ return JSON.parse(raw);
24
+ } catch {
25
+ return null;
26
+ }
27
+ }
28
+
29
+ function hasCommand(cmd) {
30
+ try {
31
+ cp.execSync(`command -v ${cmd}`, { stdio: 'ignore', shell: true });
32
+ return true;
33
+ } catch {
34
+ return false;
35
+ }
36
+ }
37
+
38
+ function isGitRepo() {
39
+ try {
40
+ cp.execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore', shell: true });
41
+ return true;
42
+ } catch {
43
+ return exists(path.join(projectRoot, '.git'));
44
+ }
45
+ }
46
+
47
+ function ensureDir(dirPath) {
48
+ if (!exists(dirPath)) {
49
+ fs.mkdirSync(dirPath, { recursive: true });
50
+ }
51
+ }
52
+
53
+ function copyTemplate(templateName, destPath, makeExecutable) {
54
+ const src = path.join(templatesDir, templateName);
55
+ ensureDir(path.dirname(destPath));
56
+ fs.copyFileSync(src, destPath);
57
+ if (makeExecutable) {
58
+ try {
59
+ fs.chmodSync(destPath, 0o755);
60
+ } catch {
61
+ // ignore chmod errors
62
+ }
63
+ }
64
+ }
65
+
66
+ function formatBool(val) {
67
+ return val ? 'ja' : 'nein';
68
+ }
69
+
70
+ async function main() {
71
+ const rl = readline.createInterface({
72
+ input: process.stdin,
73
+ output: process.stdout,
74
+ });
75
+
76
+ const ask = (question) => new Promise((resolve) => rl.question(question, resolve));
77
+
78
+ const askYesNo = async (question, defaultYes) => {
79
+ const hint = defaultYes ? 'J/n' : 'j/N';
80
+ const answer = (await ask(`${question} [${hint}] `)).trim().toLowerCase();
81
+ if (!answer) return defaultYes;
82
+ return ['j', 'ja', 'y', 'yes'].includes(answer);
83
+ };
84
+
85
+ const askInput = async (question, def) => {
86
+ const answer = (await ask(`${question} [${def}] `)).trim();
87
+ return answer || def;
88
+ };
89
+
90
+ const packagePath = path.join(projectRoot, 'package.json');
91
+ const projectPackage = readJson(packagePath);
92
+
93
+ const hasSupabase = exists(path.join(projectRoot, 'supabase')) ||
94
+ exists(path.join(projectRoot, 'supabase', 'config.toml')) ||
95
+ exists(path.join(projectRoot, 'supabase', 'functions'));
96
+ const hasSupabaseFunctions = exists(path.join(projectRoot, 'supabase', 'functions'));
97
+ const hasSrc = exists(path.join(projectRoot, 'src'));
98
+ const hasGit = isGitRepo();
99
+ const hasPackageJson = !!projectPackage;
100
+ const hasHusky = exists(path.join(projectRoot, '.husky')) ||
101
+ (projectPackage && projectPackage.devDependencies && projectPackage.devDependencies.husky);
102
+
103
+ console.log('shimwrappercheck init');
104
+ console.log('Projekt:', projectRoot);
105
+ console.log('Gefundene Signale:');
106
+ let repoType = 'unbekannt';
107
+ if (hasSupabase && hasSrc) {
108
+ repoType = 'mixed (frontend + backend)';
109
+ } else if (hasSupabase) {
110
+ repoType = hasSupabaseFunctions ? 'backend (supabase functions)' : 'backend (supabase)';
111
+ } else if (hasSrc) {
112
+ repoType = 'frontend';
113
+ }
114
+
115
+ console.log(`- Supabase: ${formatBool(hasSupabase)}${hasSupabaseFunctions ? ' (functions)' : ''}`);
116
+ console.log(`- Frontend (src/): ${formatBool(hasSrc)}`);
117
+ console.log(`- Git Repo: ${formatBool(hasGit)}`);
118
+ console.log(`- package.json: ${formatBool(hasPackageJson)}`);
119
+ console.log(`- Husky: ${formatBool(hasHusky)}`);
120
+ console.log(`- Repo-Typ: ${repoType}`);
121
+ console.log('');
122
+
123
+ const enableSupabase = await askYesNo(
124
+ 'Supabase-Shim aktivieren (Checks vor supabase CLI)?',
125
+ hasSupabase
126
+ );
127
+
128
+ const enableGitWrapper = await askYesNo(
129
+ 'Git-Wrapper aktivieren (Checks vor git push)?',
130
+ hasGit
131
+ );
132
+
133
+ let enforceCommands = 'all';
134
+ let hookCommands = 'functions,db,migration';
135
+ let defaultFunction = hasSupabaseFunctions ? 'server' : '';
136
+ let autoPush = hasGit;
137
+ let gitEnforceCommands = 'push';
138
+ let disableAiByDefault = false;
139
+
140
+ if (enableSupabase) {
141
+ let defaultEnforce = 'all';
142
+ if (hasSupabaseFunctions) {
143
+ defaultEnforce = 'functions,db,migration';
144
+ } else if (hasSupabase) {
145
+ defaultEnforce = 'db,migration';
146
+ }
147
+ enforceCommands = (await askInput(
148
+ 'Welche Supabase-Befehle sollen Checks erzwingen? (all | none | functions,db,migration)',
149
+ defaultEnforce
150
+ )).toLowerCase().replace(/\s+/g, '');
151
+
152
+ const defaultHooks = hasSupabaseFunctions ? 'functions,db,migration' : 'none';
153
+ hookCommands = (await askInput(
154
+ 'Welche Befehle sollen Post-Deploy Hooks triggern? (functions,db,migration | all | none)',
155
+ defaultHooks
156
+ )).toLowerCase().replace(/\s+/g, '');
157
+
158
+ if (hasSupabaseFunctions) {
159
+ defaultFunction = await askInput('Default Function fuer Health/Logs', defaultFunction || 'server');
160
+ } else {
161
+ defaultFunction = '';
162
+ }
163
+ autoPush = await askYesNo('Nach erfolgreichem CLI-Lauf automatisch git push?', hasGit);
164
+ }
165
+
166
+ if (enableGitWrapper) {
167
+ gitEnforceCommands = (await askInput(
168
+ 'Welche Git-Befehle sollen Checks erzwingen? (push,commit,merge,rebase,all,none)',
169
+ 'push'
170
+ )).toLowerCase().replace(/\\s+/g, '');
171
+ }
172
+
173
+ const enableAiReview = await askYesNo(
174
+ 'AI Review aktivieren (Codex default, Cursor fallback)?',
175
+ true
176
+ );
177
+ if (!enableAiReview) {
178
+ disableAiByDefault = await askYesNo('Standardmaessig --no-ai-review setzen?', true);
179
+ }
180
+
181
+ if (enableAiReview) {
182
+ const hasCodex = hasCommand('codex');
183
+ const hasAgent = hasCommand('agent');
184
+
185
+ if (hasCodex) {
186
+ const doLogin = await askYesNo('Jetzt in ChatGPT via codex login einloggen?', false);
187
+ if (doLogin) {
188
+ try {
189
+ cp.spawnSync('codex', ['login'], { stdio: 'inherit' });
190
+ } catch {
191
+ console.log('codex login fehlgeschlagen. Bitte manuell ausfuehren.');
192
+ }
193
+ }
194
+ } else if (hasAgent) {
195
+ const doLogin = await askYesNo('Codex nicht gefunden. Cursor agent login starten?', false);
196
+ if (doLogin) {
197
+ try {
198
+ cp.spawnSync('agent', ['login'], { stdio: 'inherit' });
199
+ } catch {
200
+ console.log('agent login fehlgeschlagen. Bitte manuell ausfuehren.');
201
+ }
202
+ }
203
+ } else {
204
+ console.log('Hinweis: Weder codex noch agent gefunden. Installiere/konfiguriere die CLI fuer AI Review.');
205
+ }
206
+ }
207
+
208
+ const runChecksPath = path.join(projectRoot, 'scripts', 'run-checks.sh');
209
+ if (!exists(runChecksPath)) {
210
+ const createChecks = await askYesNo('scripts/run-checks.sh aus Template anlegen?', true);
211
+ if (createChecks) {
212
+ copyTemplate('run-checks.sh', runChecksPath, true);
213
+ }
214
+ }
215
+
216
+ const aiReviewPath = path.join(projectRoot, 'scripts', 'ai-code-review.sh');
217
+ if (enableAiReview && !exists(aiReviewPath)) {
218
+ const createAiReview = await askYesNo('scripts/ai-code-review.sh aus Template anlegen?', true);
219
+ if (createAiReview) {
220
+ copyTemplate('ai-code-review.sh', aiReviewPath, true);
221
+ }
222
+ }
223
+
224
+ if (hasGit) {
225
+ let hookInstalled = false;
226
+ if (hasHusky) {
227
+ const useHusky = await askYesNo('Husky pre-push Hook installieren?', true);
228
+ if (useHusky) {
229
+ const huskyPath = path.join(projectRoot, '.husky', 'pre-push');
230
+ if (exists(huskyPath)) {
231
+ const overwrite = await askYesNo('Husky pre-push existiert. Ueberschreiben?', false);
232
+ if (!overwrite) {
233
+ hookInstalled = true;
234
+ } else {
235
+ copyTemplate('husky-pre-push', huskyPath, true);
236
+ hookInstalled = true;
237
+ }
238
+ } else {
239
+ copyTemplate('husky-pre-push', huskyPath, true);
240
+ hookInstalled = true;
241
+ }
242
+ }
243
+ }
244
+
245
+ if (!hookInstalled) {
246
+ const useGitHook = await askYesNo('Plain git pre-push Hook installieren?', true);
247
+ if (useGitHook) {
248
+ const hookPath = path.join(projectRoot, '.git', 'hooks', 'pre-push');
249
+ if (exists(hookPath)) {
250
+ const overwrite = await askYesNo('git pre-push Hook existiert. Ueberschreiben?', false);
251
+ if (!overwrite) {
252
+ // skip
253
+ } else {
254
+ copyTemplate('git-pre-push', hookPath, true);
255
+ }
256
+ } else {
257
+ copyTemplate('git-pre-push', hookPath, true);
258
+ }
259
+ }
260
+ }
261
+ }
262
+
263
+ if (hasPackageJson) {
264
+ const addScript = await askYesNo('package.json: Script "supabase:checked" eintragen?', true);
265
+ if (addScript) {
266
+ projectPackage.scripts = projectPackage.scripts || {};
267
+ if (!projectPackage.scripts['supabase:checked']) {
268
+ projectPackage.scripts['supabase:checked'] = 'supabase';
269
+ }
270
+ fs.writeFileSync(packagePath, JSON.stringify(projectPackage, null, 2) + '\n');
271
+ }
272
+
273
+ if (enableGitWrapper) {
274
+ const addGitScript = await askYesNo('package.json: Script "git:checked" eintragen?', true);
275
+ if (addGitScript) {
276
+ projectPackage.scripts = projectPackage.scripts || {};
277
+ if (!projectPackage.scripts['git:checked']) {
278
+ projectPackage.scripts['git:checked'] = 'git';
279
+ }
280
+ fs.writeFileSync(packagePath, JSON.stringify(projectPackage, null, 2) + '\n');
281
+ }
282
+ }
283
+ } else {
284
+ console.log('package.json nicht gefunden; Scripts wurden nicht angepasst.');
285
+ }
286
+
287
+ if (enableSupabase || enableGitWrapper || disableAiByDefault) {
288
+ const configPath = path.join(projectRoot, '.shimwrappercheckrc');
289
+ let writeConfig = true;
290
+ if (exists(configPath)) {
291
+ writeConfig = await askYesNo('Config .shimwrappercheckrc existiert. Ueberschreiben?', false);
292
+ if (!writeConfig) {
293
+ console.log('Config beibehalten.');
294
+ }
295
+ }
296
+ if (writeConfig) {
297
+ const lines = [];
298
+ lines.push('# shimwrappercheck config');
299
+ if (enableSupabase) {
300
+ lines.push(`SHIM_ENFORCE_COMMANDS="${enforceCommands}"`);
301
+ lines.push(`SHIM_HOOK_COMMANDS="${hookCommands}"`);
302
+ if (defaultFunction) {
303
+ lines.push(`SHIM_DEFAULT_FUNCTION="${defaultFunction}"`);
304
+ }
305
+ lines.push(`SHIM_AUTO_PUSH=${autoPush ? 1 : 0}`);
306
+ }
307
+ if (enableGitWrapper) {
308
+ lines.push(`SHIM_GIT_ENFORCE_COMMANDS="${gitEnforceCommands}"`);
309
+ }
310
+ if (disableAiByDefault) {
311
+ lines.push('SHIM_CHECKS_ARGS="--no-ai-review"');
312
+ }
313
+ fs.writeFileSync(configPath, lines.join('\n') + '\n');
314
+ }
315
+ }
316
+
317
+ console.log('');
318
+ console.log('Setup abgeschlossen. Naechste Schritte:');
319
+ if (enableSupabase) {
320
+ console.log('- nutze: npx supabase <args> oder npm run supabase:checked -- <args>');
321
+ }
322
+ if (enableGitWrapper) {
323
+ console.log('- nutze: npx git <args> oder npm run git:checked -- <args>');
324
+ }
325
+ console.log('- optional: npx shimwrappercheck install (globale PATH-Shims)');
326
+ if (enableAiReview) {
327
+ console.log('- optional: RUN_CURSOR_REVIEW=1 fuer zweiten Review-Durchlauf');
328
+ }
329
+ console.log('- pruefe ggf. scripts/run-checks.sh und passe die Checks an');
330
+ console.log('');
331
+ console.log('Einstellungen spaeter aendern: Dashboard starten mit');
332
+ console.log(' cd node_modules/shimwrappercheck/dashboard && npm install && npm run dev');
333
+ console.log(' dann http://localhost:3000 oeffnen (Presets, Checks, AGENTS.md).');
334
+
335
+ rl.close();
336
+ }
337
+
338
+ main().catch((err) => {
339
+ console.error('Init fehlgeschlagen:', err);
340
+ process.exit(1);
341
+ });