gitputra 0.2.0__tar.gz → 0.2.2__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitputra
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: AI-powered GitHub repo analyzer CLI — analyze, chat, and visualize any codebase. Run 'gitputra info' to see all commands and features.
5
5
  Author-email: Adityava Gangopadhyay <adityava49cse@gmail.com>
6
6
  License: MIT
@@ -18,6 +18,7 @@ Requires-Dist: reportlab<5.0,>=4.0
18
18
  Requires-Dist: google-generativeai<1.0,>=0.5
19
19
  Requires-Dist: openai<2.0,>=1.0
20
20
  Requires-Dist: anthropic<1.0,>=0.25
21
+ Requires-Dist: pdfplumber<1.0,>=0.9
21
22
 
22
23
  # 🔍 GitPutra
23
24
 
@@ -46,6 +47,7 @@ Requires-Dist: anthropic<1.0,>=0.25
46
47
  - 📄 **Multilingual PDF Reports** — Exports analysis as a styled PDF in ~110 languages
47
48
  - 🗂️ **Smart Indexing** — Skips re-embedding if a repo is already indexed; run `clear-db` to reset
48
49
  - 📁 **48+ File Types** — Supports Python, C/C++, Java, Go, Rust, JS/TS, and many more
50
+ - 🧠 **Context Export** — Export your entire codebase as a portable Markdown file to paste into any AI tool
49
51
 
50
52
  ---
51
53
 
@@ -64,7 +66,12 @@ Requires Python 3.10+
64
66
  ### Analyze a Repository
65
67
 
66
68
  ```bash
69
+ # Remote repo
67
70
  gitputra analyze https://github.com/user/repo --ai gemini --key YOUR_API_KEY
71
+
72
+ # Local codebase (no cloning)
73
+ gitputra analyze ./my-project --local --ai gemini --key YOUR_API_KEY
74
+ gitputra analyze "C:\path\with spaces\project" --local --ai gemini --key YOUR_API_KEY
68
75
  ```
69
76
 
70
77
  With options:
@@ -75,6 +82,9 @@ gitputra analyze https://github.com/user/repo --ai openai --key YOUR_KEY --lang
75
82
 
76
83
  # Use Claude, skip PDF
77
84
  gitputra analyze https://github.com/user/repo --ai claude --key YOUR_KEY --no-pdf
85
+
86
+ # Analyze a specific branch
87
+ gitputra analyze https://github.com/user/repo --branch dev --ai gemini --key YOUR_API_KEY
78
88
  ```
79
89
 
80
90
  Outputs saved to `./output/`:
@@ -82,10 +92,16 @@ Outputs saved to `./output/`:
82
92
  - `diagram.png` — Dependency graph
83
93
  - `mermaid.txt` — Mermaid flowchart source
84
94
 
95
+ ---
96
+
85
97
  ### Chat with a Repository
86
98
 
87
99
  ```bash
100
+ # Remote repo
88
101
  gitputra chat https://github.com/user/repo --ai gemini --key YOUR_API_KEY
102
+
103
+ # Local codebase
104
+ gitputra chat ./my-project --local --ai gemini --key YOUR_API_KEY
89
105
  ```
90
106
 
91
107
  ```
@@ -98,33 +114,59 @@ gitputra chat https://github.com/user/repo --ai gemini --key YOUR_API_KEY
98
114
 
99
115
  > If `analyze` was already run on the repo, `chat` reuses the existing index automatically.
100
116
 
101
- ### Analyze or Chat with a Specific Branch
102
-
103
117
  ```bash
104
- # Analyze a specific branch
105
- gitputra analyze https://github.com/user/repo --branch dev --ai gemini --key YOUR_API_KEY
106
-
107
118
  # Chat with a specific branch
108
119
  gitputra chat https://github.com/user/repo --branch feature/my-branch --ai gemini --key YOUR_API_KEY
109
120
  ```
110
121
 
111
122
  > If `--branch` is not specified, the repo's default branch is used.
112
123
 
113
- ### Clear the Local Index
124
+ ---
114
125
 
115
- ```bash
116
- gitputra clear-db
117
- ```
126
+ ### Export Codebase Context
118
127
 
