sase-github 0.2.12__tar.gz → 0.2.14__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.
Files changed (48) hide show
  1. sase_github-0.2.14/.release-please-manifest.json +3 -0
  2. {sase_github-0.2.12 → sase_github-0.2.14}/CHANGELOG.md +14 -0
  3. {sase_github-0.2.12 → sase_github-0.2.14}/CLAUDE.md +6 -0
  4. {sase_github-0.2.12 → sase_github-0.2.14}/Justfile +7 -1
  5. {sase_github-0.2.12 → sase_github-0.2.14}/PKG-INFO +1 -1
  6. {sase_github-0.2.12 → sase_github-0.2.14}/pyproject.toml +1 -1
  7. sase_github-0.2.14/sase/sase.yml +22 -0
  8. {sase_github-0.2.12 → sase_github-0.2.14}/src/sase_github/workspace_plugin.py +9 -4
  9. {sase_github-0.2.12 → sase_github-0.2.14}/tests/test_workspace_plugin.py +20 -0
  10. sase_github-0.2.14/tools/require_tool_run +60 -0
  11. sase_github-0.2.12/.release-please-manifest.json +0 -3
  12. {sase_github-0.2.12 → sase_github-0.2.14}/.github/workflows/ci.yml +0 -0
  13. {sase_github-0.2.12 → sase_github-0.2.14}/.github/workflows/pr-title.yml +0 -0
  14. {sase_github-0.2.12 → sase_github-0.2.14}/.github/workflows/publish.yml +0 -0
  15. {sase_github-0.2.12 → sase_github-0.2.14}/.sase_beads/beads.db +0 -0
  16. {sase_github-0.2.12 → sase_github-0.2.14}/.sase_beads/config.json +0 -0
  17. {sase_github-0.2.12 → sase_github-0.2.14}/.sase_beads/issues.jsonl +0 -0
  18. {sase_github-0.2.12 → sase_github-0.2.14}/LICENSE +0 -0
  19. {sase_github-0.2.12 → sase_github-0.2.14}/README.md +0 -0
  20. {sase_github-0.2.12 → sase_github-0.2.14}/docs/architecture.md +0 -0
  21. {sase_github-0.2.12 → sase_github-0.2.14}/docs/configuration.md +0 -0
  22. {sase_github-0.2.12 → sase_github-0.2.14}/docs/xprompts.md +0 -0
  23. {sase_github-0.2.12 → sase_github-0.2.14}/release-please-config.json +0 -0
  24. {sase_github-0.2.12 → sase_github-0.2.14}/src/sase_github/__init__.py +0 -0
  25. {sase_github-0.2.12 → sase_github-0.2.14}/src/sase_github/config.py +0 -0
  26. {sase_github-0.2.12 → sase_github-0.2.14}/src/sase_github/default_config.yml +0 -0
  27. {sase_github-0.2.12 → sase_github-0.2.14}/src/sase_github/errors.py +0 -0
  28. {sase_github-0.2.12 → sase_github-0.2.14}/src/sase_github/plugin.py +0 -0
  29. {sase_github-0.2.12 → sase_github-0.2.14}/src/sase_github/scripts/__init__.py +0 -0
  30. {sase_github-0.2.12 → sase_github-0.2.14}/src/sase_github/scripts/gh_release.py +0 -0
  31. {sase_github-0.2.12 → sase_github-0.2.14}/src/sase_github/scripts/gh_setup.py +0 -0
  32. {sase_github-0.2.12 → sase_github-0.2.14}/src/sase_github/scripts/new_pr_desc_get_context.py +0 -0
  33. {sase_github-0.2.12 → sase_github-0.2.14}/src/sase_github/task_types.py +0 -0
  34. {sase_github-0.2.12 → sase_github-0.2.14}/src/sase_github/xprompts/gh.yml +0 -0
  35. {sase_github-0.2.12 → sase_github-0.2.14}/src/sase_github/xprompts/new_pr_desc.yml +0 -0
  36. {sase_github-0.2.12 → sase_github-0.2.14}/src/sase_github/xprompts/pr_diff.yml +0 -0
  37. {sase_github-0.2.12 → sase_github-0.2.14}/src/sase_github/xprompts/prdd.yml +0 -0
  38. {sase_github-0.2.12 → sase_github-0.2.14}/tests/__init__.py +0 -0
  39. {sase_github-0.2.12 → sase_github-0.2.14}/tests/test_ci_install_contract.py +0 -0
  40. {sase_github-0.2.12 → sase_github-0.2.14}/tests/test_config.py +0 -0
  41. {sase_github-0.2.12 → sase_github-0.2.14}/tests/test_gh_workspace_claims.py +0 -0
  42. {sase_github-0.2.12 → sase_github-0.2.14}/tests/test_github_issues.py +0 -0
  43. {sase_github-0.2.12 → sase_github-0.2.14}/tests/test_github_plugin.py +0 -0
  44. {sase_github-0.2.12 → sase_github-0.2.14}/tests/test_github_pull_requests.py +0 -0
  45. {sase_github-0.2.12 → sase_github-0.2.14}/tests/test_new_pr_desc_get_context.py +0 -0
  46. {sase_github-0.2.12 → sase_github-0.2.14}/tests/test_submit_with_recorded_pr.py +0 -0
  47. {sase_github-0.2.12 → sase_github-0.2.14}/tests/test_task_types.py +0 -0
  48. {sase_github-0.2.12 → sase_github-0.2.14}/uv.lock +0 -0
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.2.14"
3
+ }
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.14](https://github.com/sase-org/sase-github/compare/v0.2.13...v0.2.14) (2026-09-22)
4
+
5
+
6
+ ### Features
7
+
8
+ * **projects:** allocate canonical names case-insensitively ([fd5b7bd](https://github.com/sase-org/sase-github/commit/fd5b7bde2ac2e0785640df172e7e321275df406b))
9
+
10
+ ## [0.2.13](https://github.com/sase-org/sase-github/compare/v0.2.12...v0.2.13) (2026-09-22)
11
+
12
+
13
+ ### Features
14
+
15
+ * **tool:** add check catalog and recipe guard ([e807f82](https://github.com/sase-org/sase-github/commit/e807f823f6ee137c72503d8367d0a80543196b97))
16
+
3
17
  ## [0.2.12](https://github.com/sase-org/sase-github/compare/v0.2.11...v0.2.12) (2026-09-15)
4
18
 
5
19
 
@@ -23,3 +23,9 @@ just check # lint + test
23
23
  - Absolute imports: `from sase_github.plugin import GitHubPlugin`
24
24
  - Target Python 3.12+
25
25
  - Follow ruff rules matching sase core
26
+
27
+ ## sase tool runs
28
+
29
+ Agents run `sase tool run check` here, not bare `just check`: `check` is
30
+ guarded and a raw agent invocation is refused with the wrapped and bypass
31
+ forms. To run raw on purpose: `SASE_TOOL_BYPASS='<why>' just check`.
@@ -65,7 +65,13 @@ fmt: _setup
65
65
  test *args: _setup
66
66
  {{ venv_bin }}/pytest {{ args }}
67
67
 
68
- check: lint test
68
+ # Agents must run guarded tools through `sase tool run` (sase docs/tool.md).
69
+ # The guard is the first dependency, ahead of `_setup`, so a refusal costs
70
+ # milliseconds rather than a dependency sync.
71
+ _require-tool-run name:
72
+ @tools/require_tool_run {{ name }}
73
+
74
+ check: (_require-tool-run "check") lint test
69
75
 
70
76
  clean:
71
77
  rm -rf build/ dist/ *.egg-info src/*.egg-info .mypy_cache/ .ruff_cache/ .pytest_cache/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: sase-github
3
- Version: 0.2.12
3
+ Version: 0.2.14
4
4
  Summary: GitHub VCS plugin for sase
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sase-github"
7
- version = "0.2.12"
7
+ version = "0.2.14"
8
8
  description = "GitHub VCS plugin for sase"
9
9
  requires-python = ">=3.12"
10
10
  license = "MIT"
@@ -0,0 +1,22 @@
1
+ # Project-owned named tools (see sase docs/tool.md, "Guarded recipes").
2
+ # Complete entries: other config layers cannot alter this catalog.
3
+ tools:
4
+ check:
5
+ argv: [just, check]
6
+ description: Run the repository scoped check.
7
+ stages: none
8
+ inputs:
9
+ - Justfile
10
+ - pyproject.toml
11
+ - uv.lock
12
+ args: deny
13
+ fingerprint:
14
+ toolchain:
15
+ python: [.venv/bin/python, --version]
16
+ just: [just, --version]
17
+ sase-github:
18
+ [
19
+ .venv/bin/python,
20
+ -c,
21
+ "from importlib.metadata import version; print(version('sase-github'))",
22
+ ]
@@ -1505,12 +1505,17 @@ def _canonical_project_name_base(user: str, project: str) -> str:
1505
1505
 
1506
1506
 
1507
1507
  def _project_refs(records: Sequence[ProjectRecordWire]) -> set[str]:
1508
+ """Return folded project refs occupying the canonical-name namespace.
1509
+
1510
+ Refs compare case-insensitively, so a canonical name is never allocated
1511
+ as a case-variant duplicate of an existing key, name, or alias.
1512
+ """
1508
1513
  occupied: set[str] = set()
1509
1514
  for record in records:
1510
- occupied.add(record.project_name)
1515
+ occupied.add(record.project_name.casefold())
1511
1516
  if display_name := getattr(record, "display_name", None):
1512
- occupied.add(display_name)
1513
- occupied.update(record.aliases)
1517
+ occupied.add(display_name.casefold())
1518
+ occupied.update(alias.casefold() for alias in record.aliases)
1514
1519
  return occupied
1515
1520
 
1516
1521
 
@@ -1524,7 +1529,7 @@ def _allocate_canonical_project_name(
1524
1529
 
1525
1530
  candidate = base
1526
1531
  suffix = 2
1527
- while candidate in occupied:
1532
+ while candidate.casefold() in occupied:
1528
1533
  candidate = f"{base}-{suffix}"
1529
1534
  suffix += 1
1530
1535
  return candidate
@@ -1830,6 +1830,26 @@ class TestResolveGhRef:
1830
1830
  encoding="utf-8"
1831
1831
  )
1832
1832
 
1833
+ @patch(
1834
+ "sase_github.workspace_plugin.get_default_branch", return_value="origin/main"
1835
+ )
1836
+ def test_repo_path_suffixes_case_variant_canonical_project_name(
1837
+ self, mock_branch: MagicMock
1838
+ ) -> None:
1839
+ with tempfile.TemporaryDirectory() as d:
1840
+ home = Path(d)
1841
+ _github_workspace(home, "alice", "foo")
1842
+ _write_project(
1843
+ home,
1844
+ "GH_ALICE__FOO",
1845
+ "WORKSPACE_DIR: /some/other/path/\nNAME: other\n",
1846
+ )
1847
+ path_patch, env_patch = _home_patches(home)
1848
+ with path_patch, env_patch:
1849
+ result = resolve_gh_ref("alice/foo")
1850
+
1851
+ assert result.project_name == "gh_alice__foo-2"
1852
+
1833
1853
  @patch(
1834
1854
  "sase_github.workspace_plugin.get_default_branch", return_value="origin/main"
1835
1855
  )
@@ -0,0 +1,60 @@
1
+ #!/bin/sh
2
+ # Guard a Just recipe so SASE agents only run it through `sase tool run`.
3
+ #
4
+ # Usage: tools/require_tool_run <tool-name>
5
+ #
6
+ # Wired as the first dependency of each guarded recipe (see `check` and
7
+ # `check-full` in the Justfile), ahead of `_setup`, so a refusal costs
8
+ # milliseconds rather than a dependency sync. Contract: docs/tool.md
9
+ # ("Guarded recipes" table). Deliberately dependency-free POSIX `sh`: this
10
+ # must never start `sase`, because the override exists precisely for when
11
+ # `sase` is broken, and because every human and CI `just check` would
12
+ # otherwise pay a startup cost for logic that needs three variables.
13
+ #
14
+ # An agent may run a guarded recipe only from inside `sase tool run <that
15
+ # tool>` for that project root, or with an explicit bypass. This is a
16
+ # guardrail against habit, not a security boundary: `env -u SASE_AGENT`
17
+ # defeats it, and the bypass is deliberately easier and more visible.
18
+ set -u
19
+
20
+ tool=${1:-}
21
+ if [ -z "$tool" ]; then
22
+ printf 'sase: usage: tools/require_tool_run <tool-name>\n' >&2
23
+ exit 2
24
+ fi
25
+
26
+ # Humans, CI, finalizers, monitors, and procs run raw: only an agent's own
27
+ # shell carries SASE_AGENT.
28
+ if [ -z "${SASE_AGENT:-}" ]; then
29
+ exit 0
30
+ fi
31
+
32
+ # Inside `sase tool run <this tool>` for this project root. SASE_TOOL_NAME
33
+ # is exported whether or not recording succeeded, so the child of a
34
+ # fail-open run is still allowed. Ad-hoc runs export SASE_TOOL_NAME=ad-hoc
35
+ # with an empty root and can never match a guarded recipe's name.
36
+ if [ "${SASE_TOOL_NAME:-}" = "$tool" ] && [ "${SASE_TOOL_PROJECT_ROOT:-}" = "$(pwd -P)" ]; then
37
+ exit 0
38
+ fi
39
+
40
+ # Explicit bypass: any non-empty value runs raw on purpose, and the value
41
+ # is the reason. Keep the notice to one line even if the reason is not.
42
+ if [ -n "${SASE_TOOL_BYPASS:-}" ]; then
43
+ nl='
44
+ '
45
+ reason=${SASE_TOOL_BYPASS%%"$nl"*}
46
+ printf 'sase: bypassing the tool-run guard (SASE_TOOL_BYPASS=%s).\n' "$reason" >&2
47
+ exit 0
48
+ fi
49
+
50
+ # Fail open without `sase`: when the wrapper is unavailable there is no
51
+ # wrapped form to print, so allow the raw run and say why.
52
+ if ! command -v sase >/dev/null 2>&1; then
53
+ printf 'sase: allowing raw `just %s`: `sase` is not on PATH.\n' "$tool" >&2
54
+ exit 0
55
+ fi
56
+
57
+ printf 'sase: refusing raw `just %s` in an agent shell (SASE_AGENT is set and SASE_TOOL_NAME names a different tool or root).\n' "$tool" >&2
58
+ printf 'sase: run `sase tool run %s` instead.\n' "$tool" >&2
59
+ printf 'sase: or run `SASE_TOOL_BYPASS='"'"'<reason>'"'"' just %s` to bypass with a named reason.\n' "$tool" >&2
60
+ exit 2
@@ -1,3 +0,0 @@
1
- {
2
- ".": "0.2.12"
3
- }
File without changes
File without changes
File without changes