yard-agent 0.1.0__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.
Files changed (30) hide show
  1. yard_agent-0.1.0/LICENSE +21 -0
  2. yard_agent-0.1.0/PKG-INFO +64 -0
  3. yard_agent-0.1.0/README.md +51 -0
  4. yard_agent-0.1.0/pyproject.toml +26 -0
  5. yard_agent-0.1.0/setup.cfg +4 -0
  6. yard_agent-0.1.0/src/yard/__init__.py +0 -0
  7. yard_agent-0.1.0/src/yard/console/console_ui.py +14 -0
  8. yard_agent-0.1.0/src/yard/console/error.py +11 -0
  9. yard_agent-0.1.0/src/yard/console/help_oargs.py +46 -0
  10. yard_agent-0.1.0/src/yard/helper/__init__.py +0 -0
  11. yard_agent-0.1.0/src/yard/helper/depfile.py +66 -0
  12. yard_agent-0.1.0/src/yard/intent_agent.py +74 -0
  13. yard_agent-0.1.0/src/yard/logger_config.py +16 -0
  14. yard_agent-0.1.0/src/yard/prompt/agent_prompt.py +3 -0
  15. yard_agent-0.1.0/src/yard/prompt/intent_prompt.py +77 -0
  16. yard_agent-0.1.0/src/yard/prompt/tasks/dockerfile_prompt.py +21 -0
  17. yard_agent-0.1.0/src/yard/prompt/tasks/examples/dockerfile_example.py +161 -0
  18. yard_agent-0.1.0/src/yard/schema/intent_schema.py +87 -0
  19. yard_agent-0.1.0/src/yard/schema/yard_schema.py +32 -0
  20. yard_agent-0.1.0/src/yard/tools/__init__.py +0 -0
  21. yard_agent-0.1.0/src/yard/tools/create_dockerfile.py +40 -0
  22. yard_agent-0.1.0/src/yard/tools/docker_ignore_file.py +26 -0
  23. yard_agent-0.1.0/src/yard/yard_agent.py +94 -0
  24. yard_agent-0.1.0/src/yard/yard_cli.py +80 -0
  25. yard_agent-0.1.0/src/yard_agent.egg-info/PKG-INFO +64 -0
  26. yard_agent-0.1.0/src/yard_agent.egg-info/SOURCES.txt +28 -0
  27. yard_agent-0.1.0/src/yard_agent.egg-info/dependency_links.txt +1 -0
  28. yard_agent-0.1.0/src/yard_agent.egg-info/entry_points.txt +2 -0
  29. yard_agent-0.1.0/src/yard_agent.egg-info/requires.txt +4 -0
  30. yard_agent-0.1.0/src/yard_agent.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Arul Dhanasekar
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,64 @@
1
+ Metadata-Version: 2.4
2
+ Name: yard_agent
3
+ Version: 0.1.0
4
+ Summary: Terminal based AI agent for Docker
5
+ Requires-Python: >=3.13
6
+ Description-Content-Type: text/markdown
7
+ License-File: LICENSE
8
+ Requires-Dist: openai>=2.33.0
9
+ Requires-Dist: python-dotenv>=1.2.2
10
+ Requires-Dist: tenacity>=9.1.4
11
+ Requires-Dist: typer>=0.25.1
12
+ Dynamic: license-file
13
+
14
+ # Yard for Docker
15
+
16
+ Yard is a terminal-based AI agent for Docker. In this first version, it helps developers quickly generate Dockerfiles for Python projects directly from the terminal.
17
+
18
+ ## Status
19
+
20
+ v1 — under active development.
21
+
22
+ ## What it does
23
+
24
+ - Generates one or more Dockerfiles and a `.dockerignore` file from a simple command
25
+ - Detects the Python version from project metadata
26
+ - Helps package Python applications for Docker quickly from the terminal
27
+
28
+ ## Install
29
+
30
+ ```bash
31
+ pipx install yard
32
+ ```
33
+
34
+ ## Setup
35
+
36
+ Yard currently uses OpenAI models.
37
+
38
+ ### Windows PowerShell
39
+
40
+ ```powershell
41
+ $env:OPENAI_API_KEY="your_api_key"
42
+ ```
43
+
44
+ ### Linux/macOS
45
+
46
+ ```bash
47
+ export OPENAI_API_KEY="your_api_key"
48
+ ```
49
+
50
+ ## Example
51
+
52
+ ```bash
53
+ yard "create a dockerfile for this main.py file"
54
+ ```
55
+
56
+ ## Privacy
57
+
58
+ - This project does not send raw files, scripts, or `.env` variables to the AI
59
+ - No data is used for training AI models
60
+ - Yard only reads `pyproject.toml` or `pyvenv.cfg` files to detect the Python version
61
+
62
+ ## License
63
+
64
+ MIT
@@ -0,0 +1,51 @@
1
+ # Yard for Docker
2
+
3
+ Yard is a terminal-based AI agent for Docker. In this first version, it helps developers quickly generate Dockerfiles for Python projects directly from the terminal.
4
+
5
+ ## Status
6
+
7
+ v1 — under active development.
8
+
9
+ ## What it does
10
+
11
+ - Generates one or more Dockerfiles and a `.dockerignore` file from a simple command
12
+ - Detects the Python version from project metadata
13
+ - Helps package Python applications for Docker quickly from the terminal
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ pipx install yard
19
+ ```
20
+
21
+ ## Setup
22
+
23
+ Yard currently uses OpenAI models.
24
+
25
+ ### Windows PowerShell
26
+
27
+ ```powershell
28
+ $env:OPENAI_API_KEY="your_api_key"
29
+ ```
30
+
31
+ ### Linux/macOS
32
+
33
+ ```bash
34
+ export OPENAI_API_KEY="your_api_key"
35
+ ```
36
+
37
+ ## Example
38
+
39
+ ```bash
40
+ yard "create a dockerfile for this main.py file"
41
+ ```
42
+
43
+ ## Privacy
44
+
45
+ - This project does not send raw files, scripts, or `.env` variables to the AI
46
+ - No data is used for training AI models
47
+ - Yard only reads `pyproject.toml` or `pyvenv.cfg` files to detect the Python version
48
+
49
+ ## License
50
+
51
+ MIT
@@ -0,0 +1,26 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "yard_agent"
7
+ version = "0.1.0"
8
+ description = "Terminal based AI agent for Docker"
9
+ readme = "README.md"
10
+ requires-python = ">=3.13"
11
+
12
+ dependencies = [
13
+ "openai>=2.33.0",
14
+ "python-dotenv>=1.2.2",
15
+ "tenacity>=9.1.4",
16
+ "typer>=0.25.1",
17
+ ]
18
+
19
+ [project.scripts]
20
+ yard = "yard.yard_cli:app"
21
+
22
+ [tool.setuptools]
23
+ package-dir = {"" = "src"}
24
+
25
+ [tool.setuptools.packages.find]
26
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,14 @@
1
+ from rich.console import Console
2
+
3
+
4
+
5
+ console = Console()
6
+
7
+ def success(message: str) -> None:
8
+ console.print(f"[green]✓{message}[/green]")
9
+
10
+ def info(message: str) -> None:
11
+ console.print(f"[cyan]{message}[/cyan]")
12
+
13
+ def error(message: str) -> None:
14
+ console.print(f"[red]{message}[/red]")
@@ -0,0 +1,11 @@
1
+ from rich import print as rprint
2
+ from rich.panel import Panel
3
+
4
+
5
+ def command_error(cmd):
6
+ rprint("[yellow]Usage:[/yellow] yard [PROMPT]")
7
+ rprint(Panel(renderable=f"No such '{cmd}' command",
8
+ title="Error",
9
+ title_align="left",
10
+ border_style="red"
11
+ ))
@@ -0,0 +1,46 @@
1
+ from rich.panel import Panel
2
+ from rich import print as rprint
3
+
4
+
5
+ def custom_help():
6
+ rprint("[bold yellow]Usage:[/bold yellow] [cyan]yard[/cyan] [white]<prompt>[/white]\n")
7
+
8
+ rprint(
9
+ Panel(
10
+ renderable=(
11
+ "[bold cyan]PROMPT[/bold cyan]\n"
12
+ "Describe the task you want the agent to perform.\n\n"
13
+ "[dim]The agent analyzes your request and generates the required project files automatically.[/dim]"
14
+ ),
15
+ title="Arguments",
16
+ title_align="left",
17
+ border_style="dim"
18
+ )
19
+ )
20
+
21
+ rprint(
22
+ Panel(
23
+ renderable=(
24
+ "[white]Supported Tasks[/white]\n\n"
25
+ "• Create Dockerfile\n"
26
+ "• Create .dockerignore\n"
27
+ ),
28
+ title="Capabilities",
29
+ title_align="left",
30
+ border_style="yellow"
31
+ )
32
+ )
33
+
34
+ rprint(
35
+ Panel(
36
+ renderable=(
37
+ '[bold yellow]yard[/bold yellow] '
38
+ '[cyan]"Create a Dockerfile for main.py"[/cyan]\n\n'
39
+ '[bold yellow]yard[/bold yellow] '
40
+ '[cyan]"Create a .dockerignore file and add the files and folders that should be ignored"[/cyan]'
41
+ ),
42
+ title="Examples",
43
+ title_align="left",
44
+ border_style="white"
45
+ )
46
+ )
File without changes
@@ -0,0 +1,66 @@
1
+ import re
2
+ from pathlib import Path
3
+ from yard.console.console_ui import console, success
4
+
5
+
6
+ def find_dep_file(start=None):
7
+ if start is None:
8
+ current_root = Path(__file__).resolve()
9
+ else:
10
+ current_root = Path(start).resolve()
11
+
12
+
13
+ project_root = [current_root] + list(current_root.parents)
14
+
15
+ for parent in project_root:
16
+
17
+ with console.status(
18
+ "[cyan]Resolving dependencies[cyan]"
19
+ ):
20
+ # Search for pyproject.toml file in the root directory
21
+ if (parent / "pyproject.toml").exists():
22
+
23
+ success("Dependencies resolved")
24
+
25
+ with Path.open(parent/"pyproject.toml") as f:
26
+ config = f.read()
27
+
28
+ with console.status(
29
+ "[cyan]Detecting Python environment[/cyan]"
30
+ ):
31
+ match = re.search(r'requires-python\s*=\s*"((?:<=|>=|==|<|>|=)\s*([0-9.]+))"', config)
32
+ if match:
33
+ version = match.group(1)
34
+
35
+ success(f"Detected Python {version}")
36
+
37
+ return "pyproject.toml", version
38
+
39
+ # Search for requirements.txt file in the root directory
40
+ elif (parent / "requirements.txt").exists():
41
+
42
+ success("✓ Dependencies resolved")
43
+
44
+ with console.status(
45
+ "[cyan]Detecting Python environment[/cyan]"
46
+ ):
47
+ for venv_name in [".venv", "venv", "env"]:
48
+ # Searching python version in the venv
49
+ cfg = parent / venv_name / "pyvenv.cfg"
50
+ if cfg.exists():
51
+ text = cfg.read_text()
52
+
53
+ match = re.search(r"version\s*=\s*([0-9.]+)", text)
54
+ if match:
55
+ version = match.group(1)
56
+
57
+ success(f"Detected Python {version}")
58
+
59
+ return "requirements.txt", version
60
+
61
+ return None, None
62
+
63
+
64
+
65
+ if __name__ == "__main__":
66
+ print(find_dep_file())
@@ -0,0 +1,74 @@
1
+ import asyncio
2
+ import os
3
+ from dotenv import load_dotenv
4
+ import json
5
+ import typer
6
+
7
+ from openai import AsyncOpenAI
8
+
9
+ from yard.prompt.intent_prompt import SYSTEM_PROMPT
10
+ from yard.schema.intent_schema import INTENT_TOOLS
11
+
12
+ from yard.logger_config import get_logger
13
+
14
+ logger = get_logger(__name__)
15
+
16
+ load_dotenv()
17
+
18
+
19
+ def get_openai_client():
20
+ if not os.getenv("OPENAI_API_KEY"):
21
+ typer.echo("OPENAI_API_KEY is not set")
22
+ raise typer.Exit(code=1)
23
+
24
+ return AsyncOpenAI(timeout=60)
25
+
26
+ client = get_openai_client()
27
+
28
+
29
+ async def intent_identifier(intent):
30
+ messages = [{
31
+ "role": "system",
32
+ "content": SYSTEM_PROMPT
33
+ },
34
+ {
35
+ "role": "user",
36
+ "content": intent
37
+ }]
38
+
39
+ response = await client.chat.completions.create(
40
+ model="gpt-4.1-nano",
41
+ messages=messages,
42
+ tools=INTENT_TOOLS,
43
+ tool_choice="auto"
44
+ )
45
+
46
+ messages = response.choices[0].message
47
+
48
+ if messages.tool_calls:
49
+ tool_call = messages.tool_calls[0]
50
+
51
+ task_name = tool_call.function.name
52
+
53
+ arguments = json.loads(
54
+ tool_call.function.arguments
55
+ )
56
+
57
+
58
+ if task_name == "non_docker_request":
59
+ logger.info("Task is not related to Docker")
60
+ return {
61
+ "success" : False,
62
+ "task" : task_name,
63
+ "data" : arguments
64
+ }
65
+
66
+ logger.info(f"Task {task_name} Indentified")
67
+ return {
68
+ "success": True,
69
+ "task": task_name,
70
+ "data": arguments
71
+ }
72
+
73
+ if __name__ == "__main__":
74
+ print(asyncio.run(intent_identifier("Create a dockerfile for modelai.py from src directory")))
@@ -0,0 +1,16 @@
1
+ import logging
2
+
3
+
4
+ logging.basicConfig(
5
+ level=logging.INFO,
6
+ format="%(asctime)s | %(name)s | %(levelname)s | %(message)s",
7
+ handlers=[
8
+ logging.FileHandler("app.log"),
9
+ ]
10
+ )
11
+
12
+
13
+ def get_logger(name):
14
+ return logging.getLogger(name)
15
+
16
+
@@ -0,0 +1,3 @@
1
+ SYSTEM_PROMPT = """
2
+ You are an experienced and highly skilled DevOps Engineer specializing in Docker containerization for Python-based backend applications.
3
+ """
@@ -0,0 +1,77 @@
1
+ SYSTEM_PROMPT = """
2
+ You are an intent extracting engine. Your job is to convert the user's message into structured JSON.
3
+
4
+ Rules:
5
+ - Understand the user's message correctly.
6
+ - Identify the filename with the .py suffix in the user's message.
7
+ - STRICTLY user intent must be related to Docker ONLY.
8
+ - If the user request is related to Docker or DevOps:
9
+ - Return results ONLY through `tool_calls`.
10
+ - `message.content` MUST remain completely empty.
11
+ - Strictly follow the provided tool schema.
12
+ - Do not change the schema structure.
13
+ - Do not rename fields.
14
+ - If the user request is NOT related to Docker, containers, DevOps, deployment, infrastructure, CI/CD, orchestration, or Docker ecosystem tasks:
15
+ - Return the `non_docker_request` tool
16
+ - Do NOT write any Docker-related files (such as "Dockerfile", "compose.yaml", or "dockerignore") as values for the "file" key in the JSON schema.
17
+
18
+ DOCKERIGNORE RULES:
19
+ - STRICTLY do NOT use `\n`, escaped newline characters, or unnecessary backslashes in `.dockerignore`.
20
+ - Write entries as clean plain lines only.
21
+ - Do not serialize file contents using escaped strings.
22
+ - Preserve raw formatting for all generated files.
23
+
24
+ #Examples
25
+
26
+ ##Example - 1:
27
+ INPUT:
28
+ Create a two dockerfiles: one for main.py and another for engine.py in src folder"
29
+
30
+ OUTPUT:
31
+ {
32
+ "tasks" : "create_dockerfile",
33
+ "runnable" : [{
34
+ "file" : "main.py",
35
+ "folder" : null
36
+ },
37
+ {
38
+ "file" : "engine.py",
39
+ "folder" : "src"
40
+ }
41
+ ]
42
+ }
43
+
44
+ ##Example - 2:
45
+ INPUT:
46
+ Create a .dockerignore file and add all necessary Python project files and folders that should be ignored, including the src/engine.py file and src/tests folder.
47
+
48
+ OUTPUT:
49
+ { "task" : "create_dockerignore",
50
+ "ignored" : [
51
+ ".env",
52
+ ".env_local",
53
+ ".python_version",
54
+ "venv/",
55
+ ".venv/",
56
+ "__pycache__/",
57
+ ".mypy_cache",
58
+ ".ruff_cache",
59
+ ".git",
60
+ "build",
61
+ "src/engine.py",
62
+ "src/tests"
63
+ ]
64
+ }
65
+
66
+ ##Example - 3:
67
+ INPUT:
68
+ Create a python file and write hello world script
69
+
70
+ OUTPUT:
71
+ {
72
+ "task" : "non_docker_request",
73
+ "message" : I am a specialized Docker DevOps Engineer. Provide tasks related to Docker.
74
+
75
+ }
76
+
77
+ """
@@ -0,0 +1,21 @@
1
+ from yard.prompt.tasks.examples.dockerfile_example import EXAMPLE_TEMPLATES
2
+
3
+
4
+
5
+ def prompt(intent, dep_file, version):
6
+ return f"""Generate a Dockerfile based on the following project intent: `{intent}`.
7
+ Use '{dep_file}' as the dependency file.
8
+ Use Python version '{version}'.
9
+
10
+ RULES:
11
+ - Generate ONLY valid Dockerfile content and Docker-related instructions that strictly follow the structure and format of the provided example template.
12
+ - Do NOT include explanations, markdown, notes, headings, or additional text outside the required Docker output.
13
+ - If generating more that one Dockerfile, name each file using the format `Dockerfile.<filename>`.
14
+ - If the dependency file is `requirements.txt`, use `pip` as the package manager.
15
+ - If the dependency file is `pyproject.toml`, use `uv sync` to install and synchronize dependencies.
16
+ - Do not assume or invent the Python version. Use only the Python version explicitly provided by the user.
17
+
18
+ EXAMPLES:
19
+ `{EXAMPLE_TEMPLATES}'
20
+
21
+ """
@@ -0,0 +1,161 @@
1
+ EXAMPLE_TEMPLATES = """
2
+ #Example-1:
3
+ INPUT:
4
+ Generate a Dockerfile based on the following project intent:
5
+ `{
6
+ "task":"create_dockerfile",
7
+ "runnable":[
8
+ {
9
+ "file":"modelai.py",
10
+ "folder":"src"
11
+ },
12
+ {
13
+ "file": "main.py",
14
+ "folder" : null
15
+ }
16
+ ]
17
+ }`.
18
+ Use 'pyproject.toml' as the dependency file.
19
+ Use Python version '=> 3.13'.
20
+
21
+ Output:
22
+ `{'dockerfiles' : [{
23
+ 'dockerfile_name' : 'Dockerfile.modelai',
24
+ 'dockerfile_instructions' : '
25
+ FROM python:3.13-slim
26
+
27
+ # Prevents Python from creating .pyc files
28
+ # Ensures logs are shown immediately in Docker
29
+ # Makes uv use the system Python inside the container
30
+ PYTHONDONETWRITEBYTECODE=1 \
31
+ PYTHONUNBUFFERED=1 \
32
+ UV_SYSTEM_PYTHON=1
33
+
34
+ # Set working directory
35
+ WORKDIR /app
36
+
37
+ # Install uv package manager
38
+ RUN pip install -no-cache-dir uv
39
+
40
+ # Copy dependency files
41
+ COPY pyproject.toml uv.lock ./
42
+
43
+ # Install project dependencies
44
+ RUN uv sync --frozen --no-dev --no-cache
45
+
46
+ # Copy application source code
47
+ COPY src/ ./src/
48
+
49
+ # Start the application
50
+ CMD ["uv", "run", "-m", "src.modelai"] '
51
+
52
+ },
53
+ {
54
+ 'dockerfile_name' : 'Dockerfile.main',
55
+ 'dockerfile_instructions' : '
56
+ FROM python:3.13-slim
57
+
58
+ # Prevents Python from creating .pyc files
59
+ # Ensures logs are shown immediately in Docker
60
+ # Makes uv use the system Python inside the container
61
+ PYTHONDONETWRITEBYTECODE=1 \
62
+ PYTHONUNBUFFERED=1 \
63
+ UV_SYSTEM_PYTHON=1
64
+
65
+ # Set working directory
66
+ WORKDIR /app
67
+
68
+ # Install uv package manager
69
+ RUN pip install -no-cache-dir uv
70
+
71
+ # Copy dependency files
72
+ COPY pyproject.toml uv.lock* ./
73
+
74
+ # Install project dependencies
75
+ RUN uv sync --frozen --no-dev --no-cache
76
+
77
+ # Copy application source code
78
+ COPY . .
79
+
80
+ # Start the application
81
+ CMD ["uv", "run", "main.py"] '
82
+
83
+ }] }`
84
+
85
+
86
+ ##Example - 2:
87
+ INPUT:
88
+ Generate a Dockerfile based on the following project intent:
89
+ `{
90
+ "task":"create_dockerfile",
91
+ "runnable":[
92
+ {
93
+ "file":"main.py",
94
+ "folder": null
95
+ },
96
+ {
97
+ "file": "app.py",
98
+ "folder" : app
99
+ }
100
+ ]
101
+ }`.
102
+ Use 'requirements.txt' as the dependency file.
103
+ Use Python version '=> 3.13'.
104
+
105
+
106
+ OUTPUT:
107
+ `{ 'dockerfiles': [
108
+ {
109
+ 'dockerfile_name' : 'Dockerfile.main',
110
+ 'docker_instructions': '
111
+ FROM python:3.13-slim
112
+
113
+ # Prevents Python from creating .pyc files
114
+ # Ensures logs are shown immediately in Docker
115
+ ENV PYTHONDONTWRITEBYTECODE=1 \
116
+ PYTHONUNBUFFERED=1
117
+
118
+ # Set working directory
119
+ WORKDIR / app
120
+
121
+ # Copy project files
122
+ COPY requirements.txt .
123
+
124
+ # Install dependencies
125
+ RUN pip install --no-cache-dir -r requirements.txt
126
+
127
+ # Copy application source code
128
+ COPY . .
129
+
130
+ # Run main.py
131
+ CMD ["python", "main.py"] '
132
+ },
133
+ {
134
+ 'dockerfile_name' : 'Dockerfile.app',
135
+ 'docker_instructions': '
136
+ FROM python:3.13-slim
137
+
138
+ # Prevents Python from creating .pyc files
139
+ # Ensures logs are shown immediately in Docker
140
+ ENV PYTHONDONTWRITEBYTECODE=1 \
141
+ PYTHONUNBUFFERED=1
142
+
143
+ # Set working directory
144
+ WORKDIR /app
145
+
146
+ # Copy dependency file
147
+ COPY requirements.txt .
148
+
149
+ # Install project dependencies
150
+ RUN pip install --no-cache-dir -r requirements.txt
151
+
152
+ # Copy application source code
153
+ COPY app/ ./app/
154
+
155
+ # Start the application
156
+ CMD["python", "-m", "app.app"]
157
+
158
+ }]}`
159
+
160
+
161
+ """
@@ -0,0 +1,87 @@
1
+ INTENT_TOOLS = [
2
+ {
3
+ "type" : "function",
4
+ "function" : {
5
+ "name" : "create_dockerfile",
6
+ "description": "Extract creating dockerfile tasks with files and folders",
7
+ "strict" : True,
8
+ "parameters" : {
9
+ "type" : "object",
10
+ "properties" : {
11
+ "task" : {
12
+ "type" : "string",
13
+ "enum" : ["create_dockerfile"]
14
+ },
15
+ "runnable" : {
16
+ "type" : "array",
17
+ "items" : {
18
+ "type" : "object",
19
+ "properties" : {
20
+ "file" : {
21
+ "type" : ["string", "null"]
22
+ },
23
+ "folder" : {
24
+ "type" : ["string", "null"]
25
+ }
26
+ },
27
+ "required" : ["file", "folder"],
28
+ "additionalProperties" : False
29
+ }
30
+ }
31
+ },
32
+ "required" : [
33
+ "task",
34
+ "runnable"
35
+ ],
36
+ "additionalProperties" : False
37
+ }
38
+ }
39
+ },
40
+ {
41
+ "type" : "function",
42
+ "function": {
43
+ "name" : "create_dockerignore",
44
+ "description" : "Extract files and folders to ignore and Create a .dockerignore file",
45
+ "strict" : True,
46
+ "parameters" : {
47
+ "type" : "object",
48
+ "properties" : {
49
+ "task" : {
50
+ "type" : "string",
51
+ "enum" : ["create_dockerignore"]
52
+ },
53
+ "ignored": {
54
+ "type" : "array",
55
+ "items" : {
56
+ "type" : "string"
57
+ },
58
+ "description" : "ingored files and folders"
59
+ }
60
+ },
61
+ "required" : ["task","ignored"],
62
+ "additionalProperties" : False
63
+ }
64
+ }
65
+ },
66
+ {
67
+ "type": "function",
68
+ "function": {
69
+ "name": "non_docker_request",
70
+ "description": "Use this when the request is not related to Docker or DevOps",
71
+ "strict": True,
72
+ "parameters": {
73
+ "type": "object",
74
+ "properties": {
75
+ "message": {
76
+ "type": "string",
77
+ "enum": [
78
+ "I am a specialized Docker DevOps Engineer. Provide tasks related to Docker."
79
+ ]
80
+ }
81
+ },
82
+ "required": ["message"],
83
+ "additionalProperties": False
84
+ }
85
+ }
86
+ }
87
+ ]
@@ -0,0 +1,32 @@
1
+ TOOLS = [
2
+ {
3
+ "type" : "function",
4
+ "function" : {
5
+ "name" : "create_dockerfile",
6
+ "description" : "Create a Dockerfile using given information",
7
+ "parameters" : {
8
+ "type" : "object",
9
+ "properties" : {
10
+ "dockerfiles" : {
11
+ "type" : "array",
12
+ "items" : {
13
+ "type" : "object",
14
+ "properties" : {
15
+ "dockerfile_name" : {
16
+ "type" : "string"
17
+ },
18
+ "dockerfile_instructions" : {
19
+ "type" : "string"
20
+ }
21
+ },
22
+ "required" : ["dockerfile_name", "dockerfile_instructions"],
23
+ "additionalProperties" : False
24
+ }
25
+ }
26
+ },
27
+ "required" : ["dockerfiles"],
28
+ "additionalProperties" : False
29
+ }
30
+ }
31
+ }
32
+ ]
File without changes
@@ -0,0 +1,40 @@
1
+ import asyncio
2
+ import json
3
+ from yard.logger_config import get_logger
4
+ from yard.yard_agent import yard_devops
5
+ from yard.helper.depfile import find_dep_file
6
+ from yard.console.console_ui import console, success
7
+ from yard.prompt.tasks.dockerfile_prompt import prompt
8
+
9
+ logger = get_logger(__name__)
10
+
11
+
12
+ async def create_dockerfile(intent):
13
+
14
+ dep_filename, version = find_dep_file()
15
+
16
+ prompts = prompt(intent, dep_filename, version)
17
+
18
+ with console.status(
19
+ "[cyan]Generating Dockerfile[/cyan]"
20
+ ):
21
+ logger.info("Dispatching request to yard agent")
22
+
23
+ output = await yard_devops(prompts)
24
+
25
+ logger.info(json.dumps(output, indent=2))
26
+
27
+
28
+ for doc in output["dockerfiles"]:
29
+
30
+ logger.info(f"creating {doc["dockerfile_name"]} and writing instructions")
31
+ success(f"Writing Docker instructions for {doc["dockerfile_name"]}")
32
+
33
+ with open(doc["dockerfile_name"], "w") as file:
34
+ file.write(doc["dockerfile_instructions"])
35
+
36
+ success(f"Wrote {doc['dockerfile_name']}")
37
+ logger.info(f"{doc["dockerfile_name"]} created successfully")
38
+
39
+ if __name__ == "__main__":
40
+ asyncio.run(create_dockerfile("""{"intent":"create_dockerfile","runnable_file":[{"file":"modelai.py","folder":"src"}],"mode":"development","response":null}"""))
@@ -0,0 +1,26 @@
1
+ import json
2
+ from pathlib import Path
3
+ from yard.console.console_ui import success
4
+ from yard.logger_config import get_logger
5
+
6
+
7
+ logger = get_logger(__name__)
8
+
9
+
10
+
11
+
12
+ async def dockerignore_file(intent):
13
+
14
+ logger.info(json.dumps(intent, indent=2))
15
+ success(f"Adding ignored files and folders")
16
+
17
+ files = intent["ignored"]
18
+
19
+ logger.info("Adding ignored files and folders")
20
+ success(f"Adding ignored files and folders")
21
+ for file in files:
22
+ with open(".dockerignore", "a+") as f:
23
+ f.write(f"{file}\n")
24
+
25
+ success(".dockerignore file created")
26
+ logger.info("dockerignore file created")
@@ -0,0 +1,94 @@
1
+ import os
2
+ import json
3
+ from logging import WARNING
4
+ from dotenv import load_dotenv
5
+ import typer
6
+
7
+ from tenacity import (
8
+ retry,
9
+ stop_after_attempt,
10
+ wait_random_exponential,
11
+ retry_if_exception_type,
12
+ before_sleep_log
13
+ )
14
+ from openai import AsyncOpenAI
15
+ from openai import (
16
+ APIConnectionError,
17
+ APITimeoutError,
18
+ RateLimitError,
19
+ InternalServerError,
20
+ )
21
+
22
+ from yard.logger_config import get_logger
23
+ from yard.prompt.agent_prompt import SYSTEM_PROMPT
24
+ from yard.schema.yard_schema import TOOLS
25
+ from yard.console.console_ui import error
26
+
27
+
28
+
29
+ logger = get_logger(__name__)
30
+ load_dotenv()
31
+
32
+
33
+ def get_openai_client():
34
+ if not os.getenv("OPENAI_API_KEY"):
35
+ typer.echo("OPENAI_API_KEY is not set")
36
+ raise typer.Exit(code=1)
37
+
38
+ return AsyncOpenAI(timeout=60)
39
+
40
+ client = get_openai_client()
41
+
42
+
43
+ @retry(
44
+ retry=retry_if_exception_type((
45
+ APIConnectionError,
46
+ APITimeoutError,
47
+ RateLimitError,
48
+ InternalServerError,
49
+ )),
50
+ wait=wait_random_exponential(
51
+ multiplier=1,
52
+ max=60,
53
+ ),
54
+ stop=stop_after_attempt(8),
55
+ before_sleep=before_sleep_log(
56
+ logger,
57
+ WARNING
58
+ ),
59
+ reraise=True
60
+ )
61
+ async def yard_devops(intent):
62
+ messages = [{
63
+ "role": "system",
64
+ "content": SYSTEM_PROMPT
65
+ },
66
+ {
67
+ "role": "user",
68
+ "content": intent
69
+ }]
70
+
71
+ logger.info("Send OpenAI request")
72
+ response = await client.chat.completions.create(
73
+ model="gpt-5.4",
74
+ messages=messages,
75
+ tools=TOOLS,
76
+ tool_choice="auto"
77
+
78
+ )
79
+
80
+ logger.info("OpenAI response received")
81
+ message = response.choices[0].message
82
+
83
+ if message.tool_calls:
84
+ content = message.tool_calls[0]
85
+
86
+ output = json.loads(
87
+ content.function.arguments
88
+ )
89
+
90
+ logger.info("Tool call parsed successfully")
91
+ return output
92
+
93
+ return None
94
+
@@ -0,0 +1,80 @@
1
+ import asyncio
2
+ import json
3
+ import typer
4
+ from typer import Typer, Context, Option, Argument
5
+ from typer.core import TyperGroup
6
+ from yard.intent_agent import intent_identifier
7
+
8
+ from yard.console.help_oargs import custom_help
9
+ from yard.console.console_ui import console, info, success
10
+ from yard.console.error import command_error
11
+ from yard.logger_config import get_logger
12
+
13
+
14
+
15
+ from yard.tools.create_dockerfile import create_dockerfile
16
+ from yard.tools.docker_ignore_file import dockerignore_file
17
+
18
+
19
+ logger = get_logger(__name__)
20
+
21
+ class CustomGroup(TyperGroup):
22
+ def get_command(self, ctx, cmd_name):
23
+ rv = super().get_command(ctx, cmd_name)
24
+
25
+ if rv is not None:
26
+ return rv
27
+
28
+ command_error(cmd_name)
29
+
30
+ raise typer.Exit(code=1)
31
+
32
+ app = Typer(cls=CustomGroup,
33
+ # Typer stops decorating exceptions
34
+ pretty_exceptions_enable=False,
35
+ context_settings={
36
+ # To prevent using the typer's default behaviour of "--help" or '-h'
37
+ "help_option_names" : []
38
+ })
39
+
40
+ @app.callback(invoke_without_command=True)
41
+ def yard_agent(
42
+ prompt: str = Argument(""),
43
+ help: bool = Option(
44
+ False,
45
+ "--help",
46
+ "-h",
47
+ is_eager=True,
48
+ ),
49
+ ):
50
+ with console.status(
51
+ "[cyan]Analyzing request[/cyan]"
52
+ ):
53
+ if help:
54
+ custom_help()
55
+ raise typer.Exit()
56
+
57
+ logger.info("Sending the user message to intent agent")
58
+
59
+ result = asyncio.run(intent_identifier(prompt))
60
+
61
+ if result["success"] is True:
62
+
63
+ intent = result["data"]
64
+
65
+ if result["data"]["task"] == "create_dockerfile":
66
+
67
+ success(f"Prepared {result['data']['task']} tasks")
68
+ logger.info("Sending the dockerfile intent to create_dockerfile function")
69
+
70
+ asyncio.run(create_dockerfile(intent))
71
+
72
+ elif result["data"]["task"] == "create_dockerignore":
73
+
74
+ success(f"Prepared {result['data']['task']} tasks")
75
+ logger.info("Sending the ignored files to dockerignore_file function")
76
+
77
+ asyncio.run(dockerignore_file(intent))
78
+ else:
79
+ logger.info(f"{result["data"]["message"]}")
80
+ info(result["data"]["message"])
@@ -0,0 +1,64 @@
1
+ Metadata-Version: 2.4
2
+ Name: yard_agent
3
+ Version: 0.1.0
4
+ Summary: Terminal based AI agent for Docker
5
+ Requires-Python: >=3.13
6
+ Description-Content-Type: text/markdown
7
+ License-File: LICENSE
8
+ Requires-Dist: openai>=2.33.0
9
+ Requires-Dist: python-dotenv>=1.2.2
10
+ Requires-Dist: tenacity>=9.1.4
11
+ Requires-Dist: typer>=0.25.1
12
+ Dynamic: license-file
13
+
14
+ # Yard for Docker
15
+
16
+ Yard is a terminal-based AI agent for Docker. In this first version, it helps developers quickly generate Dockerfiles for Python projects directly from the terminal.
17
+
18
+ ## Status
19
+
20
+ v1 — under active development.
21
+
22
+ ## What it does
23
+
24
+ - Generates one or more Dockerfiles and a `.dockerignore` file from a simple command
25
+ - Detects the Python version from project metadata
26
+ - Helps package Python applications for Docker quickly from the terminal
27
+
28
+ ## Install
29
+
30
+ ```bash
31
+ pipx install yard
32
+ ```
33
+
34
+ ## Setup
35
+
36
+ Yard currently uses OpenAI models.
37
+
38
+ ### Windows PowerShell
39
+
40
+ ```powershell
41
+ $env:OPENAI_API_KEY="your_api_key"
42
+ ```
43
+
44
+ ### Linux/macOS
45
+
46
+ ```bash
47
+ export OPENAI_API_KEY="your_api_key"
48
+ ```
49
+
50
+ ## Example
51
+
52
+ ```bash
53
+ yard "create a dockerfile for this main.py file"
54
+ ```
55
+
56
+ ## Privacy
57
+
58
+ - This project does not send raw files, scripts, or `.env` variables to the AI
59
+ - No data is used for training AI models
60
+ - Yard only reads `pyproject.toml` or `pyvenv.cfg` files to detect the Python version
61
+
62
+ ## License
63
+
64
+ MIT
@@ -0,0 +1,28 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/yard/__init__.py
5
+ src/yard/intent_agent.py
6
+ src/yard/logger_config.py
7
+ src/yard/yard_agent.py
8
+ src/yard/yard_cli.py
9
+ src/yard/console/console_ui.py
10
+ src/yard/console/error.py
11
+ src/yard/console/help_oargs.py
12
+ src/yard/helper/__init__.py
13
+ src/yard/helper/depfile.py
14
+ src/yard/prompt/agent_prompt.py
15
+ src/yard/prompt/intent_prompt.py
16
+ src/yard/prompt/tasks/dockerfile_prompt.py
17
+ src/yard/prompt/tasks/examples/dockerfile_example.py
18
+ src/yard/schema/intent_schema.py
19
+ src/yard/schema/yard_schema.py
20
+ src/yard/tools/__init__.py
21
+ src/yard/tools/create_dockerfile.py
22
+ src/yard/tools/docker_ignore_file.py
23
+ src/yard_agent.egg-info/PKG-INFO
24
+ src/yard_agent.egg-info/SOURCES.txt
25
+ src/yard_agent.egg-info/dependency_links.txt
26
+ src/yard_agent.egg-info/entry_points.txt
27
+ src/yard_agent.egg-info/requires.txt
28
+ src/yard_agent.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ yard = yard.yard_cli:app
@@ -0,0 +1,4 @@
1
+ openai>=2.33.0
2
+ python-dotenv>=1.2.2
3
+ tenacity>=9.1.4
4
+ typer>=0.25.1