sous-mcp 0.1.0__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. sous_mcp-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +116 -0
  2. sous_mcp-0.1.0/.github/ISSUE_TEMPLATE/config.yml +7 -0
  3. sous_mcp-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +50 -0
  4. sous_mcp-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +28 -0
  5. sous_mcp-0.1.0/.github/dependabot.yml +15 -0
  6. sous_mcp-0.1.0/.github/workflows/ci.yml +131 -0
  7. sous_mcp-0.1.0/.github/workflows/publish-mcp-registry.yml +54 -0
  8. sous_mcp-0.1.0/.gitignore +5 -0
  9. sous_mcp-0.1.0/.python-version +1 -0
  10. sous_mcp-0.1.0/CLAUDE.md +49 -0
  11. sous_mcp-0.1.0/CONTRIBUTING.md +113 -0
  12. sous_mcp-0.1.0/LICENSE +21 -0
  13. sous_mcp-0.1.0/PKG-INFO +262 -0
  14. sous_mcp-0.1.0/README.md +238 -0
  15. sous_mcp-0.1.0/SECURITY.md +76 -0
  16. sous_mcp-0.1.0/docs/superpowers/plans/2026-08-14-sous.md +3146 -0
  17. sous_mcp-0.1.0/docs/superpowers/specs/2026-08-14-sous-design.md +197 -0
  18. sous_mcp-0.1.0/pyproject.toml +98 -0
  19. sous_mcp-0.1.0/scripts/e2e_smoke.py +80 -0
  20. sous_mcp-0.1.0/server.json +30 -0
  21. sous_mcp-0.1.0/skills/delegating-to-local/SKILL.md +70 -0
  22. sous_mcp-0.1.0/src/sous/__init__.py +0 -0
  23. sous_mcp-0.1.0/src/sous/cli.py +83 -0
  24. sous_mcp-0.1.0/src/sous/config.py +180 -0
  25. sous_mcp-0.1.0/src/sous/engine/__init__.py +0 -0
  26. sous_mcp-0.1.0/src/sous/engine/base.py +135 -0
  27. sous_mcp-0.1.0/src/sous/engine/lm.py +69 -0
  28. sous_mcp-0.1.0/src/sous/engine/vlm.py +71 -0
  29. sous_mcp-0.1.0/src/sous/protocol.py +259 -0
  30. sous_mcp-0.1.0/src/sous/server.py +292 -0
  31. sous_mcp-0.1.0/src/sous/tasks.py +300 -0
  32. sous_mcp-0.1.0/src/sous/toolexec.py +455 -0
  33. sous_mcp-0.1.0/src/sous/worker.py +388 -0
  34. sous_mcp-0.1.0/tests/__init__.py +0 -0
  35. sous_mcp-0.1.0/tests/fake_engine.py +22 -0
  36. sous_mcp-0.1.0/tests/test_cli.py +40 -0
  37. sous_mcp-0.1.0/tests/test_commands.py +442 -0
  38. sous_mcp-0.1.0/tests/test_config.py +211 -0
  39. sous_mcp-0.1.0/tests/test_confinement.py +345 -0
  40. sous_mcp-0.1.0/tests/test_engine_base.py +121 -0
  41. sous_mcp-0.1.0/tests/test_engine_lm.py +17 -0
  42. sous_mcp-0.1.0/tests/test_engine_unloaded.py +56 -0
  43. sous_mcp-0.1.0/tests/test_engine_vlm.py +17 -0
  44. sous_mcp-0.1.0/tests/test_protocol.py +271 -0
  45. sous_mcp-0.1.0/tests/test_server.py +336 -0
  46. sous_mcp-0.1.0/tests/test_tasks.py +281 -0
  47. sous_mcp-0.1.0/tests/test_worker.py +576 -0
  48. sous_mcp-0.1.0/uv.lock +1533 -0
