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.
- sous_mcp-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +116 -0
- sous_mcp-0.1.0/.github/ISSUE_TEMPLATE/config.yml +7 -0
- sous_mcp-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +50 -0
- sous_mcp-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +28 -0
- sous_mcp-0.1.0/.github/dependabot.yml +15 -0
- sous_mcp-0.1.0/.github/workflows/ci.yml +131 -0
- sous_mcp-0.1.0/.github/workflows/publish-mcp-registry.yml +54 -0
- sous_mcp-0.1.0/.gitignore +5 -0
- sous_mcp-0.1.0/.python-version +1 -0
- sous_mcp-0.1.0/CLAUDE.md +49 -0
- sous_mcp-0.1.0/CONTRIBUTING.md +113 -0
- sous_mcp-0.1.0/LICENSE +21 -0
- sous_mcp-0.1.0/PKG-INFO +262 -0
- sous_mcp-0.1.0/README.md +238 -0
- sous_mcp-0.1.0/SECURITY.md +76 -0
- sous_mcp-0.1.0/docs/superpowers/plans/2026-08-14-sous.md +3146 -0
- sous_mcp-0.1.0/docs/superpowers/specs/2026-08-14-sous-design.md +197 -0
- sous_mcp-0.1.0/pyproject.toml +98 -0
- sous_mcp-0.1.0/scripts/e2e_smoke.py +80 -0
- sous_mcp-0.1.0/server.json +30 -0
- sous_mcp-0.1.0/skills/delegating-to-local/SKILL.md +70 -0
- sous_mcp-0.1.0/src/sous/__init__.py +0 -0
- sous_mcp-0.1.0/src/sous/cli.py +83 -0
- sous_mcp-0.1.0/src/sous/config.py +180 -0
- sous_mcp-0.1.0/src/sous/engine/__init__.py +0 -0
- sous_mcp-0.1.0/src/sous/engine/base.py +135 -0
- sous_mcp-0.1.0/src/sous/engine/lm.py +69 -0
- sous_mcp-0.1.0/src/sous/engine/vlm.py +71 -0
- sous_mcp-0.1.0/src/sous/protocol.py +259 -0
- sous_mcp-0.1.0/src/sous/server.py +292 -0
- sous_mcp-0.1.0/src/sous/tasks.py +300 -0
- sous_mcp-0.1.0/src/sous/toolexec.py +455 -0
- sous_mcp-0.1.0/src/sous/worker.py +388 -0
- sous_mcp-0.1.0/tests/__init__.py +0 -0
- sous_mcp-0.1.0/tests/fake_engine.py +22 -0
- sous_mcp-0.1.0/tests/test_cli.py +40 -0
- sous_mcp-0.1.0/tests/test_commands.py +442 -0
- sous_mcp-0.1.0/tests/test_config.py +211 -0
- sous_mcp-0.1.0/tests/test_confinement.py +345 -0
- sous_mcp-0.1.0/tests/test_engine_base.py +121 -0
- sous_mcp-0.1.0/tests/test_engine_lm.py +17 -0
- sous_mcp-0.1.0/tests/test_engine_unloaded.py +56 -0
- sous_mcp-0.1.0/tests/test_engine_vlm.py +17 -0
- sous_mcp-0.1.0/tests/test_protocol.py +271 -0
- sous_mcp-0.1.0/tests/test_server.py +336 -0
- sous_mcp-0.1.0/tests/test_tasks.py +281 -0
- sous_mcp-0.1.0/tests/test_worker.py +576 -0
- 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 @@
|
|
|
1
|
+
3.14
|
sous_mcp-0.1.0/CLAUDE.md
ADDED
|
@@ -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.
|