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.
- package/AGENTS.md +21 -0
- package/README.md +286 -0
- package/bin/git +5 -0
- package/bin/shim +5 -0
- package/bin/shimwrappercheck +2 -0
- package/bin/supabase +5 -0
- package/dashboard/.next/cache/config.json +7 -0
- package/dashboard/.next/package.json +1 -0
- package/dashboard/.next/routes-manifest.json +1 -0
- package/dashboard/.next/trace +1 -0
- package/dashboard/README.md +32 -0
- package/dashboard/app/agents/page.tsx +88 -0
- package/dashboard/app/api/agents-md/route.ts +68 -0
- package/dashboard/app/api/config/route.ts +51 -0
- package/dashboard/app/api/run-checks/route.ts +54 -0
- package/dashboard/app/api/settings/route.ts +126 -0
- package/dashboard/app/api/status/route.ts +38 -0
- package/dashboard/app/config/page.tsx +77 -0
- package/dashboard/app/globals.css +20 -0
- package/dashboard/app/layout.tsx +23 -0
- package/dashboard/app/page.tsx +122 -0
- package/dashboard/app/settings/page.tsx +422 -0
- package/dashboard/components/Nav.tsx +33 -0
- package/dashboard/components/StatusCard.tsx +27 -0
- package/dashboard/lib/presets.ts +97 -0
- package/dashboard/lib/projectRoot.ts +25 -0
- package/dashboard/next.config.js +6 -0
- package/dashboard/package-lock.json +5307 -0
- package/dashboard/package.json +28 -0
- package/dashboard/postcss.config.js +6 -0
- package/dashboard/tailwind.config.js +14 -0
- package/dashboard/tsconfig.json +20 -0
- package/docs/SHIM_WRAPPER_CONCEPT.md +79 -0
- package/docs/WORKFLOW_AND_GAP_ANALYSIS.md +159 -0
- package/package.json +24 -0
- package/scripts/cli-checked.sh +307 -0
- package/scripts/cli.js +23 -0
- package/scripts/fetch-edge-logs.sh +96 -0
- package/scripts/git-checked.sh +194 -0
- package/scripts/init.js +341 -0
- package/scripts/install.js +303 -0
- package/scripts/ping-edge-health.sh +113 -0
- package/scripts/setup.js +55 -0
- package/scripts/supabase-checked.sh +330 -0
- package/templates/ai-code-review.sh +217 -0
- package/templates/git-pre-push +41 -0
- package/templates/husky-pre-push +46 -0
- 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[@]}"
|
package/scripts/init.js
ADDED
|
@@ -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
|
+
});
|