forgex-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.
Files changed (115) hide show
  1. forgex/__init__.py +3 -0
  2. forgex/__main__.py +6 -0
  3. forgex/agents/__init__.py +36 -0
  4. forgex/agents/architect.py +233 -0
  5. forgex/agents/base.py +385 -0
  6. forgex/agents/builder.py +269 -0
  7. forgex/agents/ensemble.py +334 -0
  8. forgex/agents/expert_coder.py +283 -0
  9. forgex/agents/supervisor.py +303 -0
  10. forgex/cli/__init__.py +1 -0
  11. forgex/cli/app.py +232 -0
  12. forgex/cli/build_views.py +94 -0
  13. forgex/cli/commands/__init__.py +1 -0
  14. forgex/cli/commands/build.py +63 -0
  15. forgex/cli/commands/checkpoint.py +41 -0
  16. forgex/cli/commands/config.py +225 -0
  17. forgex/cli/commands/context.py +94 -0
  18. forgex/cli/commands/diff.py +32 -0
  19. forgex/cli/commands/do_cmd.py +50 -0
  20. forgex/cli/commands/doctor.py +175 -0
  21. forgex/cli/commands/fix.py +46 -0
  22. forgex/cli/commands/init.py +41 -0
  23. forgex/cli/commands/logs.py +102 -0
  24. forgex/cli/commands/models.py +144 -0
  25. forgex/cli/commands/plan.py +103 -0
  26. forgex/cli/commands/review.py +76 -0
  27. forgex/cli/commands/rollback.py +65 -0
  28. forgex/cli/commands/run.py +75 -0
  29. forgex/cli/commands/status.py +141 -0
  30. forgex/cli/commands/supervise.py +61 -0
  31. forgex/cli/commands/task.py +136 -0
  32. forgex/cli/commands/tools.py +116 -0
  33. forgex/cli/commands/verify.py +65 -0
  34. forgex/cli/context_memory.py +62 -0
  35. forgex/cli/intent.py +209 -0
  36. forgex/cli/progress.py +101 -0
  37. forgex/cli/repl.py +377 -0
  38. forgex/cli/runtime.py +233 -0
  39. forgex/cli/session.py +95 -0
  40. forgex/cli/ui.py +113 -0
  41. forgex/config/__init__.py +53 -0
  42. forgex/config/loader.py +189 -0
  43. forgex/config/schema.py +145 -0
  44. forgex/context/__init__.py +6 -0
  45. forgex/context/chunker.py +102 -0
  46. forgex/context/dependency_graph.py +110 -0
  47. forgex/context/indexer.py +161 -0
  48. forgex/context/manager.py +174 -0
  49. forgex/context/retriever.py +99 -0
  50. forgex/models/__init__.py +25 -0
  51. forgex/models/base.py +112 -0
  52. forgex/models/capabilities.py +21 -0
  53. forgex/models/factory.py +66 -0
  54. forgex/models/groq.py +13 -0
  55. forgex/models/local.py +10 -0
  56. forgex/models/openai_compatible.py +233 -0
  57. forgex/models/registry.py +81 -0
  58. forgex/models/router.py +332 -0
  59. forgex/orchestration/__init__.py +36 -0
  60. forgex/orchestration/checkpoints.py +117 -0
  61. forgex/orchestration/engine.py +573 -0
  62. forgex/orchestration/events.py +101 -0
  63. forgex/orchestration/lockfile.py +82 -0
  64. forgex/orchestration/retry.py +65 -0
  65. forgex/orchestration/state.py +89 -0
  66. forgex/orchestration/stores.py +90 -0
  67. forgex/orchestration/workflow.py +103 -0
  68. forgex/project.py +209 -0
  69. forgex/prompts/__init__.py +27 -0
  70. forgex/prompts/architect.md +18 -0
  71. forgex/prompts/builder.md +20 -0
  72. forgex/prompts/expert_coder.md +25 -0
  73. forgex/prompts/reviewer.md +11 -0
  74. forgex/prompts/supervisor.md +26 -0
  75. forgex/research/__init__.py +15 -0
  76. forgex/research/citations.py +16 -0
  77. forgex/research/engine.py +83 -0
  78. forgex/research/sources.py +154 -0
  79. forgex/sandbox/__init__.py +37 -0
  80. forgex/sandbox/executor.py +418 -0
  81. forgex/sandbox/permissions.py +67 -0
  82. forgex/sandbox/policies.py +172 -0
  83. forgex/schemas/__init__.py +101 -0
  84. forgex/schemas/agent.py +109 -0
  85. forgex/schemas/checkpoint.py +19 -0
  86. forgex/schemas/error.py +125 -0
  87. forgex/schemas/event.py +76 -0
  88. forgex/schemas/plan.py +84 -0
  89. forgex/schemas/project.py +92 -0
  90. forgex/schemas/research.py +25 -0
  91. forgex/schemas/result.py +68 -0
  92. forgex/schemas/task.py +118 -0
  93. forgex/storage/__init__.py +25 -0
  94. forgex/storage/database.py +40 -0
  95. forgex/storage/migrations.py +130 -0
  96. forgex/storage/repositories.py +492 -0
  97. forgex/tools/__init__.py +73 -0
  98. forgex/tools/base.py +212 -0
  99. forgex/tools/dependencies.py +137 -0
  100. forgex/tools/filesystem.py +322 -0
  101. forgex/tools/git.py +239 -0
  102. forgex/tools/project.py +39 -0
  103. forgex/tools/search.py +205 -0
  104. forgex/tools/terminal.py +81 -0
  105. forgex/tools/testing.py +100 -0
  106. forgex/tools/verification.py +174 -0
  107. forgex/utils/__init__.py +15 -0
  108. forgex/utils/logging.py +28 -0
  109. forgex/utils/paths.py +48 -0
  110. forgex/utils/redact.py +44 -0
  111. forgex_cli-0.1.0.dist-info/METADATA +189 -0
  112. forgex_cli-0.1.0.dist-info/RECORD +115 -0
  113. forgex_cli-0.1.0.dist-info/WHEEL +4 -0
  114. forgex_cli-0.1.0.dist-info/entry_points.txt +2 -0
  115. forgex_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,174 @@
