pi-python-helper 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.
package/docs/tools.md ADDED
@@ -0,0 +1,518 @@
1
+ # 도구 레퍼런스 (Tool reference)
2
+
3
+ > 이 문서는 생성된 파일입니다. 직접 편집하지 마세요.
4
+ >
5
+ > ```bash
6
+ > npm run docs # 이 문서와 docs/api-surface.json을 다시 생성
7
+ > ```
8
+ >
9
+ > 출처: `extensions/`의 도구 등록(설명·파라미터)과 `docs/api-surface.json`(반환 형태 스냅샷).
10
+
11
+ ## 응답 규격 (Response envelope)
12
+
13
+ 모든 도구는 동일한 `PyToolResult` 규격을 반환합니다. 반환 형태는 구조만 기록하며 값·경로·버전·소요시간은 스냅샷에서 제외합니다.
14
+
15
+ - `commands` (optional): array of
16
+ - `args`: array<string>
17
+ - `cwd`: string
18
+ - `executable`: string
19
+ - `risk`: string
20
+ - `data`: object
21
+ - `errors`: array of
22
+ - `code`: string
23
+ - `line` (optional): number
24
+ - `message`: string
25
+ - `path` (optional): string
26
+ - `severity`: string
27
+ - `evidence`: array of object
28
+ - `metadata`: object
29
+ - `cwd`: string
30
+ - `durationMs`: number
31
+ - `projectRoot` (optional): string
32
+ - `pythonVersion` (optional): string
33
+ - `toolVersion`: string
34
+ - `truncated`: boolean
35
+ - `ok`: boolean
36
+ - `projectRoot` (optional): string
37
+ - `pythonVersion` (optional): string
38
+ - `suggestions`: array of
39
+ - `command` (optional): string
40
+ - `confidence`: string
41
+ - `message`: string
42
+ - `summary`: string
43
+ - `warnings`: array of
44
+ - `code`: string
45
+ - `message`: string
46
+ - `path` (optional): string
47
+ - `severity`: string
48
+
49
+ `data`와 `evidence`의 내부 형태는 도구마다 다르며, 아래 각 도구 섹션에 기록되어 있습니다.
50
+
51
+ ## 스냅샷 캡처 조건 (Capture conditions)
52
+
53
+ 반환 형태는 다음 파라미터로 참조 프로젝트(`scripts/api-docs/fixture.ts`)를 진단해 캡처했습니다. 모든 호출은 읽기 전용입니다.
54
+
55
+ | 도구 | 파라미터 |
56
+ |---|---|
57
+ | `py_environment` | `{}` |
58
+ | `py_project_inspect` | `{}` |
59
+ | `py_dependency_plan` | `{"includeUnused":true}` |
60
+ | `py_tdd_checkpoint` | `{"changedPaths":["src/ledger/totals.py","tests/test_other.py"]}` |
61
+ | `py_completion_evidence` | `{"syncExecuted":false,"syncOk":false,"testExecuted":false,"testOk":false,"stale":false,"changedPaths":["src/le…` |
62
+ | `py_test_select` | `{"changedPaths":["src/ledger/totals.py"]}` |
63
+ | `py_test` | `{"execute":false}` |
64
+ | `py_failure_diagnose` | `{"output":"============================= test session starts ==============================\ncollected 2 items…` |
65
+ | `py_sync` | `{"mode":"check","execute":false}` |
66
+ | `py_validation_bundle` | `{"execute":false}` |
67
+
68
+ ## 도구 (Tools)
69
+
70
+ ### `py_completion_evidence`
71
+
72
+ Build a conservative completion report from environment sync and test execution results. Read-only.
73
+
74
+ - 시스템 프롬프트 한 줄: `Create evidence for a Python completion report`
75
+ - 라벨: Python Completion Evidence
76
+ - 프로젝트 상태 변경: 없음 (읽기 전용)
77
+
78
+ **파라미터**
79
+
80
+ | 파라미터 | 타입 | 필수 | 설명 |
81
+ |---|---|---|---|
82
+ | `changedPaths` | `array<string>` | 예 | maxItems 500 |
83
+ | `stale` | `boolean` | 예 | Whether stale artifacts were detected. |
84
+ | `syncExecuted` | `boolean` | 예 | Whether uv lock --check / uv sync actually ran. |
85
+ | `syncOk` | `boolean` | 예 | — |
86
+ | `testExecuted` | `boolean` | 예 | Whether pytest actually ran. |
87
+ | `testOk` | `boolean` | 예 | — |
88
+
89
+ **프롬프트 가이드라인**
90
+
91
+ - Use py_completion_evidence before claiming Python work is complete; a partial run is not evidence.
92
+
93
+ **반환 `data` 형태**
94
+
95
+ - `blockers`: array<string>
96
+ - `changedPaths`: array<string>
97
+ - `ok`: boolean
98
+
99
+ ### `py_dependency_plan`
100
+
101
+ Compare imports found with ast against declared dependencies, dev groups, and uv.lock, and preview the uv commands that would fix the drift. Read-only.
102
+
103
+ - 시스템 프롬프트 한 줄: `Plan Python dependency changes from declared and imported packages`
104
+ - 라벨: Python Dependency Plan
105
+ - 프로젝트 상태 변경: 없음 (읽기 전용)
106
+
107
+ **파라미터**
108
+
109
+ | 파라미터 | 타입 | 필수 | 설명 |
110
+ |---|---|---|---|
111
+ | `includeUnused` | `boolean` | 아니오 | Also report declared packages that no file imports. Off by default because runtime plugins and console tools produce false positives. |
112
+ | `path` | `string` | 아니오 | Project directory to analyse. |
113
+
114
+ **프롬프트 가이드라인**
115
+
116
+ - Use py_dependency_plan before editing dependencies, and whenever an import fails or a package may be declared in the wrong group.
117
+ - Use py_dependency_plan to detect drift between pyproject.toml and uv.lock instead of reading the lockfile by hand.
118
+
119
+ **반환 `data` 형태**
120
+
121
+ - `declared`: array of
122
+ - `groups`: array<string>
123
+ - `name`: string
124
+ - `normalized`: string
125
+ - `declaredCount`: number
126
+ - `drift`: object
127
+ - `lockPresent`: boolean
128
+ - `missingFromLock`: array<string>
129
+ - `requiresPythonMismatch`: null
130
+ - `unsatisfiedInLock`: array of
131
+ - `locked`: string
132
+ - `name`: string
133
+ - `specifier`: string
134
+ - `misplaced`: array of
135
+ - `declaredIn`: array<string>
136
+ - `distribution`: string
137
+ - `import`: string
138
+ - `runtimeFiles`: array<string>
139
+ - `notes`: array of
140
+ - `code`: string
141
+ - `message`: string
142
+ - `severity`: string
143
+ - `providerMappingReliable`: boolean
144
+ - `suggestions`: array of
145
+ - `command`: string
146
+ - `confidence`: string
147
+ - `message`: string
148
+ - `thirdPartyImportCount`: number
149
+ - `undeclared`: array of
150
+ - `fileCount`: number
151
+ - `files`: array<string>
152
+ - `import`: string
153
+ - `providers`: array<…>
154
+ - `reason`: string
155
+ - `suggestedDistribution`: string
156
+ - `typeCheckingOnly`: boolean
157
+ - `unparsable`: array<…>
158
+ - `unused`: array of
159
+ - `groups`: array<string>
160
+ - `name`: string
161
+ - `normalized`: string
162
+ - `warnings`: array of
163
+ - `code`: string
164
+ - `message`: string
165
+ - `path`: string
166
+ - `severity`: string
167
+
168
+ ### `py_environment`
169
+
170
+ Inspect the active Python interpreter, virtual environment, uv availability, and project root. Read-only.
171
+
172
+ - 시스템 프롬프트 한 줄: `Inspect the current Python interpreter and uv environment`
173
+ - 라벨: Python Environment
174
+ - 프로젝트 상태 변경: 없음 (읽기 전용)
175
+
176
+ **파라미터**
177
+
178
+ (파라미터 없음)
179
+
180
+ **프롬프트 가이드라인**
181
+
182
+ - Use py_environment before running Python commands when the active interpreter, virtual environment, or uv availability is unknown.
183
+
184
+ **반환 `data` 형태**
185
+
186
+ - `interpreter`: string
187
+ - `projectRoot`: string
188
+ - `python`: object
189
+ - `basePrefix`: string
190
+ - `candidateVenvDir`: string
191
+ - `condaPrefix`: null
192
+ - `executable`: string
193
+ - `implementation`: string
194
+ - `inVirtualEnvironment`: boolean
195
+ - `platform`: string
196
+ - `prefix`: string
197
+ - `stdlibModuleNames`: boolean
198
+ - `tomlAvailable`: boolean
199
+ - `version`: string
200
+ - `versionInfo`: array<number>
201
+ - `virtualEnv`: null
202
+ - `suggestions`: array<…>
203
+ - `tools`: array of
204
+ - `available`: boolean
205
+ - `declared`: boolean
206
+ - `executable`: string
207
+ - `name`: string
208
+ - `origin`: string
209
+ - `preferredInvocation`: string
210
+ - `version` (optional): string
211
+ - `versionSource`: string
212
+ - `uv`: object
213
+ - `available`: boolean
214
+ - `lockPresent`: boolean
215
+ - `version`: string
216
+ - `venvDir`: string
217
+ - `warnings`: array<…>
218
+
219
+ ### `py_failure_diagnose`
220
+
221
+ Classify the first actionable cause in bounded Python, pytest, or uv output and point at the first non-library traceback frame. Read-only.
222
+
223
+ - 시스템 프롬프트 한 줄: `Diagnose the first actionable Python failure`
224
+ - 라벨: Python Failure Diagnose
225
+ - 프로젝트 상태 변경: 없음 (읽기 전용)
226
+
227
+ **파라미터**
228
+
229
+ | 파라미터 | 타입 | 필수 | 설명 |
230
+ |---|---|---|---|
231
+ | `output` | `string` | 예 | Bounded stdout/stderr from the failing command. |
232
+ | `path` | `string` | 아니오 | Project directory used to classify the missing module. |
233
+
234
+ **프롬프트 가이드라인**
235
+
236
+ - Use py_failure_diagnose on bounded command output instead of reading a full traceback in context; traceback frames inside site-packages are never the cause.
237
+
238
+ **반환 `data` 형태**
239
+
240
+ - `evidence`: array of
241
+ - `file`: string
242
+ - `line`: number
243
+ - `message`: string
244
+ - `exceptionType`: string
245
+ - `firstUserFrame`: object
246
+ - `func`: string
247
+ - `library`: boolean
248
+ - `line`: number
249
+ - `path`: string
250
+ - `frames`: array of
251
+ - `func`: string
252
+ - `library`: boolean
253
+ - `line`: number
254
+ - `path`: string
255
+ - `kind`: string
256
+ - `libraryFrameCount`: number
257
+ - `suggestions`: array of
258
+ - `confidence`: string
259
+ - `message`: string
260
+ - `summary`: string
261
+ - `totalFrameCount`: number
262
+
263
+ ### `py_project_inspect`
264
+
265
+ Inspect pyproject.toml, uv.lock, dependency groups, layout, and tool configuration, and report lockfile drift. Read-only.
266
+
267
+ - 시스템 프롬프트 한 줄: `Inspect a Python project manifest and lockfile`
268
+ - 라벨: Python Project Inspect
269
+ - 프로젝트 상태 변경: 없음 (읽기 전용)
270
+
271
+ **파라미터**
272
+
273
+ | 파라미터 | 타입 | 필수 | 설명 |
274
+ |---|---|---|---|
275
+ | `path` | `string` | 아니오 | Project directory, pyproject.toml path, or uv.lock path. |
276
+
277
+ **프롬프트 가이드라인**
278
+
279
+ - Use py_project_inspect before editing pyproject.toml or uv.lock, and whenever the project layout or dependency groups are unclear.
280
+
281
+ **반환 `data` 형태**
282
+
283
+ - `buildBackend`: string
284
+ - `conformance`: object
285
+ - `checks`: object
286
+ - `installedScanned`: boolean
287
+ - `lockPresent`: boolean
288
+ - `projectEditable`: boolean
289
+ - `projectInstalled`: boolean
290
+ - `venvPresent`: boolean
291
+ - `complete`: boolean
292
+ - `counts`: object
293
+ - `conditional`: number
294
+ - `installedPackages`: number
295
+ - `lockPackages`: number
296
+ - `mismatched`: number
297
+ - `missing`: number
298
+ - `untracked`: number
299
+ - `findings`: array of
300
+ - `code`: string
301
+ - `expected`: string
302
+ - `message`: string
303
+ - `name`: string
304
+ - `notes`: array<…>
305
+ - `reason`: string
306
+ - `verdict`: string
307
+ - `warnings`: array of
308
+ - `code`: string
309
+ - `message`: string
310
+ - `severity`: string
311
+ - `dependencyCounts`: object
312
+ - `groups`: object
313
+ - `dev`: number
314
+ - `optional`: object
315
+ - `aws`: number
316
+ - `runtime`: number
317
+ - `entryPoints`: array<…>
318
+ - `importName`: string
319
+ - `installed`: object
320
+ - `count`: number
321
+ - `editableCount`: number
322
+ - `sitePackages`: string
323
+ - `layout`: string
324
+ - `lock`: object
325
+ - `packageCount`: number
326
+ - `path`: string
327
+ - `present`: boolean
328
+ - `modules`: array<string>
329
+ - `name`: string
330
+ - `notes`: array<…>
331
+ - `pyproject`: string
332
+ - `requirementsFiles`: array<…>
333
+ - `requiresPython`: string
334
+ - `root`: string
335
+ - `suggestions`: array of
336
+ - `command`: string
337
+ - `confidence`: string
338
+ - `message`: string
339
+ - `toolConfiguration`: object
340
+ - `coverage`: boolean
341
+ - `hatch`: boolean
342
+ - `mypy`: boolean
343
+ - `pyright`: boolean
344
+ - `pytest`: boolean
345
+ - `ruff`: boolean
346
+ - `ty`: boolean
347
+ - `uvLock`: string
348
+ - `uvSources`: array<…>
349
+ - `uvWorkspaceMembers`: array<…>
350
+ - `venvDir`: string
351
+ - `version`: string
352
+ - `warnings`: array of
353
+ - `code`: string
354
+ - `message`: string
355
+ - `path` (optional): string
356
+ - `severity`: string
357
+
358
+ ### `py_sync`
359
+
360
+ Preview or run uv lock --check or uv sync --frozen. Execution is opt-in because it modifies .venv. Does not edit sources.
361
+
362
+ - 시스템 프롬프트 한 줄: `Preview or run the uv environment sync`
363
+ - 라벨: Python Sync
364
+ - 프로젝트 상태 변경: `execute: true` 옵트인 필요
365
+
366
+ **파라미터**
367
+
368
+ | 파라미터 | 타입 | 필수 | 설명 |
369
+ |---|---|---|---|
370
+ | `execute` | `boolean` | 아니오 | — |
371
+ | `mode` | `"check" | "sync"` | 아니오 | check runs uv lock --check; sync runs uv sync --frozen --all-groups. |
372
+ | `path` | `string` | 아니오 | — |
373
+ | `timeoutSeconds` | `integer` | 아니오 | 1..1800 |
374
+
375
+ **프롬프트 가이드라인**
376
+
377
+ - Use py_sync with execute=false to preview the uv command, and execute=true only when the environment must be created or refreshed.
378
+
379
+ **반환 `data` 형태**
380
+
381
+ - `command`: object
382
+ - `args`: array<string>
383
+ - `cwd`: string
384
+ - `executable`: string
385
+ - `risk`: string
386
+ - `executed`: boolean
387
+ - `lockPresent`: boolean
388
+ - `mode`: string
389
+
390
+ ### `py_tdd_checkpoint`
391
+
392
+ Check whether production Python changes have related test changes before implementation is considered complete. Read-only.
393
+
394
+ - 시스템 프롬프트 한 줄: `Check the Python TDD checkpoint for changed files`
395
+ - 라벨: Python TDD Checkpoint
396
+ - 프로젝트 상태 변경: 없음 (읽기 전용)
397
+
398
+ **파라미터**
399
+
400
+ | 파라미터 | 타입 | 필수 | 설명 |
401
+ |---|---|---|---|
402
+ | `changedPaths` | `array<string>` | 아니오 | maxItems 500 |
403
+ | `testChangedPaths` | `array<string>` | 아니오 | maxItems 500 |
404
+
405
+ **프롬프트 가이드라인**
406
+
407
+ - Use py_tdd_checkpoint before reporting Python implementation work as complete.
408
+
409
+ **반환 `data` 형태**
410
+
411
+ - `changedPaths`: array<string>
412
+ - `ok`: boolean
413
+ - `reasons`: array<string>
414
+ - `source`: string
415
+ - `sourceChanges`: array<string>
416
+ - `testChanges`: array<string>
417
+
418
+ ### `py_test`
419
+
420
+ Preview or run pytest through uv run --frozen and summarise failures by test, file, and first project frame. Does not modify sources.
421
+
422
+ - 시스템 프롬프트 한 줄: `Preview or run Python tests and summarise failures`
423
+ - 라벨: Python Test
424
+ - 프로젝트 상태 변경: `execute: true` 옵트인 필요
425
+
426
+ **파라미터**
427
+
428
+ | 파라미터 | 타입 | 필수 | 설명 |
429
+ |---|---|---|---|
430
+ | `execute` | `boolean` | 아니오 | — |
431
+ | `keyword` | `string` | 아니오 | pytest -k expression. |
432
+ | `lastFailed` | `boolean` | 아니오 | Rerun only tests that failed last time (--lf). |
433
+ | `maxFail` | `integer` | 아니오 | 1..1000 |
434
+ | `path` | `string` | 아니오 | — |
435
+ | `targets` | `array<string>` | 아니오 | — |
436
+ | `timeoutSeconds` | `integer` | 아니오 | 1..3600 |
437
+
438
+ **프롬프트 가이드라인**
439
+
440
+ - Use py_test with execute=false first; a preview is never a passing test run.
441
+ - Use py_test after changing Python sources; it does not rebuild anything, so run py_sync first when dependencies changed.
442
+
443
+ **반환 `data` 형태**
444
+
445
+ - `command`: object
446
+ - `args`: array<string>
447
+ - `cwd`: string
448
+ - `executable`: string
449
+ - `risk`: string
450
+ - `executed`: boolean
451
+
452
+ ### `py_test_select`
453
+
454
+ Select focused pytest targets from changed files using pytest naming conventions, without running tests. Read-only.
455
+
456
+ - 시스템 프롬프트 한 줄: `Select focused Python tests from changed files`
457
+ - 라벨: Python Test Select
458
+ - 프로젝트 상태 변경: 없음 (읽기 전용)
459
+
460
+ **파라미터**
461
+
462
+ | 파라미터 | 타입 | 필수 | 설명 |
463
+ |---|---|---|---|
464
+ | `changedPaths` | `array<string>` | 아니오 | Changed paths; defaults to git diff plus untracked files. |
465
+ | `path` | `string` | 아니오 | Project directory to scan for test files. |
466
+ | `testFiles` | `array<string>` | 아니오 | Known test files; defaults to a project scan. |
467
+
468
+ **프롬프트 가이드라인**
469
+
470
+ - Use py_test_select after changing Python source to choose a focused pytest target instead of running the whole suite.
471
+
472
+ **반환 `data` 형태**
473
+
474
+ - `changedSourceFiles`: array<string>
475
+ - `changedTestFiles`: array<…>
476
+ - `consideredTestFiles`: array<string>
477
+ - `fellBackToAll`: boolean
478
+ - `pytestTargets`: array<string>
479
+ - `selected`: array of
480
+ - `path`: string
481
+ - `reason`: string
482
+ - `score`: number
483
+
484
+ ### `py_validation_bundle`
485
+
486
+ Preview or run one evidence-oriented sequence: uv lock --check, uv sync --frozen, pytest, and a stale-artifact check. Execution is opt-in.
487
+
488
+ - 시스템 프롬프트 한 줄: `Run the Python sync and test validation bundle`
489
+ - 라벨: Python Validation Bundle
490
+ - 프로젝트 상태 변경: `execute: true` 옵트인 필요
491
+
492
+ **파라미터**
493
+
494
+ | 파라미터 | 타입 | 필수 | 설명 |
495
+ |---|---|---|---|
496
+ | `execute` | `boolean` | 아니오 | — |
497
+ | `path` | `string` | 아니오 | — |
498
+ | `targets` | `array<string>` | 아니오 | — |
499
+ | `timeoutSeconds` | `integer` | 아니오 | 1..3600 |
500
+
501
+ **프롬프트 가이드라인**
502
+
503
+ - Use py_validation_bundle with execute=false first; a preview is never a passing validation.
504
+ - Use py_validation_bundle as the single completion gate after changing Python sources or dependencies.
505
+
506
+ **반환 `data` 형태**
507
+
508
+ - `executed`: boolean
509
+ - `lockPresent`: boolean
510
+ - `steps`: array of
511
+ - `args`: array<string>
512
+ - `cwd`: string
513
+ - `executable`: string
514
+ - `risk`: string
515
+
516
+ ## 대화형 명령어 (Commands)
517
+
518
+ - `/py-status` — Show a concise Python interpreter and uv project status
@@ -0,0 +1,28 @@
1
+ import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
2
+ import { detectPythonEnvironment } from '../src/environment/discovery.ts';
3
+ import { registerDependencyTools } from './tools/dependencies.ts';
4
+ import { registerEnvironmentTools } from './tools/environment.ts';
5
+ import { registerTestingTools } from './tools/testing.ts';
6
+ import { registerValidationTools } from './tools/validation.ts';
7
+
8
+ export default function (pi: ExtensionAPI): void {
9
+ registerEnvironmentTools(pi);
10
+ registerDependencyTools(pi);
11
+ registerTestingTools(pi);
12
+ registerValidationTools(pi);
13
+
14
+ pi.registerCommand('py-status', {
15
+ description: 'Show a concise Python interpreter and uv project status',
16
+ handler: async (_args, ctx) => {
17
+ const environment = await detectPythonEnvironment(ctx.cwd);
18
+ const python = environment.python;
19
+ const ok = Boolean(environment.interpreter);
20
+ ctx.ui.notify(
21
+ `Python ${python?.version ?? 'unavailable'} · ${python?.inVirtualEnvironment ? 'virtual environment' : 'system interpreter'} · uv ${
22
+ environment.uv.available ? (environment.uv.version ?? 'ready') : 'missing'
23
+ } · ${environment.projectRoot ?? 'no project root'}`,
24
+ ok ? 'info' : 'warning',
25
+ );
26
+ },
27
+ });
28
+ }
@@ -0,0 +1,48 @@
1
+ import type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';
2
+ import { basename, dirname, resolve } from 'node:path';
3
+ import { findProjectRoot, isDirectory, isFile } from '../src/project/root.ts';
4
+
5
+ export type Pi = ExtensionAPI;
6
+ export type Ctx = ExtensionContext;
7
+
8
+ /**
9
+ * Every tool returns JSON text plus the same object in `details`, so the UI can
10
+ * render structured results while the model reads a single stable document.
11
+ */
12
+ export function text(value: unknown): {
13
+ content: { type: 'text'; text: string }[];
14
+ details: unknown;
15
+ } {
16
+ return {
17
+ content: [{ type: 'text' as const, text: JSON.stringify(value, null, 2) }],
18
+ details: value,
19
+ };
20
+ }
21
+
22
+ /**
23
+ * Accept a project directory, a `pyproject.toml` path, or nothing (the current
24
+ * directory). Never assume the caller passed a directory.
25
+ */
26
+ export async function resolveProjectRoot(
27
+ cwd: string,
28
+ requested?: string,
29
+ ): Promise<string | undefined> {
30
+ if (!requested) return findProjectRoot(cwd);
31
+ const candidate = resolve(cwd, requested);
32
+ if ((await isFile(candidate)) && basename(candidate) === 'pyproject.toml') {
33
+ return dirname(candidate);
34
+ }
35
+ if ((await isFile(candidate)) && basename(candidate) === 'uv.lock') {
36
+ return dirname(candidate);
37
+ }
38
+ if (await isDirectory(candidate)) return (await findProjectRoot(candidate)) ?? candidate;
39
+ return undefined;
40
+ }
41
+
42
+ export async function hasDirectory(path: string): Promise<boolean> {
43
+ return isDirectory(path);
44
+ }
45
+
46
+ export function messageOf(error: unknown): string {
47
+ return error instanceof Error ? error.message : String(error);
48
+ }