devflow-tools 0.1.0__py3-none-any.whl

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.
devflow/__init__.py ADDED
@@ -0,0 +1,4 @@
1
+ from .ai import AI
2
+ from .cli import CLI
3
+ from .network import SocketServer, SocketClient
4
+ from .help import display_help,help
devflow/ai.py ADDED
@@ -0,0 +1,108 @@
1
+ import os
2
+ import time
3
+ from concurrent.futures import ThreadPoolExecutor, TimeoutError as FutureTimeoutError
4
+ from typing import Union, List, Optional
5
+ from google import genai
6
+ from google.genai import errors
7
+
8
+
9
+ class AI:
10
+ def __init__(
11
+ self,
12
+ api_keys: Union[str, List[str], bool] = False,
13
+ model: str = "gemini-3.6-flash",
14
+ ):
15
+ """Initialise l'instance AI.
16
+
17
+ :param api_keys: Clé unique (str), liste de clés (list), ou False pour
18
+ utiliser os.environ['gemini_1']
19
+ :param model: Nom du modèle Gemini (par défaut: gemini-3.6-flash)
20
+ """
21
+ self.model = model
22
+ self.key_list: List[str] = []
23
+ self.current_key_index: int = 0
24
+
25
+ # Gestion des clés d'API
26
+ if api_keys is False or api_keys is None:
27
+ env_key = os.environ.get("gemini_1") or os.environ.get("GEMINI_API_KEY")
28
+ if not env_key:
29
+ raise ValueError(
30
+ "Aucune clé trouvée. Définis la variable 'gemini_1' ou passe une clé."
31
+ )
32
+ self.key_list = [env_key]
33
+ elif isinstance(api_keys, str):
34
+ self.key_list = [api_keys]
35
+ elif isinstance(api_keys, list):
36
+ if not api_keys:
37
+ raise ValueError("La liste de clés API ne peut pas être vide.")
38
+ self.key_list = api_keys
39
+ else:
40
+ raise TypeError("Format de api_keys invalide.")
41
+
42
+ self._init_client()
43
+
44
+ def _init_client(self):
45
+ """Instancie le client Gemini avec la clé courante."""
46
+ current_key = self.key_list[self.current_key_index]
47
+ self.client = genai.Client(api_key=current_key)
48
+
49
+ def _rotate_key(self) -> bool:
50
+ """Passe à la clé suivante dans la liste si disponible."""
51
+ if len(self.key_list) <= 1:
52
+ return False
53
+ self.current_key_index = (self.current_key_index + 1) % len(self.key_list)
54
+ self._init_client()
55
+ return True
56
+
57
+ def request(self, prompt: str, timeout: Optional[float] = None) -> str:
58
+ """Envoie un prompt à Gemini et retourne la réponse complète.
59
+
60
+ :param prompt: Le texte à envoyer à l'IA
61
+ :param timeout: Temps max en secondes avant d'abandonner (Optionnel)
62
+ """
63
+ attempts = 0
64
+ max_attempts = len(self.key_list)
65
+
66
+ def _execute():
67
+ response = self.client.models.generate_content(
68
+ model=self.model, contents=prompt
69
+ )
70
+ return response.text
71
+
72
+ while attempts < max_attempts:
73
+ try:
74
+ if timeout is not None:
75
+ # Vrai timeout exécuté dans un thread séparé
76
+ with ThreadPoolExecutor(max_workers=1) as executor:
77
+ future = executor.submit(_execute)
78
+ return future.result(timeout=timeout)
79
+ else:
80
+ return _execute()
81
+
82
+ except FutureTimeoutError:
83
+ raise TimeoutError(f"La requête a dépassé le timeout de {timeout}s")
84
+ except (errors.APIError, errors.ServerError) as e:
85
+ attempts += 1
86
+ if not self._rotate_key() or attempts >= max_attempts:
87
+ raise e
88
+ except Exception as e:
89
+ raise e
90
+
91
+ def ask_stream(self, prompt: str):
92
+ """Génère du texte en streaming (compatible avec devflow.cli)."""
93
+ attempts = 0
94
+ max_attempts = len(self.key_list)
95
+
96
+ while attempts < max_attempts:
97
+ try:
98
+ response = self.client.models.generate_content_stream(
99
+ model=self.model, contents=prompt
100
+ )
101
+ for chunk in response:
102
+ if chunk.text:
103
+ yield chunk.text
104
+ return
105
+ except errors.APIError as e:
106
+ attempts += 1
107
+ if not self._rotate_key() or attempts >= max_attempts:
108
+ raise e
devflow/cli.py ADDED
@@ -0,0 +1,209 @@
1
+ from contextlib import contextmanager
2
+ import random
3
+ import time
4
+ from typing import List, Optional, Union
5
+ from rich.console import Console
6
+ from rich.live import Live
7
+ from rich.markdown import Markdown
8
+ from rich.panel import Panel
9
+ from rich.progress import Progress, SpinnerColumn, TextColumn
10
+ from rich.prompt import Prompt
11
+ from rich.text import Text
12
+
13
+ # Console unique partagée
14
+ console = Console()
15
+
16
+
17
+ class CLI:
18
+ # -------------------------------------------------------------------------
19
+ # 1. RENDU DE TEXTE ET STREAMING
20
+ # -------------------------------------------------------------------------
21
+
22
+ @staticmethod
23
+ def print_stream(generator) -> str:
24
+ """Affiche un flux de texte (generator) en Markdown et en temps réel."""
25
+ full_text = ""
26
+ with Live(
27
+ Markdown(full_text),
28
+ console=console,
29
+ refresh_per_second=12,
30
+ vertical_overflow="visible",
31
+ ) as live:
32
+ for chunk in generator:
33
+ if chunk:
34
+ full_text += chunk
35
+ live.update(Markdown(full_text))
36
+ return full_text
37
+
38
+ @staticmethod
39
+ def type_text(
40
+ text: str,
41
+ speed: float = 0.03,
42
+ color: str = "green",
43
+ cursor: bool = True,
44
+ ) -> None:
45
+ """Simule un effet de frappe à la machine à écrire avec curseur clignotant."""
46
+ displayed = ""
47
+ with Live(
48
+ console=console, refresh_per_second=20, transient=True
49
+ ) as live:
50
+ for letter in text:
51
+ displayed += letter
52
+ cur_str = (
53
+ ("_" if int(time.time() * 4) % 2 == 0 else " ")
54
+ if cursor
55
+ else ""
56
+ )
57
+ live.update(f"[{color}]{displayed}{cur_str}[/{color}]")
58
+ time.sleep(speed)
59
+
60
+ # Affichage final fixé dans la console
61
+ console.print(f"[{color}]{text}[/{color}]")
62
+
63
+ @staticmethod
64
+ def live_sequence(
65
+ items: List[str], delay: float = 0.01, repeat: int = 1
66
+ ) -> str:
67
+ """Anime l'affichage d'une liste de fragments de texte progressivement."""
68
+ full_text = ""
69
+ with Live(
70
+ Markdown(""),
71
+ console=console,
72
+ refresh_per_second=30,
73
+ vertical_overflow="visible",
74
+ ) as live_display:
75
+ for cycle in range(repeat):
76
+ full_text = ""
77
+ for chunk in items:
78
+ full_text += chunk
79
+ live_display.update(Markdown(full_text))
80
+ time.sleep(delay)
81
+ if cycle < repeat - 1:
82
+ time.sleep(0.2)
83
+ return full_text
84
+
85
+ # -------------------------------------------------------------------------
86
+ # 2. INDICATEURS DE CHARGEMENT ET PROGRESSION
87
+ # -------------------------------------------------------------------------
88
+
89
+ @staticmethod
90
+ @contextmanager
91
+ def status(message: str = "Traitement en cours..."):
92
+ """Affiche un spinner animé pendant l'exécution d'un bloc de code."""
93
+ with console.status(
94
+ f"[bold cyan]{message}[/bold cyan]", spinner="dots"
95
+ ):
96
+ yield
97
+
98
+ @staticmethod
99
+ def animate_dots(
100
+ text: str = "Chargement",
101
+ max_dots: int = 20,
102
+ speed: float = 0.08,
103
+ color: str = "cyan",
104
+ ) -> None:
105
+ """Affiche un texte avec des points d'attente animés (ex: Chargement...)."""
106
+ with Live(
107
+ console=console, refresh_per_second=15, transient=True
108
+ ) as live:
109
+ for i in range(max_dots + 1):
110
+ live.update(f"[{color}]{text}{'.' * i}[/{color}]")
111
+ time.sleep(speed)
112
+ console.print(f"[bold green]✔ {text} terminé.[/bold green]")
113
+
114
+ @staticmethod
115
+ def animate_frames(
116
+ frames: List[str],
117
+ repetitions: int = 5,
118
+ speed: float = 0.25,
119
+ color: str = "cyan",
120
+ ) -> None:
121
+ """Boucle sur une liste de frames textuelles (ex: spinners personnalisés)."""
122
+ with Live(
123
+ console=console, refresh_per_second=15, transient=True
124
+ ) as live:
125
+ for _ in range(repetitions):
126
+ for frame in frames:
127
+ live.update(Text(frame, style=color))
128
+ time.sleep(speed)
129
+
130
+ @staticmethod
131
+ def progress_bar(
132
+ title: str = "Progression",
133
+ total: int = 100,
134
+ speed: float = 0.03,
135
+ color: str = "yellow",
136
+ ) -> None:
137
+ """Affiche une barre de progression simple et fluide."""
138
+ with Progress(
139
+ SpinnerColumn(),
140
+ TextColumn("[progress.description]{task.description}"),
141
+ console=console,
142
+ transient=True,
143
+ ) as progress:
144
+ task = progress.add_task(f"[{color}]{title}", total=total)
145
+ while not progress.finished:
146
+ time.sleep(speed)
147
+ progress.update(task, advance=1)
148
+
149
+ # -------------------------------------------------------------------------
150
+ # 3. INTERFACES, PANNEAUX ET SÉPARATEURS
151
+ # -------------------------------------------------------------------------
152
+
153
+ @staticmethod
154
+ def rule(
155
+ title: str = "", character: str = "─", color: str = "bright_blue"
156
+ ) -> None:
157
+ """Trace une ligne séparatrice horizontale avec un titre optionnel."""
158
+ width = console.width
159
+ if title:
160
+ text = f" {title} "
161
+ remaining = width - len(text)
162
+ left = remaining // 2
163
+ right = remaining - left
164
+ line = character * left + text + character * right
165
+ else:
166
+ line = character * width
167
+ console.print(line, style=color)
168
+
169
+ @staticmethod
170
+ def print_panel(
171
+ content: Union[str, Markdown],
172
+ title: str = "devflow",
173
+ style: str = "cyan",
174
+ expand: bool = True,
175
+ ) -> None:
176
+ """Affiche un contenu encadré dans un panneau stylisé."""
177
+ md = Markdown(content) if isinstance(content, str) else content
178
+ console.print(
179
+ Panel(
180
+ md, title=f"[bold]{title}[/bold]", border_style=style, expand=expand
181
+ )
182
+ )
183
+
184
+ @staticmethod
185
+ def ask(prompt_text: str = "Entrez votre message") -> str:
186
+ """Pose une question à l'utilisateur via une invite propre."""
187
+ return Prompt.ask(f"[bold green]?[/bold green] {prompt_text}")
188
+
189
+ # -------------------------------------------------------------------------
190
+ # 4. SIMULATION DE LOGS & UTILS
191
+ # -------------------------------------------------------------------------
192
+
193
+ @staticmethod
194
+ def generate_logs(count: int = 10, speed: float = 0.01) -> None:
195
+ """Génère de faux logs système en défilement rapide pour des démos/tests."""
196
+ log_types = [
197
+ ("ERROR", "bold red"),
198
+ ("INFO", "bold white"),
199
+ ("WARNING", "bold yellow"),
200
+ ("SUCCESS", "bold green"),
201
+ ]
202
+ for i in range(1, count + 1):
203
+ level, color = random.choice(log_types)
204
+ pid = random.randint(1000, 9999)
205
+ code = random.randint(100000, 999999)
206
+ status = random.choice(["OK", "FAIL", "PENDING"])
207
+
208
+ msg = f"[{i:03d}] [{level}] Process_{pid} status={status} code={code}"
209
+ CLI.type_text(msg, speed=speed, color=color, cursor=False)
devflow/help.py ADDED
@@ -0,0 +1,170 @@
1
+ from rich.console import Console
2
+ from rich.panel import Panel
3
+ from rich.table import Table
4
+ from rich.syntax import Syntax
5
+
6
+ console = Console()
7
+
8
+
9
+ def display_help(module_name: str = None) -> None:
10
+ """Affiche l'aide interactive et la documentation de DevFlow."""
11
+
12
+ # Titre principal
13
+ console.print()
14
+ console.print(
15
+ Panel(
16
+ "[bold cyan]🚀 DevFlow - Documentation & Aide[/bold cyan]\n"
17
+ "[dim]Bibliothèque modulaire : IA (Gemini), CLI (Rich), Sockets TCP.[/dim]",
18
+ border_style="bright_blue",
19
+ expand=False,
20
+ )
21
+ )
22
+
23
+ # -------------------------------------------------------------------------
24
+ # MODULE : AI
25
+ # -------------------------------------------------------------------------
26
+ if module_name in (None, "ai"):
27
+ console.print("\n[bold yellow]🤖 MODULE : devflow.AI[/bold yellow]")
28
+ table_ai = Table(show_header=True, header_style="bold magenta", expand=True)
29
+ table_ai.add_column("Méthode", style="cyan", width=22)
30
+ table_ai.add_column("Paramètres", style="green")
31
+ table_ai.add_column("Description", style="white")
32
+
33
+ table_ai.add_row(
34
+ "__init__()",
35
+ "model='gemini-2.5-flash', system_instruction=None",
36
+ "Initialise l'instance AI avec la clé API Google.",
37
+ )
38
+ table_ai.add_row(
39
+ "ask()",
40
+ "prompt: str",
41
+ "Envoie une requête et retourne la réponse complète sous forme de texte.",
42
+ )
43
+ table_ai.add_row(
44
+ "ask_stream()",
45
+ "prompt: str",
46
+ "Retourne un générateur pour lire la réponse morceau par morceau.",
47
+ )
48
+ console.print(table_ai)
49
+
50
+ example_ai = (
51
+ "from devflow import AI, CLI\n\n"
52
+ "ai = AI()\n"
53
+ "# Utilisation classique\n"
54
+ "reponse = ai.ask('Explique les sockets en 2 phrases')\n"
55
+ "print(reponse)\n\n"
56
+ "# Utilisation en streaming avec la CLI\n"
57
+ "CLI.print_stream(ai.ask_stream('Raconte une histoire courte'))"
58
+ )
59
+ console.print(Panel(Syntax(example_ai, "python", theme="monokai"), title="[bold]Exemple AI[/bold]", border_style="dim"))
60
+
61
+ # -------------------------------------------------------------------------
62
+ # MODULE : CLI
63
+ # -------------------------------------------------------------------------
64
+ if module_name in (None, "cli"):
65
+ console.print("\n[bold yellow]💻 MODULE : devflow.CLI[/bold yellow]")
66
+ table_cli = Table(show_header=True, header_style="bold magenta", expand=True)
67
+ table_cli.add_column("Méthode", style="cyan", width=22)
68
+ table_cli.add_column("Paramètres", style="green")
69
+ table_cli.add_column("Description", style="white")
70
+
71
+ table_cli.add_row(
72
+ "print_stream()",
73
+ "generator",
74
+ "Affiche un flux de texte (MarkDown) en temps réel.",
75
+ )
76
+ table_cli.add_row(
77
+ "type_text()",
78
+ "text, speed=0.03, color='green', cursor=True",
79
+ "Effet machine à écrire avec curseur clignotant.",
80
+ )
81
+ table_cli.add_row(
82
+ "animate_dots()",
83
+ "text='Chargement', max_dots=20, speed=0.08, color='cyan'",
84
+ "Animation de points d'attente dynamiques.",
85
+ )
86
+ table_cli.add_row(
87
+ "progress_bar()",
88
+ "title='Progression', total=100, speed=0.03, color='yellow'",
89
+ "Barre de progression fluide.",
90
+ )
91
+ table_cli.add_row(
92
+ "rule()",
93
+ "title='', character='─', color='bright_blue'",
94
+ "Ligne séparatrice avec ou sans titre.",
95
+ )
96
+ table_cli.add_row(
97
+ "print_panel()",
98
+ "content, title='devflow', style='cyan', expand=True",
99
+ "Encadre du texte dans un panneau stylisé.",
100
+ )
101
+ table_cli.add_row(
102
+ "ask()",
103
+ "prompt_text='Entrez votre message'",
104
+ "Invite de saisie utilisateur stylisée.",
105
+ )
106
+ console.print(table_cli)
107
+
108
+ example_cli = (
109
+ "from devflow import CLI\n\n"
110
+ "CLI.rule(title='DEBUT DE TÂCHE')\n"
111
+ "CLI.type_text('Connexion au serveur...', color='cyan')\n"
112
+ "CLI.progress_bar(title='Téléchargement', total=50)\n"
113
+ "CLI.print_panel('Operation réussie !', title='Succès', style='green')"
114
+ )
115
+ console.print(Panel(Syntax(example_cli, "python", theme="monokai"), title="[bold]Exemple CLI[/bold]", border_style="dim"))
116
+
117
+ # -------------------------------------------------------------------------
118
+ # MODULE : NETWORK
119
+ # -------------------------------------------------------------------------
120
+ if module_name in (None, "network"):
121
+ console.print("\n[bold yellow]🌐 MODULE : devflow.NETWORK[/bold yellow]")
122
+ table_net = Table(show_header=True, header_style="bold magenta", expand=True)
123
+ table_net.add_column("Classe / Méthode", style="cyan", width=22)
124
+ table_net.add_column("Paramètres", style="green")
125
+ table_net.add_column("Description", style="white")
126
+
127
+ table_net.add_row(
128
+ "SocketServer()",
129
+ "host='0.0.0.0', port=65432, buffer_size=4096",
130
+ "Serveur TCP multithreadé.",
131
+ )
132
+ table_net.add_row(
133
+ "SocketServer.start()",
134
+ "handler: Callable[[str], Union[str, Generator]]",
135
+ "Lance le serveur et exécute le handler à chaque message.",
136
+ )
137
+ table_net.add_row(
138
+ "SocketClient()",
139
+ "host='127.0.0.1', port=65432, buffer_size=4096",
140
+ "Client TCP simple.",
141
+ )
142
+ table_net.add_row(
143
+ "SocketClient.send()",
144
+ "message: str, timeout=10.0",
145
+ "Envoie un message et attend la réponse complète.",
146
+ )
147
+ table_net.add_row(
148
+ "SocketClient.send_stream()",
149
+ "message: str, timeout=10.0",
150
+ "Envoie un message et reçoit la réponse en flux (generator).",
151
+ )
152
+ console.print(table_net)
153
+
154
+ example_net = (
155
+ "# SERVEUR TCP\n"
156
+ "from devflow import SocketServer\n\n"
157
+ "server = SocketServer(port=65432)\n"
158
+ "server.start(handler=lambda msg: f'Reçu : {msg}')\n\n"
159
+ "# CLIENT TCP\n"
160
+ "from devflow import SocketClient\n\n"
161
+ "client = SocketClient(port=65432)\n"
162
+ "reponse = client.send('Hello Server !')\n"
163
+ "print(reponse)"
164
+ )
165
+ console.print(Panel(Syntax(example_net, "python", theme="monokai"), title="[bold]Exemple Network[/bold]", border_style="dim"))
166
+
167
+
168
+ def help(module_name: str = None) -> None:
169
+ """Raccourci pour afficher l'aide."""
170
+ display_help(module_name)
devflow/network.py ADDED
@@ -0,0 +1,121 @@
1
+ import socket
2
+ import threading
3
+ from typing import Callable, Optional, Generator, Union
4
+
5
+
6
+ class SocketServer:
7
+ """Serveur TCP multithreadé simple et réutilisable."""
8
+
9
+ def __init__(self, host: str = "0.0.0.0", port: int = 65432, buffer_size: int = 4096):
10
+ self.host = host
11
+ self.port = port
12
+ self.buffer_size = buffer_size
13
+ self.server_socket: Optional[socket.socket] = None
14
+ self.is_running: bool = False
15
+
16
+ def _handle_client(
17
+ self,
18
+ conn: socket.socket,
19
+ addr: tuple,
20
+ handler: Callable[[str], Union[str, Generator[str, None, None]]],
21
+ ):
22
+ """Gère la communication avec un client connecté dans un thread dédié."""
23
+ try:
24
+ while self.is_running:
25
+ data = conn.recv(self.buffer_size)
26
+ if not data:
27
+ break
28
+
29
+ message = data.decode("utf-8").strip()
30
+ if not message:
31
+ continue
32
+
33
+ # Exécution de la fonction de traitement (callback)
34
+ result = handler(message)
35
+
36
+ # Si le handler est un générateur (ex: streaming Gemini)
37
+ if isinstance(result, Generator):
38
+ for chunk in result:
39
+ if chunk:
40
+ conn.sendall(chunk.encode("utf-8"))
41
+ elif isinstance(result, str):
42
+ conn.sendall(result.encode("utf-8"))
43
+
44
+ except (ConnectionResetError, BrokenPipeError):
45
+ pass
46
+ finally:
47
+ conn.close()
48
+
49
+ def start(self, handler: Callable[[str], Union[str, Generator[str, None, None]]]):
50
+ """Démarre le serveur et écoute les connexions entrantes (bloquant).
51
+
52
+ :param handler: Fonction recevant un message (str) et retournant une réponse (str ou générateur)
53
+ """
54
+ self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
55
+ self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
56
+ self.server_socket.bind((self.host, self.port))
57
+ self.server_socket.listen()
58
+ self.is_running = True
59
+
60
+ print(f"[devflow.network] Serveur actif sur {self.host}:{self.port}")
61
+
62
+ try:
63
+ while self.is_running:
64
+ conn, addr = self.server_socket.accept()
65
+ # Un thread par client connecté
66
+ client_thread = threading.Thread(
67
+ target=self._handle_client,
68
+ args=(conn, addr, handler),
69
+ daemon=True,
70
+ )
71
+ client_thread.start()
72
+ except KeyboardInterrupt:
73
+ print("\n[devflow.network] Arrêt du serveur.")
74
+ finally:
75
+ self.stop()
76
+
77
+ def stop(self):
78
+ """Arrête proprement le serveur socket."""
79
+ self.is_running = False
80
+ if self.server_socket:
81
+ self.server_socket.close()
82
+
83
+
84
+ class SocketClient:
85
+ """Client TCP rapide pour envoyer et recevoir des données."""
86
+
87
+ def __init__(self, host: str = "127.0.0.1", port: int = 65432, buffer_size: int = 4096):
88
+ self.host = host
89
+ self.port = port
90
+ self.buffer_size = buffer_size
91
+
92
+ def send(self, message: str, timeout: float = 10.0) -> str:
93
+ """Envoie un message au serveur et attend la réponse complète.
94
+
95
+ :param message: Le texte à envoyer
96
+ :param timeout: Temps d'attente maximum en secondes
97
+ :return: La réponse reçue du serveur
98
+ """
99
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client_socket:
100
+ client_socket.settimeout(timeout)
101
+ client_socket.connect((self.host, self.port))
102
+ client_socket.sendall(message.encode("utf-8"))
103
+
104
+ response = client_socket.recv(self.buffer_size)
105
+ return response.decode("utf-8")
106
+
107
+ def send_stream(self, message: str, timeout: float = 10.0) -> Generator[str, None, None]:
108
+ """Envoie un message et reçoit la réponse en streaming fragment par fragment."""
109
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client_socket:
110
+ client_socket.settimeout(timeout)
111
+ client_socket.connect((self.host, self.port))
112
+ client_socket.sendall(message.encode("utf-8"))
113
+
114
+ while True:
115
+ try:
116
+ chunk = client_socket.recv(self.buffer_size)
117
+ if not chunk:
118
+ break
119
+ yield chunk.decode("utf-8")
120
+ except socket.timeout:
121
+ break
@@ -0,0 +1,29 @@
1
+ Metadata-Version: 2.4
2
+ Name: devflow-tools
3
+ Version: 0.1.0
4
+ Summary: Un couteau suisse pour créer des CLI stylisées, du réseau TCP et intégrer l'IA.
5
+ Author: Alex
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: License :: OSI Approved :: MIT License
8
+ Classifier: Operating System :: OS Independent
9
+ Requires-Python: >=3.8
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: rich>=12.0.0
13
+ Requires-Dist: google-genai>=0.1.0
14
+ Dynamic: license-file
15
+
16
+ # DevFlow 🚀
17
+
18
+ **DevFlow** est un couteau suisse Python pour simplifier la création d'interfaces en ligne de commande (CLI) stylisées, la programmation réseau par Sockets TCP et l'intégration de l'IA (Gemini).
19
+
20
+ ## 📥 Installation
21
+
22
+ ```bash
23
+ pip install devflow-tools
24
+ ```
25
+ ---
26
+
27
+ ## 👨‍💻 À propos de l'auteur
28
+
29
+ Projet codé et conçu par **Alex74** (développeur passionné jeune) pour simplifier le développement d'outils CLI et réseau en Python.
@@ -0,0 +1,10 @@
1
+ devflow/__init__.py,sha256=qbVjXsJO5HOApalR7CAJIJAqddWuU4KKRaTy3cRMjm4,126
2
+ devflow/ai.py,sha256=uOlMsitz32r1rqDhxW6uPXnjjYCOnJTN8JCNEC6W-mo,4094
3
+ devflow/cli.py,sha256=N1ED74DoCjHPwPZs1cSJI9e8cahsTDjgSJz4-J74x64,7591
4
+ devflow/help.py,sha256=8J0dUjyeCWtQDFwLFa5vIOCJPws2Xv9CKLlZ0IBjlFY,6988
5
+ devflow/network.py,sha256=J1bJGxmzYK53ZeT2WZOHstTfjT2eCC85Qx0-JWFu8eg,4681
6
+ devflow_tools-0.1.0.dist-info/licenses/LICENSE,sha256=MgpW6G6qVEvu2-_wYDBomnxsSNEa9wHUn-RsuePNGDQ,1082
7
+ devflow_tools-0.1.0.dist-info/METADATA,sha256=FKvdLOKz6yyisNOULWMqxCbMWI-SpPkU_rOOHRTmzZA,967
8
+ devflow_tools-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
9
+ devflow_tools-0.1.0.dist-info/top_level.txt,sha256=hyulEBYO7koyGtocuOHxJi07MjoMQMD72EksFQeX2SY,8
10
+ devflow_tools-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alex74
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ devflow