unskein 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.
- unskein-0.1.0/.env.example +10 -0
- unskein-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +36 -0
- unskein-0.1.0/.github/ISSUE_TEMPLATE/config.yml +1 -0
- unskein-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +19 -0
- unskein-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +12 -0
- unskein-0.1.0/.github/workflows/ci.yml +36 -0
- unskein-0.1.0/.github/workflows/release.yml +33 -0
- unskein-0.1.0/.gitignore +40 -0
- unskein-0.1.0/.python-version +1 -0
- unskein-0.1.0/.unskein.toml.example +19 -0
- unskein-0.1.0/CHANGELOG.md +111 -0
- unskein-0.1.0/CLAUDE.md +382 -0
- unskein-0.1.0/CODE_OF_CONDUCT.md +21 -0
- unskein-0.1.0/CONTRIBUTING.md +53 -0
- unskein-0.1.0/LICENSE +21 -0
- unskein-0.1.0/PKG-INFO +222 -0
- unskein-0.1.0/README.md +192 -0
- unskein-0.1.0/SECURITY.md +21 -0
- unskein-0.1.0/docs/architecture.md +1057 -0
- unskein-0.1.0/docs/assets/banner.svg +31 -0
- unskein-0.1.0/pyproject.toml +113 -0
- unskein-0.1.0/scripts/gen_banner.py +123 -0
- unskein-0.1.0/src/unskein/__init__.py +10 -0
- unskein-0.1.0/src/unskein/__main__.py +5 -0
- unskein-0.1.0/src/unskein/ai/__init__.py +6 -0
- unskein-0.1.0/src/unskein/ai/client.py +269 -0
- unskein-0.1.0/src/unskein/ai/models.py +172 -0
- unskein-0.1.0/src/unskein/ai/prompts.py +271 -0
- unskein-0.1.0/src/unskein/cli.py +297 -0
- unskein-0.1.0/src/unskein/config.py +303 -0
- unskein-0.1.0/src/unskein/errors.py +59 -0
- unskein-0.1.0/src/unskein/graph/__init__.py +6 -0
- unskein-0.1.0/src/unskein/graph/builder.py +37 -0
- unskein-0.1.0/src/unskein/graph/metrics.py +186 -0
- unskein-0.1.0/src/unskein/i18n.py +304 -0
- unskein-0.1.0/src/unskein/logging_setup.py +65 -0
- unskein-0.1.0/src/unskein/parsers/__init__.py +6 -0
- unskein-0.1.0/src/unskein/parsers/base.py +109 -0
- unskein-0.1.0/src/unskein/parsers/discovery.py +134 -0
- unskein-0.1.0/src/unskein/parsers/indirection.py +97 -0
- unskein-0.1.0/src/unskein/parsers/models.py +176 -0
- unskein-0.1.0/src/unskein/parsers/python_parser.py +392 -0
- unskein-0.1.0/src/unskein/perf.py +59 -0
- unskein-0.1.0/src/unskein/pipeline.py +232 -0
- unskein-0.1.0/src/unskein/report/__init__.py +5 -0
- unskein-0.1.0/src/unskein/report/markdown.py +341 -0
- unskein-0.1.0/src/unskein/scan.py +225 -0
- unskein-0.1.0/tests/conftest.py +137 -0
- unskein-0.1.0/tests/fixtures/circular_imports/app/__init__.py +0 -0
- unskein-0.1.0/tests/fixtures/circular_imports/app/a.py +5 -0
- unskein-0.1.0/tests/fixtures/circular_imports/app/b.py +5 -0
- unskein-0.1.0/tests/fixtures/reexport_chain/app/__init__.py +3 -0
- unskein-0.1.0/tests/fixtures/reexport_chain/app/consumer.py +5 -0
- unskein-0.1.0/tests/fixtures/reexport_chain/app/core/__init__.py +3 -0
- unskein-0.1.0/tests/fixtures/reexport_chain/app/core/impl/__init__.py +0 -0
- unskein-0.1.0/tests/fixtures/reexport_chain/app/core/impl/engine.py +2 -0
- unskein-0.1.0/tests/fixtures/reexport_cycle/app/__init__.py +0 -0
- unskein-0.1.0/tests/fixtures/reexport_cycle/app/a/__init__.py +1 -0
- unskein-0.1.0/tests/fixtures/reexport_cycle/app/b/__init__.py +1 -0
- unskein-0.1.0/tests/fixtures/reexport_cycle/app/user.py +1 -0
- unskein-0.1.0/tests/fixtures/simple_project/app/__init__.py +0 -0
- unskein-0.1.0/tests/fixtures/simple_project/app/main.py +7 -0
- unskein-0.1.0/tests/fixtures/simple_project/app/models.py +2 -0
- unskein-0.1.0/tests/fixtures/simple_project/app/services/__init__.py +0 -0
- unskein-0.1.0/tests/fixtures/simple_project/app/services/user.py +6 -0
- unskein-0.1.0/tests/integration/test_ai_isolation.py +64 -0
- unskein-0.1.0/tests/integration/test_cli_ai.py +152 -0
- unskein-0.1.0/tests/integration/test_cli_scan.py +215 -0
- unskein-0.1.0/tests/integration/test_docstrings.py +45 -0
- unskein-0.1.0/tests/integration/test_self_analysis.py +20 -0
- unskein-0.1.0/tests/unit/test_ai_client.py +278 -0
- unskein-0.1.0/tests/unit/test_ai_models.py +39 -0
- unskein-0.1.0/tests/unit/test_ai_prompts.py +440 -0
- unskein-0.1.0/tests/unit/test_analyze.py +50 -0
- unskein-0.1.0/tests/unit/test_config.py +218 -0
- unskein-0.1.0/tests/unit/test_discovery.py +108 -0
- unskein-0.1.0/tests/unit/test_discovery_symlinks.py +63 -0
- unskein-0.1.0/tests/unit/test_graph_builder.py +66 -0
- unskein-0.1.0/tests/unit/test_graph_metrics.py +187 -0
- unskein-0.1.0/tests/unit/test_i18n.py +97 -0
- unskein-0.1.0/tests/unit/test_import_collection.py +90 -0
- unskein-0.1.0/tests/unit/test_logging_setup.py +40 -0
- unskein-0.1.0/tests/unit/test_parse_plan.py +48 -0
- unskein-0.1.0/tests/unit/test_perf.py +27 -0
- unskein-0.1.0/tests/unit/test_pipeline_parallel.py +264 -0
- unskein-0.1.0/tests/unit/test_python_parse.py +268 -0
- unskein-0.1.0/tests/unit/test_python_parser.py +124 -0
- unskein-0.1.0/tests/unit/test_reexport_resolution.py +156 -0
- unskein-0.1.0/tests/unit/test_report_markdown.py +296 -0
- unskein-0.1.0/tests/unit/test_scan.py +229 -0
- unskein-0.1.0/uv.lock +2166 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Copy to .env and fill in. Never commit .env.
|
|
2
|
+
# Precedence: env var > .unskein.toml / ~/.config/unskein/config.toml > --api-key flag.
|
|
3
|
+
|
|
4
|
+
# LiteLLM model string, e.g. "ollama/qwen2.5-coder:7b" or "openai/gpt-4o-mini"
|
|
5
|
+
UNSKEIN_AI_MODEL=
|
|
6
|
+
UNSKEIN_API_KEY=
|
|
7
|
+
UNSKEIN_AI_API_BASE=
|
|
8
|
+
|
|
9
|
+
# "es" | "en" (default: system locale, falls back to English)
|
|
10
|
+
UNSKEIN_LANG=
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Something doesn't work as expected
|
|
3
|
+
labels: ["bug"]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: what-happened
|
|
7
|
+
attributes:
|
|
8
|
+
label: What happened?
|
|
9
|
+
description: Include the command you ran and the output. Remove any API keys.
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: expected
|
|
14
|
+
attributes:
|
|
15
|
+
label: What did you expect?
|
|
16
|
+
validations:
|
|
17
|
+
required: true
|
|
18
|
+
- type: textarea
|
|
19
|
+
id: repro
|
|
20
|
+
attributes:
|
|
21
|
+
label: Minimal reproduction
|
|
22
|
+
description: A small project layout (files + imports) that triggers the problem.
|
|
23
|
+
- type: input
|
|
24
|
+
id: version
|
|
25
|
+
attributes:
|
|
26
|
+
label: unskein version
|
|
27
|
+
placeholder: unskein --version
|
|
28
|
+
validations:
|
|
29
|
+
required: true
|
|
30
|
+
- type: input
|
|
31
|
+
id: python
|
|
32
|
+
attributes:
|
|
33
|
+
label: Python version and OS
|
|
34
|
+
placeholder: 3.14 on Ubuntu 24.04
|
|
35
|
+
validations:
|
|
36
|
+
required: true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
blank_issues_enabled: false
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Suggest an idea or improvement
|
|
3
|
+
labels: ["enhancement"]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: problem
|
|
7
|
+
attributes:
|
|
8
|
+
label: Problem
|
|
9
|
+
description: What are you trying to do that unskein doesn't support?
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: proposal
|
|
14
|
+
attributes:
|
|
15
|
+
label: Proposed solution
|
|
16
|
+
- type: textarea
|
|
17
|
+
id: alternatives
|
|
18
|
+
attributes:
|
|
19
|
+
label: Alternatives considered
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
## Summary
|
|
2
|
+
|
|
3
|
+
<!-- What does this change and why? Link the related issue. -->
|
|
4
|
+
|
|
5
|
+
## Checklist
|
|
6
|
+
|
|
7
|
+
- [ ] Tests added or updated
|
|
8
|
+
- [ ] Clean Code rules followed; Google-style docstrings on every new or changed module, class and function (private ones included)
|
|
9
|
+
- [ ] `uv run pytest` and `uv run ruff check .` pass locally
|
|
10
|
+
- [ ] User-facing strings added to `unskein.i18n` in both `es` and `en`
|
|
11
|
+
- [ ] `CHANGELOG.md` updated under `[Unreleased]`
|
|
12
|
+
- [ ] No design decision from `docs/architecture.md` changed without prior discussion
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, develop]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
lint:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: astral-sh/setup-uv@v6
|
|
17
|
+
- run: uv sync --locked
|
|
18
|
+
- run: uv run ruff check .
|
|
19
|
+
- run: uv run ruff format --check .
|
|
20
|
+
|
|
21
|
+
test:
|
|
22
|
+
strategy:
|
|
23
|
+
fail-fast: false
|
|
24
|
+
matrix:
|
|
25
|
+
os: [ubuntu-latest, windows-latest]
|
|
26
|
+
python-version: ["3.12", "3.13", "3.14"]
|
|
27
|
+
runs-on: ${{ matrix.os }}
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v4
|
|
30
|
+
- uses: astral-sh/setup-uv@v6
|
|
31
|
+
with:
|
|
32
|
+
python-version: ${{ matrix.python-version }}
|
|
33
|
+
- run: uv sync --locked
|
|
34
|
+
- run: uv run pytest --cov --cov-report=term-missing
|
|
35
|
+
- name: Smoke test - scan unskein itself end to end
|
|
36
|
+
run: uv run unskein scan src --no-ai --lang en
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: astral-sh/setup-uv@v6
|
|
16
|
+
- run: uv build
|
|
17
|
+
- uses: actions/upload-artifact@v4
|
|
18
|
+
with:
|
|
19
|
+
name: dist
|
|
20
|
+
path: dist/
|
|
21
|
+
|
|
22
|
+
publish:
|
|
23
|
+
needs: build
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
environment: pypi
|
|
26
|
+
permissions:
|
|
27
|
+
id-token: write # PyPI Trusted Publishing (OIDC), no stored token
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/download-artifact@v4
|
|
30
|
+
with:
|
|
31
|
+
name: dist
|
|
32
|
+
path: dist/
|
|
33
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
unskein-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Python-generated files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[oc]
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
wheels/
|
|
7
|
+
*.egg-info
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv
|
|
11
|
+
|
|
12
|
+
# Testing / coverage
|
|
13
|
+
.pytest_cache/
|
|
14
|
+
.coverage
|
|
15
|
+
.coverage.*
|
|
16
|
+
coverage.xml
|
|
17
|
+
htmlcov/
|
|
18
|
+
.ruff_cache/
|
|
19
|
+
|
|
20
|
+
# Secrets and local config (never commit API keys)
|
|
21
|
+
.env
|
|
22
|
+
.env.*
|
|
23
|
+
!.env.example
|
|
24
|
+
.unskein.toml
|
|
25
|
+
|
|
26
|
+
# Logs
|
|
27
|
+
*.log
|
|
28
|
+
|
|
29
|
+
# graft's local graph cache — regenerable, not committed (run `graft build`).
|
|
30
|
+
graft/
|
|
31
|
+
|
|
32
|
+
# Personal editor/agent tooling (graft hooks, MCP config) — per-developer, not shared
|
|
33
|
+
.claude/
|
|
34
|
+
.mcp.json
|
|
35
|
+
.ignore
|
|
36
|
+
|
|
37
|
+
docs/superpowers/
|
|
38
|
+
docs/litellm/
|
|
39
|
+
docs/code-reviews/
|
|
40
|
+
.superpowers/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.14
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Copy to .unskein.toml (gitignored). Env vars take precedence over this file.
|
|
2
|
+
|
|
3
|
+
[general]
|
|
4
|
+
lang = "en" # "es" | "en"
|
|
5
|
+
|
|
6
|
+
[ai]
|
|
7
|
+
model = "ollama/qwen2.5-coder:7b"
|
|
8
|
+
api_base = "http://localhost:11434"
|
|
9
|
+
# api_key = "" # prefer UNSKEIN_API_KEY env var
|
|
10
|
+
|
|
11
|
+
[analysis]
|
|
12
|
+
parallel_threshold = 50
|
|
13
|
+
# max_workers = 8 # default: os.cpu_count()
|
|
14
|
+
queue_maxsize = 200
|
|
15
|
+
max_file_size_bytes = 5242880
|
|
16
|
+
per_file_timeout_seconds = 30
|
|
17
|
+
# default_encoding = "utf-8" # fallback only, never overrides a PEP 263 cookie
|
|
18
|
+
# source_roots = ["src", "lib"] # default: auto-detect src/ layout; project root is always a fallback
|
|
19
|
+
# include_tests = false # tests/, test_*.py, *_test.py, conftest.py are skipped by default
|
|
@@ -0,0 +1,111 @@
|
|
|
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-09-30
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- AI interpretation: with a model configured, the report gains a summary, an
|
|
15
|
+
architecture health rating and problems with severity, produced through LiteLLM (any
|
|
16
|
+
provider, local Ollama or a LiteLLM Proxy). The AI only sees module names and
|
|
17
|
+
metrics; its answer is checked against the dependency graph, so it cannot name a
|
|
18
|
+
module that does not exist. If the call or the answer fails, the report is still
|
|
19
|
+
produced with a notice saying why. `--min-severity` and exit code 2 now apply to it.
|
|
20
|
+
Loosely written module names (quoted, with a trailing dot, or as file paths) are
|
|
21
|
+
matched to the graph; problems that still name no real module are discarded, and the
|
|
22
|
+
console and the report say how many.
|
|
23
|
+
- Initial project scaffold: `src/` layout, pipeline contracts, test fixtures, CI.
|
|
24
|
+
- Python import parser: absolute, relative and submodule imports, `is_external`
|
|
25
|
+
detection, re-export detection in `__init__.py`, per-file warnings instead of crashes
|
|
26
|
+
(syntax errors, undecodable files, oversized files, deeply nested code).
|
|
27
|
+
- Automatic `src/` layout detection and configurable `source_roots`.
|
|
28
|
+
- Test code excluded from analysis by default; `--include-tests` to opt back in.
|
|
29
|
+
- Re-export resolution: imports through `__init__.py` facades now point at the module
|
|
30
|
+
that defines the symbol, so cycles hidden behind a package facade become visible.
|
|
31
|
+
Re-export cycles produce a single warning per cycle.
|
|
32
|
+
- Dependency graph and coupling metrics: afferent/efferent coupling and instability per
|
|
33
|
+
module, dependency cycles (capped at 100, deterministic order) and the most coupled
|
|
34
|
+
modules by nearest-rank percentile of `Ca + Ce`.
|
|
35
|
+
- Configuration loading: `~/.config/unskein/config.toml` merged with the project's
|
|
36
|
+
`.unskein.toml` (project wins), validated so typos and wrong types are reported with
|
|
37
|
+
the file and field; env vars and CLI flags on top. API keys never appear in `repr`
|
|
38
|
+
or error messages.
|
|
39
|
+
- `--no-include-tests` and `--no-follow-symlinks`, so a flag can override
|
|
40
|
+
`.unskein.toml` in both directions.
|
|
41
|
+
- `unskein scan` works end to end: Markdown report in English or Spanish (terminal via
|
|
42
|
+
rich, raw file with `-o`), warnings grouped by kind with paths relative to the
|
|
43
|
+
project, `--verbose` stats and `--log-file`. The AI section explains why it is
|
|
44
|
+
empty (disabled, not configured, or the call failed).
|
|
45
|
+
- Exit codes are enforced: usage errors now exit with 1 (click's default 2 collided with
|
|
46
|
+
"high-severity problems found"), unexpected errors exit with 3 showing the traceback.
|
|
47
|
+
- Pylint configuration aligned with the Clean Code rules, for IDEs.
|
|
48
|
+
- Tangles: groups of mutually dependent modules (strongly connected components),
|
|
49
|
+
exact and never truncated, shown before the cycle list with their size, so a
|
|
50
|
+
"100+ cycles" report reveals it is really one knot of, say, 279 modules (#8).
|
|
51
|
+
|
|
52
|
+
- Parallel parsing: projects with 500 files or more are parsed in a process pool
|
|
53
|
+
(`parallel_threshold`, `max_workers`, `queue_maxsize` in `.unskein.toml`), up to
|
|
54
|
+
2.9x faster on large projects (1.0-2.6x where processes are spawned, as on Windows), with the same result and order as the sequential parser. A
|
|
55
|
+
file that exceeds `per_file_timeout_seconds` is skipped with a warning; a worker that
|
|
56
|
+
dies makes the parser fall back to sequential. The timeout only stops waiting: a
|
|
57
|
+
worker stuck on a pathological file still delays closing the pool.
|
|
58
|
+
|
|
59
|
+
### Fixed
|
|
60
|
+
|
|
61
|
+
- The list of dependency cycles (and which ones survived the 100-cycle limit) changed
|
|
62
|
+
between runs because networkx follows the string hash seed. Cycles are now enumerated
|
|
63
|
+
over integer labels, so the report is identical on every run.
|
|
64
|
+
- The README example `--exclude "migrations/**"` only skipped a `migrations/` folder at
|
|
65
|
+
the project root, not the ones inside each app. It now reads `--exclude "migrations/"`,
|
|
66
|
+
which matches at any depth (a pattern with a slash in the middle is anchored to the
|
|
67
|
+
root, as in `.gitignore`).
|
|
68
|
+
- `--verbose` now reports the real peak memory of the run. It used to read the resident
|
|
69
|
+
memory after the analysis, so memory freed before the end was invisible (a 200 MB
|
|
70
|
+
spike showed as 21 MB) (#9).
|
|
71
|
+
- Printing the report to a Windows pipe (cp1252) no longer crashes with an internal
|
|
72
|
+
error on symbols such as "→"; unencodable characters are replaced.
|
|
73
|
+
|
|
74
|
+
### Changed
|
|
75
|
+
|
|
76
|
+
- The default `parallel_threshold` is now 500 files (was 50) and `max_workers` defaults to
|
|
77
|
+
the core count capped at 8. Measured, a pool at 50 files was 2-40x slower than
|
|
78
|
+
sequential parsing because starting it costs 130-300 ms.
|
|
79
|
+
- `LanguageAdapter.parse` is now concrete, built on the new `plan_parse` and `parse_task`
|
|
80
|
+
that language adapters implement, so the parallel and sequential paths share one code
|
|
81
|
+
path. `FileParseResult` moved to `unskein.parsers.models`.
|
|
82
|
+
- File discovery no longer enters excluded directories (`.venv/`, `build/`,
|
|
83
|
+
`node_modules/`, `tests/`…), so its cost no longer grows with ignored content. As in
|
|
84
|
+
git, a negated pattern (`!build/keep.py`) cannot re-include a file inside an excluded
|
|
85
|
+
directory: such a file is no longer analyzed.
|
|
86
|
+
- Import collection walks only statement lists instead of every AST node, making parsing
|
|
87
|
+
1.3-1.5x faster on large projects (networkx, Django, sympy) with the same graph.
|
|
88
|
+
Imports are now visited depth-first in code order, so a file's warnings come out in
|
|
89
|
+
line order and the report may show different examples per warning kind.
|
|
90
|
+
- When a package `__init__.py` re-exports the same symbol more than once (for example a
|
|
91
|
+
`try/except ImportError` fallback), the first one in the code now wins; before, the
|
|
92
|
+
last one did.
|
|
93
|
+
- The placeholder notice about the AI arriving later is gone: the report now says
|
|
94
|
+
either that the AI is disabled or not configured, or why the call failed.
|
|
95
|
+
- Precedence for non-secret options is now flag > env var > `.unskein.toml` (so
|
|
96
|
+
`--lang` beats `UNSKEIN_LANG`); secrets keep env var > `.unskein.toml` > `--api-key`.
|
|
97
|
+
- Analysis warnings are now structured (`ParseWarning` with a `WarningCode`, file, line
|
|
98
|
+
and language-neutral detail) instead of English strings, so reports can translate
|
|
99
|
+
them and group them by kind.
|
|
100
|
+
- Clean Code is now a mandatory project convention: Google-style docstrings on every
|
|
101
|
+
module, class and function (private ones included), enforced in CI with ruff's `D`
|
|
102
|
+
rules plus an AST-based test that also covers private names.
|
|
103
|
+
- Parser data classes moved from `unskein.parsers.base` to `unskein.parsers.models`
|
|
104
|
+
(still importable from `unskein.parsers`), removing an import cycle.
|
|
105
|
+
- LiteLLM is now required at `>=1.95.0,<2`. The AI client resolves how to call the
|
|
106
|
+
model once (reasoning models get `temperature=1.0`, JSON-schema output when the model
|
|
107
|
+
supports it, a 60 s limit for direct calls) and never lets LiteLLM download its price
|
|
108
|
+
map or send telemetry.
|
|
109
|
+
|
|
110
|
+
[Unreleased]: https://github.com/mapenzo/unskein/compare/v0.1.0...HEAD
|
|
111
|
+
[0.1.0]: https://github.com/mapenzo/unskein/releases/tag/v0.1.0
|