119
- ### Show Info & Capabilities
128
+ Export your entire codebase as a single Markdown file you can paste into any AI tool (Claude, ChatGPT, Gemini…) to continue work without re-explaining the project.
120
129
 
121
130
  ```bash
122
- gitputra info
131
+ # Local codebase — fast, no API key needed (no AI summaries)
132
+ gitputra export-context ./my-project
133
+
134
+ # With AI summaries (requires --use-ai and an API key)
135
+ gitputra export-context ./my-project --use-ai --ai gemini --key YOUR_API_KEY
136
+ gitputra export-context ./my-project --use-ai --ai claude --key YOUR_API_KEY
137
+
138
+ # Remote GitHub repo
139
+ gitputra export-context https://github.com/user/repo --remote --ai gemini --key YOUR_API_KEY
140
+
141
+ # Remote repo, specific branch, with AI summaries
142
+ gitputra export-context https://github.com/user/repo --remote --branch dev --use-ai --ai gemini --key YOUR_API_KEY
143
+
144
+ # Scan current directory
145
+ gitputra export-context
146
+
147
+ # Path with spaces — wrap in quotes
148
+ gitputra export-context "C:\Users\you\My Project"
123
149
  ```
124
150
 
125
- ### Check Version
151
+ Output saved to `./output/context_<project_name>_<timestamp>.md`
152
+
153
+ **How to use the exported file:**
154
+ 1. Open the `.md` file
155
+ 2. Paste it into any AI chat (Claude, ChatGPT, Gemini…)
156
+ 3. Say: *"Here is my codebase context. Read it and help me continue."*
157
+
158
+ ---
159
+
160
+ ### Other Commands
126
161
 
127
162
  ```bash
163
+ # Wipe the local ChromaDB index
164
+ gitputra clear-db
165
+
166
+ # Show all commands, supported AIs, languages, and file extensions
167
+ gitputra info
168
+
169
+ # Check version
128
170
  gitputra --version
129
171
  ```
130
172
 
@@ -146,7 +188,7 @@ gitputra --version
146
188
 
147
189
  ```env
148
190
  API_KEY=your_api_key_here
149
- GEMINI_API_KEY=your_gemini_key # only needed for --ai claude with chat
191
+ GEMINI_API_KEY=your_gemini_key # only needed for --ai claude with chat/RAG
150
192
  ```
151
193
 
152
194
  **Option 2 — CLI flag:**
@@ -25,6 +25,7 @@
25
25
  - 📄 **Multilingual PDF Reports** — Exports analysis as a styled PDF in ~110 languages
26
26
  - 🗂️ **Smart Indexing** — Skips re-embedding if a repo is already indexed; run `clear-db` to reset
27
27
  - 📁 **48+ File Types** — Supports Python, C/C++, Java, Go, Rust, JS/TS, and many more
28
+ - 🧠 **Context Export** — Export your entire codebase as a portable Markdown file to paste into any AI tool
28
29
 
29
30
  ---
30
31
 
@@ -43,7 +44,12 @@ Requires Python 3.10+
43
44
  ### Analyze a Repository
44
45
 
45
46
  ```bash
47
+ # Remote repo
46
48
  gitputra analyze https://github.com/user/repo --ai gemini --key YOUR_API_KEY
49
+
50
+ # Local codebase (no cloning)
51
+ gitputra analyze ./my-project --local --ai gemini --key YOUR_API_KEY
52
+ gitputra analyze "C:\path\with spaces\project" --local --ai gemini --key YOUR_API_KEY
47
53
  ```
48
54
 
49
55
  With options:
@@ -54,6 +60,9 @@ gitputra analyze https://github.com/user/repo --ai openai --key YOUR_KEY --lang
54
60
 
55
61
  # Use Claude, skip PDF
56
62
  gitputra analyze https://github.com/user/repo --ai claude --key YOUR_KEY --no-pdf
63
+
64
+ # Analyze a specific branch
65
+ gitputra analyze https://github.com/user/repo --branch dev --ai gemini --key YOUR_API_KEY
57
66
  ```
58
67
 
59
68
  Outputs saved to `./output/`:
@@ -61,10 +70,16 @@ Outputs saved to `./output/`:
61
70
  - `diagram.png` — Dependency graph
62
71
  - `mermaid.txt` — Mermaid flowchart source
63
72
 
73
+ ---
74
+
64
75
  ### Chat with a Repository
65
76
 
66
77
  ```bash