1
+ """Deterministic verification harness (spec sections 11, 56)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import re
7
+ from pathlib import Path
8
+
9
+ from forgex.orchestration.events import EventRecorder
10
+ from forgex.project import detect_project
11
+ from forgex.sandbox.executor import CommandExecutor
12
+ from forgex.schemas.error import CommandDeniedError
13
+ from forgex.schemas.event import EventType
14
+ from forgex.schemas.result import (
15
+ BuildResults,
16
+ CommandOutcome,
17
+ TestResults,
18
+ VerificationOutcome,
19
+ )
20
+ from forgex.tools.testing import detect_test_command
21
+
22
+
23
+ def detect_build_command(root: Path) -> list[str] | None:
24
+ det = detect_project(root)
25
+ langs = det.languages
26
+
27
+ if (root / "package.json").is_file():
28
+ try:
29
+ data = json.loads((root / "package.json").read_text(encoding="utf-8"))
30
+ if (data.get("scripts") or {}).get("build"):
31
+ return ["npm", "run", "build"]
32
+ except (OSError, json.JSONDecodeError):
33
+ pass
34
+
35
+ if "Rust" in langs or (root / "Cargo.toml").is_file():
36
+ return ["cargo", "build"]
37
+
38
+ if "Go" in langs or (root / "go.mod").is_file():
39
+ return ["go", "build", "./..."]
40
+
41
+ if "Python" in langs:
42
+ src_dir = root / "src"
43
+ if src_dir.is_dir():
44
+ return ["python", "-m", "compileall", "-q", "src"]
45
+ if list(root.glob("*.py")):
46
+ return ["python", "-m", "compileall", "-q", "."]
47
+
48
+ return None
49
+
50
+
51
+ def detect_lint_command(root: Path) -> list[str] | None:
52
+ pyproject = root / "pyproject.toml"
53
+ if pyproject.is_file():
54
+ try:
55
+ content = pyproject.read_text(encoding="utf-8", errors="replace")
56
+ if "[tool.ruff]" in content:
57
+ return ["python", "-m", "ruff", "check", "."]
58
+ except OSError:
59
+ pass
60
+
61
+ pkg = root / "package.json"
62
+ if pkg.is_file():
63
+ try:
64
+ data = json.loads(pkg.read_text(encoding="utf-8"))
65
+ if (data.get("scripts") or {}).get("lint"):
66
+ return ["npm", "run", "lint"]
67
+ except (OSError, json.JSONDecodeError):
68
+ pass
69
+
70
+ return None
71
+
72
+
73
+ def parse_pytest_summary(output: str) -> tuple[int | None, int | None, int | None]:
74
+ """Parse passed, failed, errors counts from pytest output summary."""
75
+ passed = None
76
+ failed = None
77
+ errors = None
78
+
79
+ p_match = re.search(r"(\d+)\s+passed", output)
80
+ if p_match:
81
+ passed = int(p_match.group(1))
82
+
83
+ f_match = re.search(r"(\d+)\s+failed", output)
84
+ if f_match:
85
+ failed = int(f_match.group(1))
86
+
87
+ e_match = re.search(r"(\d+)\s+error", output)
88
+ if e_match:
89
+ errors = int(e_match.group(1))
90
+
91
+ if passed is not None and failed is None:
92
+ failed = 0
93
+ if failed is not None and passed is None:
94
+ passed = 0
95
+
96
+ return passed, failed, errors
97
+
98
+
99
+ async def _step(command: list[str], executor: CommandExecutor) -> CommandOutcome:
100
+ try:
101
+ res = await executor.run(command, cwd=".")
102
+ tail = (res.stdout + "\n" + res.stderr)[-4000:].strip()
103
+ return CommandOutcome(
104
+ command=command,
105
+ exit_code=res.exit_code,
106
+ timed_out=res.timed_out,
107
+ duration_ms=round(res.duration_ms, 1),
108
+ output_tail=tail,
109
+ )
110
+ except CommandDeniedError as exc:
111
+ return CommandOutcome(
112
+ command=command,
113
+ exit_code=None,
114
+ timed_out=False,
115
+ output_tail=f"not run (command denied by policy: {exc})",
116
+ )
117
+
118
+
119
+ async def run_verification(
120
+ root: Path,
121
+ executor: CommandExecutor,
122
+ events: EventRecorder | None = None,
123
+ run_id: str | None = None,
124
+ include_build: bool = True,
125
+ include_lint: bool = True,
126
+ ) -> VerificationOutcome:
127
+ outcome = VerificationOutcome()
128
+
129
+ test_cmd = detect_test_command(root)
130
+ if test_cmd:
131
+ if events is not None:
132
+ events.record(EventType.TEST_STARTED, {"command": test_cmd}, run_id=run_id)
133
+ base = await _step(test_cmd, executor)
134
+ passed, failed, errors = parse_pytest_summary(base.output_tail)
135
+ outcome.tests = TestResults(
136
+ command=base.command,
137
+ exit_code=base.exit_code,
138
+ timed_out=base.timed_out,
139
+ duration_ms=base.duration_ms,
140
+ output_tail=base.output_tail,
141
+ passed=passed,
142
+ failed=failed,
143
+ errors=errors,
144
+ )
145
+ if events is not None:
146
+ events.record(
147
+ EventType.TEST_COMPLETED,
148
+ {
149
+ "exit_code": base.exit_code,
150
+ "passed": passed,
151
+ "failed": failed,
152
+ "timed_out": base.timed_out,
153
+ },
154
+ run_id=run_id,
155
+ )
156
+
157
+ if include_build:
158
+ build_cmd = detect_build_command(root)
159
+ if build_cmd:
160
+ base_build = await _step(build_cmd, executor)
161
+ outcome.build = BuildResults(
162
+ command=base_build.command,
163
+ exit_code=base_build.exit_code,
164
+ timed_out=base_build.timed_out,
165
+ duration_ms=base_build.duration_ms,
166
+ output_tail=base_build.output_tail,
167
+ )
168
+
169
+ if include_lint:
170
+ lint_cmd = detect_lint_command(root)
171
+ if lint_cmd:
172
+ outcome.lint = await _step(lint_cmd, executor)
173
+
174
+ return outcome
@@ -0,0 +1,15 @@
1
+ """Shared utilities."""
2
+
3
+ from forgex.utils.logging import configure_logging
4
+ from forgex.utils.paths import PathViolationError, find_project_root, resolve_in_workspace
5
+ from forgex.utils.redact import MASK, redact, redact_patterns
6
+
7
+ __all__ = [
8
+ "MASK",
9
+ "PathViolationError",
10
+ "configure_logging",
11
+ "find_project_root",
12
+ "redact",
13
+ "redact_patterns",
14
+ "resolve_in_workspace",
15
+ ]
@@ -0,0 +1,28 @@
1
+ """Internal logging for FORGEX.
2
+
3
+ Diagnostics go to stderr so stdout stays clean for --json and piped
4
+ output. Human-facing rendering lives in :mod:`forgex.cli.ui`.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import logging
10
+
11
+ _LEVELS = {
12
+ "debug": logging.DEBUG,
13
+ "info": logging.INFO,
14
+ "warning": logging.WARNING,
15
+ "error": logging.ERROR,
16
+ }
17
+
18
+
19
+ def configure_logging(level: str = "warning") -> None:
20
+ """Configure the root ``forgex`` logger with a stderr handler."""
21
+ logger = logging.getLogger("forgex")
22
+ handler = logging.StreamHandler()
23
+ handler.setFormatter(
24
+ logging.Formatter("%(asctime)s %(levelname)-7s %(name)s: %(message)s", "%H:%M:%S")
25
+ )
26
+ logger.handlers.clear()
27
+ logger.addHandler(handler)
28
+ logger.setLevel(_LEVELS.get(level.lower(), logging.WARNING))
forgex/utils/paths.py ADDED
@@ -0,0 +1,48 @@
1
+ """Workspace-safe path resolution.
2
+
3
+ Every filesystem operation performed by FORGEX tools must obtain its
4
+ target path through :func:`resolve_in_workspace`. This is a core security
5
+ boundary: agents must never touch paths outside the authorized workspace.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from pathlib import Path
11
+
12
+ from forgex.schemas.error import PathViolationError
13
+
14
+ FORGEX_DIR_NAME = ".forgex"
15
+
16
+ __all__ = ["PathViolationError", "find_project_root", "resolve_in_workspace"]
17
+
18
+
19
+ def resolve_in_workspace(workspace: str | Path, target: str | Path) -> Path:
20
+ """Resolve *target* inside *workspace* and refuse any escape.
21
+
22
+ Rules enforced:
23
+ * Relative targets are joined to the workspace.
24
+ * Absolute targets must already be inside the workspace.
25
+ * ``..`` traversal that would leave the workspace is rejected.
26
+ * Symlinks are resolved; links pointing outside the workspace are
27
+ rejected (containment is checked on the final real path).
28
+ * The workspace root itself is allowed (needed for listing).
29
+ """
30
+ workspace = Path(workspace).resolve()
31
+ candidate = Path(target)
32
+
33
+ resolved = candidate.resolve() if candidate.is_absolute() else (workspace / candidate).resolve()
34
+
35
+ if resolved != workspace and workspace not in resolved.parents:
36
+ msg = f"path escapes workspace: {target!r} -> {resolved}"
37
+ raise PathViolationError(msg)
38
+
39
+ return resolved
40
+
41
+
42
+ def find_project_root(start: str | Path | None = None) -> Path | None:
43
+ """Walk upward from *start* (default: cwd) looking for a .forgex directory."""
44
+ current = (Path(start) if start else Path.cwd()).resolve()
45
+ for candidate in (current, *current.parents):
46
+ if (candidate / FORGEX_DIR_NAME).is_dir():
47
+ return candidate
48
+ return None
forgex/utils/redact.py ADDED
@@ -0,0 +1,44 @@
1
+ """Secret redaction (spec sections 46, 75).
2
+
3
+ Secrets must never leak into logs, events, errors, or the database. Any
4
+ code formatting external text passes through redact() or redact_patterns()
5
+ with known secrets.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import re
11
+ from collections.abc import Iterable
12
+
13
+ MASK = "***REDACTED***"
14
+
15
+ _SECRET_PATTERNS: tuple[re.Pattern[str], ...] = (
16
+ re.compile(r"gsk_[A-Za-z0-9]{20,}"),
17
+ re.compile(r"sk-[A-Za-z0-9_-]{20,}"),
18
+ re.compile(r"AKIA[0-9A-Z]{16}"),
19
+ re.compile(r"ghp_[A-Za-z0-9]{30,}"),
20
+ re.compile(r"github_pat_[A-Za-z0-9_]{20,}"),
21
+ re.compile(r"glpat-[A-Za-z0-9_-]{20,}"),
22
+ re.compile(r"xox[baprs]-[A-Za-z0-9-]{10,}"),
23
+ re.compile(r"-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----"),
24
+ re.compile(
25
+ r"(?i)\b(api[_-]?key|secret|token|password|passwd)\b['\"]?\s*[:=]\s*['\"][^'\"\n]{8,}['\"]"
26
+ ),
27
+ )
28
+
29
+
30
+ def redact(text: str, secrets: Iterable[str | None]) -> str:
31
+ """Replace every occurrence of each secret with the mask."""
32
+ result = text
33
+ for secret in secrets:
34
+ if secret and len(secret) >= 4:
35
+ result = result.replace(secret, MASK)
36
+ return result
37
+
38
+
39
+ def redact_patterns(text: str) -> str:
40
+ """Scrub secret-looking content before it enters model context (§46).
41
+ Applied at every content-on-ramp: read_file, search, prefetch, context."""
42
+ for pattern in _SECRET_PATTERNS:
43
+ text = pattern.sub(MASK, text)
44
+ return text
@@ -0,0 +1,189 @@
1
+ Metadata-Version: 2.5
2
+ Name: forgex-cli
3
+ Version: 0.1.0
4
+ Summary: FORGEX — multi-agent AI software engineering. Plan. Build. Refine. Verify.
5
+ Project-URL: Homepage, https://github.com/B-max12/Forgex
6
+ Project-URL: Repository, https://github.com/B-max12/Forgex
7
+ Project-URL: Issues, https://github.com/B-max12/Forgex/issues
8
+ Project-URL: Changelog, https://github.com/B-max12/Forgex/blob/main/CHANGELOG.md
9
+ Project-URL: Documentation, https://github.com/B-max12/Forgex/tree/main/docs
10
+ Author: FORGEX Contributors
11
+ License: MIT
12
+ License-File: LICENSE
13
+ Keywords: agents,ai,automation,cli,groq,llm,openai,software-engineering
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Environment :: Console
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Topic :: Software Development
24
+ Classifier: Topic :: Software Development :: Code Generators
25
+ Requires-Python: >=3.12
26
+ Requires-Dist: click>=8.0
27
+ Requires-Dist: httpx>=0.27
28
+ Requires-Dist: prompt-toolkit>=3.0
29
+ Requires-Dist: pydantic>=2.7
30
+ Requires-Dist: rich>=13.7
31
+ Requires-Dist: tomli-w>=1.0
32
+ Requires-Dist: typer>=0.12
33
+ Provides-Extra: dev
34
+ Requires-Dist: build>=1.2; extra == 'dev'
35
+ Requires-Dist: mypy>=1.10; extra == 'dev'
36
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
37
+ Requires-Dist: pytest>=8.2; extra == 'dev'
38
+ Requires-Dist: ruff>=0.4; extra == 'dev'
39
+ Description-Content-Type: text/markdown
40
+
41
+ # FORGEX
42
+
43
+ **Plan. Build. Refine. Verify.**
44
+
45
+ FORGEX is a CLI-first autonomous AI software engineering system. Specialized
46
+ agents — **Architect**, **Builder**, **Expert Coder**, **Supervisor** —
47
+ collaborate on a real repository through permission-gated tools, Git
48
+ checkpoints, a deterministic verification harness, and a bounded self-healing
49
+ repair loop.
50
+
51
+ > FORGEX is not a chatbot and not a code generator: it is an AI software
52
+ > engineering *runtime* — with the safety rails that implies.
53
+
54
+ ```
55
+ request → Architect → research → plan → [ Builder → Expert Coder →
56
+ tests → Supervisor → PASS? done : repair ↺ ] (bounded by max_iterations)
57
+ ```
58
+
59
+ **Status: v0.1.0 alpha** — the full pipeline works end-to-end with any
60
+ OpenAI-compatible provider (Groq, OpenAI, local servers). Multi-model
61
+ ensemble review, Docker sandboxing, and a context-intelligence layer are
62
+ built in.
63
+
64
+ ## Installation
65
+
66
+ ### One-Line Install (Zero Environment Activation)
67
+
68
+ ```bash
69
+ curl -sSL https://raw.githubusercontent.com/B-max12/Forgex/main/install.sh | bash
70
+ ```
71
+ *(Or clone and run `./install.sh`)*
72
+
73
+ ### Via `pipx` or `uv tool`
74
+
75
+ ```bash
76
+ pipx install forgex-cli # once published to PyPI
77
+ # or directly from GitHub:
78
+ pipx install "git+https://github.com/B-max12/Forgex.git"
79
+ ```
80
+
81
+ ### From source (Developers):
82
+
83
+ ```bash
84
+ git clone https://github.com/B-max12/Forgex.git && cd Forgex
85
+ pip install -e ".[dev]"
86
+ ```
87
+
88
+ Python 3.12+. Optional: Git (checkpoints — strongly recommended),
89
+ Docker (sandboxing), ripgrep (faster search).
90
+
91
+ ## Quickstart
92
+
93
+ ### The Universal Way (Zero Learning Curve)
94
+
95
+ Simply run `forgex` and describe what you want in plain English:
96
+
97
+ ```bash
98
+ forgex
99
+ ```
100
+
101
+ ```text
102
+ forgex ❯ Build me a FastAPI Todo API with SQLite and pytest
103
+ ```
104
+
105
+ FORGEX handles the complete lifecycle automatically:
106
+ 1. **Auto-initializes** the project workspace and git repository if needed
107
+ 2. **Plans** architecture and task breakdown (Architect agent)
108
+ 3. **Implements** code and tests across tasks (Builder + Expert Coder)
109
+ 4. **Verifies** deterministically (tests, lint, typecheck)
110
+ 5. **Self-heals** and auto-repairs if any verification gates fail
111
+ 6. **Snapshots** a clean, reversible Git checkpoint on success!
112
+
113
+ Follow up naturally with conversational context:
114
+ ```text
115
+ forgex ❯ now add rate limiting to those endpoints
116
+ forgex ❯ fix the failing tests in users.py
117
+ ```
118
+
119
+ ### One-Shot CLI / CI/CD Mode
120
+
121
+ Run tasks directly from your shell without opening the REPL:
122
+
123
+ ```bash
124
+ forgex "Create a FastAPI Todo API with SQLite and pytest"
125
+ # Or:
126
+ forgex do "Add JWT authentication with refresh tokens"
127
+ ```
128
+
129
+ ### Granular Power User Commands
130
+
131
+ For granular manual control, individual pipeline stages remain available:
132
+
133
+ ```bash
134
+ forgex plan "Add caching layer" # run architect planning only
135
+ forgex build # execute existing plan tasks
136
+ forgex verify # run tests and lint deterministically
137
+ forgex fix # verify + repair loop without replanning
138
+ forgex checkpoint "Stable release" # snapshot Git state
139
+ forgex rollback # undo to previous checkpoint
140
+ ```
141
+
142
+ ## Commands
143
+
144
+ | Command | Purpose |
145
+ |---|---|
146
+ | `forgex init [DIR]` | initialize a project (state, events, SQLite index) |
147
+ | `forgex plan [REQUEST] [--dry-run]` | Architect: analysis → registry research → implementation plan |
148
+ | `forgex build [REQUEST] [--file F] [--resume] [--dry-run]` | full pipeline with the repair loop |
149
+ | `forgex fix` | verify + supervise + repair until green — no rebuild |
150
+ | `forgex run [-- CMD]` | run the project's start command (policy-gated) |
151
+ | `forgex task list / run ID` | inspect / execute single tasks (Builder + review) |
152
+ | `forgex review [TASK] [--base C]` | Expert Coder review — single model or ensemble |
153
+ | `forgex verify` | deterministic tests/build/lint — no LLM |
154
+ | `forgex supervise` | verification + LLM audit + quality gates → verdict |
155
+ | `forgex status / logs / diff` | project state, event stream, git diff |
156
+ | `forgex checkpoint / rollback` | Git checkpoints; reversible rollback |
157
+ | `forgex models [catalog\|health]` | bindings, capability catalog, live provider ping |
158
+ | `forgex context stats\|search` | context index and relevance retrieval |
159
+ | `forgex tools list\|run` | inspect / manually run agent tools |
160
+ | `forgex config show\|get\|set\|setup` | layered configuration |
161
+ | `forgex doctor [--ping]` | environment and provider health |
162
+
163
+ Every command supports `--json` and `--plain` (CI/SSH friendly).
164
+
165
+ ## Providers
166
+
167
+ | Type | Target | Notes |
168
+ |---|---|---|
169
+ | `groq` | Groq API | default binding: llama-3.3-70b-versatile |
170
+ | `openai_compatible` | OpenAI & any compatible endpoint | set base_url |
171
+ | `local` | Ollama, llama.cpp, vLLM, LM Studio | base_url required, keyless |
172
+
173
+ Provider-independent by design: per-agent model bindings, capability-aware
174
+ routing, retry with backoff, fallback chains, and multi-model ensemble
175
+ review (`[ensemble] enabled = true` — e.g. Groq + OpenAI reviewing the same
176
+ diff in parallel, findings corroborated before fixes are applied).
177
+
178
+ ## Security
179
+
180
+ - Agents act only through permission-gated tools; every path is workspace-confined; `.forgex/`, `.git/` and secret files are untouchable.
181
+ - Commands are argv-only (no shells), policy-filtered (deny list, optional allowlist, network approval), and — with `[sandbox] enabled = true` — run in hardened Docker containers (no network by default, host FS invisible).
182
+ - Secrets never enter prompts: env-only storage, sanitized child environments, pattern-based content scrubbing.
183
+ - Deterministic quality gates: no model verdict can pass a red test suite or an incomplete build.
184
+
185
+ Full model + residual risks: `docs/security.md`.
186
+
187
+ ## License
188
+
189
+ MIT
@@ -0,0 +1,115 @@
1
+ forgex/__init__.py,sha256=8Hu38mXfDi41oplmNAMNnTJlGm1gGGFBsqjIUItWP-Y,69
2
+ forgex/__main__.py,sha256=epF6ct9aMmoavj1WUyibBInERIleJKLpCyySZp2SCd8,104
3
+ forgex/project.py,sha256=6xRu_lEhlIqcCypeGBOQeTwnR46JlFW_FuTrllrRNos,7620
4
+ forgex/agents/__init__.py,sha256=VGgsL4k6C2g3H6Dj3ZHFIDJe1jhwzJxyVNFjC-jwXKM,1021
5
+ forgex/agents/architect.py,sha256=m7pzK4mGRdZS1j_EK7sFSXWGbdhULizFgSoScScHk7E,8249
6
+ forgex/agents/base.py,sha256=1tRtdirmo5U4WUxFKI-wZTqOmTUPKQHgYkK4nvkkkms,15187
7
+ forgex/agents/builder.py,sha256=nZZr3732XNRnx9OTb-Sd6X9Es0zHK7PgEQ0tNFs4Lnc,8854
8
+ forgex/agents/ensemble.py,sha256=2r1f3KU_sWNchwYppyIx77TDTGksZvUlHDCyL2c2uW0,10930
9
+ forgex/agents/expert_coder.py,sha256=aFme4fFDGPN7W5Ylp-domG9TAC5FXObSkgh0JPQRQpA,9149
10
+ forgex/agents/supervisor.py,sha256=7sWMeKIEmQZu8n2ZUkaR7rP8_Rw58F4kGe7JviFVb3Q,10238
11
+ forgex/cli/__init__.py,sha256=C63yWifzpA0IV7YWDatpAdrhoV8zjqxAKv0xMf09VdM,19
12
+ forgex/cli/app.py,sha256=iUVc0oIHqLxhnggUZGecxlTOJ4LcMoq7OfjnayAdS7w,8534
13
+ forgex/cli/build_views.py,sha256=a6REKszTvK5x41wuHRRrwFrQ8VG1RxL7frNNB3jwTEc,3855
14
+ forgex/cli/context_memory.py,sha256=Ql4EjzjnsO6pHDqJ6EIVKxn3F0llvP1xgCGAaBKZ-0g,2170
15
+ forgex/cli/intent.py,sha256=wcXs8WBxIEr5cK8eC-WqomRwtkejgAwBsiLwYvoVdLA,6388
16
+ forgex/cli/progress.py,sha256=c-MbILnDOD2fQWCDTK2goH_fz7dx4nDrmf0M6IC0ocM,3709
17
+ forgex/cli/repl.py,sha256=Xq-9kGHnLA5hnVj93pJfT_5ohcEBrEtmZJcIRbhq4cg,15601
18
+ forgex/cli/runtime.py,sha256=wCaw_lU9EPE-zA1TTyjXTe6WYbnxea9FuY7-5kuAgsk,7980
19
+ forgex/cli/session.py,sha256=Bs0Cu8le20JN0hhXLuBPPR_8L5Ev1chAJQlykqQqe-A,3335
20
+ forgex/cli/ui.py,sha256=fKf7SyA-h6WE7Dz-AxZCMFEhPNAaVAWodYRGXbtwhWo,3196
21
+ forgex/cli/commands/__init__.py,sha256=gQ6tnU0Rvm0-ESWFUBU-KDl5dpNOpUTG509hXOQQjwY,27
22
+ forgex/cli/commands/build.py,sha256=a5GgzFWnRnXRY06XAJbCCyyNlCRToONpxHnBowF-93U,2600
23
+ forgex/cli/commands/checkpoint.py,sha256=h8-nmQqtl1n3LzBVpuxE2QH-fTv80piTIuUy6v_pIIc,1528
24
+ forgex/cli/commands/config.py,sha256=g4cftES94Gxk6WzgiyyMEOr7jmgUVdHBPljp1ThU3wQ,8788
25
+ forgex/cli/commands/context.py,sha256=LMnR8y26LkyoFct7-h5AYIVF4DQZzk6oPt9Wjcuxxww,3532
26
+ forgex/cli/commands/diff.py,sha256=eS0RX5DZFS1ywT9LxxaP-B9ESrCouMrjWYeE-q3Bid4,1019
27
+ forgex/cli/commands/do_cmd.py,sha256=1fvmuRXKpFJYXOan3BFaHGD_-ZEB3Si7yhdwJKUP08w,1923
28
+ forgex/cli/commands/doctor.py,sha256=PkzrfOFvDhAPVvAbryYJeS8-CujEfAirRQO25MhmfRs,5994
29
+ forgex/cli/commands/fix.py,sha256=x0Q9MlNWQX3UWFkx0UI__2GoSjofVUCRMjZqWGsXqsc,1584
30
+ forgex/cli/commands/init.py,sha256=C9sAq_wo-e5npOTidSLO0u-NvzASL-DpGj-IXSN0iuI,1366
31
+ forgex/cli/commands/logs.py,sha256=gPzVb8qoXk2x9bXUGRmiXMXdEujoWUcuM8MSZFmabWA,3568
32
+ forgex/cli/commands/models.py,sha256=6UEZl9mShSHaenSfe8OZCiaslck09DJ0GuGIY6FaORI,5172
33
+ forgex/cli/commands/plan.py,sha256=pC6kKn_nZN8QVVQVF7FYHcnkJyhKzFYRHHyaV8IySPY,3704
34
+ forgex/cli/commands/review.py,sha256=pPy5wK2ajwFeUur7qzjMro4-2qab3RmN0doFoSN3h9A,2690
35
+ forgex/cli/commands/rollback.py,sha256=mVyJ_V_jhdCe1iGPSIYXGnRQ4z8VDVJIZgJzxUSXhbM,2791
36
+ forgex/cli/commands/run.py,sha256=MyNuyyUi-gPiHoztgvdKxjU1kbyRHFoSESJ3qpwaQ7A,2580
37
+ forgex/cli/commands/status.py,sha256=pliHRhCtwNS9U_hEMBqEEynENDGTvsrEvxrmXxLke-M,5484
38
+ forgex/cli/commands/supervise.py,sha256=xUH4DqMybNrH9iA4LA2WX2EfqibWQCvLtMiYewRwvPo,2162
39
+ forgex/cli/commands/task.py,sha256=A83c782eEc6tJ1XhCmX9ybBuLDWACJlW88OPfsn_140,4551
40
+ forgex/cli/commands/tools.py,sha256=QD64A2MvRzFGtyYARV1AWnZ8_k93-QtKphfDbROimbk,4357
41
+ forgex/cli/commands/verify.py,sha256=T3CurElaSVOMhvbxAwzc5RkEuoZGX-VwKDVnm1fOu-s,2355
42
+ forgex/config/__init__.py,sha256=yfn7mP0l54Ndqh2wEDdJahmt_jCpci8zPYB1LLLpY44,1097
43
+ forgex/config/loader.py,sha256=F28e5HSJFEUlJ_cnzuMiNVjMUBktvZ7vrRHjNqaaoEc,6213
44
+ forgex/config/schema.py,sha256=b11natC9BEHwkxBJWZYlrMXgJKzT2TGWdGM81arL4M8,4490
45
+ forgex/context/__init__.py,sha256=tYuaRQihCCL8YQiNkWwFvZIeOZYZTCAQAKfTcu0CrPc,254
46
+ forgex/context/chunker.py,sha256=Jt7OS2I2mtdg2dm7aLBdA_FqREyS23Dwl9s2yTHUNFk,3364
47
+ forgex/context/dependency_graph.py,sha256=cbRab1rFy7seijsMfwLCcLLzBXP0RxYVyR0oS_vF6iU,3957
48
+ forgex/context/indexer.py,sha256=H0lwwMv6_TkRNxN4paqGO5Qjn6DHE40z5jv5WyP19_A,5126
49
+ forgex/context/manager.py,sha256=Bkwd8WrHU2X-WtzGsZupL95bC1ktkhkAMbumHt0jWPw,5695
50
+ forgex/context/retriever.py,sha256=XGbTDGiU6DK0piFeO25tkiDxuMpgSgDrgvFTSNIYIfo,3348
51
+ forgex/models/__init__.py,sha256=ED7YeMCP9jFdoXfspP7zXWjLTX2OZtRy6BcKSzb3UPY,517
52
+ forgex/models/base.py,sha256=QGQrnUwPfIwsOlSOmTvJP_iPj0XsDUS1FML3ugyoFKk,2832
53
+ forgex/models/capabilities.py,sha256=avCWmlZy4nxreKoF8RfBSqIgORQihh1SyCQP0g7SEnw,681
54
+ forgex/models/factory.py,sha256=e-Vd7IHuk8Ga_kvDw3hWgUJ8amtuRnbmKbShvMAdc8k,2322
55
+ forgex/models/groq.py,sha256=HNv5VF_k_3uJQsoJMzL3t5RC5UyoGy3vHVT6RkkbDIE,526
56
+ forgex/models/local.py,sha256=naRfjGVrfpK_urNqnsfZUX8xwicvygiC-ZeANQ_K9sE,390
57
+ forgex/models/openai_compatible.py,sha256=jy_ao19M2Kh9tusr2dv8xWUxUf2QBg3nkm_RKIRXZwA,8747
58
+ forgex/models/registry.py,sha256=KRCdfcI8IvEQ8BIhCReYDneVHulpFkXcnoeWesj-Cgk,2406
59
+ forgex/models/router.py,sha256=fH6Xki4FjvMpFGpG3XK_mrZQZLYTx0a5bgJkCMRDZNc,12735
60
+ forgex/orchestration/__init__.py,sha256=-dTdrZXYL2dY3HED18GF2Oyaqt7qleqy610IgbbiStk,1039
61
+ forgex/orchestration/checkpoints.py,sha256=V-beUwx0AS26xSmL1tAuilMpJPbJwA8GnWiwcq3XnBE,3484
62
+ forgex/orchestration/engine.py,sha256=O2i6heSxL8M_0XWecyMBlPOzqY-wW7Lmk2S8ztLT1AQ,21065
63
+ forgex/orchestration/events.py,sha256=6EKqJYfYoInD5yhs9IAp2TGQ9wXulI4RptO42x-IMoI,3075
64
+ forgex/orchestration/lockfile.py,sha256=X_9ADh_dKLPMhUxdzKsG26eaWgD0BnwEq1Cd3n0Xz4g,2597
65
+ forgex/orchestration/retry.py,sha256=OPN8dQd47987ttZ-5mkIsHvBQgtdD8cep8ATgzY9mCU,2141
66
+ forgex/orchestration/state.py,sha256=lO24dJtBWEF8x7eDYpikA5Apl1EWqmBstkl9tVRPgXU,3101
67
+ forgex/orchestration/stores.py,sha256=vURGuGkmrJIyISBHfHx2G--s2apRlHaBvYcyOrIfgws,3212
68
+ forgex/orchestration/workflow.py,sha256=HINieGFQbHzl5pzlkFSut1k4cUu6nNfjrDjdNeTI2IQ,3149
69
+ forgex/prompts/__init__.py,sha256=NuAsFK03L8fgUbHmvVBjp32nU3ZHC9uqbZ3BlLAhZkE,713
70
+ forgex/prompts/architect.md,sha256=0imykrysITgy5W1BimhLqTTkvEXA5XjAixKAKqzBx8Q,1657
71
+ forgex/prompts/builder.md,sha256=1eA-3QMbQwCBjqitsZwcH33CegeGxhj0qC13YJAe7OA,1386
72
+ forgex/prompts/expert_coder.md,sha256=xqPtNj3swKX8F-SUvo-59YLXh-YaO4QwkMFRVw8Kmag,1447
73
+ forgex/prompts/reviewer.md,sha256=ao0Fz5sDYMIB55FwOw_vIN53tW_OiMGdFY4YSxMNlGc,999
74
+ forgex/prompts/supervisor.md,sha256=T_0w2pzcE5K6cmrqV5moDIY5ZTOelq7w7rFYyz2SLec,1729
75
+ forgex/research/__init__.py,sha256=wKx-9Nr-3XcyFIqJjaZOpOubG3CohLnFK1pUGz1sbIc,417
76
+ forgex/research/citations.py,sha256=fd99Xroppx69ZEfX5XKJpHrZvIHZ8PrE8Cg6-_fZHMU,613
77
+ forgex/research/engine.py,sha256=toak42FGXM4VUWb73pk_288FbGx6LQTm_qnZdViJWVc,2794
78
+ forgex/research/sources.py,sha256=8dqfRqcc-UbzaYnwIwmRfKXFBypCuRffk0_2BDkcYJc,4732
79
+ forgex/sandbox/__init__.py,sha256=RMlzPnnuT96vCAAveq9-JyQGFsCdaOEl9xCr-3X8zxI,870
80
+ forgex/sandbox/executor.py,sha256=zwUsu518Zc0QpUGuBsn3Wq8bxmNB7yBbT_eSAoM5UHU,12697
81
+ forgex/sandbox/permissions.py,sha256=7IvHZuqDnbcBoH7ptIAwMY5sQ1v9zEmr0fVpdd0NyoY,1931
82
+ forgex/sandbox/policies.py,sha256=MKhrwbZIBEi0OfELxfh68pG4FM4dov11WZZ_J_MTt5I,4276
83
+ forgex/schemas/__init__.py,sha256=OB-OpLibsph-OmR6mBMBqdDqUSdSx30yVE6C5DYxO-g,2191
84
+ forgex/schemas/agent.py,sha256=0ru2j0PbDuJrjJ5DiO35hQgxkkSNggeutG5rlcll7jU,3387
85
+ forgex/schemas/checkpoint.py,sha256=UrqisYGT24FdOwqa9tDlRN2YoNzp7YxhYXKn-28mCpE,474
86
+ forgex/schemas/error.py,sha256=wdUUnfz-h6fXlGYEJB-AqGGf7s-5YFLegFDo2VYNuK8,2779
87
+ forgex/schemas/event.py,sha256=vrKJX2BNT8H_UkQU-tXvHDHmN5uEk2LyvHX64iR1vhI,2340
88
+ forgex/schemas/plan.py,sha256=W2HFAAg8wyxtqxM8c65-BTnA4Lbp51tIa_PwKjSWv4E,2805
89
+ forgex/schemas/project.py,sha256=tWSFDALJmQ981Qbg9yppElf7JGAeFQqhzV8ea6S6Zos,2941
90
+ forgex/schemas/research.py,sha256=TGtnK6RFkhdl58HLLNYvlwKd2oYI3g-gs6WTvoWa4UE,770
91
+ forgex/schemas/result.py,sha256=6QXoMT9y0av2JOS5YN7Yv1Gk8L_53c9qYRgewrQ1eZs,1985
92
+ forgex/schemas/task.py,sha256=h3f2_QHMq2cyLtUybtd9wXGoZIbygDOSrkaJ85uyoCY,3651
93
+ forgex/storage/__init__.py,sha256=t886ETE2mAmmF2tf4q76ZP74LxV2WfNuOqAgNtJlByI,592
94
+ forgex/storage/database.py,sha256=WrutIrKtb3I80fjOE-OyIlLGXjlbJLCFXY-lF1NYhmg,1249
95
+ forgex/storage/migrations.py,sha256=hu47LK1sBUW8z-Tdj5OtB0QdvgPYoU1sUbRN7dB4K0Y,3961
96
+ forgex/storage/repositories.py,sha256=PsEhRS3tvohrIl0K9TmXigl59ZAG47XL2XbB2chxqOY,18503
97
+ forgex/tools/__init__.py,sha256=grhMS_lbiS2nMLXIDGUdgNK4rIF5qrjlM2yku1PpB7U,1996
98
+ forgex/tools/base.py,sha256=0EjtI3UMrLTfxn5z6lAFPDg5GkPI40FxGj0zJMLttng,6764
99
+ forgex/tools/dependencies.py,sha256=nmqWFGoB2nWc9ic60yIDwnMF4UoU2C9N-nyEAYgVyLU,5180
100
+ forgex/tools/filesystem.py,sha256=rWCZTQo-Raapr48KK1y1_Q3ADAtLKAWKDMv89Fv8rm0,10690
101
+ forgex/tools/git.py,sha256=mXI8AyYbGmhS-bqtXvFOtt2Q0jKp74Jo2NrzR6IPBFM,8169
102
+ forgex/tools/project.py,sha256=HqJeecfxh0Cml3vaxJpyKBox_sFANSHZG26F-3BCVXI,1387
103
+ forgex/tools/search.py,sha256=7HJNVTnACvmT4nqEAZTZ4RJWlgj_AY8mmHRG3rKmRbM,6980
104
+ forgex/tools/terminal.py,sha256=2hiQVchkZLPr_uO8gVIu8F3UY3UsGN7V_EZrnK0OQeo,2921
105
+ forgex/tools/testing.py,sha256=EHPW9IqOHubOkjv0kEwpry3-raTYRj2UogIZUB7cxak,3144
106
+ forgex/tools/verification.py,sha256=HgK5mlrffJuJpIyeRo73tdtClsa3Svn6lzhi6M1ECCs,5352
107
+ forgex/utils/__init__.py,sha256=TMSkuQnwBk254xYtqQvIpJLQlWjcYsaSvgTzPLZfz2M,397
108
+ forgex/utils/logging.py,sha256=vxSKfmQtdyQQcamqesLr1hlCaYP6zX-qPfj3s7vj3yM,801
109
+ forgex/utils/paths.py,sha256=BpGE8gxk4qZll-g6iiMJu_OWM6OFwAFXMuiFQNY3LcU,1777
110
+ forgex/utils/redact.py,sha256=13_AbyPz8wDyB_VPY6C5GFgSvzUesWO2zKgis4gije0,1468
111
+ forgex_cli-0.1.0.dist-info/METADATA,sha256=dtw77XXAx8Zss-33T_O_YiiCw8oaeWSrOI1akxb_6lY,7336
112
+ forgex_cli-0.1.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
113
+ forgex_cli-0.1.0.dist-info/entry_points.txt,sha256=iQeCcJqidt0rKckzw2pQEmXQkMb_ekLR7u6hloppViI,47
114
+ forgex_cli-0.1.0.dist-info/licenses/LICENSE,sha256=UE8dNWDqILNshGTpijfZLH3rabYi4cEHfdJ3Py3pLiQ,1081
115
+ forgex_cli-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.4
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ forgex = forgex.cli.app:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 FORGEX Contributors
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.