sindri-forge 0.3.2__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.
- sindri_forge-0.3.2/.claude-plugin/marketplace.json +33 -0
- sindri_forge-0.3.2/.claude-plugin/plugin.json +21 -0
- sindri_forge-0.3.2/.github/workflows/ci.yml +48 -0
- sindri_forge-0.3.2/.github/workflows/publish.yml +68 -0
- sindri_forge-0.3.2/.github/workflows/release-please.yml +19 -0
- sindri_forge-0.3.2/.gitignore +33 -0
- sindri_forge-0.3.2/.markdownlint.json +8 -0
- sindri_forge-0.3.2/.python-version +1 -0
- sindri_forge-0.3.2/.release-please-manifest.json +3 -0
- sindri_forge-0.3.2/CHANGELOG.md +82 -0
- sindri_forge-0.3.2/PKG-INFO +110 -0
- sindri_forge-0.3.2/README.md +95 -0
- sindri_forge-0.3.2/commands/sindri.md +31 -0
- sindri_forge-0.3.2/docs/DOGFOOD.md +164 -0
- sindri_forge-0.3.2/docs/superpowers/plans/2026-04-19-sindri-plugin-artifacts.md +2163 -0
- sindri_forge-0.3.2/docs/superpowers/plans/2026-04-19-sindri-python-core.md +4251 -0
- sindri_forge-0.3.2/docs/superpowers/specs/2026-04-19-sindri-design.md +873 -0
- sindri_forge-0.3.2/docs/superpowers/specs/assets/sindri-architecture-v2.html +551 -0
- sindri_forge-0.3.2/prompts/experiment-subagent.md +107 -0
- sindri_forge-0.3.2/pyproject.toml +46 -0
- sindri_forge-0.3.2/release-please-config.json +19 -0
- sindri_forge-0.3.2/scripts/install-plugin.sh +56 -0
- sindri_forge-0.3.2/scripts/smoke.sh +60 -0
- sindri_forge-0.3.2/skills/sindri-finalize/SKILL.md +91 -0
- sindri_forge-0.3.2/skills/sindri-loop/SKILL.md +203 -0
- sindri_forge-0.3.2/skills/sindri-scaffold-benchmark/SKILL.md +139 -0
- sindri_forge-0.3.2/skills/sindri-start/SKILL.md +144 -0
- sindri_forge-0.3.2/src/sindri/__init__.py +11 -0
- sindri_forge-0.3.2/src/sindri/__main__.py +7 -0
- sindri_forge-0.3.2/src/sindri/cli.py +630 -0
- sindri_forge-0.3.2/src/sindri/core/__init__.py +0 -0
- sindri_forge-0.3.2/src/sindri/core/git_ops.py +86 -0
- sindri_forge-0.3.2/src/sindri/core/metric.py +53 -0
- sindri_forge-0.3.2/src/sindri/core/modes.py +86 -0
- sindri_forge-0.3.2/src/sindri/core/noise.py +67 -0
- sindri_forge-0.3.2/src/sindri/core/pool.py +72 -0
- sindri_forge-0.3.2/src/sindri/core/pr_body.py +119 -0
- sindri_forge-0.3.2/src/sindri/core/state.py +140 -0
- sindri_forge-0.3.2/src/sindri/core/termination.py +96 -0
- sindri_forge-0.3.2/src/sindri/core/validators.py +224 -0
- sindri_forge-0.3.2/tests/__init__.py +0 -0
- sindri_forge-0.3.2/tests/conftest.py +38 -0
- sindri_forge-0.3.2/tests/e2e/__init__.py +0 -0
- sindri_forge-0.3.2/tests/e2e/conftest.py +67 -0
- sindri_forge-0.3.2/tests/e2e/stub_subagent.py +118 -0
- sindri_forge-0.3.2/tests/e2e/test_loop_end_to_end.py +232 -0
- sindri_forge-0.3.2/tests/fixtures/__init__.py +0 -0
- sindri_forge-0.3.2/tests/integration/__init__.py +0 -0
- sindri_forge-0.3.2/tests/integration/test_cli.py +686 -0
- sindri_forge-0.3.2/tests/integration/test_git_ops.py +106 -0
- sindri_forge-0.3.2/tests/integration/test_state.py +143 -0
- sindri_forge-0.3.2/tests/unit/__init__.py +0 -0
- sindri_forge-0.3.2/tests/unit/test_metric.py +62 -0
- sindri_forge-0.3.2/tests/unit/test_modes.py +106 -0
- sindri_forge-0.3.2/tests/unit/test_noise.py +88 -0
- sindri_forge-0.3.2/tests/unit/test_plugin_artifacts.py +217 -0
- sindri_forge-0.3.2/tests/unit/test_pool.py +158 -0
- sindri_forge-0.3.2/tests/unit/test_pr_body.py +127 -0
- sindri_forge-0.3.2/tests/unit/test_termination.py +114 -0
- sindri_forge-0.3.2/tests/unit/test_validators.py +241 -0
- sindri_forge-0.3.2/uv.lock +461 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "4kmetrics",
|
|
3
|
+
"owner": {
|
|
4
|
+
"name": "4KMetrics",
|
|
5
|
+
"email": "260103116+4kmetrics@users.noreply.github.com"
|
|
6
|
+
},
|
|
7
|
+
"metadata": {
|
|
8
|
+
"description": "Community plugins from 4KMetrics"
|
|
9
|
+
},
|
|
10
|
+
"plugins": [
|
|
11
|
+
{
|
|
12
|
+
"name": "sindri",
|
|
13
|
+
"source": "./",
|
|
14
|
+
"description": "Bounded, target-driven optimization loops for any metric you can measure deterministically. Autonomous experiments, kept-or-reverted per git commit, auto-PR on success.",
|
|
15
|
+
"author": {
|
|
16
|
+
"name": "Nimesh Kumar",
|
|
17
|
+
"email": "260103116+4kmetrics@users.noreply.github.com"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://github.com/4KMetrics/sindri",
|
|
20
|
+
"repository": "https://github.com/4KMetrics/sindri",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"keywords": [
|
|
23
|
+
"optimization",
|
|
24
|
+
"benchmarking",
|
|
25
|
+
"autonomous",
|
|
26
|
+
"experiments",
|
|
27
|
+
"performance",
|
|
28
|
+
"ci",
|
|
29
|
+
"bundle-size"
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sindri",
|
|
3
|
+
"description": "Bounded, target-driven optimization loops for any metric you can measure deterministically. Autonomous experiments, kept-or-reverted per git commit, auto-PR on success.",
|
|
4
|
+
"version": "0.3.2",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Nimesh Kumar",
|
|
7
|
+
"email": "260103116+4kmetrics@users.noreply.github.com"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/4KMetrics/sindri",
|
|
10
|
+
"repository": "https://github.com/4KMetrics/sindri",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"optimization",
|
|
14
|
+
"benchmarking",
|
|
15
|
+
"autonomous",
|
|
16
|
+
"experiments",
|
|
17
|
+
"performance",
|
|
18
|
+
"ci",
|
|
19
|
+
"bundle-size"
|
|
20
|
+
]
|
|
21
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
name: pytest (${{ matrix.python-version }})
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
with:
|
|
21
|
+
fetch-depth: 0
|
|
22
|
+
|
|
23
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
24
|
+
uses: actions/setup-python@v5
|
|
25
|
+
with:
|
|
26
|
+
python-version: ${{ matrix.python-version }}
|
|
27
|
+
cache: pip
|
|
28
|
+
|
|
29
|
+
- name: Configure git (required for git_ops + e2e)
|
|
30
|
+
run: |
|
|
31
|
+
git config --global user.email "ci@sindri.local"
|
|
32
|
+
git config --global user.name "Sindri CI"
|
|
33
|
+
|
|
34
|
+
- name: Install
|
|
35
|
+
run: |
|
|
36
|
+
python -m pip install --upgrade pip
|
|
37
|
+
pip install -e ".[dev]"
|
|
38
|
+
|
|
39
|
+
- name: Fast tests (unit + integration, skip e2e)
|
|
40
|
+
run: pytest -m "not e2e" -q
|
|
41
|
+
|
|
42
|
+
- name: End-to-end tests
|
|
43
|
+
if: matrix.python-version == '3.12'
|
|
44
|
+
run: pytest -m e2e -q
|
|
45
|
+
|
|
46
|
+
- name: Smoke
|
|
47
|
+
if: matrix.python-version == '3.12'
|
|
48
|
+
run: ./scripts/smoke.sh
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
name: publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_run:
|
|
5
|
+
workflows: ["release-please"]
|
|
6
|
+
types: [completed]
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
id-token: write # required for PyPI trusted publisher (OIDC)
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
pypi:
|
|
15
|
+
name: build + publish to PyPI
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
# workflow_run fires on every release-please completion (PR updates + real
|
|
18
|
+
# releases). Only proceed when the upstream run succeeded, and then let the
|
|
19
|
+
# release-tag check inside the job decide whether a real release happened.
|
|
20
|
+
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
with:
|
|
24
|
+
# Fetch full history + tags so we can detect release commits.
|
|
25
|
+
fetch-depth: 0
|
|
26
|
+
ref: ${{ github.event.workflow_run.head_sha || github.sha }}
|
|
27
|
+
|
|
28
|
+
- name: Detect release tag at HEAD
|
|
29
|
+
id: gate
|
|
30
|
+
run: |
|
|
31
|
+
set -euo pipefail
|
|
32
|
+
git fetch --tags --force
|
|
33
|
+
# HEAD is a release when the current commit has a matching v<semver> tag.
|
|
34
|
+
if TAG=$(git describe --tags --exact-match HEAD 2>/dev/null); then
|
|
35
|
+
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
|
|
36
|
+
echo "is_release=true" >> "$GITHUB_OUTPUT"
|
|
37
|
+
echo ">> HEAD is release tag: $TAG"
|
|
38
|
+
else
|
|
39
|
+
echo "is_release=false" >> "$GITHUB_OUTPUT"
|
|
40
|
+
echo ">> HEAD has no exact release tag — skipping publish."
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
- name: Set up Python
|
|
44
|
+
if: steps.gate.outputs.is_release == 'true'
|
|
45
|
+
uses: actions/setup-python@v5
|
|
46
|
+
with:
|
|
47
|
+
python-version: "3.12"
|
|
48
|
+
|
|
49
|
+
- name: Install build tooling
|
|
50
|
+
if: steps.gate.outputs.is_release == 'true'
|
|
51
|
+
run: |
|
|
52
|
+
python -m pip install --upgrade pip
|
|
53
|
+
python -m pip install build
|
|
54
|
+
|
|
55
|
+
- name: Build wheel + sdist
|
|
56
|
+
if: steps.gate.outputs.is_release == 'true'
|
|
57
|
+
run: python -m build
|
|
58
|
+
|
|
59
|
+
- name: Publish to PyPI
|
|
60
|
+
if: steps.gate.outputs.is_release == 'true'
|
|
61
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
62
|
+
# Uses PyPI trusted publishing — no API token needed.
|
|
63
|
+
# One-time setup on PyPI (https://pypi.org/manage/account/publishing/):
|
|
64
|
+
# PyPI Project Name: sindri-forge
|
|
65
|
+
# Owner: 4KMetrics
|
|
66
|
+
# Repository name: sindri
|
|
67
|
+
# Workflow name: publish.yml
|
|
68
|
+
# Environment name: (blank)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
name: release-please
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: write
|
|
9
|
+
pull-requests: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
release-please:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: googleapis/release-please-action@v4
|
|
16
|
+
with:
|
|
17
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
18
|
+
config-file: release-please-config.json
|
|
19
|
+
manifest-file: .release-please-manifest.json
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# sindri runtime state (always local, never committed)
|
|
2
|
+
.sindri/
|
|
3
|
+
|
|
4
|
+
# Claude Code / superpowers scratch
|
|
5
|
+
.superpowers/
|
|
6
|
+
.remember/
|
|
7
|
+
|
|
8
|
+
# Python
|
|
9
|
+
__pycache__/
|
|
10
|
+
*.py[cod]
|
|
11
|
+
*$py.class
|
|
12
|
+
.pytest_cache/
|
|
13
|
+
.ruff_cache/
|
|
14
|
+
.mypy_cache/
|
|
15
|
+
*.egg-info/
|
|
16
|
+
dist/
|
|
17
|
+
build/
|
|
18
|
+
.venv/
|
|
19
|
+
venv/
|
|
20
|
+
|
|
21
|
+
# OS
|
|
22
|
+
.DS_Store
|
|
23
|
+
Thumbs.db
|
|
24
|
+
|
|
25
|
+
# Editors
|
|
26
|
+
.vscode/
|
|
27
|
+
.idea/
|
|
28
|
+
*.swp
|
|
29
|
+
*.swo
|
|
30
|
+
|
|
31
|
+
# Environment
|
|
32
|
+
.env
|
|
33
|
+
.env.local
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.10
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.3.2](https://github.com/4KMetrics/sindri/compare/v0.3.1...v0.3.2) (2026-04-21)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* version from pyproject metadata; publish via workflow_run ([#12](https://github.com/4KMetrics/sindri/issues/12)) ([de7a1aa](https://github.com/4KMetrics/sindri/commit/de7a1aafda88692f75c2da73981cf1624461e639))
|
|
9
|
+
|
|
10
|
+
## [0.3.1](https://github.com/4KMetrics/sindri/compare/v0.3.0...v0.3.1) (2026-04-21)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* **ci:** rename PyPI dist to sindri-forge; publish on tag push ([#10](https://github.com/4KMetrics/sindri/issues/10)) ([432b50c](https://github.com/4KMetrics/sindri/commit/432b50c4247fae56bcaf17e8c5a0096cd8812871))
|
|
16
|
+
|
|
17
|
+
## [0.3.0](https://github.com/4KMetrics/sindri/compare/v0.2.0...v0.3.0) (2026-04-21)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Features
|
|
21
|
+
|
|
22
|
+
* **plugin:** add community marketplace manifest + metadata enrichment ([#7](https://github.com/4KMetrics/sindri/issues/7)) ([fff07c5](https://github.com/4KMetrics/sindri/commit/fff07c5dea056041c38011744ba8126e51c816fb))
|
|
23
|
+
|
|
24
|
+
## [0.2.0](https://github.com/4KMetrics/sindri/compare/v0.1.1...v0.2.0) (2026-04-21)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Features
|
|
28
|
+
|
|
29
|
+
* user-ready shipping (install script + dogfood + PyPI + pre-flight) ([#6](https://github.com/4KMetrics/sindri/issues/6)) ([37066ed](https://github.com/4KMetrics/sindri/commit/37066ed850e824149161d4ab10b0fa064b6567aa))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
### Bug Fixes
|
|
33
|
+
|
|
34
|
+
* **ci:** release-please feat commits should bump minor pre-1.0 ([#4](https://github.com/4KMetrics/sindri/issues/4)) ([db4fe94](https://github.com/4KMetrics/sindri/commit/db4fe94426353f81c9c4e2dba9587f1ad38be594))
|
|
35
|
+
|
|
36
|
+
## [0.1.1](https://github.com/4KMetrics/sindri/compare/v0.1.0...v0.1.1) (2026-04-20)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
### Features
|
|
40
|
+
|
|
41
|
+
* **cli:** add archive subcommand ([4d00181](https://github.com/4KMetrics/sindri/commit/4d001811da7d7f281161a28fc70ead9074b42651))
|
|
42
|
+
* **cli:** add check-termination subcommand ([c93ccdb](https://github.com/4KMetrics/sindri/commit/c93ccdb673ae2c7b52ac9408339dcec6ba211529))
|
|
43
|
+
* **cli:** add detect-mode subcommand ([d596cb3](https://github.com/4KMetrics/sindri/commit/d596cb3f6b84c67d867cd9f9242d6e90ff597d06))
|
|
44
|
+
* **cli:** add generate-pr-body subcommand ([e5e629c](https://github.com/4KMetrics/sindri/commit/e5e629cdcc139162e36f08a0541fe2faf85e1830))
|
|
45
|
+
* **cli:** add init subcommand for end-to-end run setup ([4b766f7](https://github.com/4KMetrics/sindri/commit/4b766f723b538c95c3bfe9419690785c95b76ad1))
|
|
46
|
+
* **cli:** add pick-next subcommand ([03c21bd](https://github.com/4KMetrics/sindri/commit/03c21bdbf53514cfbc7599039b0b03cf83585afe))
|
|
47
|
+
* **cli:** add read-state subcommand ([6b81247](https://github.com/4KMetrics/sindri/commit/6b81247d7e6b2b9956eaaa9279a7c66fb912f6cd))
|
|
48
|
+
* **cli:** add record-result subcommand ([11c3c39](https://github.com/4KMetrics/sindri/commit/11c3c39b6ab0e95c7867ed4ef8f980487adc62fc))
|
|
49
|
+
* **cli:** add status subcommand ([48dd080](https://github.com/4KMetrics/sindri/commit/48dd08057af680c88ff9ac309de4ef45e9519c13))
|
|
50
|
+
* **cli:** add validate-benchmark subcommand ([5a4eb0f](https://github.com/4KMetrics/sindri/commit/5a4eb0f01e1ecd1f7272e085291a1dc505e71491))
|
|
51
|
+
* **cli:** scaffold argparse dispatcher with subcommand registry ([f49b82f](https://github.com/4KMetrics/sindri/commit/f49b82f44135ef1331277c1fc18135d8033ef09d))
|
|
52
|
+
* **core:** add git subprocess wrappers ([444abe6](https://github.com/4KMetrics/sindri/commit/444abe65db19fcb6854ea3854f83011ab59ad82d))
|
|
53
|
+
* **core:** add local/remote mode detection ([7fa9710](https://github.com/4KMetrics/sindri/commit/7fa971016022543713ea4c06567b4dce6d6c9068))
|
|
54
|
+
* **core:** add METRIC line parser ([a0a325f](https://github.com/4KMetrics/sindri/commit/a0a325fcedd3e7fc9982a415ca172d51a9ba14a8))
|
|
55
|
+
* **core:** add noise floor, confidence ratio, CV stats ([fb2ddea](https://github.com/4KMetrics/sindri/commit/fb2ddeab446470cb1d0c62b3cfdbc4937d4bee6b))
|
|
56
|
+
* **core:** add pool ordering and termination predicates ([c96168a](https://github.com/4KMetrics/sindri/commit/c96168ab0aa8f91d3efa4ecb7aaf75eeeb833928))
|
|
57
|
+
* **core:** add PR body markdown renderer ([b532b3d](https://github.com/4KMetrics/sindri/commit/b532b3dc86f174ed8ca1e6c55238adf29d363e2c))
|
|
58
|
+
* **core:** add pydantic models for state, jsonl, subagent result ([e5d1308](https://github.com/4KMetrics/sindri/commit/e5d1308abe8cf80aadf27211348bbd5f7df3c437))
|
|
59
|
+
* **core:** add state.py for sindri.md and sindri.jsonl I/O ([bc3131d](https://github.com/4KMetrics/sindri/commit/bc3131d643447bd26ee36ce24351d46fab32be91))
|
|
60
|
+
* **core:** add termination decision table composing predicates ([6fc4f83](https://github.com/4KMetrics/sindri/commit/6fc4f830b0c93ace249d2cb508679142dc7ca96a))
|
|
61
|
+
* initial drop — design spec + Python core + CLI (Plan 1) ([10db076](https://github.com/4KMetrics/sindri/commit/10db076fd4d4026b3832b36dfae6cefe3f0b713d))
|
|
62
|
+
* **plugin:** sindri plugin artifacts (Plan 2) ([#2](https://github.com/4KMetrics/sindri/issues/2)) ([62cdb0f](https://github.com/4KMetrics/sindri/commit/62cdb0fa59f1aeeb558b8a456a62cd5250b55934))
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
### Bug Fixes
|
|
66
|
+
|
|
67
|
+
* **cli:** init fails fast on taken branch; goal regex uses fullmatch ([c92761f](https://github.com/4KMetrics/sindri/commit/c92761f7962e723af15f6755d649fe13f945d5af))
|
|
68
|
+
* **cli:** refuse unsafe slugs when archiving ([3b9b4e6](https://github.com/4KMetrics/sindri/commit/3b9b4e6d59b9f8de315dbbf94daf7e6665ff2451))
|
|
69
|
+
* code-review findings (StateIOError guards + wget localhost parity) ([6bd7257](https://github.com/4KMetrics/sindri/commit/6bd72578cf0a5c10a2cf390c9bbf681dbb8dce7c))
|
|
70
|
+
* **state:** surface malformed jsonl skips on stderr; clarify atomicity comment ([660b143](https://github.com/4KMetrics/sindri/commit/660b1432f44a63276584bfa546b9867a45db3054))
|
|
71
|
+
* **validators:** tz-aware started_at; None-sentinel mode-based defaults ([480ede3](https://github.com/4KMetrics/sindri/commit/480ede3e68246d3ca3969962432ca17671e0d24d))
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
### Documentation
|
|
75
|
+
|
|
76
|
+
* add initial design spec for sindri v1 ([f797819](https://github.com/4KMetrics/sindri/commit/f7978195ea9a0cff15aff22231d2aa951b133034))
|
|
77
|
+
* add Plan 1 — sindri Python core + CLI implementation ([6be79c6](https://github.com/4KMetrics/sindri/commit/6be79c603c66cd2093b75bc8a048b32aabbf3dcb))
|
|
78
|
+
* add Table of Contents to sindri design spec ([fcf2af9](https://github.com/4KMetrics/sindri/commit/fcf2af9f4b17b30a9da0af14403355219d21c6ec))
|
|
79
|
+
* clarify benchmark scaffolding is sindri's job, not a separate chat ([c4eadd5](https://github.com/4KMetrics/sindri/commit/c4eadd57f670a3342f62cd5941fd37457e610614))
|
|
80
|
+
* drop personal-roadmap framing from sindri spec ([f8ba7df](https://github.com/4KMetrics/sindri/commit/f8ba7dfd712e75b09f859d900e9267f6775cf7bb))
|
|
81
|
+
* replace ratchet metaphor with blacksmith-native language ([f5cc5b1](https://github.com/4KMetrics/sindri/commit/f5cc5b1d50349d1fd3eda4379f304fcac4be4b0b))
|
|
82
|
+
* switch user benchmark scripts from shell to Python ([eb647d0](https://github.com/4KMetrics/sindri/commit/eb647d0df634f6c1ba2ad3a0deb8dc4f2620fbf1))
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sindri-forge
|
|
3
|
+
Version: 0.3.2
|
|
4
|
+
Summary: Claude Code plugin core: bounded, target-driven optimization loops
|
|
5
|
+
Author: Nimesh Kumar
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Requires-Dist: pydantic<3.0,>=2.0
|
|
9
|
+
Provides-Extra: dev
|
|
10
|
+
Requires-Dist: pytest-cov>=4.1; extra == 'dev'
|
|
11
|
+
Requires-Dist: pytest-mock>=3.12; extra == 'dev'
|
|
12
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
13
|
+
Requires-Dist: pyyaml>=6.0; extra == 'dev'
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# sindri
|
|
17
|
+
|
|
18
|
+
A Claude Code plugin for bounded, target-driven optimization loops.
|
|
19
|
+
|
|
20
|
+
> In the myth, Sindri is the dwarf smith who forged Mjölnir by iterating under adversity — each hammer-strike tested, kept only if it survived. This plugin is the same pattern: make a change, benchmark it, keep it if it's better, revert otherwise, repeat until the target is hit or the pool of ideas is exhausted.
|
|
21
|
+
|
|
22
|
+
Inspired by [karpathy/autoresearch](https://github.com/karpathy/autoresearch) and [davebcn87/pi-autoresearch](https://github.com/davebcn87/pi-autoresearch), adapted to Claude Code.
|
|
23
|
+
|
|
24
|
+
**One commit per kept experiment. Git is the audit log. The forge runs one way.**
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
### Option 1 — Claude Code marketplace (easiest, once PyPI is up)
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
/plugin marketplace add 4KMetrics/sindri
|
|
32
|
+
/plugin install sindri@4kmetrics
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Then install the Python runtime once (the plugin calls `python -m sindri`):
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
uv pip install sindri-forge # or: pip install sindri-forge
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The PyPI distribution is `sindri-forge` (the `sindri` name was taken by an unrelated ZK-proof SDK). The imported module name is still `sindri`.
|
|
42
|
+
|
|
43
|
+
Restart Claude Code. Done.
|
|
44
|
+
|
|
45
|
+
### Option 2 — local dev install (no marketplace)
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
git clone https://github.com/4KMetrics/sindri.git ~/src/sindri
|
|
49
|
+
cd ~/src/sindri && ./scripts/install-plugin.sh
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The install script handles the Python package + the `~/.claude/plugins/sindri` symlink in one go.
|
|
53
|
+
|
|
54
|
+
That script installs the `sindri` Python package into your active environment and symlinks the plugin into `~/.claude/plugins/sindri`. Restart Claude Code afterward. Verify: `/sindri status` should report "no active run" cleanly.
|
|
55
|
+
|
|
56
|
+
To target a specific Python (e.g. a venv), pass it via env var:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
SINDRI_PYTHON=/path/to/venv/bin/python ./scripts/install-plugin.sh
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Requirements:
|
|
63
|
+
|
|
64
|
+
- Python ≥ 3.10 (pydantic 2 is the only runtime dep; pyyaml is dev-only)
|
|
65
|
+
- `git` ≥ 2.30
|
|
66
|
+
- `gh` CLI (authenticated) — needed only for `sindri-finalize` PR creation
|
|
67
|
+
- Claude Code CLI with `Task` + `ScheduleWakeup` tools
|
|
68
|
+
- `uv` (optional, faster install) or `pip`
|
|
69
|
+
|
|
70
|
+
**First-run dogfood:** walk through [`docs/DOGFOOD.md`](docs/DOGFOOD.md) on a throwaway repo before pointing sindri at real code — it verifies every skill path and catches environment gaps before you commit an overnight run.
|
|
71
|
+
|
|
72
|
+
## Quickstart
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# In the repo you want to optimize:
|
|
76
|
+
/sindri reduce bundle_bytes by 15%
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Sindri will:
|
|
80
|
+
|
|
81
|
+
1. Scaffold `.claude/scripts/sindri/benchmark.py` if missing (interactive; asks you to describe the measurement in natural language).
|
|
82
|
+
2. Scan your repo and draft a pool of 10–30 candidates.
|
|
83
|
+
3. Ask you to approve / edit the pool.
|
|
84
|
+
4. Check out a new branch `sindri/reduce-bundle-bytes-15pct` and run the baseline 3×.
|
|
85
|
+
5. Loop autonomously — one experiment per wakeup, ~60s between them.
|
|
86
|
+
6. When the target hits (or the pool drains with ≥1 win), push the branch and open a PR.
|
|
87
|
+
|
|
88
|
+
Check in anytime with `/sindri status`. Halt with `/sindri stop`. Abandon with `/sindri clear`.
|
|
89
|
+
|
|
90
|
+
## Architecture
|
|
91
|
+
|
|
92
|
+
- **Python core** (`src/sindri/`) — pure functions: statistics, state file I/O, git wrappers, pool ordering, termination predicates, PR body rendering.
|
|
93
|
+
- **Skills** (`skills/sindri-*/SKILL.md`) — prose prompts for the decisions that need Claude's inference: scaffolding a benchmark, proposing a candidate pool, orchestrating the loop.
|
|
94
|
+
- **Subagent prompt** (`prompts/experiment-subagent.md`) — the contract every experiment subagent follows. Fresh context per experiment, no context bleed.
|
|
95
|
+
|
|
96
|
+
See [`docs/superpowers/specs/2026-04-19-sindri-design.md`](docs/superpowers/specs/2026-04-19-sindri-design.md) for the full design.
|
|
97
|
+
|
|
98
|
+
## Development
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
uv pip install -e ".[dev]"
|
|
102
|
+
pytest # all test layers
|
|
103
|
+
pytest -m "not e2e" -q # skip the slow end-to-end loop test
|
|
104
|
+
pytest tests/unit/test_plugin_artifacts.py -v # just artifact validity checks
|
|
105
|
+
./scripts/smoke.sh # plumbing smoke in a throwaway repo
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## License
|
|
109
|
+
|
|
110
|
+
MIT
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# sindri
|
|
2
|
+
|
|
3
|
+
A Claude Code plugin for bounded, target-driven optimization loops.
|
|
4
|
+
|
|
5
|
+
> In the myth, Sindri is the dwarf smith who forged Mjölnir by iterating under adversity — each hammer-strike tested, kept only if it survived. This plugin is the same pattern: make a change, benchmark it, keep it if it's better, revert otherwise, repeat until the target is hit or the pool of ideas is exhausted.
|
|
6
|
+
|
|
7
|
+
Inspired by [karpathy/autoresearch](https://github.com/karpathy/autoresearch) and [davebcn87/pi-autoresearch](https://github.com/davebcn87/pi-autoresearch), adapted to Claude Code.
|
|
8
|
+
|
|
9
|
+
**One commit per kept experiment. Git is the audit log. The forge runs one way.**
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
### Option 1 — Claude Code marketplace (easiest, once PyPI is up)
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
/plugin marketplace add 4KMetrics/sindri
|
|
17
|
+
/plugin install sindri@4kmetrics
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Then install the Python runtime once (the plugin calls `python -m sindri`):
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
uv pip install sindri-forge # or: pip install sindri-forge
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The PyPI distribution is `sindri-forge` (the `sindri` name was taken by an unrelated ZK-proof SDK). The imported module name is still `sindri`.
|
|
27
|
+
|
|
28
|
+
Restart Claude Code. Done.
|
|
29
|
+
|
|
30
|
+
### Option 2 — local dev install (no marketplace)
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
git clone https://github.com/4KMetrics/sindri.git ~/src/sindri
|
|
34
|
+
cd ~/src/sindri && ./scripts/install-plugin.sh
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The install script handles the Python package + the `~/.claude/plugins/sindri` symlink in one go.
|
|
38
|
+
|
|
39
|
+
That script installs the `sindri` Python package into your active environment and symlinks the plugin into `~/.claude/plugins/sindri`. Restart Claude Code afterward. Verify: `/sindri status` should report "no active run" cleanly.
|
|
40
|
+
|
|
41
|
+
To target a specific Python (e.g. a venv), pass it via env var:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
SINDRI_PYTHON=/path/to/venv/bin/python ./scripts/install-plugin.sh
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Requirements:
|
|
48
|
+
|
|
49
|
+
- Python ≥ 3.10 (pydantic 2 is the only runtime dep; pyyaml is dev-only)
|
|
50
|
+
- `git` ≥ 2.30
|
|
51
|
+
- `gh` CLI (authenticated) — needed only for `sindri-finalize` PR creation
|
|
52
|
+
- Claude Code CLI with `Task` + `ScheduleWakeup` tools
|
|
53
|
+
- `uv` (optional, faster install) or `pip`
|
|
54
|
+
|
|
55
|
+
**First-run dogfood:** walk through [`docs/DOGFOOD.md`](docs/DOGFOOD.md) on a throwaway repo before pointing sindri at real code — it verifies every skill path and catches environment gaps before you commit an overnight run.
|
|
56
|
+
|
|
57
|
+
## Quickstart
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# In the repo you want to optimize:
|
|
61
|
+
/sindri reduce bundle_bytes by 15%
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Sindri will:
|
|
65
|
+
|
|
66
|
+
1. Scaffold `.claude/scripts/sindri/benchmark.py` if missing (interactive; asks you to describe the measurement in natural language).
|
|
67
|
+
2. Scan your repo and draft a pool of 10–30 candidates.
|
|
68
|
+
3. Ask you to approve / edit the pool.
|
|
69
|
+
4. Check out a new branch `sindri/reduce-bundle-bytes-15pct` and run the baseline 3×.
|
|
70
|
+
5. Loop autonomously — one experiment per wakeup, ~60s between them.
|
|
71
|
+
6. When the target hits (or the pool drains with ≥1 win), push the branch and open a PR.
|
|
72
|
+
|
|
73
|
+
Check in anytime with `/sindri status`. Halt with `/sindri stop`. Abandon with `/sindri clear`.
|
|
74
|
+
|
|
75
|
+
## Architecture
|
|
76
|
+
|
|
77
|
+
- **Python core** (`src/sindri/`) — pure functions: statistics, state file I/O, git wrappers, pool ordering, termination predicates, PR body rendering.
|
|
78
|
+
- **Skills** (`skills/sindri-*/SKILL.md`) — prose prompts for the decisions that need Claude's inference: scaffolding a benchmark, proposing a candidate pool, orchestrating the loop.
|
|
79
|
+
- **Subagent prompt** (`prompts/experiment-subagent.md`) — the contract every experiment subagent follows. Fresh context per experiment, no context bleed.
|
|
80
|
+
|
|
81
|
+
See [`docs/superpowers/specs/2026-04-19-sindri-design.md`](docs/superpowers/specs/2026-04-19-sindri-design.md) for the full design.
|
|
82
|
+
|
|
83
|
+
## Development
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
uv pip install -e ".[dev]"
|
|
87
|
+
pytest # all test layers
|
|
88
|
+
pytest -m "not e2e" -q # skip the slow end-to-end loop test
|
|
89
|
+
pytest tests/unit/test_plugin_artifacts.py -v # just artifact validity checks
|
|
90
|
+
./scripts/smoke.sh # plumbing smoke in a throwaway repo
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## License
|
|
94
|
+
|
|
95
|
+
MIT
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Start or manage a sindri optimization run — bounded, target-driven experiments on any deterministic metric.
|
|
3
|
+
argument-hint: <goal statement> | status | stop | scaffold-benchmark | clear
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /sindri — bounded optimization runs
|
|
7
|
+
|
|
8
|
+
You are the front-of-house router for sindri. The user's input after `/sindri` is in `$ARGUMENTS`. Dispatch based on the first token:
|
|
9
|
+
|
|
10
|
+
## Routing
|
|
11
|
+
|
|
12
|
+
| First token of `$ARGUMENTS` | Action |
|
|
13
|
+
|---|---|
|
|
14
|
+
| `status` | Run `python -m sindri status` and print its output verbatim. No skill invocation. Zero-cost read. |
|
|
15
|
+
| `stop` | `touch .sindri/current/HALT` (sentinel file — sindri-loop checks for it before every experiment and treats it as `halted_by_user` termination). Print `sindri: halt signal sent. The next wakeup will terminate without running another experiment.` |
|
|
16
|
+
| `scaffold-benchmark` | Invoke skill `sindri-scaffold-benchmark` (no active run required). |
|
|
17
|
+
| `clear` | Destructive. Read the branch name from `.sindri/current/sindri.md` JSON frontmatter **before** deleting anything. Prompt the user for confirmation (`y/N`). If confirmed: `rm -rf .sindri/current/ && git checkout main && git branch -D <branch>`. If not confirmed: print `aborted.` and stop. |
|
|
18
|
+
| anything else | Treat as a goal statement. If `.sindri/current/` exists, print a resume/abandon prompt and **do not** start a new run. Otherwise, invoke skill `sindri-start` with the goal statement. |
|
|
19
|
+
|
|
20
|
+
## Invariants
|
|
21
|
+
|
|
22
|
+
- **One run per repo.** Never start a new run while `.sindri/current/` exists.
|
|
23
|
+
- **Never mutate git state directly in this command.** Skills own that (except the `clear` branch delete, which is user-confirmed).
|
|
24
|
+
- **`status` must always be fast.** It reads files, nothing else.
|
|
25
|
+
- **`clear` always confirms first.** No silent deletion.
|
|
26
|
+
|
|
27
|
+
## After dispatch
|
|
28
|
+
|
|
29
|
+
For `status` and `stop`, you return to the user directly. For `scaffold-benchmark`, `clear`, and a new-goal invocation, control passes to the relevant skill and that skill produces the user-visible output.
|
|
30
|
+
|
|
31
|
+
For a brand-new goal, after `sindri-start` returns, sindri is running autonomously — `ScheduleWakeup` has been set. The orchestrator skill (`sindri-loop`) runs on every wakeup without further user action until termination. On clean termination with ≥1 kept commit, the loop auto-invokes `sindri-finalize` to push the branch and open the PR.
|