78
+ # Remote repo
67
79
  gitputra chat https://github.com/user/repo --ai gemini --key YOUR_API_KEY
80
+
81
+ # Local codebase
82
+ gitputra chat ./my-project --local --ai gemini --key YOUR_API_KEY
68
83
  ```
69
84
 
70
85
  ```
@@ -77,33 +92,59 @@ gitputra chat https://github.com/user/repo --ai gemini --key YOUR_API_KEY
77
92
 
78
93
  > If `analyze` was already run on the repo, `chat` reuses the existing index automatically.
79
94
 
80
- ### Analyze or Chat with a Specific Branch
81
-
82
95
  ```bash
83
- # Analyze a specific branch
84
- gitputra analyze https://github.com/user/repo --branch dev --ai gemini --key YOUR_API_KEY
85
-
86
96
  # Chat with a specific branch
87
97
  gitputra chat https://github.com/user/repo --branch feature/my-branch --ai gemini --key YOUR_API_KEY
88
98
  ```
89
99
 
90
100
  > If `--branch` is not specified, the repo's default branch is used.
91
101
 
92
- ### Clear the Local Index
102
+ ---
93
103
 
94
- ```bash
95
- gitputra clear-db
96
- ```
104
+ ### Export Codebase Context
97
105
 
98
- ### Show Info & Capabilities
106
+ Export your entire codebase as a single Markdown file you can paste into any AI tool (Claude, ChatGPT, Gemini…) to continue work without re-explaining the project.
99
107
 
100
108
  ```bash
101
- gitputra info
109
+ # Local codebase — fast, no API key needed (no AI summaries)
110
+ gitputra export-context ./my-project
111
+
112
+ # With AI summaries (requires --use-ai and an API key)
113
+ gitputra export-context ./my-project --use-ai --ai gemini --key YOUR_API_KEY
114
+ gitputra export-context ./my-project --use-ai --ai claude --key YOUR_API_KEY
115
+
116
+ # Remote GitHub repo
117
+ gitputra export-context https://github.com/user/repo --remote --ai gemini --key YOUR_API_KEY
118
+
119
+ # Remote repo, specific branch, with AI summaries
120
+ gitputra export-context https://github.com/user/repo --remote --branch dev --use-ai --ai gemini --key YOUR_API_KEY
121
+
122
+ # Scan current directory
123
+ gitputra export-context
124
+
125
+ # Path with spaces — wrap in quotes
126
+ gitputra export-context "C:\Users\you\My Project"
102
127
  ```
103
128
 
104
- ### Check Version
129
+ Output saved to `./output/context_<project_name>_<timestamp>.md`
130
+
131
+ **How to use the exported file:**
132
+ 1. Open the `.md` file
133
+ 2. Paste it into any AI chat (Claude, ChatGPT, Gemini…)
134
+ 3. Say: *"Here is my codebase context. Read it and help me continue."*
135
+
136
+ ---
137
+
138
+ ### Other Commands
105
139
 
106
140
  ```bash
141
+ # Wipe the local ChromaDB index
142
+ gitputra clear-db
143
+
144
+ # Show all commands, supported AIs, languages, and file extensions
145
+ gitputra info
146
+
147
+ # Check version
107
148
  gitputra --version
108
149
  ```
109
150
 
@@ -125,7 +166,7 @@ gitputra --version
125
166
 
126
167
  ```env
127
168
  API_KEY=your_api_key_here
128
- GEMINI_API_KEY=your_gemini_key # only needed for --ai claude with chat
169
+ GEMINI_API_KEY=your_gemini_key # only needed for --ai claude with chat/RAG
129
170
  ```
130
171
 
131
172
  **Option 2 — CLI flag:**
@@ -1,12 +1,13 @@
1
1
  import click
2
2
  from dotenv import load_dotenv
3
3
  from . import corelogic as core
4
+ from . import context_export as ctx
4
5
 
5
6
  load_dotenv()
6
7
 
7
8
 
8
9
  @click.group()
9
- @click.version_option("0.2.0", prog_name="gitputra")
10
+ @click.version_option("0.2.2", prog_name="gitputra")
10
11
  def main():
