From f794c5f335be899390b0b41b6b0726080f683b7d Mon Sep 17 00:00:00 2001 From: nrslib <38722970+nrslib@users.noreply.github.com> Date: Tue, 17 Feb 2026 23:31:39 +0900 Subject: [PATCH] =?UTF-8?q?fix(dotgitignore):=20.takt/=20=E3=83=97?= =?UTF-8?q?=E3=83=AC=E3=83=95=E3=82=A3=E3=83=83=E3=82=AF=E3=82=B9=E3=82=92?= =?UTF-8?q?=E5=89=8A=E9=99=A4=E3=81=97=E6=AD=A3=E3=81=97=E3=81=84=E7=9B=B8?= =?UTF-8?q?=E5=AF=BE=E3=83=91=E3=82=B9=E3=81=AB=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dotgitignore は .takt/.gitignore としてコピーされるため、パスは .takt/ からの相対でなければならない。 .takt/ プレフィックス付きだと .takt/.takt/pieces/ を指してしまいファセットが追跡されなかった。 回帰テストを追加。 --- builtins/project/dotgitignore | 27 ++++---- src/__tests__/it-dotgitignore.test.ts | 98 +++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 13 deletions(-) create mode 100644 src/__tests__/it-dotgitignore.test.ts diff --git a/builtins/project/dotgitignore b/builtins/project/dotgitignore index ef5b653..576c437 100644 --- a/builtins/project/dotgitignore +++ b/builtins/project/dotgitignore @@ -6,16 +6,17 @@ # Project configuration !config.yaml -!.takt/ -!.takt/pieces/ -!.takt/pieces/** -!.takt/personas/ -!.takt/personas/** -!.takt/policies/ -!.takt/policies/** -!.takt/knowledge/ -!.takt/knowledge/** -!.takt/instructions/ -!.takt/instructions/** -!.takt/output-contracts/ -!.takt/output-contracts/** + +# Facets and pieces (version-controlled) +!pieces/ +!pieces/** +!personas/ +!personas/** +!policies/ +!policies/** +!knowledge/ +!knowledge/** +!instructions/ +!instructions/** +!output-contracts/ +!output-contracts/** diff --git a/src/__tests__/it-dotgitignore.test.ts b/src/__tests__/it-dotgitignore.test.ts new file mode 100644 index 0000000..10adada --- /dev/null +++ b/src/__tests__/it-dotgitignore.test.ts @@ -0,0 +1,98 @@ +/** + * Integration test for dotgitignore + * + * Verifies that .takt/.gitignore patterns correctly track facet directories + * (pieces, personas, policies, knowledge, instructions, output-contracts) + * while ignoring runtime directories (tasks, logs, runs, completed). + */ + +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { mkdirSync, writeFileSync, rmSync, existsSync, readFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { execFileSync } from 'node:child_process'; +import { tmpdir } from 'node:os'; + +function gitTrackedFiles(cwd: string): string[] { + const output = execFileSync('git', ['ls-files', '.takt/'], { cwd, encoding: 'utf-8' }); + return output.trim().split('\n').filter(Boolean).sort(); +} + +describe('dotgitignore patterns', () => { + let testDir: string; + + beforeEach(() => { + testDir = join(tmpdir(), `takt-dotgitignore-${Date.now()}`); + mkdirSync(testDir, { recursive: true }); + + execFileSync('git', ['init'], { cwd: testDir }); + execFileSync('git', ['config', 'user.name', 'Test User'], { cwd: testDir }); + execFileSync('git', ['config', 'user.email', 'test@example.com'], { cwd: testDir }); + + // Initial commit + writeFileSync(join(testDir, 'README.md'), '# Test'); + execFileSync('git', ['add', '.'], { cwd: testDir }); + execFileSync('git', ['commit', '-m', 'Initial commit'], { cwd: testDir }); + + // Copy actual dotgitignore as .takt/.gitignore + const dotgitignorePath = join(__dirname, '..', '..', 'builtins', 'project', 'dotgitignore'); + const taktDir = join(testDir, '.takt'); + mkdirSync(taktDir, { recursive: true }); + const content = readFileSync(dotgitignorePath, 'utf-8'); + writeFileSync(join(taktDir, '.gitignore'), content); + }); + + afterEach(() => { + if (existsSync(testDir)) { + rmSync(testDir, { recursive: true, force: true }); + } + }); + + it('should track config.yaml', () => { + writeFileSync(join(testDir, '.takt', 'config.yaml'), 'language: ja\n'); + execFileSync('git', ['add', '.takt/'], { cwd: testDir }); + + const tracked = gitTrackedFiles(testDir); + expect(tracked).toContain('.takt/config.yaml'); + }); + + it('should track facet directories', () => { + const facets = ['pieces', 'personas', 'policies', 'knowledge', 'instructions', 'output-contracts']; + for (const facet of facets) { + mkdirSync(join(testDir, '.takt', facet), { recursive: true }); + writeFileSync(join(testDir, '.takt', facet, 'test.md'), `# ${facet}`); + } + + execFileSync('git', ['add', '.takt/'], { cwd: testDir }); + const tracked = gitTrackedFiles(testDir); + + for (const facet of facets) { + expect(tracked).toContain(`.takt/${facet}/test.md`); + } + }); + + it('should track nested files in facet directories', () => { + mkdirSync(join(testDir, '.takt', 'pieces', 'sub'), { recursive: true }); + writeFileSync(join(testDir, '.takt', 'pieces', 'sub', 'nested.yaml'), 'name: test'); + + execFileSync('git', ['add', '.takt/'], { cwd: testDir }); + const tracked = gitTrackedFiles(testDir); + + expect(tracked).toContain('.takt/pieces/sub/nested.yaml'); + }); + + it('should ignore runtime directories', () => { + const runtimeDirs = ['tasks', 'completed', 'logs', 'runs']; + for (const dir of runtimeDirs) { + mkdirSync(join(testDir, '.takt', dir), { recursive: true }); + writeFileSync(join(testDir, '.takt', dir, 'data.json'), '{}'); + } + + execFileSync('git', ['add', '.takt/'], { cwd: testDir }); + const tracked = gitTrackedFiles(testDir); + + for (const dir of runtimeDirs) { + const runtimeFiles = tracked.filter(f => f.startsWith(`.takt/${dir}/`)); + expect(runtimeFiles).toEqual([]); + } + }); +});