stackai-cli 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.
devai/__init__.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
devai/ai.py ADDED
@@ -0,0 +1,32 @@
1
+ import urllib.request
2
+ import urllib.error
3
+ import json
4
+
5
+
6
+ OLLAMA_URL = "http://localhost:11434/api/generate"
7
+ DEFAULT_MODEL = "llama3"
8
+
9
+
10
+ def ask_ollama(prompt: str, model: str = DEFAULT_MODEL) -> str | None:
11
+ """Envoie un prompt à Ollama et retourne la réponse complète."""
12
+ payload = json.dumps({
13
+ "model": model,
14
+ "prompt": prompt,
15
+ "stream": False,
16
+ }).encode("utf-8")
17
+
18
+ req = urllib.request.Request(
19
+ OLLAMA_URL,
20
+ data=payload,
21
+ headers={"Content-Type": "application/json"},
22
+ method="POST",
23
+ )
24
+
25
+ try:
26
+ with urllib.request.urlopen(req, timeout=60) as resp:
27
+ data = json.loads(resp.read().decode("utf-8"))
28
+ return data.get("response", "").strip()
29
+ except urllib.error.URLError:
30
+ return None
31
+ except json.JSONDecodeError:
32
+ return None
devai/cli.py ADDED
@@ -0,0 +1,56 @@
1
+ import click
2
+ from devai.detector import detect_stack
3
+ from devai.generator import generate_files
4
+ from devai.debugger import debug_container
5
+
6
+ @click.group()
7
+ @click.version_option()
8
+ def main():
9
+ """devai — AI-powered Docker assistant. No config needed."""
10
+ pass
11
+
12
+
13
+ @main.command()
14
+ @click.argument("path", default=".", type=click.Path(exists=True))
15
+ def init(path):
16
+ """Analyse ton projet et génère Dockerfile + docker-compose."""
17
+ click.echo(click.style("🔍 Analyse du projet...", fg="cyan"))
18
+
19
+ stack = detect_stack(path)
20
+ if not stack:
21
+ click.echo(click.style("❌ Impossible de détecter la stack. Vérifie que tu es dans le bon dossier.", fg="red"))
22
+ raise SystemExit(1)
23
+
24
+ click.echo(click.style(f"✅ Stack détectée : {stack['language']} / {', '.join(stack['services'])}", fg="green"))
25
+ click.echo(click.style("⚙️ Génération des fichiers Docker...", fg="cyan"))
26
+
27
+ generated = generate_files(path, stack)
28
+ for f in generated:
29
+ click.echo(click.style(f" ✅ {f} créé", fg="green"))
30
+
31
+ click.echo(click.style("\n🚀 Prêt ! Lance avec : docker compose up -d", fg="bright_green", bold=True))
32
+
33
+
34
+ @main.command()
35
+ @click.argument("container_name")
36
+ def debug(container_name):
37
+ """Analyse les logs d'un container et explique l'erreur."""
38
+ click.echo(click.style(f"🔍 Lecture des logs de '{container_name}'...", fg="cyan"))
39
+ debug_container(container_name)
40
+
41
+
42
+ @main.command()
43
+ @click.argument("path", default=".", type=click.Path(exists=True))
44
+ def scan(path):
45
+ """Affiche un résumé de la stack détectée sans rien générer."""
46
+ stack = detect_stack(path)
47
+ if not stack:
48
+ click.echo(click.style("❌ Aucune stack détectée.", fg="red"))
49
+ return
50
+
51
+ click.echo(click.style("📦 Stack détectée :", fg="cyan", bold=True))
52
+ click.echo(f" Langage : {stack['language']}")
53
+ click.echo(f" Framework : {stack.get('framework', 'inconnu')}")
54
+ click.echo(f" Port : {stack.get('port', 'inconnu')}")
55
+ click.echo(f" Services : {', '.join(stack['services']) if stack['services'] else 'aucun'}")
56
+ click.echo(f" Python ver. : {stack.get('python_version', 'N/A')}")
devai/debugger.py ADDED
@@ -0,0 +1,56 @@
1
+ import subprocess
2
+ import click
3
+ from devai.ai import ask_ollama
4
+
5
+
6
+ def debug_container(container_name: str):
7
+ """Récupère les logs Docker et les envoie à l'IA pour analyse."""
8
+
9
+ # 1. Récupérer les logs
10
+ try:
11
+ result = subprocess.run(
12
+ ["docker", "logs", "--tail", "100", container_name],
13
+ capture_output=True,
14
+ text=True,
15
+ timeout=15,
16
+ )
17
+ logs = result.stdout + result.stderr
18
+ except FileNotFoundError:
19
+ click.echo(click.style("❌ Docker n'est pas installé ou introuvable dans le PATH.", fg="red"))
20
+ return
21
+ except subprocess.TimeoutExpired:
22
+ click.echo(click.style("❌ Timeout lors de la récupération des logs.", fg="red"))
23
+ return
24
+
25
+ if not logs.strip():
26
+ click.echo(click.style("⚠️ Aucun log trouvé pour ce container.", fg="yellow"))
27
+ return
28
+
29
+ click.echo(click.style("📋 Derniers logs :", fg="cyan"))
30
+ click.echo(click.style("─" * 60, fg="bright_black"))
31
+ # Affiche les 20 dernières lignes pour ne pas surcharger
32
+ lines = logs.strip().splitlines()
33
+ for line in lines[-20:]:
34
+ click.echo(f" {line}")
35
+ click.echo(click.style("─" * 60, fg="bright_black"))
36
+
37
+ # 2. Envoyer à l'IA
38
+ click.echo(click.style("\n🤖 Analyse IA en cours...", fg="cyan"))
39
+
40
+ prompt = f"""Tu es un expert Docker et DevOps. Analyse ces logs de container Docker et réponds en français.
41
+
42
+ LOGS :
43
+ {logs[-3000:]}
44
+
45
+ Réponds avec ce format exact :
46
+ 1. PROBLÈME : [explication courte du problème]
47
+ 2. CAUSE : [pourquoi ça arrive]
48
+ 3. FIX : [commande ou modification exacte à faire]
49
+ """
50
+
51
+ response = ask_ollama(prompt)
52
+ if response:
53
+ click.echo(click.style("\n💡 Analyse :", fg="green", bold=True))
54
+ click.echo(response)
55
+ else:
56
+ click.echo(click.style("❌ Impossible de contacter Ollama. Lance-le avec : ollama serve", fg="red"))
devai/detector.py ADDED
@@ -0,0 +1,151 @@
1
+ import os
2
+ import re
3
+ from pathlib import Path
4
+
5
+
6
+ # Mapping dépendance → service Docker requis
7
+ SERVICE_HINTS = {
8
+ "psycopg2": "postgres",
9
+ "asyncpg": "postgres",
10
+ "sqlalchemy": "postgres",
11
+ "pymongo": "mongodb",
12
+ "motor": "mongodb",
13
+ "redis": "redis",
14
+ "celery": "redis",
15
+ "elasticsearch": "elasticsearch",
16
+ "kafka": "kafka",
17
+ "mysql-connector": "mysql",
18
+ "pymysql": "mysql",
19
+ }
20
+
21
+ # Mapping dépendance → framework
22
+ FRAMEWORK_HINTS = {
23
+ "fastapi": ("fastapi", 8000),
24
+ "uvicorn": ("fastapi", 8000),
25
+ "flask": ("flask", 5000),
26
+ "django": ("django", 8000),
27
+ "tornado": ("tornado", 8888),
28
+ "aiohttp": ("aiohttp", 8080),
29
+ }
30
+
31
+
32
+ def detect_stack(project_path: str) -> dict | None:
33
+ path = Path(project_path)
34
+
35
+ # --- Python ---
36
+ req_file = path / "requirements.txt"
37
+ pyproject = path / "pyproject.toml"
38
+ if req_file.exists() or pyproject.exists():
39
+ return _detect_python(path, req_file if req_file.exists() else pyproject)
40
+
41
+ # --- Node.js ---
42
+ if (path / "package.json").exists():
43
+ return _detect_node(path)
44
+
45
+ # --- Java ---
46
+ if (path / "pom.xml").exists() or (path / "build.gradle").exists():
47
+ return _detect_java(path)
48
+
49
+ # --- Go ---
50
+ if (path / "go.mod").exists():
51
+ return _detect_go(path)
52
+
53
+ return None
54
+
55
+
56
+ def _detect_python(path: Path, dep_file: Path) -> dict:
57
+ content = dep_file.read_text(encoding="utf-8", errors="ignore").lower()
58
+ deps = [line.split("==")[0].split(">=")[0].strip() for line in content.splitlines() if line.strip() and not line.startswith("#")]
59
+
60
+ framework = "generic"
61
+ port = 8000
62
+ for dep, (fw, p) in FRAMEWORK_HINTS.items():
63
+ if dep in deps:
64
+ framework = fw
65
+ port = p
66
+ break
67
+
68
+ services = list({SERVICE_HINTS[d] for d in deps if d in SERVICE_HINTS})
69
+
70
+ python_version = _detect_python_version(path)
71
+
72
+ return {
73
+ "language": "python",
74
+ "framework": framework,
75
+ "port": port,
76
+ "services": services,
77
+ "python_version": python_version,
78
+ "dep_file": dep_file.name,
79
+ }
80
+
81
+
82
+ def _detect_node(path: Path) -> dict:
83
+ import json
84
+ pkg = json.loads((path / "package.json").read_text(encoding="utf-8"))
85
+ deps = list(pkg.get("dependencies", {}).keys()) + list(pkg.get("devDependencies", {}).keys())
86
+ deps_lower = [d.lower() for d in deps]
87
+
88
+ framework = "express"
89
+ port = 3000
90
+ if "next" in deps_lower:
91
+ framework = "nextjs"
92
+ port = 3000
93
+ elif "nuxt" in deps_lower:
94
+ framework = "nuxtjs"
95
+ port = 3000
96
+ elif "react" in deps_lower:
97
+ framework = "react"
98
+ port = 3000
99
+
100
+ services = list({SERVICE_HINTS[d] for d in deps_lower if d in SERVICE_HINTS})
101
+
102
+ return {
103
+ "language": "nodejs",
104
+ "framework": framework,
105
+ "port": port,
106
+ "services": services,
107
+ "python_version": None,
108
+ "dep_file": "package.json",
109
+ }
110
+
111
+
112
+ def _detect_java(path: Path) -> dict:
113
+ return {
114
+ "language": "java",
115
+ "framework": "spring-boot",
116
+ "port": 8080,
117
+ "services": [],
118
+ "python_version": None,
119
+ "dep_file": "pom.xml" if (path / "pom.xml").exists() else "build.gradle",
120
+ }
121
+
122
+
123
+ def _detect_go(path: Path) -> dict:
124
+ return {
125
+ "language": "go",
126
+ "framework": "generic",
127
+ "port": 8080,
128
+ "services": [],
129
+ "python_version": None,
130
+ "dep_file": "go.mod",
131
+ }
132
+
133
+
134
+ def _detect_python_version(path: Path) -> str:
135
+ """Cherche la version Python dans .python-version, pyproject.toml ou runtime.txt."""
136
+ for f in [".python-version", "runtime.txt"]:
137
+ p = path / f
138
+ if p.exists():
139
+ content = p.read_text().strip()
140
+ match = re.search(r"3\.\d+", content)
141
+ if match:
142
+ return match.group()
143
+
144
+ pyproject = path / "pyproject.toml"
145
+ if pyproject.exists():
146
+ content = pyproject.read_text()
147
+ match = re.search(r'python_requires\s*=\s*">=\s*(3\.\d+)"', content)
148
+ if match:
149
+ return match.group(1)
150
+
151
+ return "3.11" # défaut
devai/generator.py ADDED
@@ -0,0 +1,243 @@
1
+ from pathlib import Path
2
+
3
+
4
+ DOCKERIGNORE = """\
5
+ __pycache__/
6
+ *.pyc
7
+ *.pyo
8
+ .env
9
+ .venv
10
+ venv/
11
+ .git/
12
+ .gitignore
13
+ *.log
14
+ .DS_Store
15
+ node_modules/
16
+ dist/
17
+ build/
18
+ """
19
+
20
+ # ──────────────────────────────────────────
21
+ # DOCKERFILE TEMPLATES
22
+ # ──────────────────────────────────────────
23
+
24
+ PYTHON_DOCKERFILE = """\
25
+ FROM python:{python_version}-slim
26
+
27
+ WORKDIR /app
28
+
29
+ # Installer les dépendances système minimales
30
+ RUN apt-get update && apt-get install -y --no-install-recommends \\
31
+ curl \\
32
+ && rm -rf /var/lib/apt/lists/*
33
+
34
+ # Copier et installer les dépendances Python
35
+ COPY {dep_file} .
36
+ RUN pip install --no-cache-dir -r {dep_file}
37
+
38
+ # Copier le code source
39
+ COPY . .
40
+
41
+ EXPOSE {port}
42
+
43
+ CMD {cmd}
44
+ """
45
+
46
+ NODE_DOCKERFILE = """\
47
+ FROM node:20-slim
48
+
49
+ WORKDIR /app
50
+
51
+ COPY package*.json ./
52
+ RUN npm ci --only=production
53
+
54
+ COPY . .
55
+
56
+ EXPOSE {port}
57
+
58
+ CMD ["node", "index.js"]
59
+ """
60
+
61
+ JAVA_DOCKERFILE = """\
62
+ FROM maven:3.9-eclipse-temurin-17 AS build
63
+ WORKDIR /app
64
+ COPY pom.xml .
65
+ RUN mvn dependency:go-offline
66
+ COPY src ./src
67
+ RUN mvn package -DskipTests
68
+
69
+ FROM eclipse-temurin:17-jre-slim
70
+ WORKDIR /app
71
+ COPY --from=build /app/target/*.jar app.jar
72
+ EXPOSE 8080
73
+ CMD ["java", "-jar", "app.jar"]
74
+ """
75
+
76
+ GO_DOCKERFILE = """\
77
+ FROM golang:1.22-alpine AS build
78
+ WORKDIR /app
79
+ COPY go.mod go.sum ./
80
+ RUN go mod download
81
+ COPY . .
82
+ RUN go build -o main .
83
+
84
+ FROM alpine:latest
85
+ WORKDIR /app
86
+ COPY --from=build /app/main .
87
+ EXPOSE 8080
88
+ CMD ["./main"]
89
+ """
90
+
91
+ # ──────────────────────────────────────────
92
+ # SERVICE TEMPLATES pour docker-compose
93
+ # ──────────────────────────────────────────
94
+
95
+ SERVICE_TEMPLATES = {
96
+ "postgres": """\
97
+ postgres:
98
+ image: postgres:16-alpine
99
+ environment:
100
+ POSTGRES_USER: user
101
+ POSTGRES_PASSWORD: password
102
+ POSTGRES_DB: appdb
103
+ ports:
104
+ - "5432:5432"
105
+ volumes:
106
+ - postgres_data:/var/lib/postgresql/data
107
+ """,
108
+ "redis": """\
109
+ redis:
110
+ image: redis:7-alpine
111
+ ports:
112
+ - "6379:6379"
113
+ """,
114
+ "mongodb": """\
115
+ mongodb:
116
+ image: mongo:7
117
+ environment:
118
+ MONGO_INITDB_ROOT_USERNAME: user
119
+ MONGO_INITDB_ROOT_PASSWORD: password
120
+ ports:
121
+ - "27017:27017"
122
+ volumes:
123
+ - mongo_data:/data/db
124
+ """,
125
+ "mysql": """\
126
+ mysql:
127
+ image: mysql:8
128
+ environment:
129
+ MYSQL_ROOT_PASSWORD: rootpassword
130
+ MYSQL_DATABASE: appdb
131
+ MYSQL_USER: user
132
+ MYSQL_PASSWORD: password
133
+ ports:
134
+ - "3306:3306"
135
+ volumes:
136
+ - mysql_data:/var/lib/mysql
137
+ """,
138
+ "elasticsearch": """\
139
+ elasticsearch:
140
+ image: elasticsearch:8.13.0
141
+ environment:
142
+ - discovery.type=single-node
143
+ - xpack.security.enabled=false
144
+ ports:
145
+ - "9200:9200"
146
+ """,
147
+ }
148
+
149
+ VOLUME_NAMES = {
150
+ "postgres": "postgres_data",
151
+ "mongodb": "mongo_data",
152
+ "mysql": "mysql_data",
153
+ }
154
+
155
+ FRAMEWORK_CMDS = {
156
+ "fastapi": '["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "{port}"]',
157
+ "flask": '["flask", "run", "--host=0.0.0.0", "--port={port}"]',
158
+ "django": '["python", "manage.py", "runserver", "0.0.0.0:{port}"]',
159
+ "generic": '["python", "main.py"]',
160
+ }
161
+
162
+
163
+ def generate_files(project_path: str, stack: dict) -> list[str]:
164
+ path = Path(project_path)
165
+ generated = []
166
+
167
+ # Dockerfile
168
+ dockerfile_content = _build_dockerfile(stack)
169
+ _write(path / "Dockerfile", dockerfile_content)
170
+ generated.append("Dockerfile")
171
+
172
+ # .dockerignore
173
+ _write(path / ".dockerignore", DOCKERIGNORE)
174
+ generated.append(".dockerignore")
175
+
176
+ # docker-compose.yaml (seulement si services détectés ou toujours utile)
177
+ compose_content = _build_compose(stack)
178
+ _write(path / "docker-compose.yaml", compose_content)
179
+ generated.append("docker-compose.yaml")
180
+
181
+ return generated
182
+
183
+
184
+ def _build_dockerfile(stack: dict) -> str:
185
+ lang = stack["language"]
186
+ port = stack["port"]
187
+
188
+ if lang == "python":
189
+ cmd = FRAMEWORK_CMDS.get(stack.get("framework", "generic"), FRAMEWORK_CMDS["generic"])
190
+ cmd = cmd.replace("{port}", str(port))
191
+ return PYTHON_DOCKERFILE.format(
192
+ python_version=stack.get("python_version", "3.11"),
193
+ dep_file=stack.get("dep_file", "requirements.txt"),
194
+ port=port,
195
+ cmd=cmd,
196
+ )
197
+ elif lang == "nodejs":
198
+ return NODE_DOCKERFILE.format(port=port)
199
+ elif lang == "java":
200
+ return JAVA_DOCKERFILE
201
+ elif lang == "go":
202
+ return GO_DOCKERFILE
203
+ else:
204
+ return f"FROM ubuntu:22.04\nWORKDIR /app\nCOPY . .\nEXPOSE {port}\n"
205
+
206
+
207
+ def _build_compose(stack: dict) -> str:
208
+ lang = stack["language"]
209
+ port = stack["port"]
210
+ services = stack.get("services", [])
211
+ framework = stack.get("framework", "generic")
212
+
213
+ # Depends_on
214
+ depends = ""
215
+ if services:
216
+ depends = "\n depends_on:\n" + "".join(f" - {s}\n" for s in services)
217
+
218
+ app_service = f"""\
219
+ app:
220
+ build: .
221
+ ports:
222
+ - "{port}:{port}"
223
+ env_file:
224
+ - .env{depends}
225
+ """
226
+
227
+ extra_services = "".join(SERVICE_TEMPLATES.get(s, "") for s in services)
228
+
229
+ volumes_needed = [VOLUME_NAMES[s] for s in services if s in VOLUME_NAMES]
230
+ volumes_block = ""
231
+ if volumes_needed:
232
+ volumes_block = "\nvolumes:\n" + "".join(f" {v}:\n" for v in volumes_needed)
233
+
234
+ return f"""\
235
+ version: '3.9'
236
+
237
+ services:
238
+ {app_service}
239
+ {extra_services}{volumes_block}"""
240
+
241
+
242
+ def _write(filepath: Path, content: str):
243
+ filepath.write_text(content, encoding="utf-8")
@@ -0,0 +1,111 @@
1
+ Metadata-Version: 2.4
2
+ Name: stackai-cli
3
+ Version: 0.1.0
4
+ Summary: AI-powered Docker assistant — detects your stack, generates Dockerfile & compose, debugs containers.
5
+ Author-email: El Mehdi Boutahar <boutahar.elmehdi@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/MehdiB7/devai
8
+ Project-URL: Repository, https://github.com/MehdiB7/devai
9
+ Project-URL: Bug-Tracker, https://github.com/MehdiB7/devai/issues
10
+ Keywords: docker,ai,devops,cli,ollama,automation
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Topic :: Software Development :: Build Tools
17
+ Requires-Python: >=3.10
18
+ Description-Content-Type: text/markdown
19
+ Requires-Dist: click>=8.1
20
+
21
+ # devai
22
+
23
+ > AI-powered Docker assistant. No config needed.
24
+
25
+ `devai` analyse ton projet, génère le Dockerfile et le docker-compose, et debug tes containers avec l'IA — le tout en une commande.
26
+
27
+ ## Installation
28
+
29
+ ```bash
30
+ pip install stackai-cli
31
+ ```
32
+
33
+ > Requiert [Ollama](https://ollama.com) installé et `ollama pull llama3` pour le mode debug AI.
34
+
35
+ ## Utilisation
36
+
37
+ ### `devai init` — Générer les fichiers Docker
38
+
39
+ ```bash
40
+ cd mon-projet
41
+ stackai init
42
+ ```
43
+
44
+ ```
45
+ 🔍 Analyse du projet...
46
+ ✅ Stack détectée : python / fastapi + postgres + redis
47
+ ⚙️ Génération des fichiers Docker...
48
+ ✅ Dockerfile créé
49
+ ✅ .dockerignore créé
50
+ ✅ docker-compose.yaml créé
51
+
52
+ 🚀 Prêt ! Lance avec : docker compose up -d
53
+ ```
54
+
55
+ ### `devai scan` — Voir la stack sans rien générer
56
+
57
+ ```bash
58
+ stackai scan
59
+ ```
60
+
61
+ ```
62
+ 📦 Stack détectée :
63
+ Langage : python
64
+ Framework : fastapi
65
+ Port : 8000
66
+ Services : postgres, redis
67
+ Python ver. : 3.11
68
+ ```
69
+
70
+ ### `devai debug` — Analyser un container en erreur
71
+
72
+ ```bash
73
+ stackai debug mon-container
74
+ ```
75
+
76
+ ```
77
+ 📋 Derniers logs :
78
+ ─────────────────────────────────────────
79
+ Error: could not connect to postgres...
80
+ ─────────────────────────────────────────
81
+
82
+ 🤖 Analyse IA en cours...
83
+
84
+ 💡 Analyse :
85
+ 1. PROBLÈME : L'application ne peut pas se connecter à PostgreSQL
86
+ 2. CAUSE : Le container postgres n'est pas encore prêt au démarrage de l'app
87
+ 3. FIX : Ajoute `depends_on: [postgres]` dans ton docker-compose.yaml
88
+ ```
89
+
90
+ ## Stacks supportées
91
+
92
+ | Langage | Frameworks détectés |
93
+ |---------|-------------------|
94
+ | Python | FastAPI, Flask, Django, générique |
95
+ | Node.js | Express, Next.js, Nuxt.js, React |
96
+ | Java | Spring Boot (Maven/Gradle) |
97
+ | Go | générique |
98
+
99
+ ## Services auto-détectés
100
+
101
+ `postgres` · `redis` · `mongodb` · `mysql` · `elasticsearch`
102
+
103
+ ## Prérequis
104
+
105
+ - Python 3.10+
106
+ - Docker installé
107
+ - [Ollama](https://ollama.com) + `ollama pull llama3` (pour `devai debug`)
108
+
109
+ ## Licence
110
+
111
+ MIT
@@ -0,0 +1,11 @@
1
+ devai/__init__.py,sha256=kUR5RAFc7HCeiqdlX36dZOHkUI5wI6V_43RpEcD8b-0,22
2
+ devai/ai.py,sha256=K5T8_7BqkP5gLta0UsjoI-S-k5UGdALqy2DpPvWDfUg,852
3
+ devai/cli.py,sha256=4jK_UZmlGHZmMH0tZOuTEn3qZnMkSurBzWqQVJ88zw8,2183
4
+ devai/debugger.py,sha256=M1cRJD8c462EAxqbE7zMKf3ndM7PHFFpZDkwzmoFE7g,1909
5
+ devai/detector.py,sha256=pPqwp3vsKDInoqrWNOLwTACymeq6ZdA1Tg4gy43reHI,4094
6
+ devai/generator.py,sha256=OCMGfp5qAl-rSvHsjsN65TNkHTMMLKXJX47K3a_CgK8,5627
7
+ stackai_cli-0.1.0.dist-info/METADATA,sha256=WxCh-fTIo67nfR7avAeLFXzKO8qBQLC9pF1SxT1TGZw,2967
8
+ stackai_cli-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
9
+ stackai_cli-0.1.0.dist-info/entry_points.txt,sha256=NLmdAbALXQnZwvZM0Y8YwsqwH_pArHd6Da8f4pHcK8U,43
10
+ stackai_cli-0.1.0.dist-info/top_level.txt,sha256=Q-WXGK56TxXgAMLtaB1F77uzL4PBxPW22GkcKjJVdTc,6
11
+ stackai_cli-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ stackai = devai.cli:main
@@ -0,0 +1 @@
1
+ devai