11
12
  """🔍 Gitputra — AI-powered GitHub repo analyzer."""
12
13
  pass
@@ -16,8 +17,8 @@ def main():
16
17
  # ANALYZE
17
18
  # ─────────────────────────────────────────────
18
19
  @main.command()
19
- @click.argument("url")
20
- @click.option("--branch", default=None, help="Branch to clone (default: repo default branch).")
20
+ @click.argument("url", default=".")
21
+ @click.option("--branch", default=None, help="Branch to clone (default: repo default branch). Ignored with --local.")
21
22
  @click.option(
22
23
  "--ai", "ai_choice",
23
24
  type=click.Choice(["gemini", "openai", "claude"], case_sensitive=False),
@@ -41,28 +42,46 @@ def main():
41
42
  "--no-diagram", is_flag=True, default=False,
42
43
  help="Skip diagram generation."
43
44
  )
44
- def analyze(url, branch, ai_choice, key, lang, no_pdf, no_diagram):
45
+ @click.option(
46
+ "--local", is_flag=True, default=False,
47
+ help="Analyze a local directory instead of cloning a GitHub URL."
48
+ )
49
+ def analyze(url, branch, ai_choice, key, lang, no_pdf, no_diagram, local):
45
50
  """Clone a GitHub repo and generate an AI analysis report.
46
51
 
47
52
  \b
48
- Example:
53
+ Remote repo:
49
54
  gitputra analyze https://github.com/user/repo --ai gemini --key AIza...
55
+ \b
56
+ Local codebase:
57
+ gitputra analyze ./my-project --local --ai gemini --key AIza...
58
+ gitputra analyze "C:\\path\\with spaces\\project" --local --ai gemini --key AIza...
50
59
  """
51
60
  if not key:
52
- raise click.UsageError("API key required. Use --key or set API_KEY in .env")
61
+ key = click.prompt("🔑 Enter API key", hide_input=True)
53
62
 
54
63
  core.init(ai_choice.lower(), key, lang)
55
64
 
56
- repo_path, repo_name = core.clone_repo(url, branch)
65
+ if local:
66
+ import os
67
+ repo_path = os.path.abspath(url.strip())
68
+ repo_name = os.path.basename(repo_path)
69
+ if not os.path.isdir(repo_path):
70
+ click.secho(f"❌ Directory not found: {repo_path}", fg="red")
71
+ raise SystemExit(1)
72
+ click.secho(f"📂 Analyzing local directory: {repo_path}", fg="cyan")
73
+ else:
74
+ repo_path, repo_name = core.clone_repo(url, branch)
75
+
57
76
  files = core.load_files(repo_path)
58
77
 
59
78
  if not files:
60
79
  click.secho("❌ No supported source files found.", fg="red")
61
80
  raise SystemExit(1)
62
81
 
63
- collection = core.get_collection(repo_name)
82
+ collection = core.get_collection(repo_name, branch)
64
83
  if collection.count() == 0:
65
- core.store_chunks(files, repo_name)
84
+ core.store_chunks(files, repo_name, branch)
66
85
  else:
67
86
  click.secho("ℹ️ Using existing index (run 'clear-db' to re-index).", fg="cyan")
68
87
 
@@ -84,8 +103,8 @@ def analyze(url, branch, ai_choice, key, lang, no_pdf, no_diagram):
84
103
  # CHAT
85
104
  # ─────────────────────────────────────────────
86
105
  @main.command()
87
- @click.argument("url")
88
- @click.option("--branch", default=None, help="Branch to clone (default: repo default branch).")
106
+ @click.argument("url", default=".")
107
+ @click.option("--branch", default=None, help="Branch to clone (default: repo default branch). Ignored with --local.")
89
108
  @click.option(
90
109
  "--ai", "ai_choice",
91
110
  type=click.Choice(["gemini", "openai", "claude"], case_sensitive=False),
@@ -97,34 +116,49 @@ def analyze(url, branch, ai_choice, key, lang, no_pdf, no_diagram):
97
116
  help="API key (or set API_KEY in .env). Avoid passing directly to keep it out of shell history."
98
117
  )
99
118
  @click.option("--lang", default="English", show_default=True)