@@ -0,0 +1,116 @@
1
+ name: Bug report
2
+ description: Something in sous behaves differently than documented
3
+ labels: ["bug"]
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: >-
8
+ Found a way out of the sandbox — writing outside `project_root`,
9
+ running a non-allowlisted command, leaking secrets past the env
10
+ scrub? Please close this and use
11
+ [private reporting](https://github.com/krcm0209/sous/security/advisories/new)
12
+ instead. See [SECURITY.md](https://github.com/krcm0209/sous/blob/main/SECURITY.md).
13
+
14
+ - type: checkboxes
15
+ id: prerequisites
16
+ attributes:
17
+ label: Prerequisites
18
+ options:
19
+ - label: I am on an Apple silicon Mac (sous does not run anywhere else)
20
+ required: true
21
+ - label: I searched existing issues
22
+ required: true
23
+
24
+ - type: input
25
+ id: version
26
+ attributes:
27
+ label: Which commit are you running?
28
+ description: >-
29
+ sous has no released versions yet, so the commit identifies the build:
30
+ run `git rev-parse --short HEAD` in the checkout you installed from.
31
+ placeholder: "02278fb"
32
+ validations:
33
+ required: true
34
+
35
+ - type: input
36
+ id: macos
37
+ attributes:
38
+ label: macOS version and chip
39
+ placeholder: "15.5, M4 Pro, 48 GB"
40
+ validations:
41
+ required: true
42
+
43
+ - type: input
44
+ id: python
45
+ attributes:
46
+ label: Python version
47
+ description: "`uv run python -V`"
48
+ placeholder: "3.14.7"
49
+ validations:
50
+ required: true
51
+
52
+ - type: dropdown
53
+ id: model
54
+ attributes:
55
+ label: Model
56
+ description: >-
57
+ Which backend loads depends on this — a model with a `vision_config`
58
+ runs through VLMEngine, everything else through LMEngine.
59
+ options:
60
+ - The default (mlx-community/Qwen3.8-27B-mxfp8)
61
+ - A different MLX model
62
+ - Not applicable — the bug does not involve the worker
63
+ validations:
64
+ required: true
65
+
66
+ - type: input
67
+ id: model_id
68
+ attributes:
69
+ label: Model id, if not the default
70
+ placeholder: "mlx-community/Qwen3-0.6B-4bit"
71
+
72
+ - type: textarea
73
+ id: what_happened
74
+ attributes:
75
+ label: What happened
76
+ description: >-
77
+ What you delegated, what you expected, and what sous did instead.
78
+ Include the exact task instructions if the worker misbehaved.
79
+ validations:
80
+ required: true
81
+
82
+ - type: textarea
83
+ id: reproduce
84
+ attributes:
85
+ label: Steps to reproduce
86
+ placeholder: |
87
+ 1. sous serve
88
+ 2. Ask Claude to delegate: "..."
89
+ 3. ...
90
+ validations:
91
+ required: true
92
+
93
+ - type: markdown
94
+ attributes:
95
+ value: >-
96
+ **Before pasting a transcript:** every worker turn is journaled, so
97
+ transcripts routinely contain files from the project sous was working
98
+ in. Please redact anything you would not publish.
99
+
100
+ - type: textarea
101
+ id: transcript
102
+ attributes:
103
+ label: Transcript excerpt (optional)
104
+ description: >-
105
+ Relevant lines from `~/.sous/tasks/<id>/transcript.jsonl`. The report
106
+ of a failed task includes its `transcript_path`.
107
+ render: text
108
+
109
+ - type: textarea
110
+ id: config
111
+ attributes:
112
+ label: Relevant config (optional)
113
+ description: >-
114
+ The parts of `~/.sous/config.toml` that matter — usually
115
+ `[commands] allowlist` for anything involving command execution.
116
+ render: toml
@@ -0,0 +1,7 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Report a security vulnerability
4
+ url: https://github.com/krcm0209/sous/security/advisories/new
5
+ about: >-
6
+ Sandbox escapes, allowlist bypasses, environment leaks and similar go
7
+ through private disclosure — please do not open a public issue for them.
@@ -0,0 +1,50 @@
1
+ name: Feature request
2
+ description: Suggest a capability or a change in behaviour
3
+ labels: ["enhancement"]
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: >-
8
+ Worth skimming
9
+ [what's in scope](https://github.com/krcm0209/sous/blob/main/CONTRIBUTING.md#whats-in-scope)
10
+ first — it saves writing up something the project has already decided
11
+ against.
12
+
13
+ - type: textarea
14
+ id: problem
15
+ attributes:
16
+ label: What problem are you hitting?
17
+ description: >-
18
+ The situation that prompted this, rather than the solution. What were
19
+ you trying to get sous to do?
20
+ validations:
21
+ required: true
22
+
23
+ - type: textarea
24
+ id: proposal
25
+ attributes:
26
+ label: What should sous do instead?
27
+ validations:
28
+ required: true
29
+
30
+ - type: textarea
31
+ id: alternatives
32
+ attributes:
33
+ label: Alternatives you considered (optional)
34
+ description: >-
35
+ Including anything you tried that nearly worked — a config change, a
36
+ different allowlist entry, a different model.
37
+
38
+ - type: checkboxes
39
+ id: scope
40
+ attributes:
41
+ label: Scope
42
+ options:
43
+ - label: >-
44
+ I understand sous deliberately targets Apple silicon, and that
45
+ running the worker elsewhere is not a current goal
46
+ required: true
47
+ - label: >-
48
+ This does not widen what a worker may do without human approval —
49
+ or if it does, I have said so explicitly above
50
+ required: true
@@ -0,0 +1,28 @@
1
+ <!--
2
+ CI already reports whether lint, types, and the fast test suite pass, so this
3
+ template only asks for the things it cannot check for you.
4
+ -->
5
+
6
+ ## What and why
7
+
8
+ <!-- What changed, and what problem it solves. The diff covers what; the why
9
+ is the part that is hard to recover later. -->
10
+
11
+ ## How you verified it
12
+
13
+ <!-- CI runs `pytest -m "not model"` and nothing else. The model-marked engine
14
+ tests and scripts/e2e_smoke.py never run there, so if you touched the
15
+ engine layer, this is the only place that evidence exists. Commands and
16
+ their output beat "tested locally". -->
17
+
18
+ ## Checklist
19
+
20
+ - [ ] Touched the engine layer (`src/sous/engine/`): ran `uv run pytest -m model`,
21
+ and/or `uv run python scripts/e2e_smoke.py`
22
+ - [ ] Touched the sandbox (`src/sous/toolexec.py`): included a test that fails
23
+ without this change
24
+ - [ ] Fixing something intermittent: ran it repeatedly rather than once, and
25
+ said how many times above
26
+ - [ ] Commit subject follows [Conventional Commits](https://www.conventionalcommits.org/)
27
+
28
+ <!-- Strike out or delete any line that does not apply. -->
@@ -0,0 +1,15 @@
1
+ # To get started with Dependabot version updates, you'll need to specify which
2
+ # package ecosystems to update and where the package manifests are located.
3
+ # Please see the documentation for all configuration options:
4
+ # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5
+
6
+ version: 2
7
+ updates:
8
+ - package-ecosystem: "uv"
9
+ directory: "/" # Location of package manifests
10
+ schedule:
11
+ interval: "weekly"
12
+ - package-ecosystem: "github-actions"
13
+ directory: "/" # Location of package manifests
14
+ schedule:
15
+ interval: "weekly"
@@ -0,0 +1,131 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ tags: ["v*"]
7
+ pull_request:
8
+ branches: [main]
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ # A newer push to the same branch/PR makes the in-flight run obsolete.
14
+ concurrency:
15
+ group: ${{ github.workflow }}-${{ github.ref }}
16
+ cancel-in-progress: true
17
+
18
+ env:
19
+ # Fail loudly if uv.lock and pyproject.toml have drifted, rather than
20
+ # silently re-resolving to versions nobody tested.
21
+ #
22
+ # UV_LOCKED, not UV_FROZEN: --frozen means "use the lockfile as-is without
23
+ # checking it", which downgrades `uv lock --check` to a validity-only check
24
+ # and lets real drift pass with exit 0. --locked asserts the lockfile is
25
+ # up to date and fails otherwise, for both `uv lock --check` and `uv sync`.
26
+ UV_LOCKED: "1"
27
+
28
+ # setup-uv is pinned to an exact release: it stopped publishing floating major
29
+ # tags after v7, so `@v10` does not resolve. Dependabot keeps it current.
30
+ jobs:
31
+ lint:
32
+ name: Lint & lockfile
33
+ runs-on: ubuntu-latest
34
+ steps:
35
+ - uses: actions/checkout@v7
36
+ - uses: astral-sh/setup-uv@v10.0.1
37
+ with:
38
+ enable-cache: true
39
+
40
+ - name: uv.lock is in sync with pyproject.toml
41
+ run: uv lock --check
42
+
43
+ # Linting needs only the pure-Python dev group. Skipping the project
44
+ # install keeps mlx (and therefore macOS) out of this job entirely.
45
+ - name: Install dev tooling
46
+ run: uv sync --only-group dev --no-install-project
47
+
48
+ - name: ruff check
49
+ run: uv run --no-sync ruff check --output-format=github .
50
+
51
+ - name: ruff format
52
+ run: uv run --no-sync ruff format --check .
53
+
54
+ test:
55
+ name: Tests
56
+ # Apple silicon: mlx-metal is sys_platform == 'darwin' gated, and this is
57
+ # the only platform sous actually targets.
58
+ runs-on: macos-15
59
+ steps:
60
+ - uses: actions/checkout@v7
61
+ - uses: astral-sh/setup-uv@v10.0.1
62
+ with:
63
+ enable-cache: true
64
+
65
+ - name: Install project and dev dependencies
66
+ run: uv sync
67
+
68
+ # The `model` marker needs a multi-GB MLX model download — local only.
69
+ # `slow` tests (process-group kills) do run here.
70
+ - name: pytest
71
+ run: uv run --no-sync pytest -m "not model"
72
+
73
+ typecheck:
74
+ name: Type check
75
+ runs-on: macos-15
76
+ steps:
77
+ - uses: actions/checkout@v7
78
+ - uses: astral-sh/setup-uv@v10.0.1
79
+ with:
80
+ enable-cache: true
81
+
82
+ # ty needs the real dependency types resolved, so this job installs
83
+ # the full environment rather than the dev group alone.
84
+ - name: Install project and dev dependencies
85
+ run: uv sync
86
+
87
+ - name: ty
88
+ run: uv run --no-sync ty check
89
+
90
+ build:
91
+ name: Build sdist + wheel
92
+ runs-on: macos-15
93
+ if: startsWith(github.ref, 'refs/tags/v')
94
+ needs: [lint, test, typecheck]
95
+ steps:
96
+ - uses: actions/checkout@v7
97
+ - uses: astral-sh/setup-uv@v10.0.1
98
+ with:
99
+ enable-cache: true
100
+
101
+ - name: uv build
102
+ run: uv build
103
+
104
+ - uses: actions/upload-artifact@v7
105
+ with:
106
+ name: dist
107
+ path: dist/
108
+ if-no-files-found: error
109
+
110
+ # Trusted publishing (OIDC): PyPI trusts this repo's `pypi` environment
111
+ # directly, so no API token exists anywhere to leak. Requires the
112
+ # trusted-publisher registration on pypi.org (project sous-mcp, owner
113
+ # krcm0209, repo sous, workflow ci.yml, environment pypi) BEFORE the
114
+ # first tag is pushed.
115
+ publish:
116
+ name: Publish to PyPI
117
+ runs-on: ubuntu-latest
118
+ if: startsWith(github.ref, 'refs/tags/v')
119
+ needs: [build]
120
+ environment:
121
+ name: pypi
122
+ url: https://pypi.org/p/sous-mcp
123
+ permissions:
124
+ id-token: write
125
+ steps:
126
+ - uses: actions/download-artifact@v8.0.1
127
+ with:
128
+ name: dist
129
+ path: dist/
130
+
131
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,54 @@
1
+ name: Publish to MCP Registry
2
+
3
+ # Deliberately manual (workflow_dispatch, never a tag trigger): listing in
4
+ # the official registry syndicates to Smithery, PulseMCP, and friends —
5
+ # i.e. it turns discovery ON. Run it from the Actions tab when the project
6
+ # is ready for that.
7
+ on:
8
+ workflow_dispatch:
9
+
10
+ permissions:
11
+ id-token: write # GitHub OIDC proves the io.github.krcm0209/* namespace
12
+ contents: read
13
+
14
+ jobs:
15
+ publish:
16
+ name: Publish server.json
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - uses: actions/checkout@v7
20
+
21
+ # The registry verifies ownership by reading the mcp-name marker out
22
+ # of the PyPI package's rendered README, so the exact version the
23
+ # entry references must already be live on PyPI — fail fast here
24
+ # instead of publishing a listing that points at nothing. The
25
+ # version-specific endpoint 404s for unpublished versions, which is
26
+ # the check; PyPI's latest-version field would wrongly reject a valid
27
+ # older release.
28
+ - name: Check server.json versions agree and are live on PyPI
29
+ run: |
30
+ declared=$(jq -r '.version' server.json)
31
+ pkgver=$(jq -r '.packages[0].version' server.json)
32
+ echo "server.json version: $declared / package version: $pkgver"
33
+ test "$declared" = "$pkgver"
34
+ curl -sf "https://pypi.org/pypi/sous-mcp/${pkgver}/json" > /dev/null \
35
+ || { echo "sous-mcp ${pkgver} is not live on PyPI"; exit 1; }
36
+
37
+ # Pinned and checksum-verified: this job can mint registry-publishing
38
+ # OIDC credentials, so it must never execute a mutable `latest`
39
+ # binary. Bump the pin deliberately; checksums come from the
40
+ # registry_<version>_checksums.txt asset on the same release.
41
+ - name: Install mcp-publisher (pinned, checksum-verified)
42
+ env:
43
+ MCP_PUBLISHER_VERSION: v1.8.1
44
+ MCP_PUBLISHER_SHA256: a06c9096dcb9727c13555b6be26c7effa707b01f06a4c561ba7a3635443cf2cc
45
+ run: |
46
+ curl -sfLO "https://github.com/modelcontextprotocol/registry/releases/download/${MCP_PUBLISHER_VERSION}/mcp-publisher_linux_amd64.tar.gz"
47
+ echo "${MCP_PUBLISHER_SHA256} mcp-publisher_linux_amd64.tar.gz" | sha256sum -c -
48
+ tar xzf mcp-publisher_linux_amd64.tar.gz mcp-publisher
49
+
50
+ - name: Authenticate to the MCP Registry (GitHub OIDC)
51
+ run: ./mcp-publisher login github-oidc
52
+
53
+ - name: Publish
54
+ run: ./mcp-publisher publish
@@ -0,0 +1,5 @@
1
+ __pycache__/
2
+ *.egg-info/
3
+ .venv/
4
+ dist/
5
+ .pytest_cache/
@@ -0,0 +1 @@
1
+ 3.14
@@ -0,0 +1,49 @@
1
+ # CLAUDE.md
2
+
3
+ sous is an MCP daemon that delegates mechanical coding tasks from Claude
4
+ Code to a sandboxed local MLX worker. macOS / Apple silicon only. The goal
5
+ is plan economics: volume output is generated locally for free so heavy
6
+ Claude Code use stretches further — evaluate features against that goal.
7
+
8
+ ## Commands
9
+
10
+ - `uv sync` — full setup (installs Python 3.14 and everything). Never pip.
11
+ - `uv run pytest -m "not model"` — the test suite. `model`-marked tests
12
+ download multi-GB weights and are local/manual only; `slow`-marked tests
13
+ spawn real processes and take seconds — run them, don't skip or mock them.
14
+ - `uv run ty check` — type check (ty, NOT mypy; covers tests and scripts too).
15
+ - `uv run ruff check . && uv run ruff format --check .` — lint/format.
16
+ - `uv lock --check` — lockfile sync. CI runs exactly these four jobs.
17
+ - `uv run python scripts/e2e_smoke.py` — agent loop against a tiny real model.
18
+
19
+ ## Gotchas
20
+
21
+ - Python >=3.14 required. `except A, B:` without parentheses (PEP 758, e.g.
22
+ src/sous/cli.py) is valid 3.14 syntax, not a Python 2 bug — don't "fix" it.
23
+ - Type-suppression pragmas are `# ty: ignore[rule]`, never `# type: ignore`.
24
+ - mlx / mlx_lm / mlx_vlm imports are deliberately function-local (absent on
25
+ non-macOS; the lint CI job runs on ubuntu; tests use fake engines). Don't
26
+ hoist them to module level.
27
+ - e2e_smoke.py often ends `failed` or `budget-exhausted` even when it worked —
28
+ the 0.6B model can't reliably emit `finish`. Judge by hello.txt content.
29
+ - Budget exhaustion is `done` with outcome `budget-exhausted`, never `failed`.
30
+ - Tests must never touch the real `~/.sous` — always pass tmp_path-based
31
+ config_path/data_dir.
32
+ - `docs/superpowers/**` are point-in-time design/plan records: never edit,
33
+ reformat, or "sync" them with current code (they are also ruff-excluded).
34
+
35
+ ## Security boundary
36
+
37
+ `src/sous/toolexec.py` is the sandbox (path confinement, command allowlist,
38
+ process-group kill, stat audit). Any change there needs a test that fails
39
+ without it. Odd-looking code is load-bearing (the un-reaped zombie during
40
+ the group kill, EPERM suppression, ctime in the audit) — read the comments
41
+ before touching. Suspected-flaky tests get run in a loop, not judged on one
42
+ pass.
43
+
44
+ ## Workflow
45
+
46
+ - Comments explain non-obvious *why*; never restate what code does.
47
+ - Conventional Commits (`feat:`/`fix:`/`docs:`/...), imperative lowercase
48
+ subject, *why* in the body.
49
+ - `main` is protected: branch + PR, all four CI jobs green.
@@ -0,0 +1,113 @@
1
+ # Contributing to sous
2
+
3
+ Thanks for looking. Contributions are welcome — bug reports especially.
4
+
5
+ A few things worth knowing before you spend time on this.
6
+
7
+ ## Before you start
8
+
9
+ **You need an Apple silicon Mac.** This is not a preference. `mlx-metal` is
10
+ `sys_platform == 'darwin'` gated, the worker runs on Metal, and CI itself runs
11
+ on macOS ARM runners. On any other machine you will not be able to run the
12
+ test suite, so there is no practical way to verify a change. Python 3.14 is
13
+ also required, though uv installs that for you.
14
+
15
+ **This is a single-maintainer project.** Reviews may take a while. If you are
16
+ planning anything beyond a focused fix, open an issue first — it is no fun to
17
+ write a large PR and then discover it conflicts with where the project is
18
+ going.
19
+
20
+ ## What's in scope
21
+
22
+ Good candidates: bug reports with a reproduction, focused fixes, clearer
23
+ documentation, additional test coverage — particularly around the sandbox.
24
+
25
+ Please open an issue before starting on: new tools exposed to the worker,
26
+ changes to the task lifecycle or MCP surface, or anything that widens what a
27
+ worker is permitted to do.
28
+
29
+ sous deliberately targets Apple silicon. Porting it to Linux or CUDA is not a
30
+ small PR, and is not currently a goal.
31
+
32
+ ## Setup
33
+
34
+ ```bash
35
+ uv sync
36
+ ```
37
+
38
+ That is the whole thing. uv resolves the interpreter and every dependency from
39
+ `uv.lock`.
40
+
41
+ ## Checks before you push
42
+
43
+ CI runs four jobs. You can reproduce all of them locally:
44
+
45
+ ```bash
46
+ uv run pytest -m "not model" # Tests
47
+ uv run ty check # Type check
48
+ uv run ruff check . && uv run ruff format --check . # Lint
49
+ uv lock --check # Lockfile in sync
50
+ ```
51
+
52
+ `ruff format .` (without `--check`) applies the formatting rather than just
53
+ reporting it.
54
+
55
+ Two suites do **not** run in CI, and are worth running yourself when you touch
56
+ the engine layer:
57
+
58
+ ```bash
59
+ uv run pytest -m model # engine tests; downloads real models
60
+ uv run python scripts/e2e_smoke.py # the full agent loop, tiny model
61
+ ```
62
+
63
+ `model`-marked tests are excluded from CI because they need multi-GB
64
+ downloads. `slow`-marked tests — the process-group kill tests, which use real
65
+ processes on purpose — *do* run in CI, so don't skip them locally.
66
+
67
+ ## Pull requests
68
+
69
+ `main` is protected, so work on a branch and open a PR. All four CI jobs must
70
+ be green.
71
+
72
+ Commit subjects follow [Conventional Commits](https://www.conventionalcommits.org/):
73
+ `feat:`, `fix:`, `docs:`, `chore:`, `ci:`, `test:`, `refactor:`. Keep the
74
+ subject imperative and lowercase after the type.
75
+
76
+ Explain *why* in the body, not just what — the diff already says what changed.
77
+ If a fix is subtle, say what you verified and how, especially for anything
78
+ timing-dependent.
79
+
80
+ ## Conventions
81
+
82
+ - **ruff** with `line-length = 100`; config lives in `pyproject.toml`.
83
+ - **ty** type-checks the whole repo, tests included. Test doubles that
84
+ deliberately implement only part of an interface use `cast`, with a comment
85
+ saying why — see `tests/test_worker.py`.
86
+ - **`docs/`** is excluded from ruff. The design spec and implementation plan
87
+ are point-in-time records; reformatting the Python inside their code blocks
88
+ rewrites history for no benefit.
89
+ - Match the comment density and style of the surrounding code. This codebase
90
+ explains non-obvious *reasoning* in comments and is fairly light on
91
+ restating what the code says.
92
+
93
+ ## Touching the sandbox
94
+
95
+ `src/sous/toolexec.py` is the security boundary: path confinement, the command
96
+ allowlist, and the process-group kill that stops a timed-out command's
97
+ descendants from writing files after the audit. The guarantees it makes are
98
+ described under [Security model](README.md#security-model).
99
+
100
+ Changes there need a test that fails without them, and will get a closer read.
101
+ Some of the behaviour is OS-level and genuinely unmockable — the existing
102
+ tests spawn real processes for that reason. If you are fixing something
103
+ intermittent, run the affected test in a loop before concluding it is fixed; a
104
+ single green run proves very little.
105
+
106
+ ## Questions
107
+
108
+ Open an issue.
109
+
110
+ For anything security-sensitive, please don't — use GitHub's private
111
+ vulnerability reporting instead (**Security** tab → **Report a vulnerability**),
112
+ so a live weakness isn't described in public while it is unfixed. See
113
+ [SECURITY.md](SECURITY.md).
sous_mcp-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 krcm0209
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.