pi-python-helper 0.1.0 → 0.2.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.
- package/CHANGELOG.md +39 -0
- package/README.md +85 -125
- package/docs/compatibility.md +17 -1
- package/docs/tools.md +25 -2
- package/extensions/shared.ts +24 -0
- package/extensions/tools/dependencies.ts +0 -120
- package/extensions/tools/environment.ts +30 -5
- package/extensions/tools/testing.ts +80 -8
- package/extensions/tools/validation.ts +427 -44
- package/helpers/scan_project.py +15 -1
- package/package.json +1 -1
- package/skills/python-development/SKILL.md +11 -6
- package/src/build/commands.ts +17 -2
- package/src/build/failure.ts +63 -55
- package/src/build/quality.ts +49 -0
- package/src/build/selection.ts +176 -9
- package/src/build/sync.ts +70 -0
- package/src/build/traceback.ts +55 -0
- package/src/dependencies/aliases.ts +101 -0
- package/src/dependencies/plan.ts +60 -112
- package/src/environment/discovery.ts +30 -3
- package/src/environment/tools.ts +41 -2
- package/src/project/conformance.ts +101 -25
- package/src/project/inspect.ts +126 -53
- package/src/project/paths.ts +15 -0
- package/src/project/pytest-config.ts +45 -0
- package/src/project/root.ts +85 -1
- package/src/project/scanner.ts +61 -9
- package/src/validation/bundle.ts +41 -4
- package/src/validation/tdd.ts +94 -21
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,45 @@ does not guarantee a stable public tool schema.
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.0] - 2026-09-22
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- `py_sync` and `py_validation_bundle` no longer delete the project's own dev tooling. `uv sync --all-groups` covers `[dependency-groups]` only, so a project declaring pytest/ruff/pyright in `[project.optional-dependencies]` had them **uninstalled**; the sync now also passes `--all-extras`, and `extras: 'none'` is available when that is not wanted. The destructive behaviour used to report `ok: true`.
|
|
15
|
+
- `py_validation_bundle` re-checks that pytest is runnable between the sync and the test step instead of trusting the sync exit code, and reports the skip reason rather than an unclassified failure.
|
|
16
|
+
- The scanner now runs under `<root>/.venv`'s interpreter when one exists, so import-to-distribution mapping describes the project environment. A host `python3` mapped 4 modules while the project interpreter mapped 84, which made `import wconfig` look unowned and produced a `uv add wconfig` suggestion for a distribution that does not exist. `py_environment` also reports the project's Python version instead of the host's.
|
|
17
|
+
- `providerMappingReliable` is no longer `true` when an interpreter could see an environment but owned none of the project's imports; partial mappings add an `UNMAPPED_IMPORTS` note.
|
|
18
|
+
- `py_project_inspect` no longer reports a false `INSTALLED_VERSION_MISMATCH` for marker-split lock entries. uv writes one entry per marker branch, and comparing a single arbitrary entry flagged a correctly synced 3.12 environment for having 25.1.0 where the 3.14 branch said 21.2.0.
|
|
19
|
+
- `py_project_inspect` no longer reports `PROJECT_NOT_INSTALLED` for a project uv records as `source = { virtual = "." }`. Such a project is intentionally never installed into `.venv`; the finding also claimed "no test can exercise it", which was false. Disclosed as the `PROJECT_VIRTUAL_SOURCE` note instead.
|
|
20
|
+
- `py_project_inspect` finds tests that live inside the package under test (`<package>/tests/`), not only `./tests`, and detects pytest configuration in `pytest.ini`, `tox.ini`, and `setup.cfg` rather than `pyproject.toml` alone.
|
|
21
|
+
- `py_failure_diagnose` classifies `Failed to spawn: \`pytest\`` and `command not found` as `tool_not_installed` instead of `unknown`, keeping the positional first-cause rule.
|
|
22
|
+
- `py_test_select` no longer matches every candidate when tests live inside the package under test. Signals shared by all candidates (the package name and its tokens) are discarded, so a package-rooted test tree narrows instead of selecting 30 of 30 files.
|
|
23
|
+
- `py_test_select` excludes test infrastructure (`tests/__init__.py`, `tests/utils.py`) from pytest targets and reports it separately, and discloses when a selection was not narrowed.
|
|
24
|
+
- `py_dependency_plan` no longer fabricates `uv add <import name>` when the providing distribution is unknown; it names the distribution from installed metadata when available and asks the caller to look it up otherwise.
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- `py_test_select` matches a test file to a changed module by the modules the test **imports**, which is the strongest available signal: `test_db_session.py` gives no naming hint that it covers `db/database.py`. The scanner reports `importModules` per file for this (protocol version 2).
|
|
29
|
+
- `py_validation_bundle` runs the lint/type tools the project declares (`quality: true` by default): `ruff` and `pyright` when declared, `mypy` only when `[tool.mypy]` exists. The bundle previously ignored the checks CI actually runs.
|
|
30
|
+
- `py_test` accepts `extraArgs` so project-standard pytest flags such as coverage options can be passed without the tool modelling each one.
|
|
31
|
+
- `py_sync` accepts `extras` (`all` by default) and reports the installed/uninstalled inventory from uv's output.
|
|
32
|
+
- `py_environment` reports `interpreterOrigin` and splits tool availability into `available` (runnable now), `installed` (console script in the project environment), and `installable` (declared in `uv.lock` but absent), with a `TOOL_NOT_INSTALLED` warning.
|
|
33
|
+
- `py_tdd_checkpoint` records which paths matched and on which tokens (`associations`) and flags a match that rests only on the shared package prefix (`weakAssociation`).
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- **Breaking:** `py_environment.tools[].available` now means "an executable was found", not "declared or found". A distribution recorded in `uv.lock` is `installable`, so a broken environment no longer looks healthy. Read `installable` for the previous meaning.
|
|
38
|
+
- **Breaking:** scanner protocol `SCANNER_VERSION` is now 2; a scanner reporting version 1 is rejected with `SCANNER_VERSION_MISMATCH`.
|
|
39
|
+
|
|
40
|
+
## [0.1.1] - 2026-09-21
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- Refactored `src/dependencies/plan.ts` by extracting static import aliases and console-only distribution tables into `src/dependencies/aliases.ts`.
|
|
45
|
+
- Refactored `src/build/failure.ts` by extracting Python and pytest traceback frame parsing and library frame detection into `src/build/traceback.ts`.
|
|
46
|
+
- Decomposed monolithic `inspectProject` in `src/project/inspect.ts` into focused diagnostic collectors for manifest, lockfile, and environment rules.
|
|
47
|
+
- Moved `py_tdd_checkpoint` and `py_completion_evidence` tools from `extensions/tools/dependencies.ts` to `extensions/tools/validation.ts` to align tool registration with module responsibilities.
|
|
48
|
+
|
|
10
49
|
## [0.1.0] - 2026-09-21
|
|
11
50
|
|
|
12
51
|
### Added
|
package/README.md
CHANGED
|
@@ -1,172 +1,132 @@
|
|
|
1
1
|
# pi-python-helper
|
|
2
2
|
|
|
3
|
-
[pi 코딩 에이전트](https://github.com/badlogic/pi-mono)를 위한 uv 기반 Python 개발 도구 확장 패키지입니다.
|
|
3
|
+
[pi 코딩 에이전트](https://github.com/badlogic/pi-mono)를 위한 uv 기반 Python 개발 도구 및 워크플로우 확장 패키지입니다.
|
|
4
4
|
|
|
5
|
-
패키지 관리자는 **uv 단일 지원**이며, 린트/타입 진단(LSP)은 의도적으로 다루지 않습니다. 해당 영역은 `@narumitw/pi-python-lsp` 같은 별도 확장에
|
|
5
|
+
패키지 관리자는 **uv 단일 지원**이며, 린트/타입 진단(LSP)은 의도적으로 다루지 않습니다. 해당 영역은 `@narumitw/pi-python-lsp` 같은 별도 확장에 위임하고, 이 패키지는 **환경 점검, 매니페스트/락파일 정합성, 의존성 그래프 분석, 테스트 선별 및 실행, 완료 검증 게이트**에 집중합니다.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
---
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## 설치 및 설정 (Installation)
|
|
10
10
|
|
|
11
|
-
###
|
|
11
|
+
### 패키지 설치
|
|
12
|
+
pi 환경에 npm 패키지로 설치합니다:
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
```bash
|
|
15
|
+
pi install npm:pi-python-helper
|
|
16
|
+
```
|
|
15
17
|
|
|
16
|
-
###
|
|
18
|
+
### 개발용/로컬 설치
|
|
19
|
+
로컬에서 확장을 개발하거나 소스 코드로 직접 로드하려면 다음과 같이 실행합니다:
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
- `TYPE_CHECKING` 블록 전용 import (`UNDECLARED_TYPE_ONLY_IMPORT` — 런타임 의존성으로 오탐하지 않음)
|
|
22
|
-
- `uv.lock` 드리프트 (`LOCKFILE_DRIFT`)
|
|
23
|
-
- `includeUnused=true`일 때만, 콘솔 전용 도구를 제외한 미사용 선언 보고
|
|
21
|
+
```bash
|
|
22
|
+
pi -e /absolute/path/to/pi-python-helper
|
|
23
|
+
```
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
또는 프로젝트의 `.pi/settings.json`에 확장 경로를 등록할 수 있습니다.
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
- `py_test` — `uv run --frozen pytest` 미리보기 또는 실행. `--lf`(직전 실패만), `-k`, `--maxfail` 지원. pytest 요약 라인을 파싱하여 passed/failed/errors/skipped/xfailed/deselected/warning 카운트와 실패 노드 ID를 반환하고, 요약이 없는 실행은 통과로 간주하지 않습니다.
|
|
29
|
-
- `py_failure_diagnose` — 제한된 출력에서 **출력상 가장 먼저 등장하는** 원인을 선택합니다. `traceback` 프레임을 `site-packages`/표준 라이브러리와 프로젝트 코드로 분리하여 마지막 프로젝트 프레임을 지목합니다.
|
|
27
|
+
---
|
|
30
28
|
|
|
31
|
-
|
|
29
|
+
## 빠른 시작 (Quick Start)
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
- `py_validation_bundle` — `uv lock --check` → `uv sync --frozen` → `pytest` → 환경 정합성 → 오래된 아티팩트 검사를 하나의 증거 지향 시퀀스로 미리보기/실행
|
|
35
|
-
- `py_tdd_checkpoint` — 프로덕션 `.py` 변경에 대응하는 테스트 변경이 있는지 확인
|
|
36
|
-
- `py_completion_evidence` — 환경 동기화와 테스트가 실제로 실행·성공했는지에 대한 보수적 완료 판정
|
|
31
|
+
설치 후 대화창에서 인터랙티브 명령어로 환경 상태를 즉시 확인할 수 있습니다:
|
|
37
32
|
|
|
38
|
-
|
|
33
|
+
```text
|
|
34
|
+
/py-status
|
|
35
|
+
```
|
|
39
36
|
|
|
40
|
-
|
|
37
|
+
활성 인터프리터, 가상환경(`.venv`), uv 가용성 및 프로젝트 매니페스트 상태를 간결하게 요약하여 보고합니다.
|
|
41
38
|
|
|
42
|
-
|
|
39
|
+
또한, 패키지에 내장된 `python-development` 스킬이 pi 에이전트에게 상황별 권장 도구 호출 순서와 해석 규칙을 자동으로 안내합니다.
|
|
43
40
|
|
|
44
|
-
|
|
41
|
+
---
|
|
45
42
|
|
|
46
|
-
|
|
47
|
-
- **상태 변경(mutating)**: `uv add/remove/sync/lock`, `pip install/uninstall`, `git commit/push`, 마이그레이션 적용
|
|
48
|
-
- **읽기 전용(read)**: `uv lock --check`, `uv sync --dry-run`, `pytest`, `ruff check --diff`, `git diff/status/log`, `python -c`
|
|
43
|
+
## 개발 워크플로우 가이드 (Workflow Guide)
|
|
49
44
|
|
|
50
|
-
|
|
45
|
+
에이전트 또는 개발자가 Python 프로젝트를 다룰 때 다음 워크플로우에 따라 점검과 작업을 진행하는 것을 권장합니다.
|
|
51
46
|
|
|
52
|
-
|
|
47
|
+
### 1. 환경 및 프로젝트 진단 (Environment & Manifest Check)
|
|
48
|
+
원시 쉘 명령어로 환경을 탐색하기 전에 전용 도구로 런타임과 설정의 무결성을 먼저 확인합니다.
|
|
49
|
+
- **인터프리터 점검**: 잘못된 Python 실행 파일이나 가상환경 미활성화로 인한 혼란을 방지합니다 (`py_environment`). `interpreterOrigin`은 분석에 쓰인 인터프리터가 프로젝트 환경인지(`venv`) PATH인지(`path`)를 밝히며, 도구 가용성은 "지금 실행 가능"을 뜻하는 `available`과 "uv sync로 설치 가능"을 뜻하는 `installable`로 구분됩니다.
|
|
50
|
+
- **3자 정합성 검사**: `pyproject.toml`(선언) ↔ `uv.lock`(해석) ↔ `.venv`(실제 설치)가 서로 일치하는지 검사하고, lockfile 드리프트나 동기화 누락을 감지합니다 (`py_project_inspect`).
|
|
53
51
|
|
|
54
|
-
|
|
52
|
+
### 2. 의존성 분석 및 관리 (Dependency Analysis)
|
|
53
|
+
소스 코드를 정적으로 분석하여 런타임 누락이나 잘못된 의존성 배치를 사전에 방지합니다 (`py_dependency_plan`).
|
|
54
|
+
- **코드 실제 import 스캔**: Python `ast`를 기반으로 실제 import된 모듈을 추출하고 배포 패키지 이름(`PIL` ↔ `pillow` 등)으로 변환합니다.
|
|
55
|
+
- **의존성 누락 및 분류 오류 탐지**:
|
|
56
|
+
- 프로덕션 코드에서 선언되지 않은 패키지를 사용하는 경우 (`UNDECLARED_IMPORT`)
|
|
57
|
+
- dev 그룹에만 선언된 패키지를 런타임 프로덕션 코드에서 import한 경우 (`RUNTIME_DEPENDENCY_IN_DEV_GROUP`)
|
|
58
|
+
- `TYPE_CHECKING` 블록 내부의 타입 전용 import 여부 구분 (`UNDECLARED_TYPE_ONLY_IMPORT` — 런타임 의존성으로 오탐 방지)
|
|
59
|
+
- **반영 계획**: 분석 결과를 바탕으로 `uv add` 또는 `uv sync` 계획을 안전하게 수립합니다.
|
|
55
60
|
|
|
56
|
-
|
|
61
|
+
### 3. 스마트 테스트 선별 및 실패 진단 (Testing & Diagnostics)
|
|
62
|
+
전체 테스트를 매번 실행하지 않고, 변경 사항에 기반하여 효율적으로 테스트를 수행합니다.
|
|
63
|
+
- **테스트 자동 선별**: pytest 규약, 실제 import 관계, 토큰 연관성을 기반으로 변경된 파일과 관련된 테스트만 선택합니다 (`py_test_select`). 패키지 안에 테스트가 있는 레이아웃(`<package>/tests/`)에서 이름만 겹치는 후보는 걸러지고, 테스트가 변경 모듈을 실제로 import하면 그것이 가장 강한 근거가 됩니다. 아무것도 좁혀지지 않으면 `narrowed: false`와 `NO_NARROWING` 경고로 밝히고, 근거가 이름뿐이면 `SELECTION_WITHOUT_IMPORT_EVIDENCE`로 알립니다.
|
|
64
|
+
- **테스트 실행 및 요약**: `uv run --frozen pytest`를 안전하게 실행하고 passed/failed/errors 카운트 및 실패 노드를 구조화하여 추출합니다 (`py_test`). `--lf`(직전 실패만 재실행), `-k` 필터, 그리고 커버리지 플래그 같은 프로젝트 표준 옵션을 위한 `extraArgs`를 지원합니다.
|
|
65
|
+
- **원인 프레임 진단**: 테스트 실패 시 긴 traceback 속에서 `site-packages`나 표준 라이브러리 프레임을 배제하고, **실제 프로젝트 코드에서 가장 먼저 발생한 원인 프레임**을 지목합니다 (`py_failure_diagnose`). 도구를 실행할 수 없는 환경 실패(`Failed to spawn: \`pytest\``, `command not found`)는 `tool_not_installed`로 분류하여 코드 문제로 오해하지 않게 합니다.
|
|
57
66
|
|
|
58
|
-
|
|
67
|
+
### 4. 품질 검증 게이트 및 동기화 (Validation Gates)
|
|
68
|
+
작업을 완료했다고 보고하기 전에 확실한 증거를 수집합니다.
|
|
69
|
+
- **TDD 체크포인트**: 프로덕션 코드 변경에 대응하는 테스트 코드 수정이 있었는지 확인합니다 (`py_tdd_checkpoint`). 어떤 경로가 어떤 토큰으로 연결되었는지(`associations`)를 근거로 남기고, 패키지 접두사만 겹치는 약한 연결은 `weakAssociation`으로 공개합니다.
|
|
70
|
+
- **가상환경 동기화**: `uv lock --check` 및 `uv sync --frozen --all-groups --all-extras`를 통해 lock과 venv를 일치시킵니다 (`py_sync`). `[project.optional-dependencies]`의 extra를 함께 요청하므로, dev 도구를 extra로 선언한 프로젝트에서도 `uv sync`가 pytest/ruff/pyright를 삭제하지 않습니다. 설치 목록에서 제거된 패키지가 있으면 `SYNC_REMOVED_PACKAGES` 경고로 알리고, sync 후 pytest가 없으면 테스트 단계를 건너뛴 이유를 명시합니다.
|
|
71
|
+
- **종합 검증 번들**: lock 검사 → sync → pytest → 선언된 품질 검사(ruff/pyright) → 환경 정합성 → 오래된 아티팩트 검사를 하나의 검증 시퀀스로 실행하여 신뢰할 수 있는 완료 판정을 도출합니다 (`py_validation_bundle`, `py_completion_evidence`).
|
|
59
72
|
|
|
60
|
-
|
|
73
|
+
> 💡 각 도구의 상세 파라미터 스키마 및 반환값 규격은 [도구 레퍼런스 (docs/tools.md)](docs/tools.md)를 참고하세요.
|
|
61
74
|
|
|
62
|
-
|
|
75
|
+
---
|
|
63
76
|
|
|
64
|
-
|
|
77
|
+
## 핵심 원칙 및 안전 모델 (Core Principles & Safety)
|
|
65
78
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
| `PROJECT_NOT_INSTALLED` | lock이 editable 설치를 기대하는데 프로젝트가 `.venv`에 없음(빌드 실패 포함). 테스트가 프로젝트를 import할 수 없음 |
|
|
71
|
-
| `PROJECT_INSTALLED_NOT_EDITABLE` | 프로젝트가 live link가 아니라 **복사본**으로 설치됨 → 테스트가 오래된 스냅샷을 import |
|
|
72
|
-
| `INSTALLED_PACKAGE_UNTRACKED` | `.venv`에 있지만 lock에 없는 패키지 (예: `uv pip install` 잔여물) |
|
|
73
|
-
| `INSTALLED_ENVIRONMENT_INDEPENDENT` | 설치본 대부분이 lock에 없음 — 이 `.venv`는 uv가 이 프로젝트용으로 만든 것이 아님 |
|
|
79
|
+
### 1. 명시적 옵트인 기반 안전 모델 (Opt-in Safety)
|
|
80
|
+
- **읽기 전용 기본**: 환경/의존성/테스트 진단 도구는 기본적으로 읽기 전용으로 안전하게 동작합니다.
|
|
81
|
+
- **상태 변경 시 옵트인 요구**: 가상환경을 생성/갱신하거나 락파일을 수정하는 도구(`py_sync`, `py_validation_bundle`)는 `execute: true`가 명시적으로 지정되지 않으면 실행하지 않고 **명령 미리보기(preview)**만 반환합니다.
|
|
82
|
+
- **소스 파일 보호**: 이 패키지의 어떤 도구도 프로젝트 소스 코드를 임의로 수정하거나 덮어쓰지 않습니다.
|
|
74
83
|
|
|
75
|
-
###
|
|
84
|
+
### 2. 3자 환경 정합성 검사 (Environment Conformance)
|
|
85
|
+
`pyproject.toml`, `uv.lock`, `.venv`의 3자 일치 상태를 비교 및 판정합니다:
|
|
86
|
+
- **`consistent`**: 세 진실 원천이 완벽히 동기화된 상태.
|
|
87
|
+
- **`drifted`**: 버전 불일치(`INSTALLED_VERSION_MISMATCH`), 필수 패키지 누락(`INSTALLED_PACKAGE_MISSING`), 복사본 설치(`PROJECT_INSTALLED_NOT_EDITABLE`) 등의 불일치가 발견된 상태.
|
|
88
|
+
- **`unverifiable`**: 가상환경 부재, 락파일 부재, 권한 문제 등으로 검증을 완료할 수 없는 상태 (결코 일치로 승격하지 않음).
|
|
76
89
|
|
|
77
|
-
|
|
78
|
-
-
|
|
79
|
-
-
|
|
80
|
-
-
|
|
81
|
-
-
|
|
90
|
+
**오탐 억제 (False-positive Control)**:
|
|
91
|
+
- OS나 파이썬 버전 조건부 패키지(`sys_platform == 'win32'`, `python_version < '3.11'`)는 환경에 맞게 설치되지 않는 것이 정상이므로, 마커를 분석하여 무조건 필요한 패키지만 누락으로 판정합니다.
|
|
92
|
+
- uv가 같은 이름을 마커별로 여러 항목으로 기록하는 경우(`argon2-cffi-bindings`가 Python 3.14에 21.2.0, 그 미만에 25.1.0), 설치 버전이 항목 중 하나와 일치하면 일치로 판정하고 `MARKER_SPLIT_LOCK_ENTRIES` 노트로 공개합니다.
|
|
93
|
+
- `[build-system]`이 없는 프로젝트는 uv가 `source = { virtual = "." }`로 기록하며 `.venv`에 설치되지 않습니다. 이 경우 누락이 아니라 `PROJECT_VIRTUAL_SOURCE` 노트로 설명합니다.
|
|
94
|
+
- pip, setuptools 등의 부트스트랩 배포판은 불일치 비교에서 제외합니다.
|
|
82
95
|
|
|
83
|
-
|
|
96
|
+
### 3. 오래된 아티팩트 탐지 (Stale Artifact Detection)
|
|
97
|
+
- 소스 코드 변경 시점보다 오래된 커버리지 데이터(`.coverage`, `coverage.xml`)가 남아 있는 경우(`STALE_COVERAGE_DATA`), 오래된 수치를 근거로 잘못 판단하지 않도록 경고합니다.
|
|
84
98
|
|
|
85
|
-
|
|
99
|
+
---
|
|
86
100
|
|
|
87
|
-
|
|
88
|
-
pi -e /absolute/path/to/pi-python-helper
|
|
89
|
-
```
|
|
101
|
+
## 문서 및 참고 자료 (Documentation)
|
|
90
102
|
|
|
91
|
-
|
|
103
|
+
- 📖 **[도구 레퍼런스 (docs/tools.md)](docs/tools.md)** — 모든 등록 도구의 설명, 입력 파라미터 스키마, 반환 데이터 규격 (자동 생성 및 테스트 검증)
|
|
104
|
+
- 🧭 **[호환성 및 성능 매트릭스 (docs/compatibility.md)](docs/compatibility.md)** — Node.js, Python(3.10~3.13), uv 지원 범위, 저하 모드 동작 방식, 작업별 측정 비용
|
|
105
|
+
- 📝 **[변경 이력 (CHANGELOG.md)](CHANGELOG.md)** — 버전별 변경 내역 및 마이그레이션 안내
|
|
106
|
+
- 🤝 **[기여 가이드 (CONTRIBUTING.md)](CONTRIBUTING.md)** 및 **[개발 에이전트 규칙 (AGENTS.md)](AGENTS.md)** — 패키지 개발 지침 및 아키텍처 규칙
|
|
92
107
|
|
|
93
|
-
|
|
94
|
-
pi install npm:pi-python-helper@latest
|
|
95
|
-
```
|
|
108
|
+
---
|
|
96
109
|
|
|
97
110
|
## 개발 및 기여 (Development)
|
|
98
111
|
|
|
99
112
|
```bash
|
|
113
|
+
# 의존성 설치
|
|
100
114
|
npm install
|
|
115
|
+
|
|
116
|
+
# 단위 테스트 및 타입 검사
|
|
101
117
|
npm test
|
|
102
118
|
npm run typecheck
|
|
103
|
-
# 또는 전체 검사 실행
|
|
104
|
-
npm run check
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
네트워크와 uv가 있는 환경에서는 실제 uv 프로젝트를 생성해 전 경로를 검증하는 e2e 테스트를 실행할 수 있습니다:
|
|
108
|
-
|
|
109
|
-
```bash
|
|
110
|
-
npm run test:e2e
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
도구의 공개 표면(이름·파라미터·반환 형태)을 변경하면 레퍼런스 문서를 다시 생성해야 합니다:
|
|
114
119
|
|
|
115
|
-
|
|
116
|
-
npm run
|
|
117
|
-
npm run docs:check # 문서가 최신인지만 확인
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
`docs/tools.md`는 생성 파일이므로 직접 편집하지 마세요. `npm test`가 도구 집합·파라미터 스키마·반환 형태·문서 최신성을 모두 검증하며, 문서가 오래되면 테스트가 실패합니다.
|
|
121
|
-
|
|
122
|
-
이 확장은 로드 시 Python이나 uv가 설치되어 있을 필요가 없습니다. Python 도구는 해석기나 uv를 사용할 수 없을 때 예외를 던지지 않고 구조화된 진단 오류를 반환합니다.
|
|
123
|
-
|
|
124
|
-
분석은 `helpers/scan_project.py`에 위임합니다. 이 스크립트는 stdin으로 JSON 요청을 받아 stdout으로 JSON을 출력하며 프로젝트를 수정하지 않습니다. import 스캔에는 `ast`, 매니페스트 파싱에는 `tomllib`(Python 3.11+) 또는 `tomli`가 필요하고, 둘 다 없으면 매니페스트 분석이 저하된 상태로 동작함을 명시적으로 경고합니다. 선언된 버전 제약과 `uv.lock`의 버전을 비교하는 기능은 분석 인터프리터의 `packaging`을 사용하며, 없으면 `SPECIFIER_CHECK_UNAVAILABLE` 노트를 남기고 이름 대조만 수행합니다.
|
|
125
|
-
|
|
126
|
-
스캐너는 독립 실행도 가능합니다:
|
|
127
|
-
|
|
128
|
-
```bash
|
|
129
|
-
python3 helpers/scan_project.py --help
|
|
130
|
-
python3 helpers/scan_project.py --mode environment,manifest --root . </dev/null
|
|
120
|
+
# 전체 검증 (테스트, 타입 검사, 포맷, 문서 동기화, 패키징 검사)
|
|
121
|
+
npm run check
|
|
131
122
|
```
|
|
132
123
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
## 성능 특성 (Measured cost)
|
|
136
|
-
|
|
137
|
-
측정 환경: Linux, Python 3.12, uv 0.12.
|
|
138
|
-
|
|
139
|
-
| 작업 | 비용 |
|
|
140
|
-
|---|---|
|
|
141
|
-
| `py_environment` 전체 | ~64 ms (스캐너 1회 + `uv --version` 1회) |
|
|
142
|
-
| 도구 가용성 판정 | ~2.3 ms (프로세스 실행 없음) |
|
|
143
|
-
| `py_project_inspect` 매니페스트 스캔 | ~31 ms |
|
|
144
|
-
| import 스캔 (`ast`) | ~76 ms (`py_dependency_plan`에만 사용) |
|
|
145
|
-
| 설치본 스캔 | ~3 ms / 22개 패키지 (`METADATA` 헤더만 읽음) |
|
|
146
|
-
|
|
147
|
-
도구 존재 여부는 `--version`을 실행해서 확인하지 않습니다. 그 방식은 도구당 프로세스 1회(pytest만 154 ms)를 썼고 호스트 버전을 프로젝트 버전으로 잘못 보고했습니다. 대신 `.venv/bin`과 PATH를 파일시스템으로 탐색하고 버전은 `uv.lock`에서 가져옵니다.
|
|
148
|
-
|
|
149
|
-
## 지원 및 호환성 (Support and compatibility)
|
|
150
|
-
|
|
151
|
-
- Node.js 20 이상
|
|
152
|
-
- Python 3.10 / 3.11 / 3.12 / 3.13 (CI 매트릭스에서 검증, 3.11+ 권장)
|
|
153
|
-
- uv 0.5+ 우선 지원, `uv.lock`(revision 2/3) 기준
|
|
154
|
-
- 정적 프로젝트/의존성 분석 도구는 uv나 `.venv` 없이도 작동합니다.
|
|
155
|
-
- 라이선스: Apache-2.0
|
|
156
|
-
|
|
157
|
-
릴리스 이력은 `CHANGELOG.md`, 도구 레퍼런스는 `docs/tools.md`, 개발 규칙은 `AGENTS.md`와 `CONTRIBUTING.md`, 지원 런타임 및 성능 정보는 `docs/compatibility.md`, 취약점 보고는 `SECURITY.md`를 참고하세요.
|
|
158
|
-
|
|
159
|
-
릴리스는 `v<version>` 태그를 푸시하면 `.github/workflows/publish.yml`이 태그와 `package.json` 버전을 검증한 뒤 npm provenance와 함께 배포합니다.
|
|
160
|
-
|
|
161
|
-
## 설계 원칙 (Design principles)
|
|
124
|
+
- 실제 uv 환경을 이용한 전 구간 검증: `npm run test:e2e`
|
|
125
|
+
- 도구 등록 정보나 스키마 수정 후 문서 재생성: `npm run docs` (이후 `npm run docs:check`로 확인)
|
|
162
126
|
|
|
163
|
-
|
|
164
|
-
- 타임아웃, 작업 취소, 출력 크기 제한을 통한 프로세스 경계 유지
|
|
165
|
-
- 사용자 입력 경로/패키지명의 쉘 문자열 보간 금지 (항상 인자 배열)
|
|
166
|
-
- 기본적으로 읽기 전용 진단 수행, 상태를 바꾸는 명령은 명시적 opt-in 요구
|
|
167
|
-
- 근거가 약한 판정은 경고하지 않고 `info` 노트로 내리거나 옵트인으로 제공
|
|
168
|
-
- 도구 수를 의도적으로 작게 유지하고, 린트/타입 진단은 다른 확장에 위임
|
|
127
|
+
---
|
|
169
128
|
|
|
170
|
-
## 출처 (Attribution)
|
|
129
|
+
## 라이선스 및 출처 (License & Attribution)
|
|
171
130
|
|
|
172
|
-
|
|
131
|
+
- **License**: Apache-2.0
|
|
132
|
+
- **Attribution**: 본 패키지의 공통 코어(구조화된 결과 규격, 프로세스 경계 래퍼, 검증 게이트 골격)는 Apache-2.0 라이선스의 [pi-ros-helper](https://github.com/wkqco33/pi-ros-helper) 구조를 참고하여 작성되었습니다.
|
package/docs/compatibility.md
CHANGED
|
@@ -19,6 +19,14 @@
|
|
|
19
19
|
|
|
20
20
|
매니페스트/락파일 분석은 Python 3.11+에서 가장 정확합니다. 3.10에서는 분석에 사용되는 인터프리터에 `tomli`가 설치되어 있어야 하며, 없으면 `TOML_PARSER_UNAVAILABLE` 경고 후 분석이 생략됩니다. 선언된 버전 제약과 `uv.lock`의 버전 비교에는 `packaging`이 필요하며, 없으면 `SPECIFIER_CHECK_UNAVAILABLE` 노트와 `python3 -m pip install packaging` 제안을 반환하고 이름 대조만 수행합니다.
|
|
21
21
|
|
|
22
|
+
## 분석 인터프리터 선택 (Which interpreter the scanner uses)
|
|
23
|
+
|
|
24
|
+
`<root>/.venv`에 인터프리터가 있으면 스캐너는 그것을 사용하고 `interpreterOrigin: 'venv'`를 반환합니다. 없을 때만 PATH의 `python3`/`python`으로 폴백하며 `'path'`로 표시합니다.
|
|
25
|
+
|
|
26
|
+
이는 정확도 문제입니다. `wconfig`처럼 import 이름과 배포 이름이 다른 모듈은 **실행 중인 인터프리터의 `site-packages`** 에서만 연결됩니다. 호스트 Python 3.14는 모듈 4개를 매핑하는 반면 프로젝트 `.venv`의 Python 3.12는 84개를 매핑하며 `wconfig → wpyconf`를 찾아냅니다. 호스트 인터프리터를 쓰면 모든 프로젝트 의존성이 "제공자 없음"으로 보이고, `providerMappingReliable`이 잘못 `true`로 남았습니다. 지금은 프로젝트의 import를 하나도 소유하지 못한 인터프리터를 신뢰하지 않으며(`providerMappingReliable: false`), 일부만 매핑되면 `UNMAPPED_IMPORTS` 노트로 범위를 좁혀 공개합니다.
|
|
27
|
+
|
|
28
|
+
같은 이유로 `py_environment`가 보고하는 Python 버전은 프로젝트 환경의 버전입니다(예: 호스트 3.14가 아니라 `.venv`의 3.12).
|
|
29
|
+
|
|
22
30
|
## 성능 특성 (Measured cost)
|
|
23
31
|
|
|
24
32
|
측정 환경: Linux, Python 3.12, `uv` 0.12, 이 저장소 기준.
|
|
@@ -50,7 +58,15 @@
|
|
|
50
58
|
| `packaging` 미설치 | `SPECIFIER_CHECK_UNAVAILABLE` 노트, 이름 대조만 수행 |
|
|
51
59
|
| import 스캔 중 구문 오류 파일 | `UNPARSABLE_FILE` 노트로 보고하고 나머지 스캔은 계속 |
|
|
52
60
|
| 스캔 파일 수 초과 | `truncated: true` 표시 |
|
|
53
|
-
| 스캐너 프로토콜 불일치 | `SCANNER_VERSION_MISMATCH` 오류 반환 (문서를 해석하지 않음) |
|
|
61
|
+
| 스캐너 프로토콜 불일치 | `SCANNER_VERSION_MISMATCH` 오류 반환 (문서를 해석하지 않음). 현재 프로토콜 버전은 **2**이며, 1은 `importModules`가 없어 거부됩니다 |
|
|
62
|
+
| `.venv` 인터프리터 실행 불가 | PATH 인터프리터로 폴백하고 `interpreterOrigin: 'path'`로 표시 |
|
|
63
|
+
| 선언된 도구의 실행 파일 부재 | `TOOL_NOT_INSTALLED` 경고와 `--all-extras` 제안 (`available: false`, `installable: true`) |
|
|
64
|
+
| sync가 패키지를 제거함 | `SYNC_REMOVED_PACKAGES` 경고; pytest가 사라졌으면 테스트 단계를 건너뛰고 그 이유를 반환 |
|
|
65
|
+
| 마커별로 분할된 lock 항목 | 설치 버전이 항목 중 하나와 일치하면 일치로 판정, `MARKER_SPLIT_LOCK_ENTRIES` 노트 |
|
|
66
|
+
| `[build-system]` 없는 프로젝트 | `PROJECT_VIRTUAL_SOURCE` 노트, 누락으로 보고하지 않음 |
|
|
67
|
+
| 테스트가 패키지 안에 있음 | 패키지 루트와 최대 3단계 하위까지 테스트 디렉터리 탐색 (`TESTS_DIRECTORY_FOUND`) |
|
|
68
|
+
| import 이름의 배포판 미확인 | `UNMAPPED_IMPORTS` 노트, `uv add` 명령을 만들지 않고 배포 이름 확인을 요청 |
|
|
69
|
+
| 테스트 선별이 전부를 선택 | `narrowed: false`와 `NO_NARROWING` 경고; import 근거가 없으면 `SELECTION_WITHOUT_IMPORT_EVIDENCE` |
|
|
54
70
|
| 읽을 수 없는 디렉터리 | 해당 항목만 "없음"으로 처리, 전체 스캔은 계속 |
|
|
55
71
|
|
|
56
72
|
## 릴리스 호환성 (Release compatibility)
|
package/docs/tools.md
CHANGED
|
@@ -150,10 +150,12 @@ Compare imports found with ast against declared dependencies, dev groups, and uv
|
|
|
150
150
|
- `fileCount`: number
|
|
151
151
|
- `files`: array<string>
|
|
152
152
|
- `import`: string
|
|
153
|
+
- `providerKnown`: boolean
|
|
153
154
|
- `providers`: array<…>
|
|
154
155
|
- `reason`: string
|
|
155
156
|
- `suggestedDistribution`: string
|
|
156
157
|
- `typeCheckingOnly`: boolean
|
|
158
|
+
- `unmappedImports`: number
|
|
157
159
|
- `unparsable`: array<…>
|
|
158
160
|
- `unused`: array of
|
|
159
161
|
- `groups`: array<string>
|
|
@@ -184,6 +186,7 @@ Inspect the active Python interpreter, virtual environment, uv availability, and
|
|
|
184
186
|
**반환 `data` 형태**
|
|
185
187
|
|
|
186
188
|
- `interpreter`: string
|
|
189
|
+
- `interpreterOrigin`: string
|
|
187
190
|
- `projectRoot`: string
|
|
188
191
|
- `python`: object
|
|
189
192
|
- `basePrefix`: string
|
|
@@ -204,6 +207,8 @@ Inspect the active Python interpreter, virtual environment, uv availability, and
|
|
|
204
207
|
- `available`: boolean
|
|
205
208
|
- `declared`: boolean
|
|
206
209
|
- `executable`: string
|
|
210
|
+
- `installable`: boolean
|
|
211
|
+
- `installed`: boolean
|
|
207
212
|
- `name`: string
|
|
208
213
|
- `origin`: string
|
|
209
214
|
- `preferredInvocation`: string
|
|
@@ -286,6 +291,7 @@ Inspect pyproject.toml, uv.lock, dependency groups, layout, and tool configurati
|
|
|
286
291
|
- `installedScanned`: boolean
|
|
287
292
|
- `lockPresent`: boolean
|
|
288
293
|
- `projectEditable`: boolean
|
|
294
|
+
- `projectInstallable`: boolean
|
|
289
295
|
- `projectInstalled`: boolean
|
|
290
296
|
- `venvPresent`: boolean
|
|
291
297
|
- `complete`: boolean
|
|
@@ -293,6 +299,7 @@ Inspect pyproject.toml, uv.lock, dependency groups, layout, and tool configurati
|
|
|
293
299
|
- `conditional`: number
|
|
294
300
|
- `installedPackages`: number
|
|
295
301
|
- `lockPackages`: number
|
|
302
|
+
- `markerSplitNames`: number
|
|
296
303
|
- `mismatched`: number
|
|
297
304
|
- `missing`: number
|
|
298
305
|
- `untracked`: number
|
|
@@ -327,7 +334,10 @@ Inspect pyproject.toml, uv.lock, dependency groups, layout, and tool configurati
|
|
|
327
334
|
- `present`: boolean
|
|
328
335
|
- `modules`: array<string>
|
|
329
336
|
- `name`: string
|
|
330
|
-
- `notes`: array
|
|
337
|
+
- `notes`: array of
|
|
338
|
+
- `code`: string
|
|
339
|
+
- `message`: string
|
|
340
|
+
- `severity`: string
|
|
331
341
|
- `pyproject`: string
|
|
332
342
|
- `requirementsFiles`: array<…>
|
|
333
343
|
- `requiresPython`: string
|
|
@@ -368,13 +378,15 @@ Preview or run uv lock --check or uv sync --frozen. Execution is opt-in because
|
|
|
368
378
|
| 파라미터 | 타입 | 필수 | 설명 |
|
|
369
379
|
|---|---|---|---|
|
|
370
380
|
| `execute` | `boolean` | 아니오 | — |
|
|
371
|
-
| `
|
|
381
|
+
| `extras` | `"all" | "none"` | 아니오 | Whether sync requests every [project.optional-dependencies] extra. Defaults to all: without it uv removes extras such as the dev tooling. |
|
|
382
|
+
| `mode` | `"check" | "sync"` | 아니오 | check runs uv lock --check; sync runs uv sync --frozen --all-groups --all-extras. |
|
|
372
383
|
| `path` | `string` | 아니오 | — |
|
|
373
384
|
| `timeoutSeconds` | `integer` | 아니오 | 1..1800 |
|
|
374
385
|
|
|
375
386
|
**프롬프트 가이드라인**
|
|
376
387
|
|
|
377
388
|
- Use py_sync with execute=false to preview the uv command, and execute=true only when the environment must be created or refreshed.
|
|
389
|
+
- Use py_sync after changing pyproject.toml or uv.lock; a sync that removed packages is reported because later steps cannot run without them.
|
|
378
390
|
|
|
379
391
|
**반환 `data` 형태**
|
|
380
392
|
|
|
@@ -384,6 +396,7 @@ Preview or run uv lock --check or uv sync --frozen. Execution is opt-in because
|
|
|
384
396
|
- `executable`: string
|
|
385
397
|
- `risk`: string
|
|
386
398
|
- `executed`: boolean
|
|
399
|
+
- `extras`: string
|
|
387
400
|
- `lockPresent`: boolean
|
|
388
401
|
- `mode`: string
|
|
389
402
|
|
|
@@ -408,12 +421,14 @@ Check whether production Python changes have related test changes before impleme
|
|
|
408
421
|
|
|
409
422
|
**반환 `data` 형태**
|
|
410
423
|
|
|
424
|
+
- `associations`: array<…>
|
|
411
425
|
- `changedPaths`: array<string>
|
|
412
426
|
- `ok`: boolean
|
|
413
427
|
- `reasons`: array<string>
|
|
414
428
|
- `source`: string
|
|
415
429
|
- `sourceChanges`: array<string>
|
|
416
430
|
- `testChanges`: array<string>
|
|
431
|
+
- `weakAssociation`: boolean
|
|
417
432
|
|
|
418
433
|
### `py_test`
|
|
419
434
|
|
|
@@ -428,6 +443,7 @@ Preview or run pytest through uv run --frozen and summarise failures by test, fi
|
|
|
428
443
|
| 파라미터 | 타입 | 필수 | 설명 |
|
|
429
444
|
|---|---|---|---|
|
|
430
445
|
| `execute` | `boolean` | 아니오 | — |
|
|
446
|
+
| `extraArgs` | `array<string>` | 아니오 | Extra pytest arguments passed verbatim as an argument array, e.g. ["--cov=my_pkg", "--cov-branch"] or ["-m", "unit"]. |
|
|
431
447
|
| `keyword` | `string` | 아니오 | pytest -k expression. |
|
|
432
448
|
| `lastFailed` | `boolean` | 아니오 | Rerun only tests that failed last time (--lf). |
|
|
433
449
|
| `maxFail` | `integer` | 아니오 | 1..1000 |
|
|
@@ -439,6 +455,7 @@ Preview or run pytest through uv run --frozen and summarise failures by test, fi
|
|
|
439
455
|
|
|
440
456
|
- Use py_test with execute=false first; a preview is never a passing test run.
|
|
441
457
|
- Use py_test after changing Python sources; it does not rebuild anything, so run py_sync first when dependencies changed.
|
|
458
|
+
- Use py_test with extraArgs to run project-standard pytest flags such as coverage options that the tool does not model directly.
|
|
442
459
|
|
|
443
460
|
**반환 `data` 형태**
|
|
444
461
|
|
|
@@ -475,11 +492,15 @@ Select focused pytest targets from changed files using pytest naming conventions
|
|
|
475
492
|
- `changedTestFiles`: array<…>
|
|
476
493
|
- `consideredTestFiles`: array<string>
|
|
477
494
|
- `fellBackToAll`: boolean
|
|
495
|
+
- `importEvidenceUsed`: boolean
|
|
496
|
+
- `narrowed`: boolean
|
|
497
|
+
- `noNarrowing`: boolean
|
|
478
498
|
- `pytestTargets`: array<string>
|
|
479
499
|
- `selected`: array of
|
|
480
500
|
- `path`: string
|
|
481
501
|
- `reason`: string
|
|
482
502
|
- `score`: number
|
|
503
|
+
- `supportFiles`: array<…>
|
|
483
504
|
|
|
484
505
|
### `py_validation_bundle`
|
|
485
506
|
|
|
@@ -495,6 +516,7 @@ Preview or run one evidence-oriented sequence: uv lock --check, uv sync --frozen
|
|
|
495
516
|
|---|---|---|---|
|
|
496
517
|
| `execute` | `boolean` | 아니오 | — |
|
|
497
518
|
| `path` | `string` | 아니오 | — |
|
|
519
|
+
| `quality` | `boolean` | 아니오 | Run the lint/type tools the project declares (ruff, pyright, and mypy when [tool.mypy] exists). Defaults to true. |
|
|
498
520
|
| `targets` | `array<string>` | 아니오 | — |
|
|
499
521
|
| `timeoutSeconds` | `integer` | 아니오 | 1..3600 |
|
|
500
522
|
|
|
@@ -507,6 +529,7 @@ Preview or run one evidence-oriented sequence: uv lock --check, uv sync --frozen
|
|
|
507
529
|
|
|
508
530
|
- `executed`: boolean
|
|
509
531
|
- `lockPresent`: boolean
|
|
532
|
+
- `quality`: array<string>
|
|
510
533
|
- `steps`: array of
|
|
511
534
|
- `args`: array<string>
|
|
512
535
|
- `cwd`: string
|
package/extensions/shared.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';
|
|
2
|
+
import { open } from 'node:fs/promises';
|
|
2
3
|
import { basename, dirname, resolve } from 'node:path';
|
|
3
4
|
import { findProjectRoot, isDirectory, isFile } from '../src/project/root.ts';
|
|
4
5
|
|
|
@@ -43,6 +44,29 @@ export async function hasDirectory(path: string): Promise<boolean> {
|
|
|
43
44
|
return isDirectory(path);
|
|
44
45
|
}
|
|
45
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Read a small configuration file, or `undefined` when it is absent.
|
|
49
|
+
*
|
|
50
|
+
* The read is bounded because `setup.cfg` and `tox.ini` can be arbitrarily
|
|
51
|
+
* large and only a section header is needed from them.
|
|
52
|
+
*/
|
|
53
|
+
export async function readTextIfExists(
|
|
54
|
+
path: string,
|
|
55
|
+
maxBytes = 64 * 1024,
|
|
56
|
+
): Promise<string | undefined> {
|
|
57
|
+
let handle;
|
|
58
|
+
try {
|
|
59
|
+
handle = await open(path, 'r');
|
|
60
|
+
const buffer = Buffer.alloc(maxBytes);
|
|
61
|
+
const { bytesRead } = await handle.read(buffer, 0, maxBytes, 0);
|
|
62
|
+
return buffer.subarray(0, bytesRead).toString('utf8');
|
|
63
|
+
} catch {
|
|
64
|
+
return undefined;
|
|
65
|
+
} finally {
|
|
66
|
+
await handle?.close().catch(() => undefined);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
46
70
|
export function messageOf(error: unknown): string {
|
|
47
71
|
return error instanceof Error ? error.message : String(error);
|
|
48
72
|
}
|