diff --git a/.release-please-manifest.json b/.release-please-manifest.json index da59f99..2aca35a 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.4.0" + ".": "0.5.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a84a2ac..f86919e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## [0.5.0](https://github.com/zenobi-us/pi-worktrees/compare/v0.4.0...v0.5.0) (2026-04-01) + + +### Features + +* **memory/epic:** add cli-only decoupling epic e9f4a1c2 ([4ce0639](https://github.com/zenobi-us/pi-worktrees/commit/4ce0639d4e09ee8f40b6baf2f20ba2848ef50ea8)) +* **memory:** add epic for exposing subcommands as tools ([bcb9abc](https://github.com/zenobi-us/pi-worktrees/commit/bcb9abcaadf2d9bef5985c8eb064ba64724c22cc)) +* **memory:** begin story 91 definition with task breakdown ([b75d1a1](https://github.com/zenobi-us/pi-worktrees/commit/b75d1a1020f89b814cea6cf83d70ecdfa81b81df)) +* **memory:** define worktrees tool discovery backlog ([8c6489f](https://github.com/zenobi-us/pi-worktrees/commit/8c6489fe2b28a2c375131ec68965c3089f30cf87)) +* **memory:** start cli-only execution planning ([43126bc](https://github.com/zenobi-us/pi-worktrees/commit/43126bcfd25c8f9f2778a81b003c4361fad98361)) +* **worktree:** add branch-first create flow with explicit generator mode ([2fdb605](https://github.com/zenobi-us/pi-worktrees/commit/2fdb605edc5346ac5e0f836aa98cf695b310f690)) +* **worktree:** improve interactive switching and lifecycle hooks (feat/interactive-ls-oncreate) ([#13](https://github.com/zenobi-us/pi-worktrees/issues/13)) ([b581f6e](https://github.com/zenobi-us/pi-worktrees/commit/b581f6eafa87badc4a4814125b09144df160d6ec)) + ## [0.4.0](https://github.com/zenobi-us/pi-worktrees/compare/v0.3.0...v0.4.0) (2026-03-21) diff --git a/package.json b/package.json index 083ae00..869ff21 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@zenobius/pi-worktrees", - "version": "0.4.0", + "version": "0.5.0", "description": "Worktrees extension for Pi Coding Agent", "author": { "name": "Zenobius", diff --git a/src/index.ts b/src/index.ts index 50f06db..3b65035 100644 --- a/src/index.ts +++ b/src/index.ts @@ -43,7 +43,7 @@ Configuration (~/.pi/agent/pi-worktrees.config.json): "onCreate": ["mise install", "bun install"], "onSwitch": "mise run dev:resume", "onBeforeRemove": "bun test", - "branchNameGenerator": "pi -p \"branch name for $PI_WORKTREE_PROMPT\" --model local/model", + "branchNameGenerator": "pi -p 'branch name for $PI_WORKTREE_PROMPT' --model local/model", }, "github.com/org/*": { "worktreeRoot": "~/work/org-other", diff --git a/src/services/branchNameGenerator.ts b/src/services/branchNameGenerator.ts index 5db2ad3..dc81315 100644 --- a/src/services/branchNameGenerator.ts +++ b/src/services/branchNameGenerator.ts @@ -40,7 +40,6 @@ function renderCommand(template: string, input: string): string { return template.replace(/\{\{prompt\}\}|\{prompt\}/g, quotedInput); } - async function validateBranchName(branchName: string, cwd: string): Promise { const checker = spawn('git', ['check-ref-format', '--branch', branchName], { cwd, @@ -54,7 +53,9 @@ async function validateBranchName(branchName: string, cwd: string): Promise { +export async function generateBranchName( + params: GenerateBranchNameParams +): Promise { const timeoutMs = params.timeoutMs ?? BRANCH_NAME_GENERATOR_TIMEOUT_MS; if (!params.commandTemplate?.trim()) { return { @@ -86,7 +87,7 @@ export async function generateBranchName(params: GenerateBranchNameParams): Prom let stderr = ''; let done = false; - const timer = setTimeout(() => { + const timer = globalThis.setTimeout(() => { if (done) { return; } @@ -110,7 +111,7 @@ export async function generateBranchName(params: GenerateBranchNameParams): Prom } done = true; - clearTimeout(timer); + globalThis.clearTimeout(timer); resolve({ kind: 'spawn-error', error: error.message }); }); @@ -120,7 +121,7 @@ export async function generateBranchName(params: GenerateBranchNameParams): Prom } done = true; - clearTimeout(timer); + globalThis.clearTimeout(timer); resolve({ kind: 'success', stdout, stderr, code: code ?? 1 }); }); }); diff --git a/tests/cmds/cmdCreate.branch-first.integration.test.ts b/tests/cmds/cmdCreate.branch-first.integration.test.ts index 50eef69..940ae53 100644 --- a/tests/cmds/cmdCreate.branch-first.integration.test.ts +++ b/tests/cmds/cmdCreate.branch-first.integration.test.ts @@ -178,7 +178,9 @@ describe('cmdCreate branch-first integration', () => { ); const messages = notify.mock.calls.map(([message]) => String(message)).join('\n'); - expect(messages).toContain("Using generated branch 'feature/from-generator' from branchNameGenerator"); + expect(messages).toContain( + "Using generated branch 'feature/from-generator' from branchNameGenerator" + ); }); it('never uses generator when branch is explicitly provided without --generate', async () => { @@ -189,7 +191,13 @@ describe('cmdCreate branch-first integration', () => { expect(branchGeneratorService.generateBranchName).not.toHaveBeenCalled(); expect(gitService.git).toHaveBeenCalledWith( - ['worktree', 'add', '-b', 'feature/direct-branch', '/tmp/repo.worktrees/feature-direct-branch'], + [ + 'worktree', + 'add', + '-b', + 'feature/direct-branch', + '/tmp/repo.worktrees/feature-direct-branch', + ], '/main/repo' ); }); diff --git a/tests/services/branchNameGenerator.test.ts b/tests/services/branchNameGenerator.test.ts index 2caa91a..edc722b 100644 --- a/tests/services/branchNameGenerator.test.ts +++ b/tests/services/branchNameGenerator.test.ts @@ -5,7 +5,7 @@ import { generateBranchName } from '../../src/services/branchNameGenerator.ts'; describe('branchNameGenerator', () => { it('returns generated branch on success', async () => { const result = await generateBranchName({ - commandTemplate: "node -e \"process.stdout.write('feature/generated\\n')\"", + commandTemplate: 'node -e "process.stdout.write(\'feature/generated\\n\')"', input: 'ignored', cwd: process.cwd(), timeoutMs: 200, @@ -14,13 +14,13 @@ describe('branchNameGenerator', () => { expect(result).toEqual({ ok: true, branchName: 'feature/generated', - command: "node -e \"process.stdout.write('feature/generated\\n')\"", + command: 'node -e "process.stdout.write(\'feature/generated\\n\')"', }); }); it('fails on timeout', async () => { const result = await generateBranchName({ - commandTemplate: "node -e \"setTimeout(() => process.stdout.write('feature/late'), 500)\"", + commandTemplate: 'node -e "setTimeout(() => process.stdout.write(\'feature/late\'), 500)"', input: 'ignored', cwd: process.cwd(), timeoutMs: 25, @@ -36,7 +36,7 @@ describe('branchNameGenerator', () => { it('fails on non-zero exit', async () => { const result = await generateBranchName({ - commandTemplate: "node -e \"process.stderr.write('boom'); process.exit(2)\"", + commandTemplate: 'node -e "process.stderr.write(\'boom\'); process.exit(2)"', input: 'ignored', cwd: process.cwd(), timeoutMs: 200, @@ -52,7 +52,7 @@ describe('branchNameGenerator', () => { it('fails on empty output', async () => { const result = await generateBranchName({ - commandTemplate: "node -e \"process.stdout.write(' ')\"", + commandTemplate: 'node -e "process.stdout.write(\' \')"', input: 'ignored', cwd: process.cwd(), timeoutMs: 200, @@ -68,7 +68,7 @@ describe('branchNameGenerator', () => { it('fails on invalid branch output', async () => { const result = await generateBranchName({ - commandTemplate: "node -e \"process.stdout.write('not a branch')\"", + commandTemplate: 'node -e "process.stdout.write(\'not a branch\')"', input: 'ignored', cwd: process.cwd(), timeoutMs: 200, diff --git a/tests/services/config.schema.test.ts b/tests/services/config.schema.test.ts index 722d13e..73bb9d4 100644 --- a/tests/services/config.schema.test.ts +++ b/tests/services/config.schema.test.ts @@ -44,6 +44,5 @@ describe('PiWorktreeConfigSchema branchNameGenerator', () => { }, }, }); - }); -}); \ No newline at end of file +});