100
- def chat(url, branch, ai_choice, key, lang):
119
+ @click.option(
120
+ "--local", is_flag=True, default=False,
121
+ help="Chat about a local directory instead of cloning a GitHub URL."
122
+ )
123
+ def chat(url, branch, ai_choice, key, lang, local):
101
124
  """Start an interactive RAG chat about a repo.
102
125
 
103
126
  \b
104
- Example:
127
+ Remote repo:
105
128
  gitputra chat https://github.com/user/repo --ai gemini
129
+ \b
130
+ Local codebase:
131
+ gitputra chat ./my-project --local --ai gemini --key AIza...
106
132
  Type 'exit' or Ctrl+C to quit.
107
133
  """
108
134
  if not key:
109
- raise click.UsageError("API key required. Use --key or set API_KEY in .env")
110
-
135
+ key = click.prompt("🔑 Enter API key", hide_input=True)
111
136
  core.init(ai_choice.lower(), key, lang)
112
137
 
113
- repo_path, repo_name = core.clone_repo(url, branch)
138
+ if local:
139
+ import os
140
+ repo_path = os.path.abspath(url.strip())
141
+ repo_name = os.path.basename(repo_path)
142
+ if not os.path.isdir(repo_path):
143
+ click.secho(f"❌ Directory not found: {repo_path}", fg="red")
144
+ raise SystemExit(1)
145
+ click.secho(f"📂 Using local directory: {repo_path}", fg="cyan")
146
+ else:
147
+ repo_path, repo_name = core.clone_repo(url, branch)
114
148
 
115
- collection = core.get_collection(repo_name)
149
+ collection = core.get_collection(repo_name, branch)
116
150
  if collection.count() == 0:
117
- click.secho("📂 No existing index found — indexing repo now...", fg="yellow")
151
+ click.secho("📂 No existing index found — indexing now...", fg="yellow")
118
152
  files = core.load_files(repo_path)
119
153
  if not files:
120
154
  click.secho("❌ No supported source files found.", fg="red")
121
155
  raise SystemExit(1)
122
- core.store_chunks(files, repo_name)
123
- click.secho("✅ Repo indexed.", fg="green")
156
+ core.store_chunks(files, repo_name, branch)
157
+ click.secho("✅ Indexed.", fg="green")
124
158
  else:
125
159
  click.secho("✅ Using existing index.", fg="cyan")
126
160
 
127
- click.secho(f"\n💬 Chat mode [{repo_name}] — ask anything about the repo. Type 'exit' to quit.\n", fg="cyan")
161
+ click.secho(f"\n💬 Chat mode [{repo_name}] — ask anything about the codebase. Type 'exit' to quit.\n", fg="cyan")
128
162
 
129
163
  while True:
130
164
  try:
@@ -137,10 +171,76 @@ def chat(url, branch, ai_choice, key, lang):
137
171
  click.echo("Bye!")
138
172
  break
139
173
 
140
- answer = core.query_rag(question, repo_name)
174
+ answer = core.query_rag(question, repo_name, branch)
141
175
  click.echo(f"\n{answer}\n")
142
176
 
143
177
 
