geet-geet 0.1.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/lib/doctor.sh ADDED
@@ -0,0 +1,252 @@
1
+ # doctor.sh — sanity checks for this repo + template layers
2
+ # Usage:
3
+ # source doctor.sh
4
+ # doctor
5
+ #
6
+ # Read-only health checks to catch common setup issues
7
+
8
+ doctor() {
9
+
10
+ # Show help if requested
11
+ if [[ "${1:-}" == "help" || "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
12
+ cat <<EOF
13
+ $GEET_ALIAS doctor — run health checks on your geet setup
14
+
15
+ Goal:
16
+ - Help you quickly answer: "Is this repo set up correctly?"
17
+ - Catch common foot-guns (especially committing dot-git/)
18
+
19
+ What it checks:
20
+ ✅ App repo exists (.git)
21
+ ✅ Layer scripts present (git.sh, init.sh, etc.)
22
+ ✅ Template git repo exists (dot-git/)
23
+ ✅ Whitelist files exist (.geetinclude, .geetexclude)
24
+ ✅ dot-git/ is NOT tracked by app repo (critical!)
25
+ ✅ Detects other template layers
26
+
27
+ This is READ-ONLY:
28
+ - Does not modify any files
29
+ - Does not run merges/resets/cleans
30
+ - Safe to run anytime
31
+
32
+ Usage:
33
+ $GEET_ALIAS doctor
34
+
35
+ Exit code:
36
+ 0 - All checks passed
37
+ 1 - One or more issues found
38
+
39
+ Examples:
40
+ $GEET_ALIAS doctor # Run all checks
41
+ EOF
42
+ return 0
43
+ fi
44
+
45
+ # digest-and-locate.sh provides: APP_DIR, TEMPLATE_DIR, DOTGIT, TEMPLATE_NAME,
46
+ # TEMPLATE_GEETINCLUDE, TEMPLATE_GEETEXCLUDE, GEET_LIB, die, log, debug
47
+
48
+ # Additional paths
49
+ APP_GIT="$APP_DIR/.git"
50
+
51
+ # Pretty printing helpers
52
+ ok() { echo "[$TEMPLATE_NAME doctor] ✅ $*"; }
53
+ warn() { echo "[$TEMPLATE_NAME doctor] ⚠️ $*" >&2; }
54
+ bad() { echo "[$TEMPLATE_NAME doctor] ❌ $*" >&2; }
55
+ info() { echo "[$TEMPLATE_NAME doctor] $*"; }
56
+
57
+ # Track whether we should exit nonzero
58
+ HAS_ERRORS=0
59
+ fail() { bad "$*"; HAS_ERRORS=1; }
60
+
61
+ ###############################################################################
62
+ # Helpers
63
+ ###############################################################################
64
+
65
+ # Returns 0 if command exists
66
+ have() { command -v "$1" >/dev/null 2>&1; }
67
+
68
+ # Print a short list of dot-directories that look like layers
69
+ # A "layer" is defined as: hidden dir at repo root containing git.sh
70
+ detect_layers() {
71
+ # Only look one level deep, and only at hidden dirs.
72
+ # (We intentionally ignore .git and other common dotdirs.)
73
+ find "$APP_DIR" -maxdepth 1 -mindepth 1 -type d -name ".*" 2>/dev/null \
74
+ | while IFS= read -r d; do
75
+ base="$(basename "$d")"
76
+ case "$base" in
77
+ .|..|.git) continue ;;
78
+ esac
79
+ if [[ -f "$d/lib/git.sh" && -f "$d/lib/init.sh" ]]; then
80
+ echo "$d"
81
+ fi
82
+ done | sort
83
+ }
84
+
85
+ # Check whether a path is tracked by the APP repo (not ignored).
86
+ # If it is tracked, that's a big red flag for dot-git.
87
+ app_tracks_path() {
88
+ local path="$1"
89
+ git -C "$APP_DIR" ls-files --error-unmatch -- "$path" >/dev/null 2>&1
90
+ }
91
+
92
+ ###############################################################################
93
+ # Start
94
+ ###############################################################################
95
+
96
+ info "repo root: $APP_DIR"
97
+ info "this layer: $TEMPLATE_NAME"
98
+
99
+ echo
100
+
101
+ ###############################################################################
102
+ # 1) Basic file presence
103
+ ###############################################################################
104
+
105
+ if [[ -d "$APP_GIT" && -f "$APP_GIT/HEAD" ]]; then
106
+ ok "app repo present (.git exists)"
107
+ else
108
+ fail "app repo missing or invalid: $APP_GIT"
109
+ fi
110
+
111
+ if [[ -f "$GEET_LIB/git.sh" ]]; then
112
+ ok "layer git wrapper present: $GEET_LIB/git.sh"
113
+ else
114
+ fail "missing layer git wrapper: $GEET_LIB/git.sh"
115
+ fi
116
+
117
+ if [[ -f "$GEET_LIB/init.sh" ]]; then
118
+ ok "layer init script present"
119
+ else
120
+ fail "missing layer init script"
121
+ fi
122
+
123
+ # Check for post-init hook
124
+ POST_INIT_SH="$LAYER_DIR/post-init.sh"
125
+ if [[ -f "$POST_INIT_SH" ]]; then
126
+ if [[ -x "$POST_INIT_SH" ]]; then
127
+ ok "post-init hook present and executable: $POST_INIT_SH"
128
+ else
129
+ warn "post-init hook exists but is NOT executable: $POST_INIT_SH"
130
+ info "fix with: chmod +x $POST_INIT_SH"
131
+ fi
132
+ fi
133
+
134
+
135
+ echo
136
+
137
+ ###############################################################################
138
+ # 2) Layer initialization state
139
+ ###############################################################################
140
+
141
+ if [[ -d "$DOTGIT" && -f "$DOTGIT/HEAD" ]]; then
142
+ ok "layer initialized (dot-git exists)"
143
+ else
144
+ warn "layer NOT initialized (missing $DOTGIT/HEAD)"
145
+ info "run: $LAYER_NAME init"
146
+ fi
147
+
148
+ # If dot-git exists, make sure compiled exclude exists (after git.sh runs at least once)
149
+ if [[ -d "$DOTGIT" && -f "$DOTGIT/HEAD" ]]; then
150
+ if [[ -f "$TEMPLATE_DIR/.geetexclude" ]]; then
151
+ ok "compiled exclude present: $TEMPLATE_DIR/.geetexclude"
152
+ else
153
+ warn "compiled exclude missing: $TEMPLATE_DIR/.geetexclude"
154
+ info "run: $LAYER_NAME status (this compiles include/exclude rules)"
155
+ fi
156
+ fi
157
+
158
+ echo
159
+
160
+ ###############################################################################
161
+ # 3) Critical safety: dot-git must NOT be tracked by app repo
162
+ ###############################################################################
163
+
164
+ # dot-git is a git database; committing it is disastrous.
165
+ # We warn hard if it's tracked or if ignore rules look missing.
166
+ if [[ -d "$DOTGIT" ]]; then
167
+ # If ANY files under dot-git are tracked, that's a serious problem.
168
+ # Check the exact path relative to root
169
+ rel_dotgit="${DOTGIT#"$ROOT/"}"
170
+
171
+ if app_tracks_path "$rel_dotgit" || git -C "$ROOT" ls-files -- "$rel_dotgit" | grep -q .; then
172
+ fail "SECURITY: app repo is tracking $rel_dotgit (must be ignored; remove from git history)"
173
+ info "fix (careful):"
174
+ info " git rm -r --cached -- \"$rel_dotgit\""
175
+ info " add to app .geetexclude: **/dot-git/"
176
+ else
177
+ ok "dot-git is not tracked by app repo"
178
+ fi
179
+
180
+ # Also check that app ignore rules contain something like dot-git
181
+ # This is a heuristic (gitignore can be split across files), so it's a warning not failure.
182
+ if [[ -f "$ROOT/.geetexclude" ]]; then
183
+ if grep -Eq '(^|\s)(\*\*/dot-git/|\.geet/dot-git/|dot-git/)\s*$' "$ROOT/.geetexclude"; then
184
+ ok "app .geetexclude appears to ignore dot-git/"
185
+ else
186
+ warn "app .geetexclude does not obviously ignore dot-git/ (recommended: **/dot-git/)"
187
+ fi
188
+ else
189
+ warn "app .geetexclude missing (recommended: ignore **/dot-git/)"
190
+ fi
191
+ fi
192
+
193
+ echo
194
+
195
+ ###############################################################################
196
+ # 4) Light functional checks (read-only)
197
+ ###############################################################################
198
+ # These checks make sure the layer git wrapper can talk to the layer repo
199
+ # WITHOUT modifying anything.
200
+
201
+ if [[ -d "$DOTGIT" && -f "$DOTGIT/HEAD" ]]; then
202
+ if "$GEET_LIB/git.sh" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
203
+ ok "layer git wrapper can run git commands"
204
+ else
205
+ fail "layer git wrapper failed to run git (check permissions, env, or dot-git validity)"
206
+ fi
207
+
208
+ # Check that HEAD resolves
209
+ if "$GEET_LIB/git.sh" rev-parse HEAD >/dev/null 2>&1; then
210
+ ok "layer HEAD resolves"
211
+ else
212
+ warn "layer HEAD does not resolve yet (maybe no commits in layer repo)"
213
+ info "after defining whitelist and committing, this will become OK"
214
+ fi
215
+ fi
216
+
217
+ echo
218
+
219
+ ###############################################################################
220
+ # 5) Report other detected layers (informational)
221
+ ###############################################################################
222
+ info "detected layers at repo root:"
223
+ layers="$(detect_layers || true)"
224
+ if [[ -z "$layers" ]]; then
225
+ info " (none found)"
226
+ else
227
+ while IFS= read -r d; do
228
+ base="$(basename "$d")"
229
+ name="${base#.}"
230
+ dotgit="$d/dot-git"
231
+ if [[ -f "$dotgit/HEAD" ]]; then
232
+ info " .$name (initialized)"
233
+ else
234
+ info " .$name (not initialized)"
235
+ fi
236
+ done <<< "$layers"
237
+ fi
238
+
239
+ echo
240
+
241
+ ###############################################################################
242
+ # Exit status
243
+ ###############################################################################
244
+ if [[ "$HAS_ERRORS" -eq 1 ]]; then
245
+ bad "doctor found errors"
246
+ return 1
247
+ fi
248
+
249
+ ok "doctor looks good"
250
+ return 0
251
+
252
+ } # end of doctor()
package/lib/flags.sh ADDED
@@ -0,0 +1,52 @@
1
+ # has-flag.sh
2
+
3
+ has_flag() {
4
+ local flag="$1"
5
+ local outvar="$2"
6
+
7
+ local -a cleaned=()
8
+ local found=""
9
+
10
+ for arg in "${GEET_ARGS[@]}"; do
11
+ if [[ $arg == "$flag" ]]; then
12
+ found="true"
13
+ else
14
+ cleaned+=("$arg")
15
+ fi
16
+ done
17
+
18
+ GEET_ARGS=("${cleaned[@]}")
19
+ local -n out="$outvar"
20
+ out="$found"
21
+ }
22
+
23
+ extract_flag() {
24
+ local flag="$1"
25
+ local outvar="$2"
26
+
27
+ local -a cleaned=()
28
+ local value=""
29
+ local i=0
30
+
31
+ while (( i < ${#GEET_ARGS[@]} )); do
32
+ local arg="${GEET_ARGS[i]}"
33
+
34
+ if [[ $arg == "$flag" ]]; then
35
+ # consume optional value
36
+ if (( i+1 < ${#GEET_ARGS[@]} )); then
37
+ value="${GEET_ARGS[i+1]}"
38
+ i=$((i+2))
39
+ else
40
+ i=$((i+1))
41
+ fi
42
+ continue
43
+ fi
44
+
45
+ cleaned+=("$arg")
46
+ i=$((i+1))
47
+ done
48
+
49
+ GEET_ARGS=("${cleaned[@]}")
50
+ local -n out="$outvar"
51
+ out="$value"
52
+ }
package/lib/ghcli.sh ADDED
@@ -0,0 +1,386 @@
1
+ # ghcli.sh — GitHub CLI integration for geet
2
+ # Usage:
3
+ # source ghcli.sh
4
+ # ghcli <subcommand> [args...]
5
+
6
+
7
+ # digest-and-locate.sh provides: APP_DIR, TEMPLATE_NAME, TEMPLATE_GH_USER,
8
+ # TEMPLATE_GH_NAME, TEMPLATE_GH_URL, GEET_ALIAS, die, log
9
+
10
+ info() { echo "[$TEMPLATE_NAME gh] $*" >&2; }
11
+
12
+ ###############################################################################
13
+ # CHECKS
14
+ ###############################################################################
15
+
16
+ # Check if gh is installed
17
+ check_gh_installed() {
18
+ command -v gh >/dev/null 2>&1
19
+ }
20
+
21
+ # Check if gh is authenticated
22
+ check_gh_authenticated() {
23
+ gh auth status >/dev/null 2>&1
24
+ }
25
+
26
+ # Ensure gh is installed and authenticated (auto-setup if needed)
27
+ ensure_gh_ready() {
28
+ local needs_setup=0
29
+
30
+ if ! check_gh_installed; then
31
+ needs_setup=1
32
+ elif ! check_gh_authenticated; then
33
+ needs_setup=1
34
+ fi
35
+
36
+ if [[ "$needs_setup" -eq 1 ]]; then
37
+ log "gh CLI not ready, running setup..."
38
+ setup_cmd
39
+ fi
40
+ }
41
+
42
+ ###############################################################################
43
+ # COMMANDS
44
+ ###############################################################################
45
+
46
+ setup_cmd() {
47
+ log "checking GitHub CLI setup..."
48
+
49
+ # Check if gh is installed
50
+ if ! check_gh_installed; then
51
+ log "gh CLI not found"
52
+ log "installing gh CLI..."
53
+
54
+ # Detect package manager
55
+ if command -v apt >/dev/null 2>&1; then
56
+ log "using apt package manager"
57
+ sudo apt update
58
+ sudo apt install -y gh
59
+ elif command -v brew >/dev/null 2>&1; then
60
+ log "using homebrew package manager"
61
+ brew install gh
62
+ elif command -v dnf >/dev/null 2>&1; then
63
+ log "using dnf package manager"
64
+ sudo dnf install -y gh
65
+ elif command -v yum >/dev/null 2>&1; then
66
+ log "using yum package manager"
67
+ sudo yum install -y gh
68
+ else
69
+ die "could not detect package manager (apt, brew, dnf, yum). Please install gh manually: https://cli.github.com/"
70
+ fi
71
+
72
+ # Verify installation
73
+ if ! check_gh_installed; then
74
+ die "gh installation failed"
75
+ fi
76
+
77
+ log "gh CLI installed successfully"
78
+ else
79
+ log "gh CLI already installed: $(gh --version | head -1)"
80
+ fi
81
+
82
+ # Check authentication
83
+ if ! check_gh_authenticated; then
84
+ log "gh CLI not authenticated"
85
+ log "starting authentication flow..."
86
+ gh auth login
87
+
88
+ # Verify authentication
89
+ if ! check_gh_authenticated; then
90
+ die "gh authentication failed"
91
+ fi
92
+
93
+ log "gh CLI authenticated successfully"
94
+ else
95
+ log "gh CLI already authenticated"
96
+ gh auth status
97
+ fi
98
+
99
+ log "GitHub CLI setup complete!"
100
+ }
101
+
102
+ publish_app() {
103
+ GEET_ARGS=("$@")
104
+
105
+ log "here in publish app with $APP_DIR and $APP_NAME"
106
+ # Auto-setup if needed
107
+ ensure_gh_ready
108
+
109
+ # Get GitHub username
110
+ get_gh_user
111
+
112
+ log "publishing app repository to GitHub..."
113
+ log " source: $APP_NAME"
114
+ log " name: $APP_NAME"
115
+ log " owner: $GH_USER"
116
+
117
+ local APP_HOMEPAGE=${APP_HOMEPAGE:-};
118
+ local APP_DESC=${APP_DESC:-};
119
+ extract_flag --homepage _HOMEPAGE
120
+ extract_flag --desc _DESC
121
+
122
+ local PUB_APP=""
123
+ local PRI_APP=""
124
+ local INT_APP=""
125
+
126
+ has_flag --public PUB_APP
127
+ has_flag --private PRI_APP
128
+ has_flag --internal INT_APP
129
+
130
+ # Determine publish visibility
131
+ local publish_visibility="public"
132
+ if [[ -n "$PUB_APP" ]]; then
133
+ publish_visibility="public"
134
+ elif [[ -n "$PRI_APP" ]]; then
135
+ publish_visibility="private"
136
+ elif [[ -n "$INT_APP" ]]; then
137
+ publish_visibility="internal"
138
+ fi
139
+
140
+ [[ -n "$_DESC" ]] && APP_DESC="$_DESC";
141
+ [[ -n "$_HOMEPAGE" ]] && APP_HOMEPAGE="$_HOMEPAGE";
142
+
143
+ # Build args for gh repo create
144
+ local -a gh_args=(
145
+ repo
146
+ create
147
+ "$APP_NAME"
148
+ --source
149
+ .
150
+ --description
151
+ "$APP_DESC"
152
+ "--$publish_visibility"
153
+ --push
154
+ )
155
+
156
+ # Add any extra args passed by user
157
+ if [[ $# -gt 0 ]]; then
158
+ gh_args+=("${GEET_ARGS[@]}")
159
+ fi
160
+
161
+ if [[ -n "$APP_HOMEPAGE" ]]; then
162
+ gh_args+=("--homepage")
163
+ gh_args+=("$APP_HOMEPAGE")
164
+ fi
165
+
166
+ log "calling \`${gh_args[@]}\`"
167
+
168
+ # Run gh repo create from APP_DIR
169
+ (
170
+ cd "$APP_DIR"
171
+ gh "${gh_args[@]}"
172
+ )
173
+
174
+ log "app repository published successfully"
175
+ }
176
+
177
+ publish_template() {
178
+ GEET_ARGS=("$@")
179
+ # Auto-setup if needed
180
+ ensure_gh_ready
181
+
182
+ [[ -z "$TEMPLATE_DIR" ]] && die "TEMPLATE_DIR not set - cannot publish template"
183
+
184
+ # Get GitHub username
185
+ get_gh_user
186
+
187
+ # Get the repo name - use TEMPLATE_GH_NAME if set, otherwise derive from TEMPLATE_NAME
188
+ local template_repo_name
189
+ if [[ -n "$TEMPLATE_GH_NAME" ]]; then
190
+ template_repo_name="$TEMPLATE_GH_NAME"
191
+ elif [[ -n "$TEMPLATE_NAME" ]]; then
192
+ template_repo_name="$TEMPLATE_NAME"
193
+ else
194
+ template_repo_name="$(basename "$TEMPLATE_DIR")"
195
+ fi
196
+
197
+ # Remove leading period if present
198
+ template_repo_name="${template_repo_name#.}"
199
+
200
+ log "publishing template repository to GitHub..."
201
+ log " source: $TEMPLATE_DIR"
202
+ log " name: $template_repo_name"
203
+ log " owner: $GH_USER"
204
+
205
+ TEMPLATE_HOMEPAGE=${TEMPLATE_HOMEPAGE:-};
206
+ TEMPLATE_DESC=${TEMPLATE_DESC:-};
207
+ TEMPLATE_TOPICS=${TEMPLATE_TOPICS:-"geet,template"};
208
+ extract_flag --topics _TOPICS
209
+ extract_flag --homepage _HOMEPAGE
210
+ extract_flag --desc _DESC
211
+
212
+ local PUB_APP=""
213
+ local PRI_APP=""
214
+ local INT_APP=""
215
+
216
+ has_flag --public PUB_APP
217
+ has_flag --private PRI_APP
218
+ has_flag --internal INT_APP
219
+
220
+ # Determine publish visibility
221
+ local publish_visibility="public"
222
+ if [[ -n "$PUB_APP" ]]; then
223
+ publish_visibility="public"
224
+ elif [[ -n "$PRI_APP" ]]; then
225
+ publish_visibility="private"
226
+ elif [[ -n "$INT_APP" ]]; then
227
+ publish_visibility="internal"
228
+ fi
229
+
230
+ [[ -n "$_DESC" ]] && TEMPLATE_DESC="$_DESC";
231
+ [[ -n "$_HOMEPAGE" ]] && TEMPLATE_HOMEPAGE="$_HOMEPAGE";
232
+ [[ -n "$_TOPICS" ]] && TEMPLATE_TOPICS="$_TOPICS";
233
+
234
+ # Clean up topics: remove trailing commas, trim whitespace
235
+ TEMPLATE_TOPICS="${TEMPLATE_TOPICS%,}" # Remove trailing comma
236
+ TEMPLATE_TOPICS="${TEMPLATE_TOPICS#,}" # Remove leading comma
237
+ TEMPLATE_TOPICS="${TEMPLATE_TOPICS// /}" # Remove spaces
238
+
239
+ # Set default if empty
240
+ [[ -z "$TEMPLATE_TOPICS" ]] && TEMPLATE_TOPICS="geet,template"
241
+
242
+ # Build args for gh repo create (without --source and --push, we'll handle that manually)
243
+ local -a gh_args=(
244
+ repo
245
+ create
246
+ "$template_repo_name"
247
+ --description
248
+ "$TEMPLATE_DESC"
249
+ "--$publish_visibility"
250
+ )
251
+
252
+ # Add any extra args passed by user
253
+ if [[ $# -gt 0 ]]; then
254
+ gh_args+=("$@")
255
+ fi
256
+
257
+ if [[ -n "$TEMPLATE_HOMEPAGE" ]]; then
258
+ gh_args+=("--homepage")
259
+ gh_args+=("$TEMPLATE_HOMEPAGE")
260
+ fi
261
+
262
+ log "calling \`${gh_args[@]}\`"
263
+
264
+ # Create the remote repo
265
+ gh "${gh_args[@]}"
266
+
267
+ log "configuring template settings..."
268
+ gh repo edit "$GH_USER/$template_repo_name" --template
269
+ gh repo edit "$GH_USER/$template_repo_name" --add-topic "$TEMPLATE_TOPICS"
270
+
271
+ log "adding remote and pushing using geet_git..."
272
+
273
+ # Get the remote URL
274
+ local remote_url
275
+ remote_url="$(gh repo view "$GH_USER/$template_repo_name" --json sshUrl -q .sshUrl)"
276
+
277
+ log "remote URL: $remote_url"
278
+
279
+ # Add remote and push using geet_git (which handles custom git setup)
280
+ geet_git remote add origin "$remote_url" 2>/dev/null || geet_git remote set-url origin "$remote_url"
281
+ geet_git push -u origin HEAD
282
+
283
+ log "template repository published successfully"
284
+ }
285
+
286
+ publish_cmd() {
287
+ local subcommand="${1:-}"
288
+
289
+ case "$subcommand" in
290
+ app)
291
+ shift
292
+ publish_app "$@"
293
+ ;;
294
+ template)
295
+ shift
296
+ publish_template "$@"
297
+ ;;
298
+ -h|--help|help)
299
+ cat <<EOF
300
+ [$TEMPLATE_NAME gh] Publish command
301
+
302
+ Usage:
303
+ $GEET_ALIAS publish <type> [options...]
304
+
305
+ Types:
306
+ app Publish as a regular repository
307
+ template Publish as a template repository (with --template flag and topics)
308
+
309
+ Options:
310
+ --public Create as public repository (default)
311
+ --private Create as private repository
312
+ --internal Create as internal repository
313
+ --desc Repository description
314
+ --homepage Repository homepage URL
315
+ --topics Comma-separated topics (template only, default: "geet,template")
316
+
317
+ Examples:
318
+ $GEET_ALIAS publish app
319
+ $GEET_ALIAS publish template --private --desc "My template"
320
+ $GEET_ALIAS publish app --desc "My app" --homepage "https://example.com"
321
+ EOF
322
+ ;;
323
+ "")
324
+ die "publish command requires a type argument: 'app' or 'template'. Use '$GEET_ALIAS publish help' for more info."
325
+ ;;
326
+ *)
327
+ die "unknown publish type: '$subcommand'. Use 'app' or 'template'. Use '$GEET_ALIAS publish help' for more info."
328
+ ;;
329
+ esac
330
+ }
331
+
332
+ usage() {
333
+ cat <<EOF
334
+ [$TEMPLATE_NAME gh] GitHub CLI integration
335
+
336
+ Usage:
337
+ $GEET_ALIAS gh <command> [args...]
338
+
339
+ Commands:
340
+ setup Install and authenticate GitHub CLI (runs automatically if needed)
341
+ publish <type> Create and push repo to GitHub (type: 'app' or 'template')
342
+ <any> Pass through to gh CLI (e.g., 'gh pr list' -> '$GEET_ALIAS gh pr list')
343
+ help Show this help
344
+
345
+ Examples:
346
+ $GEET_ALIAS publish app # Publish as regular repository
347
+ $GEET_ALIAS publish template # Publish as template repository
348
+ $GEET_ALIAS publish app --private --desc "My cool project"
349
+ $GEET_ALIAS gh pr list # Auto-setup if needed, then list PRs
350
+ $GEET_ALIAS gh repo view
351
+
352
+ Note:
353
+ All commands automatically run 'setup' if gh is not installed or authenticated.
354
+ You rarely need to run 'setup' manually.
355
+ Use '$GEET_ALIAS publish help' for detailed publish options.
356
+ EOF
357
+ }
358
+
359
+
360
+ handle(){
361
+ ###############################################################################
362
+ # COMMAND DISPATCH
363
+ ###############################################################################
364
+ cmd="${1:-help}"
365
+ shift || true
366
+
367
+ case "$cmd" in
368
+ help|-h|--help)
369
+ usage
370
+ ;;
371
+
372
+ setup)
373
+ setup_cmd "$@"
374
+ ;;
375
+
376
+ pub|publish)
377
+ publish_cmd "$@"
378
+ ;;
379
+
380
+ *)
381
+ # Pass through to gh CLI (auto-setup if needed)
382
+ ensure_gh_ready
383
+ gh "$cmd" "$@"
384
+ ;;
385
+ esac
386
+ }