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,303 @@
1
+ #!/usr/bin/env node
2
+ const fs = require('fs');
3
+ const os = require('os');
4
+ const path = require('path');
5
+
6
+ const args = process.argv.slice(2).filter((arg, idx) => !(idx === 0 && arg === 'install'));
7
+
8
+ const options = {
9
+ binDir: process.env.SHIM_BIN_DIR || path.join(os.homedir(), '.local', 'bin'),
10
+ installSupabase: true,
11
+ installGit: true,
12
+ installShim: true,
13
+ overwrite: false,
14
+ dryRun: false,
15
+ interactive: args.length === 0,
16
+ addPath: false,
17
+ };
18
+
19
+ function consumeValue(flag, idx) {
20
+ if (idx + 1 >= args.length) {
21
+ console.error(`Missing value for ${flag}`);
22
+ process.exit(1);
23
+ }
24
+ return args[idx + 1];
25
+ }
26
+
27
+ for (let i = 0; i < args.length; i += 1) {
28
+ const arg = args[i];
29
+ if (arg === '--bin-dir') {
30
+ options.binDir = consumeValue(arg, i);
31
+ i += 1;
32
+ continue;
33
+ }
34
+ if (arg === '--interactive') {
35
+ options.interactive = true;
36
+ continue;
37
+ }
38
+ if (arg === '--no-interactive') {
39
+ options.interactive = false;
40
+ continue;
41
+ }
42
+ if (arg === '--add-path') {
43
+ options.addPath = true;
44
+ continue;
45
+ }
46
+ if (arg === '--overwrite') {
47
+ options.overwrite = true;
48
+ continue;
49
+ }
50
+ if (arg === '--dry-run') {
51
+ options.dryRun = true;
52
+ continue;
53
+ }
54
+ if (arg === '--no-supabase') {
55
+ options.installSupabase = false;
56
+ continue;
57
+ }
58
+ if (arg === '--no-git') {
59
+ options.installGit = false;
60
+ continue;
61
+ }
62
+ if (arg === '--no-shim') {
63
+ options.installShim = false;
64
+ continue;
65
+ }
66
+ if (arg === '--only') {
67
+ const value = consumeValue(arg, i);
68
+ i += 1;
69
+ options.installSupabase = false;
70
+ options.installGit = false;
71
+ options.installShim = false;
72
+ value.split(',').map((v) => v.trim()).forEach((v) => {
73
+ if (v === 'supabase') options.installSupabase = true;
74
+ if (v === 'git') options.installGit = true;
75
+ if (v === 'shim') options.installShim = true;
76
+ });
77
+ continue;
78
+ }
79
+ if (arg === '--help' || arg === '-h') {
80
+ printHelp();
81
+ process.exit(0);
82
+ }
83
+ }
84
+
85
+ function printHelp() {
86
+ console.log('shimwrappercheck install');
87
+ console.log('');
88
+ console.log('Options:');
89
+ console.log(' --bin-dir <path> Install path (default: ~/.local/bin)');
90
+ console.log(' --interactive Ask questions (default when no flags)');
91
+ console.log(' --no-interactive Disable prompts');
92
+ console.log(' --add-path Add bin dir to shell config');
93
+ console.log(' --overwrite Overwrite existing shims');
94
+ console.log(' --dry-run Show actions without writing');
95
+ console.log(' --no-supabase Skip supabase shim');
96
+ console.log(' --no-git Skip git shim');
97
+ console.log(' --no-shim Skip generic shim');
98
+ console.log(' --only <list> Comma list: supabase,git,shim');
99
+ }
100
+
101
+ function ensureDir(dirPath) {
102
+ if (!fs.existsSync(dirPath)) {
103
+ fs.mkdirSync(dirPath, { recursive: true });
104
+ }
105
+ }
106
+
107
+ function isInPath(binDir) {
108
+ const pathEntries = (process.env.PATH || '').split(path.delimiter);
109
+ return pathEntries.includes(binDir);
110
+ }
111
+
112
+ function detectShellConfig() {
113
+ const shell = process.env.SHELL || '';
114
+ const home = os.homedir();
115
+ if (shell.includes('zsh')) {
116
+ const zshrc = path.join(home, '.zshrc');
117
+ const zprofile = path.join(home, '.zprofile');
118
+ if (fs.existsSync(zshrc)) return zshrc;
119
+ if (fs.existsSync(zprofile)) return zprofile;
120
+ return zshrc;
121
+ }
122
+ if (shell.includes('bash')) {
123
+ const bashrc = path.join(home, '.bashrc');
124
+ const bashProfile = path.join(home, '.bash_profile');
125
+ if (fs.existsSync(bashrc)) return bashrc;
126
+ if (fs.existsSync(bashProfile)) return bashProfile;
127
+ return bashrc;
128
+ }
129
+ return path.join(home, '.profile');
130
+ }
131
+
132
+ function readFileSafe(filePath) {
133
+ try {
134
+ return fs.readFileSync(filePath, 'utf8');
135
+ } catch {
136
+ return '';
137
+ }
138
+ }
139
+
140
+ function appendPathExport(filePath, binDir, dryRun) {
141
+ const marker = '# shimwrappercheck PATH';
142
+ const line = `export PATH="${binDir}:$PATH"`;
143
+ const content = readFileSafe(filePath);
144
+ if (content.includes(line) || content.includes(marker)) {
145
+ console.log(`PATH already configured in ${filePath}`);
146
+ return;
147
+ }
148
+ if (dryRun) {
149
+ console.log(`[dry-run] append PATH to ${filePath}`);
150
+ return;
151
+ }
152
+ const payload = `\n${marker}\n${line}\n`;
153
+ fs.appendFileSync(filePath, payload);
154
+ console.log(`Added PATH entry to ${filePath}`);
155
+ }
156
+
157
+ async function runInteractive() {
158
+ const readline = require('readline');
159
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
160
+ const ask = (question) => new Promise((resolve) => rl.question(question, resolve));
161
+ const askYesNo = async (question, defaultYes) => {
162
+ const hint = defaultYes ? 'J/n' : 'j/N';
163
+ const answer = (await ask(`${question} [${hint}] `)).trim().toLowerCase();
164
+ if (!answer) return defaultYes;
165
+ return ['j', 'ja', 'y', 'yes'].includes(answer);
166
+ };
167
+ const askInput = async (question, def) => {
168
+ const answer = (await ask(`${question} [${def}] `)).trim();
169
+ return answer || def;
170
+ };
171
+
172
+ options.binDir = await askInput('Bin-Verzeichnis?', options.binDir);
173
+
174
+ const installSupabase = await askYesNo('Shim fuer supabase installieren?', options.installSupabase);
175
+ const installGit = await askYesNo('Shim fuer git installieren?', options.installGit);
176
+ const installShim = await askYesNo('Generischen shim installieren?', options.installShim);
177
+
178
+ options.installSupabase = installSupabase;
179
+ options.installGit = installGit;
180
+ options.installShim = installShim;
181
+
182
+ options.overwrite = await askYesNo('Vorhandene Dateien ueberschreiben?', options.overwrite);
183
+ options.dryRun = await askYesNo('Dry-run?', options.dryRun);
184
+
185
+ if (!isInPath(options.binDir)) {
186
+ options.addPath = await askYesNo('PATH automatisch erweitern?', false);
187
+ if (options.addPath) {
188
+ const shellConfig = detectShellConfig();
189
+ const home = os.homedir();
190
+ const shell = process.env.SHELL || '';
191
+ const candidates = [];
192
+
193
+ if (shell.includes('zsh')) {
194
+ candidates.push(path.join(home, '.zshrc'));
195
+ candidates.push(path.join(home, '.zprofile'));
196
+ } else if (shell.includes('bash')) {
197
+ candidates.push(path.join(home, '.bashrc'));
198
+ candidates.push(path.join(home, '.bash_profile'));
199
+ } else {
200
+ candidates.push(path.join(home, '.profile'));
201
+ }
202
+
203
+ const existing = candidates.filter((file) => fs.existsSync(file));
204
+ if (existing.length > 1) {
205
+ console.log('Mehrere Shell-Configs gefunden:');
206
+ existing.forEach((file, idx) => {
207
+ console.log(` [${idx + 1}] ${file}`);
208
+ });
209
+ const selection = await askInput('Welche Datei soll geaendert werden?', String(existing.indexOf(shellConfig) + 1 || 1));
210
+ const selectedIdx = Math.max(1, Math.min(existing.length, Number(selection))) - 1;
211
+ options.shellConfigOverride = existing[selectedIdx];
212
+ } else if (existing.length === 1) {
213
+ options.shellConfigOverride = existing[0];
214
+ } else {
215
+ options.shellConfigOverride = shellConfig;
216
+ }
217
+ }
218
+ }
219
+
220
+ rl.close();
221
+ }
222
+
223
+ function writeShim(name, content) {
224
+ const target = path.join(options.binDir, name);
225
+ if (fs.existsSync(target) && !options.overwrite) {
226
+ console.log(`Skip ${name}: exists (${target})`);
227
+ return;
228
+ }
229
+ if (options.dryRun) {
230
+ console.log(`[dry-run] write ${target}`);
231
+ return;
232
+ }
233
+ fs.writeFileSync(target, content, { mode: 0o755 });
234
+ console.log(`Installed ${name} -> ${target}`);
235
+ }
236
+
237
+ function shimHeader() {
238
+ return '#!/usr/bin/env bash\nset -euo pipefail\n\n';
239
+ }
240
+
241
+ function pathWithoutBin() {
242
+ return [
243
+ 'BIN_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"',
244
+ 'PATH_NO_BIN="$(echo "$PATH" | tr ":" "\n" | grep -v "^$BIN_DIR$" | paste -sd: -)"',
245
+ ].join('\n');
246
+ }
247
+
248
+ function supabaseShim() {
249
+ return [
250
+ shimHeader(),
251
+ pathWithoutBin(),
252
+ 'REAL_BIN="$(PATH="$PATH_NO_BIN" command -v supabase || true)"',
253
+ 'if [[ -n "$REAL_BIN" ]]; then export SUPABASE_REAL_BIN="$REAL_BIN"; fi',
254
+ 'exec npx --yes --package shimwrappercheck -- supabase "$@"',
255
+ ''
256
+ ].join('\n');
257
+ }
258
+
259
+ function gitShim() {
260
+ return [
261
+ shimHeader(),
262
+ pathWithoutBin(),
263
+ 'REAL_BIN="$(PATH="$PATH_NO_BIN" command -v git || true)"',
264
+ 'if [[ -n "$REAL_BIN" ]]; then export SHIM_GIT_REAL_BIN="$REAL_BIN"; fi',
265
+ 'exec npx --yes --package shimwrappercheck -- git "$@"',
266
+ ''
267
+ ].join('\n');
268
+ }
269
+
270
+ function genericShim() {
271
+ return [
272
+ shimHeader(),
273
+ 'exec npx --yes --package shimwrappercheck -- shim "$@"',
274
+ ''
275
+ ].join('\n');
276
+ }
277
+
278
+ async function main() {
279
+ if (options.interactive) {
280
+ await runInteractive();
281
+ }
282
+
283
+ ensureDir(options.binDir);
284
+
285
+ if (options.installSupabase) writeShim('supabase', supabaseShim());
286
+ if (options.installGit) writeShim('git', gitShim());
287
+ if (options.installShim) writeShim('shim', genericShim());
288
+
289
+ if (!isInPath(options.binDir)) {
290
+ console.log('');
291
+ console.log(`Add to PATH: export PATH="${options.binDir}:$PATH"`);
292
+ if (options.addPath) {
293
+ const shellConfig = options.shellConfigOverride || detectShellConfig();
294
+ appendPathExport(shellConfig, options.binDir, options.dryRun);
295
+ console.log('Reload your shell or source the config file.');
296
+ }
297
+ }
298
+ }
299
+
300
+ main().catch((err) => {
301
+ console.error('Install failed:', err);
302
+ process.exit(1);
303
+ });
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env bash
2
+ # Ping deployed Edge Function health endpoints after deploy/push.
3
+ # Project ref: SUPABASE_PROJECT_REF env, or file supabase/project-ref (one line).
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
+ REF="${SUPABASE_PROJECT_REF:-}"
29
+ if [[ -z "$REF" ]] && [[ -f "$PROJECT_ROOT/supabase/project-ref" ]]; then
30
+ REF="$(head -n1 "$PROJECT_ROOT/supabase/project-ref" | tr -d '\r\n' | tr -d ' ')"
31
+ fi
32
+
33
+ if [[ -z "$REF" ]]; then
34
+ echo "Edge health: skipped (set SUPABASE_PROJECT_REF or create supabase/project-ref)"
35
+ exit 0
36
+ fi
37
+
38
+ function_names=()
39
+ if [[ "$ARGS_TEXT" == *" functions "* ]] && [[ "$ARGS_TEXT" == *" deploy "* ]]; then
40
+ for i in "${!ARGS[@]}"; do
41
+ if [[ "${ARGS[$i]}" == "deploy" ]]; then
42
+ next_index=$((i + 1))
43
+ if [[ $next_index -lt ${#ARGS[@]} ]]; then
44
+ candidate="${ARGS[$next_index]}"
45
+ if [[ -n "$candidate" ]] && [[ "$candidate" != -* ]]; then
46
+ function_names+=("$candidate")
47
+ fi
48
+ fi
49
+ break
50
+ fi
51
+ done
52
+ fi
53
+
54
+ if [[ -n "${SHIM_HEALTH_FUNCTIONS:-}" ]]; then
55
+ IFS=',' read -r -a extra_names <<< "${SHIM_HEALTH_FUNCTIONS}"
56
+ for name in "${extra_names[@]}"; do
57
+ name="$(echo "$name" | xargs)"
58
+ [[ -n "$name" ]] && function_names+=("$name")
59
+ done
60
+ fi
61
+
62
+ if [[ "${#function_names[@]}" -eq 0 ]]; then
63
+ if [[ -z "${SHIM_DEFAULT_FUNCTION+x}" ]]; then
64
+ default_fn="server"
65
+ else
66
+ default_fn="${SHIM_DEFAULT_FUNCTION}"
67
+ fi
68
+ if [[ -n "$default_fn" ]]; then
69
+ function_names+=("$default_fn")
70
+ else
71
+ echo "Edge health: skipped (no function name detected; set SHIM_HEALTH_FUNCTIONS)"
72
+ exit 0
73
+ fi
74
+ fi
75
+
76
+ health_paths=()
77
+ if [[ -n "${SHIM_HEALTH_PATHS:-}" ]]; then
78
+ IFS=',' read -r -a health_paths <<< "${SHIM_HEALTH_PATHS}"
79
+ else
80
+ health_paths=("/functions/v1/{fn}/health" "/functions/v1/{fn}/{fn}/health")
81
+ fi
82
+
83
+ for fn in "${function_names[@]}"; do
84
+ fn_ok=false
85
+ last_code="000"
86
+ last_url=""
87
+
88
+ for path in "${health_paths[@]}"; do
89
+ path_trimmed="$(echo "$path" | xargs)"
90
+ [[ -z "$path_trimmed" ]] && continue
91
+ if [[ "$path_trimmed" != *"{fn}"* ]]; then
92
+ echo "Edge health: invalid SHIM_HEALTH_PATHS entry (missing {fn}): $path_trimmed" >&2
93
+ continue
94
+ fi
95
+ url_path="${path_trimmed//\{fn\}/$fn}"
96
+ url="https://${REF}.supabase.co${url_path}"
97
+ code="$(curl -s -o /dev/null -w '%{http_code}' --connect-timeout 10 --max-time 15 "$url" 2>/dev/null || echo "000")"
98
+ last_code="$code"
99
+ last_url="$url"
100
+
101
+ if [[ "$code" == "200" ]]; then
102
+ fn_ok=true
103
+ echo "Edge health: OK ($url)"
104
+ break
105
+ fi
106
+ done
107
+
108
+ if [[ "$fn_ok" != true ]]; then
109
+ echo "Edge health: HTTP $last_code ($last_url)"
110
+ fi
111
+ done
112
+
113
+ exit 0
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Single-entry setup: ensure shimwrappercheck is installed, then run init wizard.
4
+ * Usage: npx shimwrappercheck setup
5
+ */
6
+ const path = require('path');
7
+ const fs = require('fs');
8
+ const cp = require('child_process');
9
+
10
+ const projectRoot = process.cwd();
11
+ const pkgPath = path.join(projectRoot, 'package.json');
12
+
13
+ function exists(p) {
14
+ try {
15
+ return fs.existsSync(p);
16
+ } catch {
17
+ return false;
18
+ }
19
+ }
20
+
21
+ function readJson(filePath) {
22
+ try {
23
+ const raw = fs.readFileSync(filePath, 'utf8');
24
+ return JSON.parse(raw);
25
+ } catch {
26
+ return null;
27
+ }
28
+ }
29
+
30
+ function main() {
31
+ const pkg = readJson(pkgPath);
32
+ const hasDep = pkg && (
33
+ (pkg.dependencies && pkg.dependencies.shimwrappercheck) ||
34
+ (pkg.devDependencies && pkg.devDependencies.shimwrappercheck)
35
+ );
36
+
37
+ if (!hasDep && pkg) {
38
+ console.log('shimwrappercheck nicht in package.json. Installiere als devDependency...');
39
+ try {
40
+ cp.execSync('npm install shimwrappercheck --save-dev', {
41
+ cwd: projectRoot,
42
+ stdio: 'inherit',
43
+ });
44
+ } catch (e) {
45
+ console.error('Installation fehlgeschlagen. Führe manuell aus: npm i -D shimwrappercheck');
46
+ process.exit(1);
47
+ }
48
+ } else if (!pkg) {
49
+ console.log('Kein package.json gefunden. Führe init trotzdem aus (globale Nutzung).');
50
+ }
51
+
52
+ require(path.join(__dirname, 'init'));
53
+ }
54
+
55
+ main();