pi-python-helper 0.4.2 → 0.4.3

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 CHANGED
@@ -7,6 +7,16 @@ does not guarantee a stable public tool schema.
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.4.3] - 2026-09-21
11
+
12
+ ### Fixed
13
+
14
+ - A project named through `path` now runs `uv` and `pytest` in the resolved project root instead of `ctx.cwd`. `py_sync`, `py_test`, `py_test_select`, `py_validation_bundle`, and `py_tdd_checkpoint` previously generated and executed their commands in the session directory, so a project reached through `path` failed with "No `pyproject.toml` found in current directory" or ran the wrong tests.
15
+
16
+ ### Added
17
+
18
+ - `py_tdd_checkpoint` accepts an optional `path` for git discovery.
19
+
10
20
  ## [0.4.2] - 2026-09-21
11
21
 
12
22
  ### Removed
package/docs/tools.md CHANGED
@@ -415,6 +415,7 @@ Check whether production Python changes have related test changes before impleme
415
415
  | 파라미터 | 타입 | 필수 | 설명 |
416
416
  |---|---|---|---|
417
417
  | `changedPaths` | `array<string>` | 아니오 | maxItems 500 |
418
+ | `path` | `string` | 아니오 | — |
418
419
  | `testChangedPaths` | `array<string>` | 아니오 | maxItems 500 |
419
420
 
420
421
  **프롬프트 가이드라인**