178
+ # ─────────────────────────────────────────────
179
+ # EXPORT CONTEXT
180
+ # ─────────────────────────────────────────────
181
+ @main.command("export-context")
182
+ @click.argument("project_path", default=".", metavar="PROJECT_PATH", type=click.Path())
183
+ @click.option("--branch", default=None, help="Branch to clone (only used with --remote).")
184
+ @click.option("--remote", is_flag=True, default=False, help="Treat PROJECT_PATH as a GitHub URL and clone it first.")
185
+
186
+ @click.option(
187
+ "--ai", "ai_choice",
188
+ type=click.Choice(["gemini", "openai", "claude"], case_sensitive=False),
189
+ default="gemini", show_default=True,
190
+ help="AI provider to use for summaries."
191
+ )
192
+ @click.option(
193
+ "--key",
194
+ envvar="API_KEY", default=None,
195
+ help="API key (or set API_KEY in .env)."
196
+ )
197
+ @click.option(
198
+ "--lang", default="English", show_default=True,
199
+ help="Language for AI-generated summaries."
200
+ )
201
+ @click.option(
202
+ "--use-ai", "no_ai", is_flag=True, default=True, flag_value=False,
203
+ help="Use LLM to generate AI summaries and project overview (requires --ai and --key)."
204
+ )
205
+ def export_context_cmd(project_path, branch, remote, ai_choice, key, lang, no_ai):
206
+ """Export a portable context file from a local or remote codebase.
207
+
208
+ Scans PROJECT_PATH (defaults to current directory), generates AI summaries
209
+ of every file, and writes a single Markdown file you can paste into any AI
210
+ tool (Claude, ChatGPT, Gemini ...) to continue work without re-explaining
211
+ the project.
212
+
213
+ \b
214
+ IMPORTANT — wrap paths in quotes if they contain spaces:
215
+ gitputra export-context "C:\\Users\\you\\My Project"
216
+
217
+ \b
218
+ Examples:
219
+ gitputra export-context ./my-project (fast, no key needed)
220
+ gitputra export-context "C:\\path\\with spaces\\project"
221
+ gitputra export-context ./my-project --use-ai --ai gemini --key AIza...
222
+ gitputra export-context ./my-project --use-ai --ai claude --key sk-ant-...
223
+ gitputra export-context (scans current dir)
224
+ """
225
+ if remote:
226
+ project_path, _ = core.clone_repo(project_path, branch)
227
+
228
+ if no_ai:
229
+ gen_fn = lambda _prompt: None
230
+ else:
231
+ if not key:
232
+ key = click.prompt("🔑 Enter API key", hide_input=True)
233
+ core.init(ai_choice.lower(), key, lang)
234
+ gen_fn = core.safe_generate
235
+
236
+ ctx.export_context(
237
+ project_path = project_path,
238
+ safe_generate_fn = gen_fn,
239
+ language = lang,
240
+ skip_ai = no_ai,
241
+ )
242
+
243
+
144
244
  # ─────────────────────────────────────────────
145
245
  # CLEAR DB
146
246
  # ─────────────────────────────────────────────
@@ -161,7 +261,7 @@ def info():
161
261
  """Show supported AIs, languages, extensions, and commands."""
162
262
  click.secho("\n 🔍 GitPutra — AI-powered GitHub Repo Analyzer", fg="cyan", bold=True)
163
263
  click.secho(" 🤖 Chat with your codebase using Gemini, OpenAI or Claude", fg="bright_cyan")
164
- click.secho(" 🏷️ Version: 0.2.0", fg="cyan")
264
+ click.secho(" 🏷️ Version: 0.2.2", fg="cyan")
165
265
  click.secho(" 👨‍💻 Author: Adityava Gangopadhyay", fg="magenta")
166
266
  click.secho(" 💼 Email: adityava49cse@gmail.com", fg="bright_magenta")
167
267
  click.secho(" 🔗 LinkedIn: https://www.linkedin.com/in/adityava-gangopadhyay/", fg="blue")
@@ -171,13 +271,19 @@ def info():
171
271
 
172
272
  click.secho("📦 Commands:", fg="yellow", bold=True)
173
273
  commands = [
174
- ("analyze", "Clone a repo and generate a full AI report (supports --branch)"),
175
- ("chat", "Interactive RAG-based Q&A about a repo (supports --branch)"),
176
- ("clear-db","Wipe the local ChromaDB index"),
177
- ("info", "Show this info screen"),
274
+ ("analyze <url>", "Clone & analyze a remote GitHub repo"),
275
+ ("analyze <path> --local","Analyze a local codebase (no cloning)"),
276
+ ("chat <url>", "RAG chat about a remote GitHub repo"),
277
+ ("chat <path> --local", "RAG chat about a local codebase"),
278
+ ("export-context", "Export portable context .md (AI off by default)"),
279
+ ("export-context <path>", "Export context .md from local dir (AI off by default)"),
280
+ ("export-context <url> --remote", "Export context .md from remote GitHub repo"),
281
+ ("clear-db", "Wipe the local ChromaDB index"),
282
+ ("info", "Show this info screen"),
283
+
178
284
  ]
179
285
  for cmd, desc in commands:
180
- click.echo(f" {'gitputra ' + cmd:<25} {desc}")
286
+ click.echo(f" {'gitputra ' + cmd:<40} {desc}")
181
287
 
182
288
  click.secho("\n🤖 Supported AI Providers:", fg="yellow", bold=True)
183
289
  ais = [