agenticstackfile 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.
File without changes
File without changes
@@ -0,0 +1,12 @@
1
+ from abc import ABC, abstractmethod
2
+ from pathlib import Path
3
+
4
+
5
+ class BaseAnalyzer(ABC):
6
+
7
+ def __init__(self, root_path: Path):
8
+ self.root_path = root_path
9
+
10
+ @abstractmethod
11
+ def analyze(self) -> dict:
12
+ pass
@@ -0,0 +1,136 @@
1
+ import ast
2
+ import os
3
+ from pathlib import Path
4
+ from .base import BaseAnalyzer
5
+ import fnmatch
6
+
7
+ FILE_PATTERNS = {
8
+ "models": ["model", "models"],
9
+ "views": ["view", "views"],
10
+ "serializers": ["serializer", "serializers"],
11
+ "services": ["service", "services"],
12
+ "tests": ["test", "tests"],
13
+ "admin": ["admin"],
14
+ "urls": ["url", "urls"],
15
+ "filters": ["filter", "filters"],
16
+ "tasks": ["task", "tasks"],
17
+ "utils": ["util", "utils", "helper", "helpers"],
18
+ }
19
+
20
+
21
+ class PythonAnalyzer(BaseAnalyzer):
22
+
23
+ def __init__(self, root_path: Path):
24
+ super().__init__(root_path)
25
+ self.results = {
26
+ "classes": [],
27
+ "functions": [],
28
+ "imports": [],
29
+ "file_map": {},
30
+ "errors": [],
31
+ }
32
+
33
+ def _load_ignore_patterns(self) -> list:
34
+ ignore_file = self.root_path / ".agenticstackignore"
35
+ patterns = []
36
+
37
+ if not ignore_file.exists():
38
+ return patterns
39
+
40
+ for line in ignore_file.read_text(encoding="utf-8").splitlines():
41
+ line = line.strip()
42
+ if line and not line.startswith("#"):
43
+ patterns.append(line)
44
+
45
+ return patterns
46
+
47
+ def _is_ignored(self, path: Path, patterns: list) -> bool:
48
+ relative = str(path.relative_to(self.root_path))
49
+
50
+ for pattern in patterns:
51
+ if fnmatch.fnmatch(relative, pattern):
52
+ return True
53
+ if fnmatch.fnmatch(path.name, pattern):
54
+ return True
55
+
56
+ return False
57
+
58
+ def _classify_file(self, filepath: str) -> str:
59
+ path = Path(filepath)
60
+ filename = path.stem.lower()
61
+ parent = path.parent.name.lower()
62
+
63
+ for file_type, patterns in FILE_PATTERNS.items():
64
+ for pattern in patterns:
65
+ if pattern in filename or pattern in parent:
66
+ return file_type
67
+
68
+ return "other"
69
+
70
+ def _parse_file(self, filepath: Path) -> None:
71
+ relative_path = str(filepath.relative_to(self.root_path))
72
+ file_type = self._classify_file(str(filepath))
73
+ self.results["file_map"][relative_path] = file_type
74
+
75
+ try:
76
+ source = filepath.read_text(encoding="utf-8")
77
+ tree = ast.parse(source)
78
+ except (SyntaxError, UnicodeDecodeError) as e:
79
+ self.results["errors"].append({
80
+ "file": relative_path,
81
+ "error": str(e)
82
+ })
83
+ return
84
+
85
+ for node in ast.walk(tree):
86
+ if isinstance(node, ast.ClassDef):
87
+ self.results["classes"].append({
88
+ "name": node.name,
89
+ "file": relative_path,
90
+ "type": file_type,
91
+ "line": node.lineno,
92
+ })
93
+ elif isinstance(node, ast.FunctionDef):
94
+ self.results["functions"].append({
95
+ "name": node.name,
96
+ "file": relative_path,
97
+ "type": file_type,
98
+ "line": node.lineno,
99
+ })
100
+ elif isinstance(node, ast.Import):
101
+ for alias in node.names:
102
+ self.results["imports"].append({
103
+ "name": alias.name,
104
+ "file": relative_path,
105
+ })
106
+ elif isinstance(node, ast.ImportFrom):
107
+ self.results["imports"].append({
108
+ "name": f"from {node.module} import ...",
109
+ "file": relative_path,
110
+ })
111
+
112
+ def _get_python_files(self) -> list:
113
+ python_files = []
114
+ skip_dirs = {".venv", "__pycache__", ".git", "migrations", ".tox", "build", "dist"}
115
+ patterns = self._load_ignore_patterns()
116
+
117
+ for root, dirs, files in os.walk(self.root_path):
118
+ dirs[:] = [
119
+ d for d in dirs
120
+ if d not in skip_dirs
121
+ and not self._is_ignored(Path(root) / d, patterns)
122
+ ]
123
+ for file in files:
124
+ filepath = Path(root) / file
125
+ if file.endswith(".py") and not self._is_ignored(filepath, patterns):
126
+ python_files.append(filepath)
127
+
128
+ return python_files
129
+
130
+ def analyze(self) -> dict:
131
+ python_files = self._get_python_files()
132
+
133
+ for filepath in python_files:
134
+ self._parse_file(filepath)
135
+
136
+ return self.results
agenticstack/cli.py ADDED
@@ -0,0 +1,45 @@
1
+ import click
2
+ from pathlib import Path
3
+ from .config import create_default_config, config_exists, write_config, read_config, create_default_ignore_file
4
+ from .core import run
5
+
6
+
7
+ @click.group()
8
+ def main():
9
+ """AgenticStack — instant codebase map for AI agents."""
10
+ pass
11
+
12
+
13
+ @main.command()
14
+ @click.option("--api-key", default=None, help="Your LLM provider API key")
15
+ @click.option("--provider", default=None, help="Provider: anthropic or openai")
16
+ @click.option("--model", default=None, help="Model: default, fast, or smart")
17
+ def init(api_key, provider, model):
18
+ """Initialize AgenticStack in this project."""
19
+ if config_exists():
20
+ click.echo("AgenticStack: Config already exists. Use 'agenticstack update' to refresh.")
21
+ return
22
+
23
+ create_default_config()
24
+ click.echo("AgenticStack: Created .agenticstack.ini")
25
+ create_default_ignore_file()
26
+ click.echo("AgenticStack: Created .agenticstackignore")
27
+
28
+ if api_key:
29
+ write_config("api_key", api_key)
30
+ if provider:
31
+ write_config("provider", provider)
32
+ if model:
33
+ write_config("model", model)
34
+
35
+ run(Path.cwd())
36
+
37
+
38
+ @main.command()
39
+ def update():
40
+ """Refresh AgenticStack.txt for this project."""
41
+ if not config_exists():
42
+ click.echo("AgenticStack: No config found. Run 'agenticstack init' first.")
43
+ return
44
+
45
+ run(Path.cwd())
agenticstack/config.py ADDED
@@ -0,0 +1,98 @@
1
+ import configparser
2
+ from pathlib import Path
3
+
4
+ CONFIG_FILE = ".agenticstack.ini"
5
+ SECTION = "agenticstack"
6
+
7
+ DEFAULTS = {
8
+ "model": "default",
9
+ "api_key": "None",
10
+ "provider": "anthropic",
11
+ "language": "auto",
12
+ "output_file": "AgenticStack.txt",
13
+ "depth": "standard",
14
+ }
15
+
16
+
17
+ def get_config_path() -> Path:
18
+ return Path.cwd() / CONFIG_FILE
19
+
20
+
21
+ def config_exists() -> bool:
22
+ return get_config_path().exists()
23
+
24
+
25
+ def create_default_config() -> None:
26
+ config = configparser.ConfigParser()
27
+ config[SECTION] = DEFAULTS
28
+ with open(get_config_path(), "w") as f:
29
+ config.write(f)
30
+ _add_to_gitignore()
31
+
32
+
33
+ def read_config() -> dict:
34
+ if not config_exists():
35
+ create_default_config()
36
+ config = configparser.ConfigParser()
37
+ config.read(get_config_path())
38
+ return dict(config[SECTION])
39
+
40
+
41
+ def write_config(key: str, value: str) -> None:
42
+ config = configparser.ConfigParser()
43
+ config.read(get_config_path())
44
+ if SECTION not in config:
45
+ config[SECTION] = {}
46
+ config[SECTION][key] = value
47
+ with open(get_config_path(), "w") as f:
48
+ config.write(f)
49
+
50
+
51
+ def _add_to_gitignore() -> None:
52
+ gitignore_path = Path.cwd() / ".gitignore"
53
+ entry = CONFIG_FILE
54
+
55
+ if gitignore_path.exists():
56
+ content = gitignore_path.read_text()
57
+ if entry not in content:
58
+ with open(gitignore_path, "a") as f:
59
+ f.write(f"\n{entry}\n")
60
+ else:
61
+ with open(gitignore_path, "w") as f:
62
+ f.write(f"{entry}\n")
63
+
64
+
65
+ def create_default_ignore_file() -> None:
66
+ ignore_path = Path.cwd() / ".agenticstackignore"
67
+
68
+ if ignore_path.exists():
69
+ return
70
+
71
+ content = """# AgenticStack Ignore File
72
+ # Patterns follow Unix glob syntax — same as .gitignore
73
+
74
+ # Virtual environments
75
+ .venv
76
+ venv
77
+ env
78
+
79
+ # Test directories
80
+ test_project
81
+ tests
82
+
83
+ # Build artifacts
84
+ build
85
+ dist
86
+ *.egg-info
87
+
88
+ # Cache
89
+ __pycache__
90
+ *.pyc
91
+
92
+ # Migrations
93
+ **/migrations/**
94
+
95
+ # Node modules (for projects with frontend)
96
+ node_modules
97
+ """
98
+ ignore_path.write_text(content, encoding="utf-8")
agenticstack/core.py ADDED
@@ -0,0 +1,54 @@
1
+ from pathlib import Path
2
+ from .config import read_config
3
+ from .analyzer.python import PythonAnalyzer
4
+ from .formatter.static import StaticFormatter
5
+ from .prompt import build_prompt
6
+ from .writer import write_output
7
+
8
+ def _run_ai_mode(
9
+ static_output: str,
10
+ api_key: str,
11
+ provider_name: str,
12
+ model: str
13
+ ) -> str:
14
+ try:
15
+ from .providers.factory import get_provider
16
+ provider = get_provider(provider_name, api_key, model)
17
+ prompt = build_prompt(static_output)
18
+ print("AgenticStack: Sending to LLM...")
19
+ response = provider.generate(prompt)
20
+ return response
21
+ except Exception as e:
22
+ print(f"AgenticStack: AI mode failed — {e}")
23
+ print("AgenticStack: Falling back to static output")
24
+ return static_output
25
+
26
+
27
+ def run(root_path: Path = None) -> None:
28
+ if root_path is None:
29
+ root_path = Path.cwd()
30
+
31
+ print("AgenticStack: Analyzing codebase...")
32
+
33
+ config = read_config()
34
+
35
+ analyzer = PythonAnalyzer(root_path)
36
+ results = analyzer.analyze()
37
+
38
+ formatter = StaticFormatter(results, root_path)
39
+ static_output = formatter.format()
40
+
41
+ api_key = config.get("api_key", "None")
42
+ provider_name = config.get("provider", "anthropic")
43
+ model = config.get("model", "default")
44
+
45
+ if api_key and api_key != "None":
46
+ print(f"AgenticStack: AI mode — using {provider_name} ({model})")
47
+ final_output = _run_ai_mode(static_output, api_key, provider_name, model)
48
+ else:
49
+ print("AgenticStack: Static mode — no API key found")
50
+ final_output = static_output
51
+
52
+ output_file = config.get("output_file", "AgenticStack.txt")
53
+ write_output(final_output, Path(output_file))
54
+ print(f"AgenticStack: Done. Output written to {output_file}")
agenticstack/errors.py ADDED
File without changes
File without changes
@@ -0,0 +1,68 @@
1
+ from pathlib import Path
2
+ from collections import defaultdict
3
+
4
+ class StaticFormatter:
5
+
6
+ def __init__(self, results: dict, root_path: Path):
7
+ self.results = results
8
+ self.root_path = root_path
9
+ self.output = []
10
+
11
+ def _format_file_map(self) -> None:
12
+ self.output.append("=" * 60)
13
+ self.output.append("SECTION 1: WHERE THINGS LIVE")
14
+ self.output.append("=" * 60)
15
+
16
+ grouped = defaultdict(list)
17
+ for filepath, file_type in self.results["file_map"].items():
18
+ grouped[file_type].append(filepath)
19
+
20
+ for file_type, files in sorted(grouped.items()):
21
+ self.output.append(f"\n[{file_type.upper()}]")
22
+ for f in sorted(files):
23
+ self.output.append(f" {f}")
24
+
25
+ def _format_classes(self) -> None:
26
+ self.output.append("\n" + "=" * 60)
27
+ self.output.append("SECTION 2: CLASSES")
28
+ self.output.append("=" * 60)
29
+
30
+ grouped = defaultdict(list)
31
+ for cls in self.results["classes"]:
32
+ grouped[cls["type"]].append(cls)
33
+
34
+ for file_type, classes in sorted(grouped.items()):
35
+ self.output.append(f"\n[{file_type.upper()}]")
36
+ for cls in sorted(classes, key=lambda x: x["name"]):
37
+ self.output.append(f" {cls['name']} -> {cls['file']} (line {cls['line']})")
38
+
39
+
40
+ def _format_functions(self) -> None:
41
+ self.output.append("\n" + "=" * 60)
42
+ self.output.append("SECTION 3: FUNCTIONS")
43
+ self.output.append("=" * 60)
44
+
45
+ grouped = defaultdict(list)
46
+ for func in self.results["functions"]:
47
+ grouped[func["type"]].append(func)
48
+
49
+ for file_type, functions in sorted(grouped.items()):
50
+ self.output.append(f"\n[{file_type.upper()}]")
51
+ for func in sorted(functions, key=lambda x: x["name"]):
52
+ self.output.append(f" {func['name']} -> {func['file']} (line {func['line']})")
53
+
54
+ def _format_errors(self) -> None:
55
+ if not self.results["errors"]:
56
+ return
57
+ self.output.append("\n" + "=" * 60)
58
+ self.output.append("SECTION 4: PARSE ERRORS")
59
+ self.output.append("=" * 60)
60
+ for error in self.results["errors"]:
61
+ self.output.append(f" {error['file']}: {error['error']}")
62
+
63
+ def format(self) -> str:
64
+ self._format_file_map()
65
+ self._format_classes()
66
+ self._format_functions()
67
+ self._format_errors()
68
+ return "\n".join(self.output)
agenticstack/prompt.py ADDED
@@ -0,0 +1,73 @@
1
+ PROMPT_TEMPLATE = """You are an expert software architect analyzing a codebase.
2
+
3
+ You have been given a static analysis of a Python project below.
4
+ Your job is to produce a structured AgenticStack.txt file that helps AI agents
5
+ understand this codebase before making any changes.
6
+
7
+ STATIC ANALYSIS INPUT:
8
+ {static_analysis}
9
+
10
+ Produce output with EXACTLY these five sections. Do not add extra sections.
11
+ Do not use markdown. Use plain text only.
12
+
13
+ ============================================================
14
+ SECTION 1: WHERE THINGS LIVE
15
+ ============================================================
16
+ List every important file with its exact path and one line description.
17
+ Group by type: MODELS, VIEWS, SERIALIZERS, SERVICES, TESTS, OTHER.
18
+ Format:
19
+ filename.py -> what this file does in one line
20
+
21
+ ============================================================
22
+ SECTION 2: CHANGE RULES
23
+ ============================================================
24
+ Based on the files found, write rules that tell an agent WHEN to touch which file.
25
+ Use this decision tree format for each change type:
26
+
27
+ When adding a new [thing]:
28
+ Step 1: Always edit [file]
29
+ Step 2: If [condition] -> also edit [file]
30
+ Step 3: If [condition] -> also edit [file]
31
+
32
+ Cover these change types if the relevant files exist in the project:
33
+ - Adding a model field (always include migration step for Django projects)
34
+ - Adding a new endpoint
35
+ - Adding a new model
36
+ - Adding business logic
37
+
38
+ Every step must reference an actual file path from this project.
39
+ Never write generic steps like "update the model file" — always write
40
+ "edit test_project/app/models/user.py" using the real path.
41
+
42
+ ============================================================
43
+ SECTION 3: FIELD EXPOSURE MAP
44
+ ============================================================
45
+ List which models are exposed via API (have a serializer) vs internal only.
46
+ Format:
47
+ ModelName -> EXPOSED (serializer: path/to/serializer.py)
48
+ ModelName -> INTERNAL (no serializer found)
49
+
50
+ ============================================================
51
+ SECTION 4: HOW-TO GUIDES
52
+ ============================================================
53
+ Write step by step guides using the ACTUAL file paths found in this project.
54
+ Cover:
55
+ - How to add a model field
56
+ - How to add a new API endpoint
57
+
58
+ Each step must reference a real file path from this codebase.
59
+ Do not use generic placeholders like myapp/ or yourproject/.
60
+
61
+ ============================================================
62
+ SECTION 5: AGENT NOTES
63
+ ============================================================
64
+ Write 5 to 10 critical things an agent must know before editing this codebase.
65
+ Focus on patterns, constraints, and things that break easily.
66
+ Base these notes on what you can infer from the file structure and class names found.
67
+
68
+ Be specific. Be direct. No filler text.
69
+ """
70
+
71
+
72
+ def build_prompt(static_analysis: str) -> str:
73
+ return PROMPT_TEMPLATE.format(static_analysis=static_analysis)
File without changes
@@ -0,0 +1,31 @@
1
+ from .base import BaseProvider
2
+
3
+
4
+ ANTHROPIC_MODELS = {
5
+ "default": "claude-sonnet-4-20250514",
6
+ "fast": "claude-haiku-4-5-20251001",
7
+ "smart": "claude-opus-4-20250514",
8
+ }
9
+
10
+
11
+ class AnthropicProvider(BaseProvider):
12
+
13
+ def __init__(self, api_key: str, model: str = "default"):
14
+ resolved_model = ANTHROPIC_MODELS.get(model, ANTHROPIC_MODELS["default"])
15
+ super().__init__(api_key, resolved_model)
16
+
17
+ def generate(self, prompt: str) -> str:
18
+ try:
19
+ import anthropic
20
+ client = anthropic.Anthropic(api_key=self.api_key)
21
+ message = client.messages.create(
22
+ model=self.model,
23
+ max_tokens=8096,
24
+ messages=[{"role": "user", "content": prompt}],
25
+ )
26
+ return message.content[0].text
27
+ except ImportError:
28
+ raise ImportError(
29
+ "Anthropic SDK not installed. "
30
+ "Run: pip install agenticstack[anthropic]"
31
+ )
@@ -0,0 +1,12 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+
4
+ class BaseProvider(ABC):
5
+
6
+ def __init__(self, api_key: str, model: str):
7
+ self.api_key = api_key
8
+ self.model = model
9
+
10
+ @abstractmethod
11
+ def generate(self, prompt: str) -> str:
12
+ pass
@@ -0,0 +1,21 @@
1
+ from .anthropic import AnthropicProvider
2
+ from .openai import OpenAIProvider
3
+ from .base import BaseProvider
4
+
5
+
6
+ PROVIDERS = {
7
+ "anthropic": AnthropicProvider,
8
+ "openai": OpenAIProvider,
9
+ }
10
+
11
+
12
+ def get_provider(provider: str, api_key: str, model: str) -> BaseProvider:
13
+ provider_class = PROVIDERS.get(provider.lower())
14
+
15
+ if provider_class is None:
16
+ raise ValueError(
17
+ f"Unknown provider '{provider}'. "
18
+ f"Valid options are: {', '.join(PROVIDERS.keys())}"
19
+ )
20
+
21
+ return provider_class(api_key=api_key, model=model)
@@ -0,0 +1,31 @@
1
+ from .base import BaseProvider
2
+
3
+
4
+ OPENAI_MODELS = {
5
+ "default": "gpt-4o",
6
+ "fast": "gpt-4o-mini",
7
+ "smart": "gpt-4o",
8
+ }
9
+
10
+
11
+ class OpenAIProvider(BaseProvider):
12
+
13
+ def __init__(self, api_key: str, model: str = "default"):
14
+ resolved_model = OPENAI_MODELS.get(model, OPENAI_MODELS["default"])
15
+ super().__init__(api_key, resolved_model)
16
+
17
+ def generate(self, prompt: str) -> str:
18
+ try:
19
+ import openai
20
+ client = openai.OpenAI(api_key=self.api_key)
21
+ response = client.chat.completions.create(
22
+ model=self.model,
23
+ max_tokens=8096,
24
+ messages=[{"role": "user", "content": prompt}],
25
+ )
26
+ return response.choices[0].message.content
27
+ except ImportError:
28
+ raise ImportError(
29
+ "OpenAI SDK not installed. "
30
+ "Run: pip install agenticstack[openai]"
31
+ )
agenticstack/writer.py ADDED
@@ -0,0 +1,5 @@
1
+ from pathlib import Path
2
+
3
+
4
+ def write_output(content: str, output_path: Path) -> None:
5
+ output_path.write_text(content, encoding="utf-8")
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.4
2
+ Name: agenticstackfile
3
+ Version: 0.1.0
4
+ Summary: Instant codebase map for AI agents — understand any project before making changes
5
+ Author-email: Rajat Handa <handarajat111@gmail.com>
6
+ License-Expression: MIT
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ Provides-Extra: anthropic
10
+ Requires-Dist: anthropic>=0.20.0; extra == "anthropic"
11
+ Provides-Extra: openai
12
+ Requires-Dist: openai>=1.0.0; extra == "openai"
13
+ Provides-Extra: all
14
+ Requires-Dist: anthropic>=0.20.0; extra == "all"
15
+ Requires-Dist: openai>=1.0.0; extra == "all"
@@ -0,0 +1,22 @@
1
+ agenticstack/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ agenticstack/cli.py,sha256=PRoXFZHFlEYdO8RZj-73cFCT4yxP-1a5XGnbd0eO_8k,1349
3
+ agenticstack/config.py,sha256=AAOdDVyoVHYMiN2SZQs57C4pAkYlqAnGsroK2WOC1no,2282
4
+ agenticstack/core.py,sha256=d7jNpoDYmLXpk7TEFOhwzASzS6DkiImf_WpKbVvgL0Q,1759
5
+ agenticstack/errors.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ agenticstack/prompt.py,sha256=9sI2hmwZ_0WaSIzphITLFnjDSwNaRHCYIdvBT49y6AI,2917
7
+ agenticstack/writer.py,sha256=LDeSxHb1w9b2iI7bpwfGD4BvBaIeoxhKwv_Fi3DKmH4,139
8
+ agenticstack/analyzer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ agenticstack/analyzer/base.py,sha256=BPep-mb2q-g0R9TSGPu2YPmwr7hDTD6swUH9KVUwGS4,229
10
+ agenticstack/analyzer/python.py,sha256=VYWUYdv5vNYdRCFolzAWcGx45ioyipKczdlL4FS9N3o,4410
11
+ agenticstack/formatter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ agenticstack/formatter/static.py,sha256=MeJLrpZIaj2bEV9iYIjNr5ACQg2zQc6vUna-n8qVaXw,2472
13
+ agenticstack/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ agenticstack/providers/anthropic.py,sha256=IY9GblE0yu8ano_n24XcVNE4ZzpNVtN2yZPUBOS6o-M,978
15
+ agenticstack/providers/base.py,sha256=hAQEwZR-ZiLlNTeiowQNuKCdbVUP3vv7InnBRNG7QVo,249
16
+ agenticstack/providers/factory.py,sha256=-0NlcrSJLL0wVDatjrVni9Zse_LMCMnrQhaTDgPUfPg,556
17
+ agenticstack/providers/openai.py,sha256=rVdoTxWM0gOxkIaOwU-d_FlHi1SjhOIHhQBx2-FbHoY,924
18
+ agenticstackfile-0.1.0.dist-info/METADATA,sha256=IwXiajKR01HPMjv4BbCvxBFAj6erSTo5EjKXMzRQn4Y,558
19
+ agenticstackfile-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
20
+ agenticstackfile-0.1.0.dist-info/entry_points.txt,sha256=CXFHpk2Qkn3VUwf9Sjc_uQli06a9ZfIPAvwJhcufXJs,55
21
+ agenticstackfile-0.1.0.dist-info/top_level.txt,sha256=yQdgfr5cjlysSInqzDO5E_8lpauYK2tFaYVxcBaX8C0,13
22
+ agenticstackfile-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
+ agenticstack = agenticstack.cli:main
@@ -0,0 +1 @@
1
+ agenticstack