zwischen 0.1.0
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.
- checksums.yaml +7 -0
- data/.zwischen.yml.example +49 -0
- data/CHANGELOG.md +21 -0
- data/DEVELOPMENT.md +154 -0
- data/README.md +207 -0
- data/TESTING.md +374 -0
- data/bin/zwischen +7 -0
- data/lib/zwischen/ai/analyzer.rb +121 -0
- data/lib/zwischen/ai/anthropic_client.rb +59 -0
- data/lib/zwischen/ai/base_client.rb +27 -0
- data/lib/zwischen/ai/ollama_client.rb +59 -0
- data/lib/zwischen/ai/openai_client.rb +56 -0
- data/lib/zwischen/cli.rb +225 -0
- data/lib/zwischen/config.rb +159 -0
- data/lib/zwischen/credentials.rb +68 -0
- data/lib/zwischen/finding/aggregator.rb +78 -0
- data/lib/zwischen/finding/finding.rb +85 -0
- data/lib/zwischen/git_diff.rb +74 -0
- data/lib/zwischen/hooks.rb +93 -0
- data/lib/zwischen/installer.rb +215 -0
- data/lib/zwischen/project_detector.rb +217 -0
- data/lib/zwischen/reporter/sarif.rb +115 -0
- data/lib/zwischen/reporter/terminal.rb +190 -0
- data/lib/zwischen/scanner/base.rb +89 -0
- data/lib/zwischen/scanner/gitleaks.rb +115 -0
- data/lib/zwischen/scanner/orchestrator.rb +99 -0
- data/lib/zwischen/scanner/semgrep.rb +78 -0
- data/lib/zwischen/setup.rb +167 -0
- data/lib/zwischen/version.rb +5 -0
- data/lib/zwischen.rb +29 -0
- data/zwischen.gemspec +34 -0
- metadata +145 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: beb48773e7ec58217010758614752399220a826e232e7af0c4b46a0ae603e304
|
|
4
|
+
data.tar.gz: 4bd6a34f9299e1e623843476523e13bd736c5c2c21c19b6e37029d9a63af2810
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 72d2fda3d959f4b2621e8011fb03359e70b08db713665f30bff2129a98b2c09f19d2243a191d2a07739dec397570eb13a1c924e9a2f4d4ff71331c94ede9df0a
|
|
7
|
+
data.tar.gz: 624e96024130b6ab369f2d137d294941808d98c331a6ec1f2ecb1830cdbd5a30750be67a5840d179fd05f7c8ca3b2196c2643a2e32aff2baea025fae2d5e47a7
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Zwischen Configuration
|
|
2
|
+
# Copy this file to .zwischen.yml in your project root
|
|
3
|
+
|
|
4
|
+
# AI Provider Configuration
|
|
5
|
+
ai:
|
|
6
|
+
enabled: true # Enable AI for manual 'zwischen scan'
|
|
7
|
+
pre_push_enabled: false # Disable AI in pre-push hooks (performance)
|
|
8
|
+
provider: claude # Options: claude, ollama, openai
|
|
9
|
+
|
|
10
|
+
# Provider-specific settings
|
|
11
|
+
ollama:
|
|
12
|
+
model: llama3
|
|
13
|
+
url: http://localhost:11434
|
|
14
|
+
|
|
15
|
+
openai:
|
|
16
|
+
model: gpt-4
|
|
17
|
+
|
|
18
|
+
claude:
|
|
19
|
+
model: claude-3-5-sonnet-20241022
|
|
20
|
+
|
|
21
|
+
# api_key: null # Leave null to use ~/.zwischen/credentials or env vars (ANTHROPIC_API_KEY, OPENAI_API_KEY)
|
|
22
|
+
|
|
23
|
+
# What blocks a push
|
|
24
|
+
blocking:
|
|
25
|
+
severity: high # block on high or critical (default)
|
|
26
|
+
# severity: critical # only block on critical
|
|
27
|
+
# severity: none # never block, just warn
|
|
28
|
+
|
|
29
|
+
# Scanner Configuration
|
|
30
|
+
scanners:
|
|
31
|
+
gitleaks: true # Auto-installed to ~/.zwischen/bin/ if missing
|
|
32
|
+
semgrep: true # Optional, install with: pip install semgrep
|
|
33
|
+
# Or use detailed format:
|
|
34
|
+
# gitleaks:
|
|
35
|
+
# enabled: true
|
|
36
|
+
# semgrep:
|
|
37
|
+
# enabled: true
|
|
38
|
+
# config: p/security-audit # Open ruleset, no login required
|
|
39
|
+
# # Other options: p/secrets, p/owasp-top-ten, or path to custom ruleset
|
|
40
|
+
|
|
41
|
+
# Ignored Paths — findings in matching paths are dropped before reporting.
|
|
42
|
+
|
|
43
|
+
ignore:
|
|
44
|
+
- "**/vendor/**"
|
|
45
|
+
- "**/node_modules/**"
|
|
46
|
+
- "**/.git/**"
|
|
47
|
+
- "**/dist/**"
|
|
48
|
+
- "**/build/**"
|
|
49
|
+
- "**/test/fixtures/**"
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-06-10
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Initial release
|
|
14
|
+
- Gitleaks and Semgrep scanner orchestration with auto-install
|
|
15
|
+
- AI-powered finding triage via Ollama, OpenAI, or Anthropic
|
|
16
|
+
- Pre-push git hook that blocks pushes introducing secrets or security issues
|
|
17
|
+
- Project type detection (Next.js, React, Django, Rails, and more)
|
|
18
|
+
- npm (`zwischen`) and pip (`zwischen-cli`) wrapper packages
|
|
19
|
+
|
|
20
|
+
[Unreleased]: https://github.com/cjordan223/zwischen/compare/v0.1.0...HEAD
|
|
21
|
+
[0.1.0]: https://github.com/cjordan223/zwischen/releases/tag/v0.1.0
|
data/DEVELOPMENT.md
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Development Guide
|
|
2
|
+
|
|
3
|
+
This repository contains three installable surfaces for the same product idea:
|
|
4
|
+
|
|
5
|
+
- `lib/zwischen`: the Ruby gem and canonical implementation.
|
|
6
|
+
- `packages/npm`: a Node.js wrapper package.
|
|
7
|
+
- `packages/pip`: a Python wrapper package.
|
|
8
|
+
|
|
9
|
+
When behavior differs, treat the Ruby gem as the source of truth unless the change is explicitly package-wrapper work.
|
|
10
|
+
|
|
11
|
+
## Ruby Command Flow
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
bin/zwischen
|
|
15
|
+
-> Zwischen::CLI
|
|
16
|
+
-> Config.load
|
|
17
|
+
-> ProjectDetector.detect
|
|
18
|
+
-> Scanner::Orchestrator
|
|
19
|
+
-> Scanner::Gitleaks
|
|
20
|
+
-> Scanner::Semgrep
|
|
21
|
+
-> Finding::Aggregator
|
|
22
|
+
-> AI::Analyzer, when enabled
|
|
23
|
+
-> Reporter::Terminal
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`zwischen init` follows a separate path:
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
CLI#init
|
|
30
|
+
-> Setup.run
|
|
31
|
+
-> Installer checks/installs Gitleaks
|
|
32
|
+
-> Credentials saves ANTHROPIC_API_KEY when present
|
|
33
|
+
-> Hooks installs .git/hooks/pre-push
|
|
34
|
+
-> Config.init creates .zwischen.yml
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`zwischen scan --pre-push` uses `GitDiff.changed_files`, passes those files to scanner adapters, filters findings to changed files again as a safety net, and only prints compact output for blocking findings.
|
|
38
|
+
|
|
39
|
+
## Config Contract
|
|
40
|
+
|
|
41
|
+
Project config lives at `.zwischen.yml`.
|
|
42
|
+
|
|
43
|
+
Current Ruby defaults:
|
|
44
|
+
|
|
45
|
+
- `ai.enabled: true`
|
|
46
|
+
- `ai.pre_push_enabled: false`
|
|
47
|
+
- `ai.provider: claude`
|
|
48
|
+
- `blocking.severity: high`
|
|
49
|
+
- Gitleaks and Semgrep enabled
|
|
50
|
+
- Semgrep config: `p/security-audit`
|
|
51
|
+
|
|
52
|
+
Credential lookup order is environment variables first, then `~/.zwischen/credentials`.
|
|
53
|
+
|
|
54
|
+
Supported Ruby credential environment variables:
|
|
55
|
+
|
|
56
|
+
- `ANTHROPIC_API_KEY` for `claude`
|
|
57
|
+
- `OPENAI_API_KEY` for `openai`
|
|
58
|
+
|
|
59
|
+
Local tool installs use `~/.zwischen/bin`.
|
|
60
|
+
|
|
61
|
+
## Common Change Checklists
|
|
62
|
+
|
|
63
|
+
Scanner changes:
|
|
64
|
+
|
|
65
|
+
- Add or edit a scanner under `lib/zwischen/scanner`.
|
|
66
|
+
- Return `Zwischen::Finding::Finding` objects with normalized `type`, `scanner`, `severity`, `file`, `line`, `message`, and `rule_id`.
|
|
67
|
+
- Register new scanner selection in `Scanner::Orchestrator`.
|
|
68
|
+
- Update `.zwischen.yml.example`, `README.md`, and relevant specs.
|
|
69
|
+
- Add or update scanner specs.
|
|
70
|
+
|
|
71
|
+
AI provider changes:
|
|
72
|
+
|
|
73
|
+
- Add a client under `lib/zwischen/ai`.
|
|
74
|
+
- Wire provider selection in `AI::Analyzer`.
|
|
75
|
+
- Add credential mapping in `Credentials` if the provider needs an API key.
|
|
76
|
+
- Update `.zwischen.yml.example`, `README.md`, and AI specs.
|
|
77
|
+
|
|
78
|
+
Hook changes:
|
|
79
|
+
|
|
80
|
+
- Update `Hooks` and `Setup`.
|
|
81
|
+
- Run installed-gem tests from `TESTING.md`.
|
|
82
|
+
- Update `TESTING.md` with exact hook behavior and bypass behavior.
|
|
83
|
+
|
|
84
|
+
Package-wrapper parity changes:
|
|
85
|
+
|
|
86
|
+
- Mirror command flags in `packages/npm/bin/zwischen.js` and `packages/pip/zwischen/cli.py`.
|
|
87
|
+
- Keep config key names aligned with `.zwischen.yml.example`.
|
|
88
|
+
- Update wrapper package READMEs.
|
|
89
|
+
|
|
90
|
+
## Known Iteration Points
|
|
91
|
+
|
|
92
|
+
- Ruby `Hooks.handle_existing_hook` has backup/append/skip logic, but `Setup#install_hook` currently backs up and replaces existing non-Zwischen hooks directly.
|
|
93
|
+
- Ruby config exposes `ignore` and `severity.fail_on`, but scanner adapters currently do not enforce ignore globs and blocking uses `blocking.severity`.
|
|
94
|
+
- npm and pip wrappers do not yet match Ruby feature parity. They do not support `uninstall`, `--only`, Ruby's changed-file pre-push filtering, or the Ruby JSON summary shape.
|
|
95
|
+
- npm and pip wrappers default AI provider to Ollama, while Ruby defaults to Claude.
|
|
96
|
+
|
|
97
|
+
## Verification
|
|
98
|
+
|
|
99
|
+
Ruby unit suite:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
bundle exec rspec
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Installed-gem workflow:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
./scripts/test_as_gem.sh
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Then follow `TESTING.md` from a temporary directory outside this repository.
|
|
112
|
+
|
|
113
|
+
npm wrapper smoke checks:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
cd packages/npm
|
|
117
|
+
npm test
|
|
118
|
+
node bin/zwischen.js --help
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
pip wrapper smoke checks:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
cd packages/pip
|
|
125
|
+
python -m pip install -e .
|
|
126
|
+
zwischen --help
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Releasing
|
|
130
|
+
|
|
131
|
+
Releases are automated by `.github/workflows/release.yml`, triggered by a
|
|
132
|
+
version tag:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
# bump lib/zwischen/version.rb, packages/npm/package.json,
|
|
136
|
+
# packages/pip/pyproject.toml, and CHANGELOG.md first
|
|
137
|
+
git tag v0.1.0
|
|
138
|
+
git push origin v0.1.0
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
One-time registry setup (first release only):
|
|
142
|
+
|
|
143
|
+
1. **RubyGems** — add a *pending trusted publisher* for the `zwischen` gem at
|
|
144
|
+
<https://rubygems.org/profile/oidc/pending_trusted_publishers>:
|
|
145
|
+
repository `cjordan223/zwischen`, workflow `release.yml`, environment `release`.
|
|
146
|
+
2. **PyPI** — add a *pending trusted publisher* for the `zwischen-cli` project at
|
|
147
|
+
<https://pypi.org/manage/account/publishing/> with the same repository,
|
|
148
|
+
workflow, and environment. (The bare `zwischen` name is taken on PyPI by an
|
|
149
|
+
unrelated package, so the distribution is `zwischen-cli`; the installed
|
|
150
|
+
command is still `zwischen`.)
|
|
151
|
+
3. **npm** — create an automation token at npmjs.com and save it as the
|
|
152
|
+
`NPM_TOKEN` repository secret.
|
|
153
|
+
4. **GitHub** — create a `release` environment in the repo settings
|
|
154
|
+
(Settings → Environments) so the OIDC claims match.
|
data/README.md
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Zwischen
|
|
2
|
+
|
|
3
|
+
[](https://github.com/cjordan223/zwischen/actions/workflows/ci.yml)
|
|
4
|
+
[](https://rubygems.org/gems/zwischen)
|
|
5
|
+
[](https://www.npmjs.com/package/zwischen)
|
|
6
|
+
[](https://pypi.org/project/zwischen-cli/)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
**Zwischen blocks `git push` the moment you're about to leak a secret** — the
|
|
12
|
+
last point where a leaked credential is still a local problem instead of an
|
|
13
|
+
incident. It orchestrates [Gitleaks](https://github.com/gitleaks/gitleaks)
|
|
14
|
+
and [Semgrep](https://semgrep.dev), normalizes their findings, and (optionally)
|
|
15
|
+
asks an AI provider — including fully local models via Ollama — to prioritize
|
|
16
|
+
the results, flag false positives, and suggest fixes.
|
|
17
|
+
|
|
18
|
+
One command sets everything up:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
zwischen init # installs gitleaks if missing, creates config, installs the pre-push hook
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Try it on a deliberately vulnerable app
|
|
25
|
+
|
|
26
|
+
The [zwischen-demo](https://github.com/cjordan223/zwischen-demo) repository
|
|
27
|
+
is a small Express app seeded with fake secrets and real vulnerability
|
|
28
|
+
patterns. Clone it and watch a push get blocked in under a minute.
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
gem install zwischen # canonical implementation (Ruby)
|
|
34
|
+
npm install -g zwischen # Node wrapper
|
|
35
|
+
pip install zwischen-cli # Python wrapper (command is still `zwischen`)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
For local development from this repository:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
bundle install
|
|
42
|
+
bundle exec ruby -Ilib bin/zwischen --help
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Quick start
|
|
46
|
+
|
|
47
|
+
Run these from the project you want to protect:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
zwischen init # config + pre-push hook + gitleaks auto-install
|
|
51
|
+
zwischen scan # manual scan with full report
|
|
52
|
+
zwischen doctor # check tool status
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## AI triage
|
|
56
|
+
|
|
57
|
+
Raw scanner output tells you *what matched*. The AI pass tells you *what to
|
|
58
|
+
fix first and how* — see the real before/after in
|
|
59
|
+
[docs/triage-example.md](docs/triage-example.md), generated with a local
|
|
60
|
+
model via Ollama.
|
|
61
|
+
|
|
62
|
+
| Provider | Flag | Setup |
|
|
63
|
+
| --- | --- | --- |
|
|
64
|
+
| Claude | `--ai claude` | Set `ANTHROPIC_API_KEY` or pass `--api-key`. |
|
|
65
|
+
| Ollama | `--ai ollama` | Install Ollama and pull a model. Local-first: nothing leaves your machine. |
|
|
66
|
+
| OpenAI | `--ai openai` | Set `OPENAI_API_KEY` or pass `--api-key`. |
|
|
67
|
+
|
|
68
|
+
Manual scans use AI when `--ai` is passed or config enables it. Pre-push
|
|
69
|
+
scans stay scanner-only unless `ai.pre_push_enabled: true` — blocking
|
|
70
|
+
decisions should be fast and deterministic. The design rationale is in
|
|
71
|
+
[docs/design.md](docs/design.md).
|
|
72
|
+
|
|
73
|
+
## Commands
|
|
74
|
+
|
|
75
|
+
| Command | What it does |
|
|
76
|
+
| --- | --- |
|
|
77
|
+
| `zwischen init` | Installs/checks tools, creates config, installs pre-push hook (backs up an existing non-Zwischen hook). |
|
|
78
|
+
| `zwischen scan` | Runs enabled scanners, prints a terminal report. |
|
|
79
|
+
| `zwischen scan --changed` | Scans only files changed since the default branch. |
|
|
80
|
+
| `zwischen scan --only secrets,sast` | Limits to Gitleaks (`secrets`) and/or Semgrep (`sast`). |
|
|
81
|
+
| `zwischen scan --ai <provider>` | Adds AI prioritization, fix suggestions, false-positive detection. |
|
|
82
|
+
| `zwischen scan --format json` | Machine-readable summary + findings. |
|
|
83
|
+
| `zwischen scan --format sarif` | SARIF 2.1.0 for GitHub code scanning. |
|
|
84
|
+
| `zwischen scan --pre-push` | Quiet hook mode: changed files only, compact output only when blocking. |
|
|
85
|
+
| `zwischen doctor` | Shows Gitleaks and Semgrep status. |
|
|
86
|
+
| `zwischen uninstall` | Removes the hook, optionally config/credentials. |
|
|
87
|
+
|
|
88
|
+
Escape hatches: `git push --no-verify` or `ZWISCHEN_SKIP=1 git push`.
|
|
89
|
+
|
|
90
|
+
## GitHub Action
|
|
91
|
+
|
|
92
|
+
Run the same scan in CI and feed the GitHub Security tab:
|
|
93
|
+
|
|
94
|
+
```yaml
|
|
95
|
+
- uses: cjordan223/zwischen@main
|
|
96
|
+
with:
|
|
97
|
+
sarif-file: zwischen.sarif
|
|
98
|
+
- uses: github/codeql-action/upload-sarif@v3
|
|
99
|
+
if: always()
|
|
100
|
+
with:
|
|
101
|
+
sarif_file: zwischen.sarif
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Configuration
|
|
105
|
+
|
|
106
|
+
Create or edit `.zwischen.yml` in the scanned project:
|
|
107
|
+
|
|
108
|
+
```yaml
|
|
109
|
+
ai:
|
|
110
|
+
enabled: true
|
|
111
|
+
pre_push_enabled: false
|
|
112
|
+
provider: claude # claude, ollama, or openai
|
|
113
|
+
ollama:
|
|
114
|
+
model: llama3
|
|
115
|
+
url: http://localhost:11434
|
|
116
|
+
timeout: 180 # seconds; local models can be slow to load
|
|
117
|
+
|
|
118
|
+
blocking:
|
|
119
|
+
severity: high # high, critical, or none
|
|
120
|
+
|
|
121
|
+
scanners:
|
|
122
|
+
gitleaks:
|
|
123
|
+
enabled: true
|
|
124
|
+
semgrep:
|
|
125
|
+
enabled: true
|
|
126
|
+
config: p/security-audit,p/expressjs # comma-separated rulesets
|
|
127
|
+
|
|
128
|
+
ignore: # findings under these globs are dropped
|
|
129
|
+
- "**/node_modules/**"
|
|
130
|
+
- "**/test/fixtures/**"
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Boolean scanner entries (`gitleaks: true`) work too. Credentials are read
|
|
134
|
+
from environment variables first, then `~/.zwischen/credentials` (written by
|
|
135
|
+
`zwischen init` when `ANTHROPIC_API_KEY` is set).
|
|
136
|
+
|
|
137
|
+
## Architecture
|
|
138
|
+
|
|
139
|
+
```mermaid
|
|
140
|
+
flowchart LR
|
|
141
|
+
push[git push] --> hook[pre-push hook]
|
|
142
|
+
hook --> diff[GitDiff<br/>changed files]
|
|
143
|
+
diff --> orch[Orchestrator]
|
|
144
|
+
cli[zwischen scan] --> orch
|
|
145
|
+
orch --> gl[Gitleaks<br/>secrets]
|
|
146
|
+
orch --> sg[Semgrep<br/>SAST]
|
|
147
|
+
gl --> agg[Aggregator<br/>normalize + dedupe<br/>+ ignore globs]
|
|
148
|
+
sg --> agg
|
|
149
|
+
agg --> ai{AI enabled?}
|
|
150
|
+
ai -- yes --> llm[Claude / OpenAI / Ollama<br/>priority · fixes · false positives]
|
|
151
|
+
ai -- no --> rep
|
|
152
|
+
llm --> rep[Reporter]
|
|
153
|
+
rep --> term[terminal / compact]
|
|
154
|
+
rep --> mach[json / sarif]
|
|
155
|
+
term --> block{blocking<br/>finding?}
|
|
156
|
+
block -- yes --> deny[⛔ push blocked]
|
|
157
|
+
block -- no --> allow[✓ push proceeds]
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The Ruby gem is the canonical implementation; the npm and pip packages are
|
|
161
|
+
convenience wrappers with a smaller command surface.
|
|
162
|
+
|
|
163
|
+
## Wrapper parity
|
|
164
|
+
|
|
165
|
+
| Capability | Ruby gem | npm / pip wrappers |
|
|
166
|
+
| --- | --- | --- |
|
|
167
|
+
| `init` / `scan` / `doctor` | ✓ | ✓ |
|
|
168
|
+
| `uninstall` | ✓ | — |
|
|
169
|
+
| `--only` scanner selection | ✓ | — |
|
|
170
|
+
| `--changed` / changed-file pre-push filtering | ✓ | — (hook scans the project) |
|
|
171
|
+
| `--format json` | ✓ | ✓ |
|
|
172
|
+
| `--format sarif` | ✓ | — |
|
|
173
|
+
| AI providers | claude, ollama, openai | ollama, openai, anthropic |
|
|
174
|
+
|
|
175
|
+
The wrapper gaps are intentional scope decisions, not bugs: the wrappers
|
|
176
|
+
exist so `npm install -g zwischen` / `pip install zwischen-cli` work in
|
|
177
|
+
ecosystems where a Ruby gem is friction, and they track the core workflow
|
|
178
|
+
(init → hook → scan) rather than every flag.
|
|
179
|
+
|
|
180
|
+
## Repository layout
|
|
181
|
+
|
|
182
|
+
```text
|
|
183
|
+
bin/zwischen Ruby executable
|
|
184
|
+
lib/zwischen/ Ruby gem implementation
|
|
185
|
+
lib/zwischen/scanner/ Gitleaks and Semgrep adapters
|
|
186
|
+
lib/zwischen/ai/ Claude, Ollama, and OpenAI clients
|
|
187
|
+
lib/zwischen/reporter/ Terminal and SARIF reporters
|
|
188
|
+
packages/npm/ Node wrapper package
|
|
189
|
+
packages/pip/ Python wrapper package
|
|
190
|
+
spec/ RSpec suite
|
|
191
|
+
action.yml Composite GitHub Action
|
|
192
|
+
docs/ Design write-up, triage example, demo GIF
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Development
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
bundle exec rspec # 196+ examples
|
|
199
|
+
./scripts/test_as_gem.sh # install and exercise as a real gem
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
See [DEVELOPMENT.md](DEVELOPMENT.md) for architecture notes and the release
|
|
203
|
+
process, and [TESTING.md](TESTING.md) for the end-to-end test plan.
|
|
204
|
+
|
|
205
|
+
## License
|
|
206
|
+
|
|
207
|
+
MIT
|