@@ -89,7 +89,7 @@ export function registerTestingTools(pi: Pi): void {
89
89
  let changed = params.changedPaths ?? [];
90
90
  let changedSource = 'argument';
91
91
  if (!changed.length) {
92
- const discovered = await changedPaths(ctx.cwd, signal);
92
+ const discovered = await changedPaths(root, signal);
93
93
  changed = discovered.paths;
94
94
  changedSource = discovered.source === 'git' ? 'git' : (discovered.error ?? 'none');
95
95
  }
@@ -109,7 +109,7 @@ export function registerTestingTools(pi: Pi): void {
109
109
  (entry) => isRunnableTestFile(entry.path) || basename(entry.path) === 'conftest.py',
110
110
  )
111
111
  .map((entry) => entry.path);
112
- const command = pytestCommand(ctx.cwd, {
112
+ const command = pytestCommand(root, {
113
113
  targets: selection.fellBackToAll ? [] : targets,
114
114
  });
115
115
 
@@ -232,7 +232,7 @@ export function registerTestingTools(pi: Pi): void {
232
232
  const started = Date.now();
233
233
  try {
234
234
  const root = (await resolveProjectRoot(ctx.cwd, params.path)) ?? ctx.cwd;
235
- const command = pytestCommand(ctx.cwd, {
235
+ const command = pytestCommand(root, {
236
236
  targets: params.targets,
237
237
  lastFailed: params.lastFailed,
238
238
  keyword: params.keyword,
@@ -261,7 +261,7 @@ export function registerTestingTools(pi: Pi): void {
261
261
  }
262
262
 
263
263
  const run = await runCommand(command.executable, command.args, {
264
- cwd: ctx.cwd,
264
+ cwd: root,
265
265
  signal,
266
266
  timeoutMs: (params.timeoutSeconds ?? 900) * 1000,
267
267
  maxBytes: 512 * 1024,
@@ -73,7 +73,7 @@ export function registerValidationTools(pi: Pi): void {
73
73
  const root = (await resolveProjectRoot(ctx.cwd, params.path)) ?? ctx.cwd;
74
74
  const mode = params.mode ?? 'check';
75
75
  const extras = params.extras ?? 'all';
76
- const command = mode === 'check' ? uvLockCheck(ctx.cwd) : uvSyncFrozen(ctx.cwd, { extras });
76
+ const command = mode === 'check' ? uvLockCheck(root) : uvSyncFrozen(root, { extras });
77
77
  const lockPresent = await isFile(join(root, 'uv.lock'));
78
78
 
79
79
  if (!params.execute) {
@@ -109,7 +109,7 @@ export function registerValidationTools(pi: Pi): void {
109
109
  }
110
110
 
111
111
  const run = await runCommand(command.executable, command.args, {
112
- cwd: ctx.cwd,
112
+ cwd: root,
113
113
  signal,
114
114
  timeoutMs: (params.timeoutSeconds ?? 600) * 1000,
115
115
  maxBytes: 256 * 1024,
@@ -296,9 +296,9 @@ export function registerValidationTools(pi: Pi): void {
296
296
  try {
297
297
  const root = (await resolveProjectRoot(ctx.cwd, params.path)) ?? ctx.cwd;
298
298
  const lockPresent = await isFile(join(root, 'uv.lock'));
299
- const lock = uvLockCheck(ctx.cwd);
300
- const sync = uvSyncFrozen(ctx.cwd);
301
- const test = pytestCommand(ctx.cwd, { targets: params.targets });
299
+ const lock = uvLockCheck(root);
300
+ const sync = uvSyncFrozen(root);
301
+ const test = pytestCommand(root, { targets: params.targets });
302
302
  const timeoutMs = (params.timeoutSeconds ?? 1800) * 1000;
303
303
  const runQuality = params.quality ?? true;
304
304
 
@@ -317,7 +317,7 @@ export function registerValidationTools(pi: Pi): void {
317
317
  toolConfiguration: manifestScan.payload?.manifest?.toolConfiguration,
318
318
  })
319
319
  : [];
320
- const quality = qualityCommands(ctx.cwd, runners);
320
+ const quality = qualityCommands(root, runners);
321
321
  const commands = [lock, sync, test, ...quality];
322
322
 
323
323
  if (!params.execute) {
@@ -369,14 +369,14 @@ export function registerValidationTools(pi: Pi): void {
369
369
 
370
370
  const lockRun = lockPresent
371
371
  ? await runCommand(lock.executable, lock.args, {
372
- cwd: ctx.cwd,
372
+ cwd: root,
373
373
  signal,
374
374
  timeoutMs,
375
375
  maxBytes: 256 * 1024,
376
376
  })
377
377
  : undefined;
378
378
  const syncRun = await runCommand(sync.executable, sync.args, {
379
- cwd: ctx.cwd,
379
+ cwd: root,
380
380
  signal,
381
381
  timeoutMs,
382
382
  maxBytes: 256 * 1024,
@@ -400,7 +400,7 @@ export function registerValidationTools(pi: Pi): void {
400
400
 
401
401
  const testRun = syncOk
402
402
  ? await runCommand(test.executable, test.args, {
403
- cwd: ctx.cwd,
403
+ cwd: root,
404
404
  signal,
405
405
  timeoutMs,
406
406
  maxBytes: 512 * 1024,
@@ -466,7 +466,7 @@ export function registerValidationTools(pi: Pi): void {
466
466
  }
467
467
  const preview = quality[index];
468
468
  const run = await runCommand(preview.executable, preview.args, {
469
- cwd: ctx.cwd,
469
+ cwd: root,
470
470
  signal,
471
471
  timeoutMs,
472
472
  maxBytes: 256 * 1024,
@@ -615,14 +615,16 @@ export function registerValidationTools(pi: Pi): void {
615
615
  parameters: Type.Object({
616
616
  changedPaths: Type.Optional(Type.Array(Type.String(), { maxItems: 500 })),
617
617
  testChangedPaths: Type.Optional(Type.Array(Type.String(), { maxItems: 500 })),
618
+ path: Type.Optional(Type.String()),
618
619
  }),
619
620
  async execute(_id, params, signal, _update, ctx) {
620
621
  const started = Date.now();
622
+ const root = (await resolveProjectRoot(ctx.cwd, params.path)) ?? ctx.cwd;
621
623
  let changedPaths = params.changedPaths ?? [];
622
624
  let source = 'argument';
623
625
  if (!changedPaths.length) {
624
626
  const diff = await runCommand('git', ['diff', '--name-only', 'HEAD'], {
625
- cwd: ctx.cwd,
627
+ cwd: root,
626
628
  signal,
627
629
  timeoutMs: 10000,
628
630
  maxBytes: 100_000,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-python-helper",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "Python (uv) development tools for the pi coding agent",
5
5
  "type": "module",
6
6
  "keywords": [