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.
Files changed (61) hide show
  1. sindri_forge-0.3.2/.claude-plugin/marketplace.json +33 -0
  2. sindri_forge-0.3.2/.claude-plugin/plugin.json +21 -0
  3. sindri_forge-0.3.2/.github/workflows/ci.yml +48 -0
  4. sindri_forge-0.3.2/.github/workflows/publish.yml +68 -0
  5. sindri_forge-0.3.2/.github/workflows/release-please.yml +19 -0
  6. sindri_forge-0.3.2/.gitignore +33 -0
  7. sindri_forge-0.3.2/.markdownlint.json +8 -0
  8. sindri_forge-0.3.2/.python-version +1 -0
  9. sindri_forge-0.3.2/.release-please-manifest.json +3 -0
  10. sindri_forge-0.3.2/CHANGELOG.md +82 -0
  11. sindri_forge-0.3.2/PKG-INFO +110 -0
  12. sindri_forge-0.3.2/README.md +95 -0
  13. sindri_forge-0.3.2/commands/sindri.md +31 -0
  14. sindri_forge-0.3.2/docs/DOGFOOD.md +164 -0
  15. sindri_forge-0.3.2/docs/superpowers/plans/2026-04-19-sindri-plugin-artifacts.md +2163 -0
  16. sindri_forge-0.3.2/docs/superpowers/plans/2026-04-19-sindri-python-core.md +4251 -0
  17. sindri_forge-0.3.2/docs/superpowers/specs/2026-04-19-sindri-design.md +873 -0
  18. sindri_forge-0.3.2/docs/superpowers/specs/assets/sindri-architecture-v2.html +551 -0
  19. sindri_forge-0.3.2/prompts/experiment-subagent.md +107 -0
  20. sindri_forge-0.3.2/pyproject.toml +46 -0
  21. sindri_forge-0.3.2/release-please-config.json +19 -0
  22. sindri_forge-0.3.2/scripts/install-plugin.sh +56 -0
  23. sindri_forge-0.3.2/scripts/smoke.sh +60 -0
  24. sindri_forge-0.3.2/skills/sindri-finalize/SKILL.md +91 -0
  25. sindri_forge-0.3.2/skills/sindri-loop/SKILL.md +203 -0
  26. sindri_forge-0.3.2/skills/sindri-scaffold-benchmark/SKILL.md +139 -0
  27. sindri_forge-0.3.2/skills/sindri-start/SKILL.md +144 -0
  28. sindri_forge-0.3.2/src/sindri/__init__.py +11 -0
  29. sindri_forge-0.3.2/src/sindri/__main__.py +7 -0
  30. sindri_forge-0.3.2/src/sindri/cli.py +630 -0
  31. sindri_forge-0.3.2/src/sindri/core/__init__.py +0 -0
  32. sindri_forge-0.3.2/src/sindri/core/git_ops.py +86 -0
  33. sindri_forge-0.3.2/src/sindri/core/metric.py +53 -0
  34. sindri_forge-0.3.2/src/sindri/core/modes.py +86 -0
  35. sindri_forge-0.3.2/src/sindri/core/noise.py +67 -0
  36. sindri_forge-0.3.2/src/sindri/core/pool.py +72 -0
  37. sindri_forge-0.3.2/src/sindri/core/pr_body.py +119 -0
  38. sindri_forge-0.3.2/src/sindri/core/state.py +140 -0
  39. sindri_forge-0.3.2/src/sindri/core/termination.py +96 -0
  40. sindri_forge-0.3.2/src/sindri/core/validators.py +224 -0
  41. sindri_forge-0.3.2/tests/__init__.py +0 -0
  42. sindri_forge-0.3.2/tests/conftest.py +38 -0
  43. sindri_forge-0.3.2/tests/e2e/__init__.py +0 -0
  44. sindri_forge-0.3.2/tests/e2e/conftest.py +67 -0
  45. sindri_forge-0.3.2/tests/e2e/stub_subagent.py +118 -0
  46. sindri_forge-0.3.2/tests/e2e/test_loop_end_to_end.py +232 -0
  47. sindri_forge-0.3.2/tests/fixtures/__init__.py +0 -0
  48. sindri_forge-0.3.2/tests/integration/__init__.py +0 -0
  49. sindri_forge-0.3.2/tests/integration/test_cli.py +686 -0
  50. sindri_forge-0.3.2/tests/integration/test_git_ops.py +106 -0
  51. sindri_forge-0.3.2/tests/integration/test_state.py +143 -0
  52. sindri_forge-0.3.2/tests/unit/__init__.py +0 -0
  53. sindri_forge-0.3.2/tests/unit/test_metric.py +62 -0
  54. sindri_forge-0.3.2/tests/unit/test_modes.py +106 -0
  55. sindri_forge-0.3.2/tests/unit/test_noise.py +88 -0
  56. sindri_forge-0.3.2/tests/unit/test_plugin_artifacts.py +217 -0
  57. sindri_forge-0.3.2/tests/unit/test_pool.py +158 -0
  58. sindri_forge-0.3.2/tests/unit/test_pr_body.py +127 -0
  59. sindri_forge-0.3.2/tests/unit/test_termination.py +114 -0
  60. sindri_forge-0.3.2/tests/unit/test_validators.py +241 -0
  61. 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,8 @@
1
+ {
2
+ "default": true,
3
+ "MD013": false,
4
+ "MD032": false,
5
+ "MD033": false,
6
+ "MD041": false,
7
+ "MD060": false
8
+ }
@@ -0,0 +1 @@
1
+ 3.10
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.3.2"
3
+ }
@@ -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.