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.
- forgex/__init__.py +3 -0
- forgex/__main__.py +6 -0
- forgex/agents/__init__.py +36 -0
- forgex/agents/architect.py +233 -0
- forgex/agents/base.py +385 -0
- forgex/agents/builder.py +269 -0
- forgex/agents/ensemble.py +334 -0
- forgex/agents/expert_coder.py +283 -0
- forgex/agents/supervisor.py +303 -0
- forgex/cli/__init__.py +1 -0
- forgex/cli/app.py +232 -0
- forgex/cli/build_views.py +94 -0
- forgex/cli/commands/__init__.py +1 -0
- forgex/cli/commands/build.py +63 -0
- forgex/cli/commands/checkpoint.py +41 -0
- forgex/cli/commands/config.py +225 -0
- forgex/cli/commands/context.py +94 -0
- forgex/cli/commands/diff.py +32 -0
- forgex/cli/commands/do_cmd.py +50 -0
- forgex/cli/commands/doctor.py +175 -0
- forgex/cli/commands/fix.py +46 -0
- forgex/cli/commands/init.py +41 -0
- forgex/cli/commands/logs.py +102 -0
- forgex/cli/commands/models.py +144 -0
- forgex/cli/commands/plan.py +103 -0
- forgex/cli/commands/review.py +76 -0
- forgex/cli/commands/rollback.py +65 -0
- forgex/cli/commands/run.py +75 -0
- forgex/cli/commands/status.py +141 -0
- forgex/cli/commands/supervise.py +61 -0
- forgex/cli/commands/task.py +136 -0
- forgex/cli/commands/tools.py +116 -0
- forgex/cli/commands/verify.py +65 -0
- forgex/cli/context_memory.py +62 -0
- forgex/cli/intent.py +209 -0
- forgex/cli/progress.py +101 -0
- forgex/cli/repl.py +377 -0
- forgex/cli/runtime.py +233 -0
- forgex/cli/session.py +95 -0
- forgex/cli/ui.py +113 -0
- forgex/config/__init__.py +53 -0
- forgex/config/loader.py +189 -0
- forgex/config/schema.py +145 -0
- forgex/context/__init__.py +6 -0
- forgex/context/chunker.py +102 -0
- forgex/context/dependency_graph.py +110 -0
- forgex/context/indexer.py +161 -0
- forgex/context/manager.py +174 -0
- forgex/context/retriever.py +99 -0
- forgex/models/__init__.py +25 -0
- forgex/models/base.py +112 -0
- forgex/models/capabilities.py +21 -0
- forgex/models/factory.py +66 -0
- forgex/models/groq.py +13 -0
- forgex/models/local.py +10 -0
- forgex/models/openai_compatible.py +233 -0
- forgex/models/registry.py +81 -0
- forgex/models/router.py +332 -0
- forgex/orchestration/__init__.py +36 -0
- forgex/orchestration/checkpoints.py +117 -0
- forgex/orchestration/engine.py +573 -0
- forgex/orchestration/events.py +101 -0
- forgex/orchestration/lockfile.py +82 -0
- forgex/orchestration/retry.py +65 -0
- forgex/orchestration/state.py +89 -0
- forgex/orchestration/stores.py +90 -0
- forgex/orchestration/workflow.py +103 -0
- forgex/project.py +209 -0
- forgex/prompts/__init__.py +27 -0
- forgex/prompts/architect.md +18 -0
- forgex/prompts/builder.md +20 -0
- forgex/prompts/expert_coder.md +25 -0
- forgex/prompts/reviewer.md +11 -0
- forgex/prompts/supervisor.md +26 -0
- forgex/research/__init__.py +15 -0
- forgex/research/citations.py +16 -0
- forgex/research/engine.py +83 -0
- forgex/research/sources.py +154 -0
- forgex/sandbox/__init__.py +37 -0
- forgex/sandbox/executor.py +418 -0
- forgex/sandbox/permissions.py +67 -0
- forgex/sandbox/policies.py +172 -0
- forgex/schemas/__init__.py +101 -0
- forgex/schemas/agent.py +109 -0
- forgex/schemas/checkpoint.py +19 -0
- forgex/schemas/error.py +125 -0
- forgex/schemas/event.py +76 -0
- forgex/schemas/plan.py +84 -0
- forgex/schemas/project.py +92 -0
- forgex/schemas/research.py +25 -0
- forgex/schemas/result.py +68 -0
- forgex/schemas/task.py +118 -0
- forgex/storage/__init__.py +25 -0
- forgex/storage/database.py +40 -0
- forgex/storage/migrations.py +130 -0
- forgex/storage/repositories.py +492 -0
- forgex/tools/__init__.py +73 -0
- forgex/tools/base.py +212 -0
- forgex/tools/dependencies.py +137 -0
- forgex/tools/filesystem.py +322 -0
- forgex/tools/git.py +239 -0
- forgex/tools/project.py +39 -0
- forgex/tools/search.py +205 -0
- forgex/tools/terminal.py +81 -0
- forgex/tools/testing.py +100 -0
- forgex/tools/verification.py +174 -0
- forgex/utils/__init__.py +15 -0
- forgex/utils/logging.py +28 -0
- forgex/utils/paths.py +48 -0
- forgex/utils/redact.py +44 -0
- forgex_cli-0.1.0.dist-info/METADATA +189 -0
- forgex_cli-0.1.0.dist-info/RECORD +115 -0
- forgex_cli-0.1.0.dist-info/WHEEL +4 -0
- forgex_cli-0.1.0.dist-info/entry_points.txt +2 -0
- 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
|
forgex/utils/__init__.py
ADDED
|
@@ -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
|
+
]
|
forgex/utils/logging.py
ADDED
|
@@ -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,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.
|