forgeoptimizer 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.
Files changed (159) hide show
  1. forgecli/__init__.py +4 -0
  2. forgecli/build/__init__.py +135 -0
  3. forgecli/build/apply.py +218 -0
  4. forgecli/build/caveman_optimize.py +37 -0
  5. forgecli/build/diff_extract.py +218 -0
  6. forgecli/build/llm.py +167 -0
  7. forgecli/build/optimize.py +37 -0
  8. forgecli/build/pipeline.py +76 -0
  9. forgecli/build/retrieval.py +157 -0
  10. forgecli/build/summarize.py +76 -0
  11. forgecli/build/test_run.py +75 -0
  12. forgecli/builder/__init__.py +11 -0
  13. forgecli/builder/builder.py +53 -0
  14. forgecli/builder/editor.py +36 -0
  15. forgecli/builder/formatter.py +31 -0
  16. forgecli/cli/__init__.py +5 -0
  17. forgecli/cli/bootstrap.py +281 -0
  18. forgecli/cli/commands_graph.py +149 -0
  19. forgecli/cli/commands_wrappers.py +80 -0
  20. forgecli/cli/daemon.py +744 -0
  21. forgecli/cli/main.py +234 -0
  22. forgecli/cli/ui.py +56 -0
  23. forgecli/config/__init__.py +28 -0
  24. forgecli/config/loader.py +85 -0
  25. forgecli/config/settings.py +206 -0
  26. forgecli/config/writer.py +90 -0
  27. forgecli/core/__init__.py +35 -0
  28. forgecli/core/container.py +68 -0
  29. forgecli/core/context.py +54 -0
  30. forgecli/core/credentials.py +114 -0
  31. forgecli/core/errors.py +27 -0
  32. forgecli/core/events.py +67 -0
  33. forgecli/core/logging.py +47 -0
  34. forgecli/core/models.py +214 -0
  35. forgecli/core/plugins.py +54 -0
  36. forgecli/core/service.py +22 -0
  37. forgecli/docs/__init__.py +5 -0
  38. forgecli/docs/generator.py +115 -0
  39. forgecli/engine/__init__.py +105 -0
  40. forgecli/engine/context.py +163 -0
  41. forgecli/engine/defaults.py +89 -0
  42. forgecli/engine/events.py +185 -0
  43. forgecli/engine/execution.py +519 -0
  44. forgecli/engine/plugins.py +145 -0
  45. forgecli/engine/runner.py +158 -0
  46. forgecli/engine/stages/__init__.py +33 -0
  47. forgecli/engine/stages/caveman_optimizer.py +47 -0
  48. forgecli/engine/stages/context_optimizer.py +44 -0
  49. forgecli/engine/stages/execution_engine_stage.py +102 -0
  50. forgecli/engine/stages/git_engine.py +30 -0
  51. forgecli/engine/stages/intent_analyzer.py +38 -0
  52. forgecli/engine/stages/model_router.py +65 -0
  53. forgecli/engine/stages/planning_engine.py +45 -0
  54. forgecli/engine/stages/repository_analyzer.py +54 -0
  55. forgecli/engine/stages/validation_engine.py +87 -0
  56. forgecli/git/__init__.py +9 -0
  57. forgecli/git/repo.py +55 -0
  58. forgecli/git/service.py +45 -0
  59. forgecli/graph/__init__.py +56 -0
  60. forgecli/graph/backend_graphify.py +448 -0
  61. forgecli/graph/edge.py +19 -0
  62. forgecli/graph/graph.py +85 -0
  63. forgecli/graph/graphify.py +405 -0
  64. forgecli/graph/indexer.py +82 -0
  65. forgecli/graph/node.py +47 -0
  66. forgecli/graph/repository.py +164 -0
  67. forgecli/memory/__init__.py +12 -0
  68. forgecli/memory/cache.py +61 -0
  69. forgecli/memory/history.py +136 -0
  70. forgecli/memory/store.py +85 -0
  71. forgecli/optimizer/__init__.py +13 -0
  72. forgecli/optimizer/caveman/__init__.py +169 -0
  73. forgecli/optimizer/caveman/cli.py +62 -0
  74. forgecli/optimizer/caveman/decorator.py +67 -0
  75. forgecli/optimizer/caveman/factory.py +51 -0
  76. forgecli/optimizer/caveman/ruleset.py +156 -0
  77. forgecli/optimizer/caveman/state.py +50 -0
  78. forgecli/optimizer/chunker.py +85 -0
  79. forgecli/optimizer/optimizer.py +81 -0
  80. forgecli/optimizer/ponytail/__init__.py +181 -0
  81. forgecli/optimizer/ponytail/cli.py +159 -0
  82. forgecli/optimizer/ponytail/decorator.py +70 -0
  83. forgecli/optimizer/ponytail/factory.py +51 -0
  84. forgecli/optimizer/ponytail/ruleset.py +168 -0
  85. forgecli/optimizer/ponytail/state.py +51 -0
  86. forgecli/optimizer/ranker.py +37 -0
  87. forgecli/optimizer/summarizer.py +44 -0
  88. forgecli/orchestrator/__init__.py +706 -0
  89. forgecli/planner/__init__.py +56 -0
  90. forgecli/planner/agent.py +61 -0
  91. forgecli/planner/plan.py +65 -0
  92. forgecli/planner/planner.py +17 -0
  93. forgecli/planner/render.py +267 -0
  94. forgecli/planner/serialize.py +104 -0
  95. forgecli/planner/software.py +832 -0
  96. forgecli/platform/__init__.py +91 -0
  97. forgecli/platform/core.py +201 -0
  98. forgecli/platform/deps.py +361 -0
  99. forgecli/platform/paths.py +234 -0
  100. forgecli/platform/shell.py +176 -0
  101. forgecli/platform/update.py +253 -0
  102. forgecli/plugins/__init__.py +249 -0
  103. forgecli/prompts/__init__.py +11 -0
  104. forgecli/prompts/loader.py +28 -0
  105. forgecli/prompts/registry.py +32 -0
  106. forgecli/prompts/renderer.py +23 -0
  107. forgecli/providers/__init__.py +39 -0
  108. forgecli/providers/anthropic.py +207 -0
  109. forgecli/providers/base.py +204 -0
  110. forgecli/providers/builtin.py +26 -0
  111. forgecli/providers/conversation.py +74 -0
  112. forgecli/providers/google.py +290 -0
  113. forgecli/providers/http_base.py +207 -0
  114. forgecli/providers/mock.py +111 -0
  115. forgecli/providers/openai.py +206 -0
  116. forgecli/providers/openai_compatible.py +787 -0
  117. forgecli/providers/router.py +340 -0
  118. forgecli/providers/router_state.py +89 -0
  119. forgecli/review/__init__.py +45 -0
  120. forgecli/review/analyzer.py +124 -0
  121. forgecli/review/analyzers/__init__.py +1 -0
  122. forgecli/review/analyzers/architecture.py +255 -0
  123. forgecli/review/analyzers/complexity.py +162 -0
  124. forgecli/review/analyzers/dead_code.py +255 -0
  125. forgecli/review/analyzers/duplicates.py +161 -0
  126. forgecli/review/analyzers/performance.py +161 -0
  127. forgecli/review/analyzers/security.py +244 -0
  128. forgecli/review/finding.py +68 -0
  129. forgecli/review/report.py +321 -0
  130. forgecli/review/repository.py +130 -0
  131. forgecli/review/suggestions.py +98 -0
  132. forgecli/runtime/__init__.py +6 -0
  133. forgecli/runtime/cache_store.py +75 -0
  134. forgecli/runtime/mcp_config.py +118 -0
  135. forgecli/runtime/prepare.py +203 -0
  136. forgecli/runtime/wrappers.py +150 -0
  137. forgecli/sdk/__init__.py +132 -0
  138. forgecli/sdk/events.py +206 -0
  139. forgecli/sdk/interfaces.py +282 -0
  140. forgecli/sdk/loader.py +239 -0
  141. forgecli/sdk/manager.py +678 -0
  142. forgecli/sdk/manifest.py +395 -0
  143. forgecli/sdk/sandbox.py +247 -0
  144. forgecli/sdk/version.py +313 -0
  145. forgecli/templates/__init__.py +9 -0
  146. forgecli/templates/engine.py +37 -0
  147. forgecli/templates/registry.py +32 -0
  148. forgecli/utils/__init__.py +19 -0
  149. forgecli/utils/fs.py +121 -0
  150. forgecli/utils/ids.py +11 -0
  151. forgecli/utils/io.py +27 -0
  152. forgecli/utils/paths.py +78 -0
  153. forgecli/utils/stats.py +179 -0
  154. forgecli/utils/timing.py +25 -0
  155. forgeoptimizer-0.1.0.dist-info/METADATA +177 -0
  156. forgeoptimizer-0.1.0.dist-info/RECORD +159 -0
  157. forgeoptimizer-0.1.0.dist-info/WHEEL +4 -0
  158. forgeoptimizer-0.1.0.dist-info/entry_points.txt +2 -0
  159. forgeoptimizer-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,89 @@
