frankcode 0.1.0__tar.gz → 0.1.1__tar.gz
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.
- frankcode-0.1.1/.github/workflows/publish.yml +42 -0
- frankcode-0.1.1/.github/workflows/test.yml +33 -0
- frankcode-0.1.1/.gitignore +74 -0
- {frankcode-0.1.0/src/frankcode.egg-info → frankcode-0.1.1}/PKG-INFO +1 -1
- {frankcode-0.1.0 → frankcode-0.1.1}/pyproject.toml +5 -2
- {frankcode-0.1.0 → frankcode-0.1.1/src/frankcode.egg-info}/PKG-INFO +1 -1
- {frankcode-0.1.0 → frankcode-0.1.1}/src/frankcode.egg-info/SOURCES.txt +8 -1
- frankcode-0.1.1/tests/conftest.py +0 -0
- frankcode-0.1.1/tests/unit/test_environment.py +27 -0
- frankcode-0.1.1/tests/unit/test_setup.py +27 -0
- frankcode-0.1.1/tests/unit/test_watchdog.py +19 -0
- {frankcode-0.1.0 → frankcode-0.1.1}/LICENSE +0 -0
- {frankcode-0.1.0 → frankcode-0.1.1}/README.md +0 -0
- {frankcode-0.1.0 → frankcode-0.1.1}/setup.cfg +0 -0
- {frankcode-0.1.0 → frankcode-0.1.1}/src/frankcode/__init__.py +0 -0
- {frankcode-0.1.0 → frankcode-0.1.1}/src/frankcode/cli.py +0 -0
- {frankcode-0.1.0 → frankcode-0.1.1}/src/frankcode/configs/oh-my-opencode-slim.json +0 -0
- {frankcode-0.1.0 → frankcode-0.1.1}/src/frankcode/configs/opencode-verify.json +0 -0
- {frankcode-0.1.0 → frankcode-0.1.1}/src/frankcode/configs/opencode.json +0 -0
- {frankcode-0.1.0 → frankcode-0.1.1}/src/frankcode/configs/prompt-rules.json +0 -0
- {frankcode-0.1.0 → frankcode-0.1.1}/src/frankcode/environment.py +0 -0
- {frankcode-0.1.0 → frankcode-0.1.1}/src/frankcode/orchestrator.py +0 -0
- {frankcode-0.1.0 → frankcode-0.1.1}/src/frankcode/setup.py +0 -0
- {frankcode-0.1.0 → frankcode-0.1.1}/src/frankcode/utils.py +0 -0
- {frankcode-0.1.0 → frankcode-0.1.1}/src/frankcode.egg-info/dependency_links.txt +0 -0
- {frankcode-0.1.0 → frankcode-0.1.1}/src/frankcode.egg-info/entry_points.txt +0 -0
- {frankcode-0.1.0 → frankcode-0.1.1}/src/frankcode.egg-info/requires.txt +0 -0
- {frankcode-0.1.0 → frankcode-0.1.1}/src/frankcode.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: Publish to PyPI and GitHub Releases
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*' # Triggers when a new tag like v0.1.0 is pushed
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build-and-publish:
|
|
10
|
+
name: Build and publish
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
id-token: write # Mandatory for PyPI Trusted Publishing
|
|
15
|
+
contents: write # Mandatory for creating GitHub Releases
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
with:
|
|
20
|
+
fetch-depth: 0 # Needed for setuptools_scm to resolve version correctly
|
|
21
|
+
|
|
22
|
+
- name: Set up Python
|
|
23
|
+
uses: actions/setup-python@v5
|
|
24
|
+
with:
|
|
25
|
+
python-version: "3.12"
|
|
26
|
+
|
|
27
|
+
- name: Install build tool
|
|
28
|
+
run: python -m pip install --upgrade pip build
|
|
29
|
+
|
|
30
|
+
- name: Build package
|
|
31
|
+
run: python -m build
|
|
32
|
+
|
|
33
|
+
- name: Create GitHub Release
|
|
34
|
+
uses: softprops/action-gh-release@v2
|
|
35
|
+
with:
|
|
36
|
+
files: dist/*
|
|
37
|
+
generate_release_notes: true
|
|
38
|
+
|
|
39
|
+
- name: Publish package distributions to PyPI
|
|
40
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
41
|
+
# Note: PyPI publish will fail if you haven't set up Trusted Publishing on PyPI yet.
|
|
42
|
+
# If it fails, the GitHub Release will still be created!
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ "master", "main" ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ "master", "main" ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
cache: 'pip'
|
|
24
|
+
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
run: |
|
|
27
|
+
python -m pip install --upgrade pip
|
|
28
|
+
pip install pytest pytest-asyncio
|
|
29
|
+
pip install -e .
|
|
30
|
+
|
|
31
|
+
- name: Run tests
|
|
32
|
+
run: |
|
|
33
|
+
pytest
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.pycover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Environments
|
|
55
|
+
.env
|
|
56
|
+
.venv
|
|
57
|
+
env/
|
|
58
|
+
venv/
|
|
59
|
+
ENV/
|
|
60
|
+
env.bak/
|
|
61
|
+
venv.bak/
|
|
62
|
+
|
|
63
|
+
# opencode specific and frankcode runtime
|
|
64
|
+
.frankcode/
|
|
65
|
+
.opencode/
|
|
66
|
+
logs/
|
|
67
|
+
quarantine/
|
|
68
|
+
|
|
69
|
+
# Editors
|
|
70
|
+
.vscode/
|
|
71
|
+
.idea/
|
|
72
|
+
*.swp
|
|
73
|
+
*.swo
|
|
74
|
+
.DS_Store
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "frankcode"
|
|
3
|
-
|
|
3
|
+
dynamic = ["version"]
|
|
4
4
|
description = "Advanced multi-agent orchestrator for OpenCode"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -32,9 +32,12 @@ Issues = "https://github.com/joeyism/frankcode/issues"
|
|
|
32
32
|
frankcode = "frankcode.cli:app"
|
|
33
33
|
|
|
34
34
|
[build-system]
|
|
35
|
-
requires = ["setuptools>=61.0"]
|
|
35
|
+
requires = ["setuptools>=61.0", "setuptools_scm>=8.0"]
|
|
36
36
|
build-backend = "setuptools.build_meta"
|
|
37
37
|
|
|
38
|
+
[tool.setuptools_scm]
|
|
39
|
+
|
|
40
|
+
|
|
38
41
|
[tool.setuptools.package-data]
|
|
39
42
|
frankcode = ["configs/*.json"]
|
|
40
43
|
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
.gitignore
|
|
1
2
|
LICENSE
|
|
2
3
|
README.md
|
|
3
4
|
pyproject.toml
|
|
5
|
+
.github/workflows/publish.yml
|
|
6
|
+
.github/workflows/test.yml
|
|
4
7
|
src/frankcode/__init__.py
|
|
5
8
|
src/frankcode/cli.py
|
|
6
9
|
src/frankcode/environment.py
|
|
@@ -16,4 +19,8 @@ src/frankcode.egg-info/top_level.txt
|
|
|
16
19
|
src/frankcode/configs/oh-my-opencode-slim.json
|
|
17
20
|
src/frankcode/configs/opencode-verify.json
|
|
18
21
|
src/frankcode/configs/opencode.json
|
|
19
|
-
src/frankcode/configs/prompt-rules.json
|
|
22
|
+
src/frankcode/configs/prompt-rules.json
|
|
23
|
+
tests/conftest.py
|
|
24
|
+
tests/unit/test_environment.py
|
|
25
|
+
tests/unit/test_setup.py
|
|
26
|
+
tests/unit/test_watchdog.py
|
|
File without changes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import asyncio
|
|
3
|
+
from frankcode.environment import LocalEnvironment
|
|
4
|
+
|
|
5
|
+
@pytest.mark.asyncio
|
|
6
|
+
async def test_local_environment_exec_success():
|
|
7
|
+
env = LocalEnvironment()
|
|
8
|
+
result = await env.exec("echo 'hello world'")
|
|
9
|
+
assert result.stdout == "hello world"
|
|
10
|
+
assert result.exit_code == 0
|
|
11
|
+
|
|
12
|
+
@pytest.mark.asyncio
|
|
13
|
+
async def test_local_environment_exec_failure():
|
|
14
|
+
env = LocalEnvironment()
|
|
15
|
+
result = await env.exec("ls /non_existent_directory_for_frankcode_test")
|
|
16
|
+
assert result.exit_code != 0
|
|
17
|
+
assert "No such file or directory" in result.stderr
|
|
18
|
+
|
|
19
|
+
@pytest.mark.asyncio
|
|
20
|
+
async def test_local_environment_cwd(tmp_path):
|
|
21
|
+
env = LocalEnvironment()
|
|
22
|
+
# Create a file in a temp dir
|
|
23
|
+
test_file = tmp_path / "test.txt"
|
|
24
|
+
test_file.write_text("content")
|
|
25
|
+
|
|
26
|
+
result = await env.exec("ls", cwd=str(tmp_path))
|
|
27
|
+
assert "test.txt" in result.stdout
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import sys
|
|
3
|
+
from unittest.mock import AsyncMock, patch
|
|
4
|
+
from frankcode.setup import FrankSetup
|
|
5
|
+
|
|
6
|
+
@pytest.mark.asyncio
|
|
7
|
+
async def test_frank_setup_scaffolding(tmp_path):
|
|
8
|
+
# Mock environment to avoid real FS/network calls
|
|
9
|
+
env = AsyncMock()
|
|
10
|
+
setup = FrankSetup(env, str(tmp_path))
|
|
11
|
+
|
|
12
|
+
await setup.ensure_directories()
|
|
13
|
+
|
|
14
|
+
assert (tmp_path / ".frankcode").exists()
|
|
15
|
+
assert (tmp_path / ".frankcode" / "logs").exists()
|
|
16
|
+
assert (tmp_path / ".frankcode" / "quarantine").exists()
|
|
17
|
+
|
|
18
|
+
@pytest.mark.asyncio
|
|
19
|
+
async def test_frank_setup_os_detection():
|
|
20
|
+
env = AsyncMock()
|
|
21
|
+
setup = FrankSetup(env, "/tmp")
|
|
22
|
+
|
|
23
|
+
with patch("sys.platform", "linux"):
|
|
24
|
+
assert setup.get_os() == "linux"
|
|
25
|
+
|
|
26
|
+
with patch("sys.platform", "darwin"):
|
|
27
|
+
assert setup.get_os() == "macos"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import hashlib
|
|
3
|
+
from frankcode.utils import check_error_loop
|
|
4
|
+
|
|
5
|
+
def test_check_error_loop_detection():
|
|
6
|
+
# Same error multiple times
|
|
7
|
+
error_text = "AssertionError: 1 != 2"
|
|
8
|
+
history = [hashlib.md5(error_text.encode()).hexdigest()] * 3
|
|
9
|
+
|
|
10
|
+
# Should detect loop on 3rd identical hash
|
|
11
|
+
assert check_error_loop(history, threshold=3) is True
|
|
12
|
+
|
|
13
|
+
def test_check_error_loop_no_loop():
|
|
14
|
+
history = [
|
|
15
|
+
hashlib.md5("Error A".encode()).hexdigest(),
|
|
16
|
+
hashlib.md5("Error B".encode()).hexdigest(),
|
|
17
|
+
hashlib.md5("Error A".encode()).hexdigest()
|
|
18
|
+
]
|
|
19
|
+
assert check_error_loop(history, threshold=3) is False
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|