forgeoptimizer 1.0.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 (156) 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 +137 -0
  19. forgecli/cli/commands_wrappers.py +63 -0
  20. forgecli/cli/main.py +122 -0
  21. forgecli/cli/ui.py +56 -0
  22. forgecli/config/__init__.py +28 -0
  23. forgecli/config/loader.py +85 -0
  24. forgecli/config/settings.py +204 -0
  25. forgecli/config/writer.py +90 -0
  26. forgecli/core/__init__.py +35 -0
  27. forgecli/core/container.py +68 -0
  28. forgecli/core/context.py +54 -0
  29. forgecli/core/credentials.py +114 -0
  30. forgecli/core/errors.py +27 -0
  31. forgecli/core/events.py +67 -0
  32. forgecli/core/logging.py +47 -0
  33. forgecli/core/models.py +159 -0
  34. forgecli/core/plugins.py +56 -0
  35. forgecli/core/service.py +22 -0
  36. forgecli/docs/__init__.py +5 -0
  37. forgecli/docs/generator.py +119 -0
  38. forgecli/engine/__init__.py +105 -0
  39. forgecli/engine/context.py +171 -0
  40. forgecli/engine/defaults.py +89 -0
  41. forgecli/engine/events.py +185 -0
  42. forgecli/engine/execution.py +526 -0
  43. forgecli/engine/plugins.py +146 -0
  44. forgecli/engine/runner.py +155 -0
  45. forgecli/engine/stages/__init__.py +33 -0
  46. forgecli/engine/stages/caveman_optimizer.py +47 -0
  47. forgecli/engine/stages/context_optimizer.py +44 -0
  48. forgecli/engine/stages/execution_engine_stage.py +108 -0
  49. forgecli/engine/stages/git_engine.py +30 -0
  50. forgecli/engine/stages/intent_analyzer.py +38 -0
  51. forgecli/engine/stages/model_router.py +66 -0
  52. forgecli/engine/stages/planning_engine.py +45 -0
  53. forgecli/engine/stages/repository_analyzer.py +54 -0
  54. forgecli/engine/stages/validation_engine.py +89 -0
  55. forgecli/git/__init__.py +9 -0
  56. forgecli/git/repo.py +55 -0
  57. forgecli/git/service.py +45 -0
  58. forgecli/graph/__init__.py +56 -0
  59. forgecli/graph/backend_graphify.py +453 -0
  60. forgecli/graph/edge.py +19 -0
  61. forgecli/graph/graph.py +85 -0
  62. forgecli/graph/graphify.py +412 -0
  63. forgecli/graph/indexer.py +82 -0
  64. forgecli/graph/node.py +47 -0
  65. forgecli/graph/repository.py +164 -0
  66. forgecli/memory/__init__.py +12 -0
  67. forgecli/memory/cache.py +61 -0
  68. forgecli/memory/history.py +138 -0
  69. forgecli/memory/store.py +87 -0
  70. forgecli/optimizer/__init__.py +14 -0
  71. forgecli/optimizer/caveman/__init__.py +173 -0
  72. forgecli/optimizer/caveman/cli.py +64 -0
  73. forgecli/optimizer/caveman/decorator.py +67 -0
  74. forgecli/optimizer/caveman/factory.py +51 -0
  75. forgecli/optimizer/caveman/ruleset.py +156 -0
  76. forgecli/optimizer/caveman/state.py +52 -0
  77. forgecli/optimizer/chunker.py +85 -0
  78. forgecli/optimizer/optimizer.py +81 -0
  79. forgecli/optimizer/ponytail/__init__.py +183 -0
  80. forgecli/optimizer/ponytail/cli.py +168 -0
  81. forgecli/optimizer/ponytail/decorator.py +70 -0
  82. forgecli/optimizer/ponytail/factory.py +51 -0
  83. forgecli/optimizer/ponytail/ruleset.py +168 -0
  84. forgecli/optimizer/ponytail/state.py +53 -0
  85. forgecli/optimizer/ranker.py +37 -0
  86. forgecli/optimizer/summarizer.py +44 -0
  87. forgecli/orchestrator/__init__.py +707 -0
  88. forgecli/planner/__init__.py +56 -0
  89. forgecli/planner/agent.py +61 -0
  90. forgecli/planner/plan.py +65 -0
  91. forgecli/planner/planner.py +17 -0
  92. forgecli/planner/render.py +271 -0
  93. forgecli/planner/serialize.py +106 -0
  94. forgecli/planner/software.py +834 -0
  95. forgecli/platform/__init__.py +91 -0
  96. forgecli/platform/core.py +201 -0
  97. forgecli/platform/deps.py +354 -0
  98. forgecli/platform/paths.py +236 -0
  99. forgecli/platform/shell.py +176 -0
  100. forgecli/platform/update.py +252 -0
  101. forgecli/plugins/__init__.py +251 -0
  102. forgecli/prompts/__init__.py +11 -0
  103. forgecli/prompts/loader.py +28 -0
  104. forgecli/prompts/registry.py +32 -0
  105. forgecli/prompts/renderer.py +23 -0
  106. forgecli/providers/__init__.py +39 -0
  107. forgecli/providers/anthropic.py +206 -0
  108. forgecli/providers/base.py +206 -0
  109. forgecli/providers/builtin.py +26 -0
  110. forgecli/providers/conversation.py +78 -0
  111. forgecli/providers/google.py +295 -0
  112. forgecli/providers/http_base.py +211 -0
  113. forgecli/providers/mock.py +113 -0
  114. forgecli/providers/openai.py +202 -0
  115. forgecli/providers/openai_compatible.py +728 -0
  116. forgecli/providers/router.py +345 -0
  117. forgecli/providers/router_state.py +89 -0
  118. forgecli/review/__init__.py +45 -0
  119. forgecli/review/analyzer.py +124 -0
  120. forgecli/review/analyzers/__init__.py +1 -0
  121. forgecli/review/analyzers/architecture.py +258 -0
  122. forgecli/review/analyzers/complexity.py +167 -0
  123. forgecli/review/analyzers/dead_code.py +262 -0
  124. forgecli/review/analyzers/duplicates.py +163 -0
  125. forgecli/review/analyzers/performance.py +165 -0
  126. forgecli/review/analyzers/security.py +251 -0
  127. forgecli/review/finding.py +68 -0
  128. forgecli/review/report.py +311 -0
  129. forgecli/review/repository.py +131 -0
  130. forgecli/review/suggestions.py +98 -0
  131. forgecli/runtime/__init__.py +6 -0
  132. forgecli/runtime/cache_store.py +77 -0
  133. forgecli/runtime/prepare.py +199 -0
  134. forgecli/runtime/wrappers.py +152 -0
  135. forgecli/sdk/__init__.py +132 -0
  136. forgecli/sdk/events.py +206 -0
  137. forgecli/sdk/interfaces.py +282 -0
  138. forgecli/sdk/loader.py +243 -0
  139. forgecli/sdk/manager.py +693 -0
  140. forgecli/sdk/manifest.py +397 -0
  141. forgecli/sdk/sandbox.py +197 -0
  142. forgecli/sdk/version.py +316 -0
  143. forgecli/templates/__init__.py +9 -0
  144. forgecli/templates/engine.py +37 -0
  145. forgecli/templates/registry.py +32 -0
  146. forgecli/utils/__init__.py +19 -0
  147. forgecli/utils/fs.py +91 -0
  148. forgecli/utils/ids.py +11 -0
  149. forgecli/utils/io.py +27 -0
  150. forgecli/utils/paths.py +70 -0
  151. forgecli/utils/timing.py +25 -0
  152. forgeoptimizer-1.0.0.dist-info/METADATA +169 -0
  153. forgeoptimizer-1.0.0.dist-info/RECORD +156 -0
  154. forgeoptimizer-1.0.0.dist-info/WHEEL +4 -0
  155. forgeoptimizer-1.0.0.dist-info/entry_points.txt +2 -0
  156. forgeoptimizer-1.0.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,89 @@