1
+ """Default stage wiring for the Execution Engine.
2
+
3
+ Provides :func:`default_registry` which populates a
4
+ :class:`~forgecli.engine.execution.StageRegistry` with all eight
5
+ DEFAULT_PIPELINE stages. Callers can then build an engine with::
6
+
7
+ registry = default_registry()
8
+ engine = ExecutionEngine.from_registry(registry)
9
+ result = await engine.run(context)
10
+
11
+ Individual stages can be overridden via
12
+ :meth:`StageRegistry.replace` before building the engine.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from typing import Any
18
+
19
+ from forgecli.engine.execution import Stage, StageRegistry
20
+ from forgecli.engine.stages import (
21
+ CavemanOptimizerStage,
22
+ ContextOptimizerStage,
23
+ ExecutionEngineStage,
24
+ GitEngineStage,
25
+ IntentAnalyzerStage,
26
+ ModelRouterStage,
27
+ PlanningEngineStage,
28
+ RepositoryAnalyzerStage,
29
+ ValidationEngineStage,
30
+ )
31
+
32
+
33
+ def default_registry(
34
+ *,
35
+ provider: Any = None,
36
+ optimizer: Any = None,
37
+ caveman_optimizer: Any = None,
38
+ graph: Any = None,
39
+ classifier: Any = None,
40
+ router: Any = None,
41
+ test_command: str | None = None,
42
+ plugin_registry: Any = None,
43
+ **stage_kwargs: Any,
44
+ ) -> StageRegistry:
45
+ """Create a :class:`StageRegistry` with all nine default pipeline stages.
46
+
47
+ Keyword arguments are forwarded to stage constructors:
48
+
49
+ * ``provider`` → :class:`ExecutionEngineStage`
50
+ * ``optimizer`` → :class:`ContextOptimizerStage`
51
+ * ``caveman_optimizer`` → :class:`CavemanOptimizerStage`
52
+ * ``graph`` → :class:`RepositoryAnalyzerStage`
53
+ * ``classifier`` → :class:`IntentAnalyzerStage`
54
+ * ``router`` → :class:`ModelRouterStage`
55
+ * ``test_command`` → :class:`ValidationEngineStage`
56
+ * ``plugin_registry`` → a :class:`~forgecli.plugins.PluginRegistry` whose
57
+ stages are bulk-loaded into the registry after default stages.
58
+ The plugin registry is also linked so future
59
+ :meth:`~forgecli.plugins.PluginRegistry.register_stage` /
60
+ :meth:`~forgecli.plugins.PluginRegistry.replace_stage` calls
61
+ are mirrored into this registry.
62
+
63
+ Remaining kwargs are passed to stages that accept them by name
64
+ (e.g. ``auto_commit`` for :class:`GitEngineStage`).
65
+ """
66
+ registry = StageRegistry()
67
+
68
+ stages: list[Stage] = [
69
+ IntentAnalyzerStage(classifier=classifier),
70
+ RepositoryAnalyzerStage(graph=graph),
71
+ CavemanOptimizerStage(optimizer=caveman_optimizer),
72
+ ContextOptimizerStage(optimizer=optimizer),
73
+ PlanningEngineStage(**{k: v for k, v in stage_kwargs.items() if k in ("enabled",)}),
74
+ ModelRouterStage(router=router),
75
+ ExecutionEngineStage(provider=provider),
76
+ ValidationEngineStage(test_command=test_command),
77
+ GitEngineStage(),
78
+ ]
79
+
80
+ for stage in stages:
81
+ registry.register(stage)
82
+
83
+ if plugin_registry is not None:
84
+ plugin_registry.link_engine_registry(registry)
85
+
86
+ return registry
87
+
88
+
89
+ __all__ = ["default_registry"]
@@ -0,0 +1,185 @@
1
+ """Structured event bus for the Execution Engine.
2
+
3
+ The :class:`EventBus` is a tiny pub/sub dispatcher for the four
4
+ event kinds the engine emits:
5
+
6
+ * :class:`StageEvent` — a stage transitioned to a new state.
7
+ * :class:`ProgressEvent` — fractional progress within a stage.
8
+ * :class:`TextLogEvent` — a free-form log line (the engine's
9
+ "streaming log" surface).
10
+ * :class:`EngineEvent` — the abstract base; every concrete event
11
+ inherits from it.
12
+
13
+ Subscribers can be sync or async; the engine handles fan-out
14
+ sequentially. A single :class:`asyncio.Event` (the cancellation
15
+ token) is checked between stages and during long operations so a
16
+ caller can abort cleanly.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import asyncio
22
+ import contextlib
23
+ from collections.abc import Awaitable, Callable
24
+ from dataclasses import dataclass, field
25
+ from datetime import UTC, datetime
26
+ from enum import Enum
27
+
28
+
29
+ class LogLevel(str, Enum):
30
+ """Severity for :class:`TextLogEvent`."""
31
+
32
+ DEBUG = "debug"
33
+ INFO = "info"
34
+ WARN = "warn"
35
+ ERROR = "error"
36
+
37
+
38
+ @dataclass(frozen=True)
39
+ class EngineEvent:
40
+ """Abstract base of every event the engine emits."""
41
+
42
+ timestamp: datetime = field(default_factory=lambda: datetime.now(UTC))
43
+ run_id: str = ""
44
+
45
+
46
+ @dataclass(frozen=True)
47
+ class StageEvent(EngineEvent):
48
+ """A stage transitioned to a new state."""
49
+
50
+ stage: str = ""
51
+ status: str = "running" # "running" | "succeeded" | "failed" | "skipped" | "retrying"
52
+ attempt: int = 1
53
+ note: str | None = None
54
+
55
+
56
+ @dataclass(frozen=True)
57
+ class ProgressEvent(EngineEvent):
58
+ """Fractional progress within a stage (0.0 - 1.0)."""
59
+
60
+ stage: str = ""
61
+ progress: float = 0.0
62
+ message: str | None = None
63
+
64
+
65
+ @dataclass(frozen=True)
66
+ class TextLogEvent(EngineEvent):
67
+ """A free-form log line emitted by a stage."""
68
+
69
+ level: LogLevel = LogLevel.INFO
70
+ source: str = ""
71
+ message: str = ""
72
+
73
+
74
+ EventHandler = Callable[[EngineEvent], "None | Awaitable[None]"]
75
+
76
+
77
+ class EventBus:
78
+ """In-process pub/sub bus for engine events.
79
+
80
+ Subscribers are registered per event class. Handlers may be sync
81
+ or async; the engine awaits any coroutines it gets back. A single
82
+ :class:`asyncio.Event` (the cancellation token) is checked between
83
+ stages so the caller can abort cleanly.
84
+ """
85
+
86
+ def __init__(self) -> None:
87
+ self._subscribers: dict[type, list[EventHandler]] = {}
88
+ self.cancellation = asyncio.Event()
89
+ self.history: list[EngineEvent] = []
90
+
91
+ # ------------------------------------------------------------------
92
+ # Subscription
93
+ # ------------------------------------------------------------------
94
+
95
+ def subscribe(self, event_cls: type[EngineEvent], handler: EventHandler) -> None:
96
+ self._subscribers.setdefault(event_cls, []).append(handler)
97
+
98
+ def unsubscribe(self, event_cls: type[EngineEvent], handler: EventHandler) -> None:
99
+ bucket = self._subscribers.get(event_cls)
100
+ if not bucket:
101
+ return
102
+ with contextlib.suppress(ValueError):
103
+ bucket.remove(handler)
104
+
105
+ # ------------------------------------------------------------------
106
+ # Cancellation
107
+ # ------------------------------------------------------------------
108
+
109
+ def cancel(self) -> None:
110
+ """Request cancellation. The engine checks ``cancellation`` between stages."""
111
+ self.cancellation.set()
112
+
113
+ def reset_cancellation(self) -> None:
114
+ self.cancellation.clear()
115
+
116
+ def is_cancelled(self) -> bool:
117
+ return self.cancellation.is_set()
118
+
119
+ # ------------------------------------------------------------------
120
+ # Publishing
121
+ # ------------------------------------------------------------------
122
+
123
+ def publish(self, event: EngineEvent) -> None:
124
+ """Publish synchronously; async handlers are scheduled."""
125
+ self.history.append(event)
126
+ for handler in list(self._subscribers.get(type(event), ())):
127
+ try:
128
+ result = handler(event)
129
+ except Exception:
130
+ continue
131
+ if asyncio.iscoroutine(result):
132
+ # Best-effort: schedule on the running loop if any.
133
+ try:
134
+ loop = asyncio.get_running_loop()
135
+ task = loop.create_task(result)
136
+ task.add_done_callback(self._discard_task)
137
+ except RuntimeError:
138
+ pass
139
+
140
+ @staticmethod
141
+ def _discard_task(task: asyncio.Task) -> None:
142
+ # Placeholder to keep a strong reference to the task until it
143
+ # completes. Without this, asyncio may garbage-collect the
144
+ # task mid-flight and emit "Task was destroyed but it is
145
+ # pending" warnings.
146
+ return None
147
+
148
+ async def publish_and_drain(self, event: EngineEvent) -> None:
149
+ """Publish and await any async handler results."""
150
+ self.history.append(event)
151
+ for handler in list(self._subscribers.get(type(event), ())):
152
+ try:
153
+ result = handler(event)
154
+ except Exception:
155
+ continue
156
+ if asyncio.iscoroutine(result):
157
+ await result
158
+
159
+ # ------------------------------------------------------------------
160
+ # Helpers
161
+ # ------------------------------------------------------------------
162
+
163
+ def drain(self) -> list[EngineEvent]:
164
+ return list(self.history)
165
+
166
+
167
+ # ---------------------------------------------------------------------------
168
+ # Cancellation sentinel exception
169
+ # ---------------------------------------------------------------------------
170
+
171
+
172
+ class EngineCancelledError(Exception):
173
+ """Raised by the engine when the cancellation token is set."""
174
+
175
+
176
+ __all__ = [
177
+ "EngineCancelledError",
178
+ "EngineEvent",
179
+ "EventBus",
180
+ "EventHandler",
181
+ "LogLevel",
182
+ "ProgressEvent",
183
+ "StageEvent",
184
+ "TextLogEvent",
185
+ ]