1
+ """Stage 7 — Validation Engine.
2
+
3
+ Applies the extracted diff to disk, runs tests, and produces a
4
+ summary of the results. Combines :func:`forgecli.build.apply.apply_diff`,
5
+ :func:`forgecli.build.test_run.run_tests`, and
6
+ :func:`forgecli.build.summarize.summarize` into a single stage.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from pathlib import Path
12
+
13
+ from forgecli.build import BuildContext
14
+ from forgecli.build.apply import apply_diff
15
+ from forgecli.build.summarize import summarize
16
+ from forgecli.build.test_run import run_tests
17
+ from forgecli.engine.execution import StageContext, StageResult, StageStatus
18
+
19
+
20
+ class ValidationEngineStage:
21
+ """Apply the diff, run tests, and produce a summary."""
22
+
23
+ name = "validation-engine"
24
+
25
+ def __init__(self, test_command: str | None = None) -> None:
26
+ self._test_command = test_command
27
+
28
+ async def __call__(self, context: StageContext) -> StageResult:
29
+ decision = context.engine.extras.get("decision")
30
+ build_ctx = BuildContext(
31
+ prompt=context.engine.prompt,
32
+ root=Path(context.engine.cwd),
33
+ decision=decision,
34
+ )
35
+ build_ctx.diff_text = context.engine.diff_text
36
+ if context.engine.retrieval is not None:
37
+ build_ctx.retrieval = context.engine.retrieval.context_text
38
+
39
+ test_command = (
40
+ self._test_command or context.engine.extras.get("test_command")
41
+ )
42
+ if test_command:
43
+ build_ctx.extras["test_command"] = test_command
44
+ test_timeout = context.engine.extras.get("test_timeout")
45
+ if test_timeout:
46
+ build_ctx.extras["test_timeout"] = test_timeout
47
+
48
+ build_ctx = await apply_diff(build_ctx)
49
+ build_ctx = await run_tests(build_ctx)
50
+ build_ctx = await summarize(build_ctx)
51
+
52
+ context.engine.applied_files = build_ctx.applied_files
53
+ context.engine.diff_text = build_ctx.diff_text
54
+ context.engine.test_stdout = build_ctx.test_stdout
55
+ context.engine.test_stderr = build_ctx.test_stderr
56
+ context.engine.test_returncode = build_ctx.test_returncode
57
+ context.engine.extras["summary"] = build_ctx.summary
58
+
59
+ notes: list[str] = []
60
+ if build_ctx.applied_files:
61
+ notes.append(f"applied {len(build_ctx.applied_files)} files")
62
+ else:
63
+ notes.append("no files applied")
64
+
65
+ if build_ctx.test_returncode is None:
66
+ notes.append("tests skipped")
67
+ elif build_ctx.test_returncode == 0:
68
+ notes.append("tests passed")
69
+ else:
70
+ notes.append(f"tests FAILED (exit {build_ctx.test_returncode})")
71
+ return StageResult(
72
+ status=StageStatus.FAILED,
73
+ data={
74
+ "applied_files": [str(p) for p in build_ctx.applied_files],
75
+ "test_returncode": build_ctx.test_returncode,
76
+ "test_stderr": build_ctx.test_stderr[-500:],
77
+ },
78
+ notes=tuple(notes),
79
+ error=f"tests failed with exit code {build_ctx.test_returncode}",
80
+ )
81
+
82
+ return StageResult(
83
+ status=StageStatus.SUCCEEDED,
84
+ data={
85
+ "applied_files": [str(p) for p in build_ctx.applied_files],
86
+ "test_returncode": build_ctx.test_returncode,
87
+ },
88
+ notes=tuple(notes),
89
+ )
@@ -0,0 +1,9 @@
1
+ """Git automation layer built on top of GitPython."""
2
+
3
+ from forgecli.git.repo import GitRepo
4
+ from forgecli.git.service import GitService
5
+
6
+ __all__ = [
7
+ "GitRepo",
8
+ "GitService",
9
+ ]
forgecli/git/repo.py ADDED
@@ -0,0 +1,55 @@
1
+ """Thin wrapper around GitPython's :class:`Repo`."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ from git import Repo as _PyRepo
9
+ from git.exc import GitError as _PyGitError
10
+
11
+ from forgecli.core.errors import GitError
12
+
13
+
14
+ class GitRepo:
15
+ """Wrapper around GitPython's :class:`Repo` with typed errors."""
16
+
17
+ def __init__(self, path: str | Path) -> None:
18
+ self._path = Path(path)
19
+ try:
20
+ self._repo = _PyRepo(str(self._path))
21
+ except _PyGitError as exc:
22
+ raise GitError(f"Not a git repository: {self._path}") from exc
23
+ except Exception as exc:
24
+ raise GitError(f"Failed to open repository at {self._path}: {exc}") from exc
25
+
26
+ @property
27
+ def path(self) -> Path:
28
+ return self._path
29
+
30
+ @property
31
+ def raw(self) -> _PyRepo:
32
+ return self._repo
33
+
34
+ @property
35
+ def head(self) -> str:
36
+ try:
37
+ return str(self._repo.head.commit)
38
+ except _PyGitError as exc:
39
+ raise GitError(f"Unable to read HEAD: {exc}") from exc
40
+
41
+ def is_clean(self) -> bool:
42
+ return not self._repo.is_dirty(untracked_files=True)
43
+
44
+ def status(self) -> dict[str, Any]:
45
+ """Return a small status summary; placeholder for richer UX."""
46
+ return {
47
+ "branch": self._safe_branch(),
48
+ "clean": self.is_clean(),
49
+ }
50
+
51
+ def _safe_branch(self) -> str:
52
+ try:
53
+ return self._repo.active_branch.name
54
+ except Exception:
55
+ return "detached"
@@ -0,0 +1,45 @@
1
+ """High-level git operations: stage, branch, push, diff."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Iterable
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from forgecli.core.errors import GitError
10
+ from forgecli.core.service import Service
11
+ from forgecli.git.repo import GitRepo
12
+
13
+
14
+ class GitService(Service):
15
+ """Convenience operations built on top of :class:`GitRepo`."""
16
+
17
+ name = "git.service"
18
+
19
+ def __init__(
20
+ self,
21
+ repo: GitRepo,
22
+ *,
23
+ default_branch: str = "main",
24
+ ) -> None:
25
+ super().__init__()
26
+ self._repo = repo
27
+ self._default_branch = default_branch
28
+
29
+ @property
30
+ def repo(self) -> GitRepo:
31
+ return self._repo
32
+
33
+ def stage(self, paths: Iterable[str | Path]) -> None:
34
+ """Add ``paths`` to the index."""
35
+ try:
36
+ self._repo.raw.index.add([str(p) for p in paths])
37
+ except Exception as exc:
38
+ raise GitError(f"Failed to stage files: {exc}") from exc
39
+
40
+ def current_branch(self) -> str:
41
+ return self._repo._safe_branch()
42
+
43
+ def diff(self, *args: Any) -> str:
44
+ """Return ``git diff`` output; placeholder."""
45
+ raise NotImplementedError("GitService.diff() is a scaffold placeholder")
@@ -0,0 +1,56 @@
1
+ """Repository graph: indexing, parsing, code intelligence.
2
+
3
+ The package exposes two layers:
4
+
5
+ * A small in-memory graph (:class:`CodeGraph`, :class:`Node`, :class:`Edge`)
6
+ used by lightweight callers and tests.
7
+ * A back-end abstraction (:class:`RepositoryGraph`) implemented by
8
+ :class:`GraphifyRepositoryGraph`, which delegates to the external
9
+ Graphify CLI.
10
+ """
11
+
12
+ from forgecli.graph.backend_graphify import GraphifyRepositoryGraph
13
+ from forgecli.graph.edge import Edge, EdgeKind
14
+ from forgecli.graph.graph import CodeGraph
15
+ from forgecli.graph.graphify import (
16
+ GraphifyArtifacts,
17
+ GraphifyBuildOutcome,
18
+ GraphifyClient,
19
+ GraphifyInvocationError,
20
+ GraphifyNotFoundError,
21
+ )
22
+ from forgecli.graph.indexer import Indexer
23
+ from forgecli.graph.node import Node, NodeKind
24
+ from forgecli.graph.repository import (
25
+ BuildResult,
26
+ ExplainResult,
27
+ GraphCommunity,
28
+ GraphEdge,
29
+ GraphNode,
30
+ GraphSnapshot,
31
+ QueryResult,
32
+ RepositoryGraph,
33
+ )
34
+
35
+ __all__ = [
36
+ "BuildResult",
37
+ "CodeGraph",
38
+ "Edge",
39
+ "EdgeKind",
40
+ "ExplainResult",
41
+ "GraphCommunity",
42
+ "GraphEdge",
43
+ "GraphNode",
44
+ "GraphSnapshot",
45
+ "GraphifyArtifacts",
46
+ "GraphifyBuildOutcome",
47
+ "GraphifyClient",
48
+ "GraphifyInvocationError",
49
+ "GraphifyNotFoundError",
50
+ "GraphifyRepositoryGraph",
51
+ "Indexer",
52
+ "Node",
53
+ "NodeKind",
54
+ "QueryResult",
55
+ "RepositoryGraph",
56
+ ]