zettelkasten-starter 1.0.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/LICENSE +21 -0
- package/README.md +55 -0
- package/__tests__/cli.test.js +217 -0
- package/__tests__/creator.test.js +207 -0
- package/__tests__/integration.test.js +214 -0
- package/bin/create-zettelkasten.js +56 -0
- package/index.js +5 -0
- package/lib/creator.js +553 -0
- package/package.json +57 -0
package/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2025 Enokisan
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# 私のZettelkasten
|
2
|
+
|
3
|
+
Zettelkastenメソッドを使った知識管理システムへようこそ!
|
4
|
+
|
5
|
+
## 📁 ディレクトリ構造
|
6
|
+
|
7
|
+
このプロジェクトは4つの基本的なノートタイプで構成されています:
|
8
|
+
|
9
|
+
### 01_FleetingNote(フリーティングノート)
|
10
|
+
|
11
|
+
- 瞬間的なアイデアや思いつき
|
12
|
+
- 後で整理するための一時的なメモ
|
13
|
+
- 短時間で記録する簡潔なノート
|
14
|
+
|
15
|
+
### 02_LiteratureNote(文献ノート)
|
16
|
+
|
17
|
+
- 本、記事、動画などの内容要約
|
18
|
+
- 引用や参考文献の記録
|
19
|
+
- 自分の言葉での解釈や感想
|
20
|
+
|
21
|
+
### 03_PermanentNote(パーマネントノート)
|
22
|
+
|
23
|
+
- 完全に自分の言葉で表現された知識
|
24
|
+
- 他のノートと関連付けられた洞察
|
25
|
+
- 長期的に価値のある知見
|
26
|
+
|
27
|
+
### 04_StructureNote(構造ノート)
|
28
|
+
|
29
|
+
- テーマやトピックの概要
|
30
|
+
- 関連するノートへのインデックス
|
31
|
+
- 知識の地図やナビゲーション
|
32
|
+
|
33
|
+
## 💡 使い方のコツ
|
34
|
+
|
35
|
+
1. **小さく始める**: まずはFleetingNoteに思いついたことを書く
|
36
|
+
2. **定期的な整理**: FleetingNoteを他のノートタイプに昇格させる
|
37
|
+
3. **リンクを作る**: `[[ノート名]]`で他のノートと関連付ける
|
38
|
+
4. **継続が重要**: 毎日少しずつでも続ける
|
39
|
+
|
40
|
+
## 📝 ファイル命名規則
|
41
|
+
|
42
|
+
- 日付ベース: `YYYY-MM-DD_タイトル.md`
|
43
|
+
- ID ベース: `001_タイトル.md`
|
44
|
+
- 自由形式: `意味のあるタイトル.md`
|
45
|
+
|
46
|
+
あなたに合った方法を選んでください!
|
47
|
+
|
48
|
+
## 🚀 さあ、始めましょう!
|
49
|
+
|
50
|
+
各ディレクトリにサンプルファイルが用意されています。
|
51
|
+
それらを参考に、あなた独自のZettelkastenを構築していってください。
|
52
|
+
|
53
|
+
---
|
54
|
+
|
55
|
+
**Happy Note-Taking! 📚✨**
|
@@ -0,0 +1,217 @@
|
|
1
|
+
const fs = require('fs-extra');
|
2
|
+
const path = require('path');
|
3
|
+
const { spawn } = require('child_process');
|
4
|
+
|
5
|
+
// テスト用の一時ディレクトリ
|
6
|
+
const testDir = path.join(__dirname, 'cli-test-output');
|
7
|
+
|
8
|
+
describe('CLI Tool', () => {
|
9
|
+
beforeEach(async () => {
|
10
|
+
// 各テスト前にクリーンな状態にする
|
11
|
+
await fs.remove(testDir);
|
12
|
+
await fs.ensureDir(testDir);
|
13
|
+
});
|
14
|
+
|
15
|
+
afterEach(async () => {
|
16
|
+
// 各テスト後にクリーンアップ
|
17
|
+
await fs.remove(testDir);
|
18
|
+
});
|
19
|
+
|
20
|
+
const runCLI = (args = [], input = '') => {
|
21
|
+
return new Promise((resolve, reject) => {
|
22
|
+
const cliPath = path.join(__dirname, '../bin/create-zettelkasten.js');
|
23
|
+
const child = spawn('node', [cliPath, ...args], {
|
24
|
+
cwd: testDir,
|
25
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
26
|
+
});
|
27
|
+
|
28
|
+
let stdout = '';
|
29
|
+
let stderr = '';
|
30
|
+
|
31
|
+
child.stdout.on('data', (data) => {
|
32
|
+
stdout += data.toString();
|
33
|
+
});
|
34
|
+
|
35
|
+
child.stderr.on('data', (data) => {
|
36
|
+
stderr += data.toString();
|
37
|
+
});
|
38
|
+
|
39
|
+
child.on('close', (code) => {
|
40
|
+
resolve({ code, stdout, stderr });
|
41
|
+
});
|
42
|
+
|
43
|
+
child.on('error', (err) => {
|
44
|
+
reject(err);
|
45
|
+
});
|
46
|
+
|
47
|
+
if (input) {
|
48
|
+
child.stdin.write(input);
|
49
|
+
child.stdin.end();
|
50
|
+
}
|
51
|
+
});
|
52
|
+
};
|
53
|
+
|
54
|
+
test('プロジェクト名を引数で指定してプロジェクトが作成される', async () => {
|
55
|
+
const projectName = 'test-project-arg';
|
56
|
+
const result = await runCLI([projectName]);
|
57
|
+
|
58
|
+
expect(result.code).toBe(0);
|
59
|
+
expect(result.stdout).toContain('🎉 Zettelkasten project created successfully!');
|
60
|
+
expect(result.stdout).toContain(`📁 Location: ${path.join(testDir, projectName)}`);
|
61
|
+
|
62
|
+
// プロジェクトディレクトリが作成されているか確認
|
63
|
+
const projectPath = path.join(testDir, projectName);
|
64
|
+
expect(await fs.pathExists(projectPath)).toBe(true);
|
65
|
+
|
66
|
+
// 基本ディレクトリが作成されているか確認
|
67
|
+
expect(await fs.pathExists(path.join(projectPath, '01_FleetingNote'))).toBe(true);
|
68
|
+
expect(await fs.pathExists(path.join(projectPath, 'README.md'))).toBe(true);
|
69
|
+
}, 10000);
|
70
|
+
|
71
|
+
test('日本語オプションでプロジェクトが作成される', async () => {
|
72
|
+
const projectName = 'test-project-ja';
|
73
|
+
const result = await runCLI([projectName, '--lang', 'ja']);
|
74
|
+
|
75
|
+
expect(result.code).toBe(0);
|
76
|
+
expect(result.stdout).toContain('🎉 Zettelkasten project created successfully!');
|
77
|
+
|
78
|
+
// 日本語のREADMEが作成されているか確認
|
79
|
+
const readmePath = path.join(testDir, projectName, 'README.md');
|
80
|
+
const readmeContent = await fs.readFile(readmePath, 'utf8');
|
81
|
+
expect(readmeContent).toContain('私のZettelkasten');
|
82
|
+
}, 10000);
|
83
|
+
|
84
|
+
test('-lフラグでも言語指定ができる', async () => {
|
85
|
+
const projectName = 'test-project-l-flag';
|
86
|
+
const result = await runCLI([projectName, '-l', 'ja']);
|
87
|
+
|
88
|
+
expect(result.code).toBe(0);
|
89
|
+
|
90
|
+
const readmePath = path.join(testDir, projectName, 'README.md');
|
91
|
+
const readmeContent = await fs.readFile(readmePath, 'utf8');
|
92
|
+
expect(readmeContent).toContain('私のZettelkasten');
|
93
|
+
}, 10000);
|
94
|
+
|
95
|
+
test('バージョン情報が表示される', async () => {
|
96
|
+
const result = await runCLI(['--version']);
|
97
|
+
|
98
|
+
expect(result.code).toBe(0);
|
99
|
+
expect(result.stdout).toContain('1.0.0');
|
100
|
+
});
|
101
|
+
|
102
|
+
test('ヘルプが表示される', async () => {
|
103
|
+
const result = await runCLI(['--help']);
|
104
|
+
|
105
|
+
expect(result.code).toBe(0);
|
106
|
+
expect(result.stdout).toContain('Create a Zettelkasten project');
|
107
|
+
expect(result.stdout).toContain('Usage:');
|
108
|
+
expect(result.stdout).toContain('Options:');
|
109
|
+
});
|
110
|
+
|
111
|
+
test('対話モードでプロジェクト名を入力して作成される', async () => {
|
112
|
+
const projectName = 'interactive-project';
|
113
|
+
const result = await runCLI([], `${projectName}\n`);
|
114
|
+
|
115
|
+
expect(result.code).toBe(0);
|
116
|
+
expect(result.stdout).toContain('📝 Enter project name:');
|
117
|
+
expect(result.stdout).toContain('🎉 Zettelkasten project created successfully!');
|
118
|
+
|
119
|
+
// プロジェクトが作成されているか確認
|
120
|
+
const projectPath = path.join(testDir, projectName);
|
121
|
+
expect(await fs.pathExists(projectPath)).toBe(true);
|
122
|
+
}, 10000);
|
123
|
+
|
124
|
+
test('空のプロジェクト名で対話モードが失敗する', async () => {
|
125
|
+
const result = await runCLI([], '\n');
|
126
|
+
|
127
|
+
expect(result.code).toBe(1);
|
128
|
+
expect(result.stdout).toContain('❌ Project name cannot be empty');
|
129
|
+
}, 10000);
|
130
|
+
|
131
|
+
test('既存のディレクトリ名を指定しても正常に動作する', async () => {
|
132
|
+
const projectName = 'existing-dir';
|
133
|
+
const projectPath = path.join(testDir, projectName);
|
134
|
+
|
135
|
+
// 既存のディレクトリを作成
|
136
|
+
await fs.ensureDir(projectPath);
|
137
|
+
await fs.writeFile(path.join(projectPath, 'existing.txt'), 'test');
|
138
|
+
|
139
|
+
const result = await runCLI([projectName]);
|
140
|
+
|
141
|
+
expect(result.code).toBe(0);
|
142
|
+
expect(result.stdout).toContain('🎉 Zettelkasten project created successfully!');
|
143
|
+
|
144
|
+
// 既存ファイルが残っているか確認
|
145
|
+
expect(await fs.pathExists(path.join(projectPath, 'existing.txt'))).toBe(true);
|
146
|
+
// 新しいディレクトリも作成されているか確認
|
147
|
+
expect(await fs.pathExists(path.join(projectPath, '01_FleetingNote'))).toBe(true);
|
148
|
+
}, 10000);
|
149
|
+
|
150
|
+
test('サポートされていない言語でもエラーにならない', async () => {
|
151
|
+
const projectName = 'test-unsupported-lang';
|
152
|
+
const result = await runCLI([projectName, '--lang', 'fr']);
|
153
|
+
|
154
|
+
expect(result.code).toBe(0);
|
155
|
+
expect(result.stdout).toContain('🎉 Zettelkasten project created successfully!');
|
156
|
+
|
157
|
+
// 英語のREADMEが作成されているか確認(フォールバック)
|
158
|
+
const readmePath = path.join(testDir, projectName, 'README.md');
|
159
|
+
const readmeContent = await fs.readFile(readmePath, 'utf8');
|
160
|
+
expect(readmeContent).toContain('My Zettelkasten');
|
161
|
+
}, 10000);
|
162
|
+
|
163
|
+
test('相対パスでプロジェクトが作成される', async () => {
|
164
|
+
const projectName = 'relative-path-project';
|
165
|
+
const result = await runCLI([projectName]);
|
166
|
+
|
167
|
+
expect(result.code).toBe(0);
|
168
|
+
|
169
|
+
// カレントディレクトリ(testDir)にプロジェクトが作成されているか確認
|
170
|
+
const projectPath = path.join(testDir, projectName);
|
171
|
+
expect(await fs.pathExists(projectPath)).toBe(true);
|
172
|
+
expect(result.stdout).toContain(`📁 Location: ${projectPath}`);
|
173
|
+
}, 10000);
|
174
|
+
|
175
|
+
test('使用方法の指示が正しく表示される', async () => {
|
176
|
+
const projectName = 'usage-test';
|
177
|
+
const result = await runCLI([projectName]);
|
178
|
+
|
179
|
+
expect(result.code).toBe(0);
|
180
|
+
expect(result.stdout).toContain('📝 To get started:');
|
181
|
+
expect(result.stdout).toContain(`cd ${projectName}`);
|
182
|
+
expect(result.stdout).toContain('Check README.md for guidance');
|
183
|
+
}, 10000);
|
184
|
+
});
|
185
|
+
|
186
|
+
describe('CLIエラーハンドリング', () => {
|
187
|
+
test('無効なオプションでエラーが発生する', async () => {
|
188
|
+
const runCLI = (args = []) => {
|
189
|
+
return new Promise((resolve) => {
|
190
|
+
const cliPath = path.join(__dirname, '../bin/create-zettelkasten.js');
|
191
|
+
const child = spawn('node', [cliPath, ...args], {
|
192
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
193
|
+
});
|
194
|
+
|
195
|
+
let stdout = '';
|
196
|
+
let stderr = '';
|
197
|
+
|
198
|
+
child.stdout.on('data', (data) => {
|
199
|
+
stdout += data.toString();
|
200
|
+
});
|
201
|
+
|
202
|
+
child.stderr.on('data', (data) => {
|
203
|
+
stderr += data.toString();
|
204
|
+
});
|
205
|
+
|
206
|
+
child.on('close', (code) => {
|
207
|
+
resolve({ code, stdout, stderr });
|
208
|
+
});
|
209
|
+
});
|
210
|
+
};
|
211
|
+
|
212
|
+
const result = await runCLI(['--invalid-option']);
|
213
|
+
|
214
|
+
expect(result.code).not.toBe(0);
|
215
|
+
expect(result.stderr).toContain('unknown option');
|
216
|
+
});
|
217
|
+
});
|
@@ -0,0 +1,207 @@
|
|
1
|
+
const fs = require('fs-extra');
|
2
|
+
const path = require('path');
|
3
|
+
const { createZettelkasten } = require('../lib/creator');
|
4
|
+
|
5
|
+
// テスト用の一時ディレクトリを管理
|
6
|
+
const testDir = path.join(__dirname, 'test-output');
|
7
|
+
|
8
|
+
describe('Zettelkasten Creator', () => {
|
9
|
+
beforeEach(async () => {
|
10
|
+
// 各テスト前にクリーンな状態にする
|
11
|
+
await fs.remove(testDir);
|
12
|
+
});
|
13
|
+
|
14
|
+
afterEach(async () => {
|
15
|
+
// 各テスト後にクリーンアップ
|
16
|
+
await fs.remove(testDir);
|
17
|
+
});
|
18
|
+
|
19
|
+
describe('createZettelkasten', () => {
|
20
|
+
test('英語版のZettelkastenプロジェクトが正常に作成される', async () => {
|
21
|
+
const projectPath = path.join(testDir, 'test-zettelkasten-en');
|
22
|
+
|
23
|
+
await createZettelkasten(projectPath, 'en');
|
24
|
+
|
25
|
+
// ディレクトリが作成されているか確認
|
26
|
+
expect(await fs.pathExists(projectPath)).toBe(true);
|
27
|
+
|
28
|
+
// 基本ディレクトリが作成されているか確認
|
29
|
+
const expectedDirs = [
|
30
|
+
'01_FleetingNote',
|
31
|
+
'02_LiteratureNote',
|
32
|
+
'03_PermanentNote',
|
33
|
+
'04_StructureNote'
|
34
|
+
];
|
35
|
+
|
36
|
+
for (const dir of expectedDirs) {
|
37
|
+
const dirPath = path.join(projectPath, dir);
|
38
|
+
expect(await fs.pathExists(dirPath)).toBe(true);
|
39
|
+
expect((await fs.stat(dirPath)).isDirectory()).toBe(true);
|
40
|
+
}
|
41
|
+
});
|
42
|
+
|
43
|
+
test('日本語版のZettelkastenプロジェクトが正常に作成される', async () => {
|
44
|
+
const projectPath = path.join(testDir, 'test-zettelkasten-ja');
|
45
|
+
|
46
|
+
await createZettelkasten(projectPath, 'ja');
|
47
|
+
|
48
|
+
// ディレクトリが作成されているか確認
|
49
|
+
expect(await fs.pathExists(projectPath)).toBe(true);
|
50
|
+
|
51
|
+
// README.mdが作成され、日本語コンテンツが含まれているか確認
|
52
|
+
const readmePath = path.join(projectPath, 'README.md');
|
53
|
+
expect(await fs.pathExists(readmePath)).toBe(true);
|
54
|
+
|
55
|
+
const readmeContent = await fs.readFile(readmePath, 'utf8');
|
56
|
+
expect(readmeContent).toContain('私のZettelkasten');
|
57
|
+
expect(readmeContent).toContain('フリーティングノート');
|
58
|
+
expect(readmeContent).toContain('ディレクトリ構造');
|
59
|
+
});
|
60
|
+
|
61
|
+
test('英語版のREADMEファイルが正しく作成される', async () => {
|
62
|
+
const projectPath = path.join(testDir, 'test-zettelkasten-en');
|
63
|
+
|
64
|
+
await createZettelkasten(projectPath, 'en');
|
65
|
+
|
66
|
+
const readmePath = path.join(projectPath, 'README.md');
|
67
|
+
expect(await fs.pathExists(readmePath)).toBe(true);
|
68
|
+
|
69
|
+
const readmeContent = await fs.readFile(readmePath, 'utf8');
|
70
|
+
expect(readmeContent).toContain('My Zettelkasten');
|
71
|
+
expect(readmeContent).toContain('FleetingNote');
|
72
|
+
expect(readmeContent).toContain('Directory Structure');
|
73
|
+
expect(readmeContent).toContain('Happy Note-Taking!');
|
74
|
+
});
|
75
|
+
|
76
|
+
test('サンプルファイルが正しく作成される', async () => {
|
77
|
+
const projectPath = path.join(testDir, 'test-zettelkasten-sample');
|
78
|
+
|
79
|
+
await createZettelkasten(projectPath, 'en');
|
80
|
+
|
81
|
+
// 各ディレクトリにサンプルファイルが作成されているか確認
|
82
|
+
const fleetingNotePath = path.join(projectPath, '01_FleetingNote');
|
83
|
+
const literatureNotePath = path.join(projectPath, '02_LiteratureNote');
|
84
|
+
const permanentNotePath = path.join(projectPath, '03_PermanentNote');
|
85
|
+
const structureNotePath = path.join(projectPath, '04_StructureNote');
|
86
|
+
|
87
|
+
// 各ディレクトリにファイルが存在することを確認
|
88
|
+
const fleetingFiles = await fs.readdir(fleetingNotePath);
|
89
|
+
const literatureFiles = await fs.readdir(literatureNotePath);
|
90
|
+
const permanentFiles = await fs.readdir(permanentNotePath);
|
91
|
+
const structureFiles = await fs.readdir(structureNotePath);
|
92
|
+
|
93
|
+
expect(fleetingFiles.length).toBeGreaterThan(0);
|
94
|
+
expect(literatureFiles.length).toBeGreaterThan(0);
|
95
|
+
expect(permanentFiles.length).toBeGreaterThan(0);
|
96
|
+
expect(structureFiles.length).toBeGreaterThan(0);
|
97
|
+
|
98
|
+
// サンプルファイルの内容を確認
|
99
|
+
const sampleFleetingFile = path.join(fleetingNotePath, fleetingFiles[0]);
|
100
|
+
const content = await fs.readFile(sampleFleetingFile, 'utf8');
|
101
|
+
expect(content).toBeTruthy();
|
102
|
+
expect(content.length).toBeGreaterThan(0);
|
103
|
+
});
|
104
|
+
|
105
|
+
test('既存のディレクトリに対しても正常に動作する', async () => {
|
106
|
+
const projectPath = path.join(testDir, 'existing-dir');
|
107
|
+
|
108
|
+
// 既存のディレクトリを作成
|
109
|
+
await fs.ensureDir(projectPath);
|
110
|
+
await fs.writeFile(path.join(projectPath, 'existing-file.txt'), 'existing content');
|
111
|
+
|
112
|
+
await createZettelkasten(projectPath, 'en');
|
113
|
+
|
114
|
+
// 既存ファイルが残っているか確認
|
115
|
+
expect(await fs.pathExists(path.join(projectPath, 'existing-file.txt'))).toBe(true);
|
116
|
+
|
117
|
+
// 新しいディレクトリも作成されているか確認
|
118
|
+
expect(await fs.pathExists(path.join(projectPath, '01_FleetingNote'))).toBe(true);
|
119
|
+
expect(await fs.pathExists(path.join(projectPath, 'README.md'))).toBe(true);
|
120
|
+
});
|
121
|
+
|
122
|
+
test('デフォルト言語は英語である', async () => {
|
123
|
+
const projectPath = path.join(testDir, 'test-default-lang');
|
124
|
+
|
125
|
+
// 言語パラメータを指定せずに呼び出し
|
126
|
+
await createZettelkasten(projectPath);
|
127
|
+
|
128
|
+
const readmePath = path.join(projectPath, 'README.md');
|
129
|
+
const readmeContent = await fs.readFile(readmePath, 'utf8');
|
130
|
+
|
131
|
+
// 英語のコンテンツが含まれているか確認
|
132
|
+
expect(readmeContent).toContain('My Zettelkasten');
|
133
|
+
expect(readmeContent).not.toContain('私のZettelkasten');
|
134
|
+
});
|
135
|
+
|
136
|
+
test('無効な言語が指定された場合は英語で作成される', async () => {
|
137
|
+
const projectPath = path.join(testDir, 'test-invalid-lang');
|
138
|
+
|
139
|
+
await createZettelkasten(projectPath, 'invalid-language');
|
140
|
+
|
141
|
+
const readmePath = path.join(projectPath, 'README.md');
|
142
|
+
const readmeContent = await fs.readFile(readmePath, 'utf8');
|
143
|
+
|
144
|
+
// 英語のコンテンツが含まれているか確認
|
145
|
+
expect(readmeContent).toContain('My Zettelkasten');
|
146
|
+
});
|
147
|
+
|
148
|
+
test('空の文字列パスでエラーがスローされる', async () => {
|
149
|
+
await expect(createZettelkasten('')).rejects.toThrow();
|
150
|
+
});
|
151
|
+
|
152
|
+
test('権限がない場所への作成でエラーがスローされる', async () => {
|
153
|
+
// アクセス権限がない場所を指定
|
154
|
+
const restrictedPath = '/root/no-permission';
|
155
|
+
|
156
|
+
await expect(createZettelkasten(restrictedPath)).rejects.toThrow();
|
157
|
+
});
|
158
|
+
});
|
159
|
+
|
160
|
+
describe('ファイル構造の検証', () => {
|
161
|
+
test('作成されたプロジェクトが期待される構造を持つ', async () => {
|
162
|
+
const projectPath = path.join(testDir, 'structure-test');
|
163
|
+
|
164
|
+
await createZettelkasten(projectPath, 'ja');
|
165
|
+
|
166
|
+
// ルートレベルのファイル確認
|
167
|
+
expect(await fs.pathExists(path.join(projectPath, 'README.md'))).toBe(true);
|
168
|
+
|
169
|
+
// ディレクトリ構造確認
|
170
|
+
const expectedStructure = [
|
171
|
+
'01_FleetingNote',
|
172
|
+
'02_LiteratureNote',
|
173
|
+
'03_PermanentNote',
|
174
|
+
'04_StructureNote'
|
175
|
+
];
|
176
|
+
|
177
|
+
for (const dir of expectedStructure) {
|
178
|
+
const dirPath = path.join(projectPath, dir);
|
179
|
+
expect(await fs.pathExists(dirPath)).toBe(true);
|
180
|
+
|
181
|
+
// 各ディレクトリにサンプルファイルが存在することを確認
|
182
|
+
const files = await fs.readdir(dirPath);
|
183
|
+
expect(files.length).toBeGreaterThan(0);
|
184
|
+
|
185
|
+
// 少なくとも1つのmarkdownファイルが存在することを確認
|
186
|
+
const markdownFiles = files.filter(file => file.endsWith('.md'));
|
187
|
+
expect(markdownFiles.length).toBeGreaterThan(0);
|
188
|
+
}
|
189
|
+
});
|
190
|
+
|
191
|
+
test('README.mdの内容が適切な形式である', async () => {
|
192
|
+
const projectPath = path.join(testDir, 'readme-test');
|
193
|
+
|
194
|
+
await createZettelkasten(projectPath, 'ja');
|
195
|
+
|
196
|
+
const readmePath = path.join(projectPath, 'README.md');
|
197
|
+
const content = await fs.readFile(readmePath, 'utf8');
|
198
|
+
|
199
|
+
// Markdownの基本的な要素が含まれているか確認
|
200
|
+
expect(content).toMatch(/^#\s/m); // H1ヘッダーが存在
|
201
|
+
expect(content).toMatch(/^##\s/m); // H2ヘッダーが存在
|
202
|
+
expect(content).toMatch(/^###\s/m); // H3ヘッダーが存在
|
203
|
+
expect(content).toContain('`'); // コードブロックが存在
|
204
|
+
expect(content).toContain('📁'); // 絵文字が含まれている
|
205
|
+
});
|
206
|
+
});
|
207
|
+
});
|
@@ -0,0 +1,214 @@
|
|
1
|
+
const fs = require('fs-extra');
|
2
|
+
const path = require('path');
|
3
|
+
const { createZettelkasten } = require('../lib/creator');
|
4
|
+
|
5
|
+
// テスト用の一時ディレクトリ
|
6
|
+
const testDir = path.join(__dirname, 'integration-test-output');
|
7
|
+
|
8
|
+
describe('統合テスト', () => {
|
9
|
+
beforeEach(async () => {
|
10
|
+
await fs.remove(testDir);
|
11
|
+
await fs.ensureDir(testDir);
|
12
|
+
});
|
13
|
+
|
14
|
+
afterEach(async () => {
|
15
|
+
await fs.remove(testDir);
|
16
|
+
});
|
17
|
+
|
18
|
+
test('完全なZettelkastenプロジェクトが期待通りに機能する', async () => {
|
19
|
+
const projectPath = path.join(testDir, 'full-project');
|
20
|
+
|
21
|
+
// プロジェクトを作成
|
22
|
+
await createZettelkasten(projectPath, 'ja');
|
23
|
+
|
24
|
+
// 1. プロジェクト構造の完全性確認
|
25
|
+
const expectedDirs = [
|
26
|
+
'01_FleetingNote',
|
27
|
+
'02_LiteratureNote',
|
28
|
+
'03_PermanentNote',
|
29
|
+
'04_StructureNote'
|
30
|
+
];
|
31
|
+
|
32
|
+
for (const dir of expectedDirs) {
|
33
|
+
const dirPath = path.join(projectPath, dir);
|
34
|
+
expect(await fs.pathExists(dirPath)).toBe(true);
|
35
|
+
|
36
|
+
// 各ディレクトリにサンプルファイルが存在することを確認
|
37
|
+
const files = await fs.readdir(dirPath);
|
38
|
+
expect(files.length).toBeGreaterThan(0);
|
39
|
+
|
40
|
+
// 最初のファイルの内容を確認
|
41
|
+
const firstFile = files[0];
|
42
|
+
const filePath = path.join(dirPath, firstFile);
|
43
|
+
const content = await fs.readFile(filePath, 'utf8');
|
44
|
+
expect(content.trim().length).toBeGreaterThan(0);
|
45
|
+
}
|
46
|
+
|
47
|
+
// 2. README.mdの内容確認
|
48
|
+
const readmePath = path.join(projectPath, 'README.md');
|
49
|
+
expect(await fs.pathExists(readmePath)).toBe(true);
|
50
|
+
|
51
|
+
const readmeContent = await fs.readFile(readmePath, 'utf8');
|
52
|
+
|
53
|
+
// 必要なセクションが全て含まれているか確認
|
54
|
+
expect(readmeContent).toContain('私のZettelkasten');
|
55
|
+
expect(readmeContent).toContain('📁 ディレクトリ構造');
|
56
|
+
expect(readmeContent).toContain('01_FleetingNote(フリーティングノート)');
|
57
|
+
expect(readmeContent).toContain('02_LiteratureNote(文献ノート)');
|
58
|
+
expect(readmeContent).toContain('03_PermanentNote(パーマネントノート)');
|
59
|
+
expect(readmeContent).toContain('04_StructureNote(構造ノート)');
|
60
|
+
expect(readmeContent).toContain('💡 使い方のコツ');
|
61
|
+
expect(readmeContent).toContain('🏷️ タグの使い方');
|
62
|
+
expect(readmeContent).toContain('📝 ファイル命名規則');
|
63
|
+
expect(readmeContent).toContain('🚀 さあ、始めましょう!');
|
64
|
+
|
65
|
+
// 3. ファイル拡張子の確認
|
66
|
+
for (const dir of expectedDirs) {
|
67
|
+
const dirPath = path.join(projectPath, dir);
|
68
|
+
const files = await fs.readdir(dirPath);
|
69
|
+
|
70
|
+
for (const file of files) {
|
71
|
+
expect(file).toMatch(/\.md$/); // 全てのファイルがMarkdown形式
|
72
|
+
}
|
73
|
+
}
|
74
|
+
});
|
75
|
+
|
76
|
+
test('英語と日本語のプロジェクトが共存できる', async () => {
|
77
|
+
const enProjectPath = path.join(testDir, 'en-project');
|
78
|
+
const jaProjectPath = path.join(testDir, 'ja-project');
|
79
|
+
|
80
|
+
// 英語版プロジェクト作成
|
81
|
+
await createZettelkasten(enProjectPath, 'en');
|
82
|
+
|
83
|
+
// 日本語版プロジェクト作成
|
84
|
+
await createZettelkasten(jaProjectPath, 'ja');
|
85
|
+
|
86
|
+
// 両方のプロジェクトが存在することを確認
|
87
|
+
expect(await fs.pathExists(enProjectPath)).toBe(true);
|
88
|
+
expect(await fs.pathExists(jaProjectPath)).toBe(true);
|
89
|
+
|
90
|
+
// 英語版READMEの確認
|
91
|
+
const enReadme = await fs.readFile(path.join(enProjectPath, 'README.md'), 'utf8');
|
92
|
+
expect(enReadme).toContain('My Zettelkasten');
|
93
|
+
expect(enReadme).toContain('Directory Structure');
|
94
|
+
|
95
|
+
// 日本語版READMEの確認
|
96
|
+
const jaReadme = await fs.readFile(path.join(jaProjectPath, 'README.md'), 'utf8');
|
97
|
+
expect(jaReadme).toContain('私のZettelkasten');
|
98
|
+
expect(jaReadme).toContain('ディレクトリ構造');
|
99
|
+
|
100
|
+
// 両プロジェクトが同じディレクトリ構造を持つことを確認
|
101
|
+
const enDirs = await fs.readdir(enProjectPath);
|
102
|
+
const jaDirs = await fs.readdir(jaProjectPath);
|
103
|
+
|
104
|
+
const expectedDirs = [
|
105
|
+
'01_FleetingNote',
|
106
|
+
'02_LiteratureNote',
|
107
|
+
'03_PermanentNote',
|
108
|
+
'04_StructureNote',
|
109
|
+
'README.md'
|
110
|
+
];
|
111
|
+
|
112
|
+
expectedDirs.forEach(item => {
|
113
|
+
expect(enDirs).toContain(item);
|
114
|
+
expect(jaDirs).toContain(item);
|
115
|
+
});
|
116
|
+
});
|
117
|
+
|
118
|
+
test('既存ファイルがある環境での安全な作成', async () => {
|
119
|
+
const projectPath = path.join(testDir, 'existing-content');
|
120
|
+
|
121
|
+
// 既存コンテンツを作成
|
122
|
+
await fs.ensureDir(projectPath);
|
123
|
+
await fs.writeFile(path.join(projectPath, 'my-notes.md'), '# 既存のメモ\n重要な内容');
|
124
|
+
await fs.ensureDir(path.join(projectPath, 'custom-folder'));
|
125
|
+
await fs.writeFile(path.join(projectPath, 'custom-folder', 'data.txt'), 'カスタムデータ');
|
126
|
+
|
127
|
+
// Zettelkastenプロジェクトを追加
|
128
|
+
await createZettelkasten(projectPath, 'ja');
|
129
|
+
|
130
|
+
// 既存ファイルが保持されているか確認
|
131
|
+
expect(await fs.pathExists(path.join(projectPath, 'my-notes.md'))).toBe(true);
|
132
|
+
expect(await fs.pathExists(path.join(projectPath, 'custom-folder', 'data.txt'))).toBe(true);
|
133
|
+
|
134
|
+
const existingContent = await fs.readFile(path.join(projectPath, 'my-notes.md'), 'utf8');
|
135
|
+
expect(existingContent).toContain('重要な内容');
|
136
|
+
|
137
|
+
// 新しいZettelkastenディレクトリも作成されているか確認
|
138
|
+
expect(await fs.pathExists(path.join(projectPath, '01_FleetingNote'))).toBe(true);
|
139
|
+
expect(await fs.pathExists(path.join(projectPath, 'README.md'))).toBe(true);
|
140
|
+
});
|
141
|
+
|
142
|
+
test('サンプルファイルの品質確認', async () => {
|
143
|
+
const projectPath = path.join(testDir, 'sample-quality');
|
144
|
+
|
145
|
+
await createZettelkasten(projectPath, 'ja');
|
146
|
+
|
147
|
+
const directories = [
|
148
|
+
'01_FleetingNote',
|
149
|
+
'02_LiteratureNote',
|
150
|
+
'03_PermanentNote',
|
151
|
+
'04_StructureNote'
|
152
|
+
];
|
153
|
+
|
154
|
+
for (const dir of directories) {
|
155
|
+
const dirPath = path.join(projectPath, dir);
|
156
|
+
const files = await fs.readdir(dirPath);
|
157
|
+
|
158
|
+
for (const file of files) {
|
159
|
+
const filePath = path.join(dirPath, file);
|
160
|
+
const content = await fs.readFile(filePath, 'utf8');
|
161
|
+
|
162
|
+
// Markdownファイルの基本的な品質確認
|
163
|
+
expect(content).toMatch(/^#/m); // ヘッダーが存在
|
164
|
+
expect(content.length).toBeGreaterThan(50); // 適度な長さ
|
165
|
+
expect(content).toContain('\n'); // 複数行
|
166
|
+
|
167
|
+
// 日本語のサンプルファイルであることを確認
|
168
|
+
expect(content).toMatch(/[ひらがなカタカナ漢字]/);
|
169
|
+
}
|
170
|
+
}
|
171
|
+
});
|
172
|
+
|
173
|
+
test('大量のファイル作成でもパフォーマンスが問題ない', async () => {
|
174
|
+
const startTime = Date.now();
|
175
|
+
|
176
|
+
// 複数のプロジェクトを連続作成
|
177
|
+
const projectPromises = [];
|
178
|
+
for (let i = 0; i < 5; i++) {
|
179
|
+
const projectPath = path.join(testDir, `performance-test-${i}`);
|
180
|
+
projectPromises.push(createZettelkasten(projectPath, i % 2 === 0 ? 'en' : 'ja'));
|
181
|
+
}
|
182
|
+
|
183
|
+
await Promise.all(projectPromises);
|
184
|
+
|
185
|
+
const endTime = Date.now();
|
186
|
+
const elapsedTime = endTime - startTime;
|
187
|
+
|
188
|
+
// 5秒以内に完了することを確認(合理的なパフォーマンス要件)
|
189
|
+
expect(elapsedTime).toBeLessThan(5000);
|
190
|
+
|
191
|
+
// 全てのプロジェクトが正常に作成されたことを確認
|
192
|
+
for (let i = 0; i < 5; i++) {
|
193
|
+
const projectPath = path.join(testDir, `performance-test-${i}`);
|
194
|
+
expect(await fs.pathExists(projectPath)).toBe(true);
|
195
|
+
expect(await fs.pathExists(path.join(projectPath, 'README.md'))).toBe(true);
|
196
|
+
}
|
197
|
+
});
|
198
|
+
|
199
|
+
test('エラー発生後の状態確認', async () => {
|
200
|
+
const projectPath = path.join(testDir, 'error-recovery');
|
201
|
+
|
202
|
+
// 権限のないディレクトリの親を作成してから権限を変更する代わりに
|
203
|
+
// 無効なパス文字を使用してエラーを発生させる
|
204
|
+
const invalidPath = path.join(testDir, 'invalid\x00path');
|
205
|
+
|
206
|
+
await expect(createZettelkasten(invalidPath, 'ja')).rejects.toThrow();
|
207
|
+
|
208
|
+
// その後正常なプロジェクト作成ができることを確認
|
209
|
+
await createZettelkasten(projectPath, 'ja');
|
210
|
+
|
211
|
+
expect(await fs.pathExists(projectPath)).toBe(true);
|
212
|
+
expect(await fs.pathExists(path.join(projectPath, 'README.md'))).toBe(true);
|
213
|
+
});
|
214
|
+
});
|
@@ -0,0 +1,56 @@
|
|
1
|
+
#!/usr/bin/env node
|
2
|
+
|
3
|
+
const { program } = require('commander');
|
4
|
+
const path = require('path');
|
5
|
+
const readline = require('readline');
|
6
|
+
const { createZettelkasten } = require('../lib/creator');
|
7
|
+
|
8
|
+
/**
|
9
|
+
* 対話的にプロジェクト名を取得
|
10
|
+
*/
|
11
|
+
function askProjectName() {
|
12
|
+
return new Promise((resolve) => {
|
13
|
+
const rl = readline.createInterface({
|
14
|
+
input: process.stdin,
|
15
|
+
output: process.stdout
|
16
|
+
});
|
17
|
+
|
18
|
+
rl.question('📝 Enter project name: ', (answer) => {
|
19
|
+
rl.close();
|
20
|
+
const projectName = answer.trim();
|
21
|
+
if (projectName === '') {
|
22
|
+
console.log('❌ Project name cannot be empty. Please try again.');
|
23
|
+
process.exit(1);
|
24
|
+
}
|
25
|
+
resolve(projectName);
|
26
|
+
});
|
27
|
+
});
|
28
|
+
}
|
29
|
+
|
30
|
+
program
|
31
|
+
.version('1.0.0')
|
32
|
+
.argument('[project-name]', 'Project name (optional - will prompt if not provided)')
|
33
|
+
.option('-l, --lang <language>', 'Generation language (en|ja)', 'en')
|
34
|
+
.description('Create a Zettelkasten project')
|
35
|
+
.action(async (projectName, options) => {
|
36
|
+
try {
|
37
|
+
// プロジェクト名が提供されていない場合は対話モードで取得
|
38
|
+
if (!projectName) {
|
39
|
+
projectName = await askProjectName();
|
40
|
+
}
|
41
|
+
|
42
|
+
const targetPath = path.resolve(process.cwd(), projectName);
|
43
|
+
await createZettelkasten(targetPath, options.lang);
|
44
|
+
|
45
|
+
console.log('🎉 Zettelkasten project created successfully!');
|
46
|
+
console.log(`📁 Location: ${targetPath}`);
|
47
|
+
console.log(`\n📝 To get started:`);
|
48
|
+
console.log(` cd ${projectName}`);
|
49
|
+
console.log(` Check README.md for guidance`);
|
50
|
+
} catch (error) {
|
51
|
+
console.error('❌ Error occurred:', error.message);
|
52
|
+
process.exit(1);
|
53
|
+
}
|
54
|
+
});
|
55
|
+
|
56
|
+
program.parse();
|
package/index.js
ADDED
package/lib/creator.js
ADDED
@@ -0,0 +1,553 @@
|
|
1
|
+
const fs = require('fs-extra');
|
2
|
+
const path = require('path');
|
3
|
+
|
4
|
+
/**
|
5
|
+
* Zettelkastenプロジェクトを作成する
|
6
|
+
* @param {string} targetPath - 作成先のパス
|
7
|
+
* @param {string} language - 言語 ('en' | 'ja')
|
8
|
+
*/
|
9
|
+
async function createZettelkasten(targetPath, language = 'en') {
|
10
|
+
// ディレクトリが存在しない場合は作成
|
11
|
+
await fs.ensureDir(targetPath);
|
12
|
+
|
13
|
+
// 4つの基本ディレクトリを作成
|
14
|
+
const directories = [
|
15
|
+
'01_FleetingNote',
|
16
|
+
'02_LiteratureNote',
|
17
|
+
'03_PermanentNote',
|
18
|
+
'04_StructureNote'
|
19
|
+
];
|
20
|
+
|
21
|
+
for (const dir of directories) {
|
22
|
+
await fs.ensureDir(path.join(targetPath, dir));
|
23
|
+
}
|
24
|
+
|
25
|
+
// README.mdを作成
|
26
|
+
const readmeContent = getReadmeContent(language);
|
27
|
+
await fs.writeFile(path.join(targetPath, 'README.md'), readmeContent);
|
28
|
+
|
29
|
+
// サンプルファイルを作成
|
30
|
+
await createSampleFiles(targetPath, language);
|
31
|
+
}
|
32
|
+
|
33
|
+
/**
|
34
|
+
* 言語に応じたREADMEコンテンツを取得
|
35
|
+
* @param {string} language - 言語
|
36
|
+
* @returns {string} READMEの内容
|
37
|
+
*/
|
38
|
+
function getReadmeContent(language) {
|
39
|
+
if (language === 'ja') {
|
40
|
+
return `# 私のZettelkasten
|
41
|
+
|
42
|
+
Zettelkastenメソッドを使った知識管理システムへようこそ!
|
43
|
+
|
44
|
+
## 📁 ディレクトリ構造
|
45
|
+
|
46
|
+
このプロジェクトは4つの基本的なノートタイプで構成されています:
|
47
|
+
|
48
|
+
### 01_FleetingNote(フリーティングノート)
|
49
|
+
|
50
|
+
- 瞬間的なアイデアや思いつき
|
51
|
+
- 後で整理するための一時的なメモ
|
52
|
+
- 短時間で記録する簡潔なノート
|
53
|
+
|
54
|
+
### 02_LiteratureNote(文献ノート)
|
55
|
+
|
56
|
+
- 本、記事、動画などの内容要約
|
57
|
+
- 引用や参考文献の記録
|
58
|
+
- 自分の言葉での解釈や感想
|
59
|
+
|
60
|
+
### 03_PermanentNote(パーマネントノート)
|
61
|
+
|
62
|
+
- 完全に自分の言葉で表現された知識
|
63
|
+
- 他のノートと関連付けられた洞察
|
64
|
+
- 長期的に価値のある知見
|
65
|
+
|
66
|
+
### 04_StructureNote(構造ノート)
|
67
|
+
|
68
|
+
- テーマやトピックの概要
|
69
|
+
- 関連するノートへのインデックス
|
70
|
+
- 知識の地図やナビゲーション
|
71
|
+
|
72
|
+
## 💡 使い方のコツ
|
73
|
+
|
74
|
+
1. **小さく始める**: まずはFleetingNoteに思いついたことを書く
|
75
|
+
2. **定期的な整理**: FleetingNoteを他のノートタイプに昇格させる
|
76
|
+
3. **リンクを作る**: \`[[ノート名]]\`で他のノートと関連付ける
|
77
|
+
4. **タグを活用**: \`#concept\` \`#method\` \`#review\`などでカテゴライズ
|
78
|
+
5. **継続が重要**: 毎日少しずつでも続ける
|
79
|
+
|
80
|
+
## 🏷️ タグの使い方
|
81
|
+
|
82
|
+
タグは情報の分類と検索を効率化します:
|
83
|
+
|
84
|
+
- \`#concept\` - 概念や理論
|
85
|
+
- \`#method\` - 方法論やテクニック
|
86
|
+
- \`#review\` - 振り返りや評価
|
87
|
+
- \`#source/book\` - 書籍からの情報
|
88
|
+
- \`#source/article\` - 記事からの情報
|
89
|
+
- \`#draft\` - 下書きや未完成
|
90
|
+
- \`#important\` - 重要度の高い内容
|
91
|
+
- \`#todo\` - アクションアイテム
|
92
|
+
- \`#気の赴くままに\` - 好きなタグを追加していきましょう
|
93
|
+
|
94
|
+
## 📝 ファイル命名規則
|
95
|
+
|
96
|
+
- 日付ベース: \`YYYY-MM-DD_タイトル.md\`
|
97
|
+
- ID ベース: \`001_タイトル.md\`
|
98
|
+
- 自由形式: \`意味のあるタイトル.md\`
|
99
|
+
|
100
|
+
あなたに合った方法を選んでください!
|
101
|
+
|
102
|
+
## 🚀 さあ、始めましょう!
|
103
|
+
|
104
|
+
各ディレクトリにサンプルファイルが用意されています。
|
105
|
+
それらを参考に、あなた独自のZettelkastenを構築していってください。
|
106
|
+
|
107
|
+
---
|
108
|
+
|
109
|
+
**Happy Note-Taking! 📚✨**
|
110
|
+
`;
|
111
|
+
} else {
|
112
|
+
return `# My Zettelkasten
|
113
|
+
|
114
|
+
Welcome to your knowledge management system using the Zettelkasten method!
|
115
|
+
|
116
|
+
## 📁 Directory Structure
|
117
|
+
|
118
|
+
This project consists of four basic note types:
|
119
|
+
|
120
|
+
### 01_FleetingNote
|
121
|
+
|
122
|
+
- Fleeting ideas and quick thoughts
|
123
|
+
- Temporary notes for later organization
|
124
|
+
- Brief notes recorded in a short time
|
125
|
+
|
126
|
+
### 02_LiteratureNote
|
127
|
+
|
128
|
+
- Summaries of books, articles, videos, etc.
|
129
|
+
- Citations and references
|
130
|
+
- Your interpretations and reflections
|
131
|
+
|
132
|
+
### 03_PermanentNote
|
133
|
+
|
134
|
+
- Knowledge expressed entirely in your own words
|
135
|
+
- Insights linked to other notes
|
136
|
+
- Long-term valuable insights
|
137
|
+
|
138
|
+
### 04_StructureNote
|
139
|
+
|
140
|
+
- Overview of themes and topics
|
141
|
+
- Index to related notes
|
142
|
+
- Knowledge maps and navigation
|
143
|
+
|
144
|
+
## 💡 Tips for Usage
|
145
|
+
|
146
|
+
1. **Start small**: First write down your thoughts in FleetingNote
|
147
|
+
2. **Regular organization**: Promote FleetingNotes to other note types
|
148
|
+
3. **Create links**: Use \`[[Note Name]]\` to connect with other notes
|
149
|
+
4. **Use tags**: Categorize with \`#concept\` \`#method\` \`#review\` etc.
|
150
|
+
5. **Consistency is key**: Continue a little bit every day
|
151
|
+
|
152
|
+
## 🏷️ How to Use Tags
|
153
|
+
|
154
|
+
Tags help organize and search information efficiently:
|
155
|
+
|
156
|
+
- \`#concept\` - Concepts and theories
|
157
|
+
- \`#method\` - Methodologies and techniques
|
158
|
+
- \`#review\` - Reflections and evaluations
|
159
|
+
- \`#source/book\` - Information from books
|
160
|
+
- \`#source/article\` - Information from articles
|
161
|
+
- \`#draft\` - Drafts and work in progress
|
162
|
+
- \`#important\` - High priority content
|
163
|
+
- \`#todo\` - Action items
|
164
|
+
- \`#asyouwish\` - Add your own tags as you go
|
165
|
+
|
166
|
+
## 📝 File Naming Conventions
|
167
|
+
|
168
|
+
- Date-based: \`YYYY-MM-DD_title.md\`
|
169
|
+
- ID-based: \`001_title.md\`
|
170
|
+
- Free-form: \`meaningful-title.md\`
|
171
|
+
|
172
|
+
Choose the method that works best for you!
|
173
|
+
|
174
|
+
## 🚀 Let's Get Started!
|
175
|
+
|
176
|
+
Sample files are prepared in each directory.
|
177
|
+
Use them as references to build your unique Zettelkasten.
|
178
|
+
|
179
|
+
---
|
180
|
+
|
181
|
+
**Happy Note-Taking! 📚✨**
|
182
|
+
`;
|
183
|
+
}
|
184
|
+
}
|
185
|
+
|
186
|
+
/**
|
187
|
+
* サンプルファイルを作成
|
188
|
+
* @param {string} targetPath - 作成先のパス
|
189
|
+
* @param {string} language - 言語
|
190
|
+
*/
|
191
|
+
async function createSampleFiles(targetPath, language) {
|
192
|
+
const samples = getSampleContents(language);
|
193
|
+
|
194
|
+
for (const [filePath, content] of Object.entries(samples)) {
|
195
|
+
await fs.writeFile(path.join(targetPath, filePath), content);
|
196
|
+
}
|
197
|
+
}
|
198
|
+
|
199
|
+
/**
|
200
|
+
* サンプルファイルの内容を取得
|
201
|
+
* @param {string} language - 言語
|
202
|
+
* @returns {Object} ファイルパスとコンテンツのオブジェクト
|
203
|
+
*/
|
204
|
+
function getSampleContents(language) {
|
205
|
+
if (language === 'ja') {
|
206
|
+
return {
|
207
|
+
'01_FleetingNote/sample_fleeting.md': `# フリーティングノートのサンプル
|
208
|
+
|
209
|
+
日付: ${new Date().toISOString().split('T')[0]}
|
210
|
+
タグ: #fleeting #zettelkasten #idea #draft
|
211
|
+
|
212
|
+
## 💭 今日の思いつき
|
213
|
+
|
214
|
+
- Zettelkastenを始めてみた
|
215
|
+
- アイデアを素早くキャッチするのが重要
|
216
|
+
- 後で整理することを前提に、まずは記録
|
217
|
+
|
218
|
+
## 🔗 関連する可能性
|
219
|
+
|
220
|
+
- [[学習方法について]]
|
221
|
+
- [[知識管理システム]]
|
222
|
+
|
223
|
+
## 📋 次のアクション
|
224
|
+
|
225
|
+
- [ ] このアイデアをより詳しく調べる #todo
|
226
|
+
- [ ] 関連する文献を探す #todo
|
227
|
+
|
228
|
+
---
|
229
|
+
|
230
|
+
*これはサンプルファイルです。自由に編集・削除してください。*
|
231
|
+
`,
|
232
|
+
'02_LiteratureNote/sample_literature.md': `# 文献ノートのサンプル
|
233
|
+
|
234
|
+
タグ: #literature #source/book #zettelkasten #method #notes
|
235
|
+
|
236
|
+
## 📚 書籍情報
|
237
|
+
|
238
|
+
- **タイトル**: How to Take Smart Notes
|
239
|
+
- **著者**: Sönke Ahrens
|
240
|
+
- **出版年**: 2017
|
241
|
+
- **読了日**: ${new Date().toISOString().split('T')[0]}
|
242
|
+
|
243
|
+
## 📝 主要ポイント
|
244
|
+
|
245
|
+
### Zettelkastenの原則
|
246
|
+
|
247
|
+
1. **一つのアイデア、一つのノート**
|
248
|
+
- 各ノートは単一のアイデアに焦点を当てる
|
249
|
+
- 複雑な概念は複数のノートに分割
|
250
|
+
|
251
|
+
2. **自分の言葉で書く**
|
252
|
+
- 単なるコピペではなく、理解した内容を表現
|
253
|
+
- 後で読み返した時に理解できるように
|
254
|
+
|
255
|
+
3. **リンクを作る**
|
256
|
+
- ノート間の関連性を明示的に作る
|
257
|
+
- 新しい洞察が生まれる可能性
|
258
|
+
|
259
|
+
## 🤔 私の考察
|
260
|
+
|
261
|
+
Zettelkastenは単なるノート取りツールではなく、思考のツールだと感じた。
|
262
|
+
重要なのは... #concept #important
|
263
|
+
|
264
|
+
## 💡 アクションアイテム
|
265
|
+
|
266
|
+
- [ ] 実際にZettelkastenシステムを1週間試してみる #todo
|
267
|
+
- [ ] 効果を測定する方法を考える #todo
|
268
|
+
|
269
|
+
## 🔗 関連ノート
|
270
|
+
|
271
|
+
- [[001_zettelkasten_principles]]
|
272
|
+
- [[note_taking_methods]]
|
273
|
+
|
274
|
+
---
|
275
|
+
|
276
|
+
*これはサンプルファイルです。実際の読書ノートに置き換えてください。*
|
277
|
+
`,
|
278
|
+
'03_PermanentNote/sample_permanent.md': `# パーマネントノートのサンプル
|
279
|
+
|
280
|
+
ID: 001
|
281
|
+
作成日: ${new Date().toISOString().split('T')[0]}
|
282
|
+
タグ: #permanent #concept #knowledge-management #connection #important
|
283
|
+
|
284
|
+
## 🎯 知識管理における「つながり」の重要性
|
285
|
+
|
286
|
+
知識管理システムで最も重要なのは、個々の情報ではなく、それらの**つながり**である。
|
287
|
+
|
288
|
+
### なぜつながりが重要なのか
|
289
|
+
|
290
|
+
1. **新しい洞察の創出**
|
291
|
+
- 異なる分野の知識が結びつくことで、予期しない発見が生まれる
|
292
|
+
- 創造性は既存要素の新しい組み合わせから生まれる
|
293
|
+
|
294
|
+
2. **理解の深化**
|
295
|
+
- 関連する概念と結びつけることで、個別の知識がより深く理解される
|
296
|
+
- 文脈の中で知識を捉えることができる
|
297
|
+
|
298
|
+
3. **記憶の強化**
|
299
|
+
- 関連付けられた情報は、単独の情報よりも記憶に残りやすい
|
300
|
+
- 想起の手がかりが増える
|
301
|
+
|
302
|
+
### 実践的な方法
|
303
|
+
|
304
|
+
- 新しいノートを作成する際は、既存の3つ以上のノートとの関連を考える
|
305
|
+
- 定期的にノート間のリンクを見直し、新しいつながりを発見する
|
306
|
+
- 構造ノートを使って、テーマごとの知識の地図を作る
|
307
|
+
|
308
|
+
## 🔗 関連するノート
|
309
|
+
|
310
|
+
- [[creativity_and_connection]]
|
311
|
+
- [[memory_techniques]]
|
312
|
+
- [[zettelkasten_principles]]
|
313
|
+
|
314
|
+
## 📚 参考文献
|
315
|
+
|
316
|
+
- Ahrens, S. (2017). How to Take Smart Notes
|
317
|
+
- [[literature_note_smart_notes]]
|
318
|
+
|
319
|
+
---
|
320
|
+
|
321
|
+
*これは完全に自分の言葉で表現された知識です。*
|
322
|
+
`,
|
323
|
+
'04_StructureNote/sample_structure.md': `# 構造ノート: Zettelkastenとは
|
324
|
+
|
325
|
+
作成日: ${new Date().toISOString().split('T')[0]}
|
326
|
+
タグ: #structure #zettelkasten #index #map #overview
|
327
|
+
|
328
|
+
## 🗺️ このトピックの概要
|
329
|
+
|
330
|
+
このノートは「Zettelkasten」というテーマの入り口として機能します。
|
331
|
+
関連するすべてのノートへのナビゲーションを提供します。
|
332
|
+
|
333
|
+
## 📖 基本概念
|
334
|
+
|
335
|
+
### Zettelkastenの原則
|
336
|
+
|
337
|
+
- [[001_zettelkasten_principles]] - 基本的な考え方
|
338
|
+
- [[connection_importance]] - つながりの重要性
|
339
|
+
- [[atomic_notes]] - アトミックノートの概念
|
340
|
+
|
341
|
+
### 実践方法
|
342
|
+
|
343
|
+
- [[daily_note_routine]] - 日々のノート習慣
|
344
|
+
- [[note_linking_strategies]] - リンク戦略
|
345
|
+
- [[review_process]] - 定期的な見直し
|
346
|
+
|
347
|
+
## 📚 参考文献とソース
|
348
|
+
|
349
|
+
### 文献ノート
|
350
|
+
- [[literature_note_smart_notes]] - "How to Take Smart Notes"
|
351
|
+
- [[literature_note_second_brain]] - "Building a Second Brain"
|
352
|
+
|
353
|
+
### フリーティングノート
|
354
|
+
- [[fleeting_zettelkasten_discovery]] - Zettelkasten発見時の印象
|
355
|
+
- [[fleeting_digital_vs_analog]] - デジタル vs アナログの考察
|
356
|
+
|
357
|
+
## 🔍 関連トピック
|
358
|
+
|
359
|
+
- [[knowledge_management]] - より広い知識管理の文脈
|
360
|
+
- [[creative_thinking]] - 創造的思考との関連
|
361
|
+
- [[learning_systems]] - 学習システム全般
|
362
|
+
|
363
|
+
## 📊 進捗状況
|
364
|
+
|
365
|
+
- ✅ 基本概念の理解
|
366
|
+
- 🔄 実践方法の模索中
|
367
|
+
- ⭕ 自分なりのシステム構築(今後の課題)
|
368
|
+
|
369
|
+
---
|
370
|
+
|
371
|
+
*この構造ノートは定期的に更新し、新しいノートが追加されたら関連付けを行います。*
|
372
|
+
`
|
373
|
+
};
|
374
|
+
} else {
|
375
|
+
return {
|
376
|
+
'01_FleetingNote/sample_fleeting.md': `# Fleeting Note Sample
|
377
|
+
|
378
|
+
Date: ${new Date().toISOString().split('T')[0]}
|
379
|
+
Tags: #fleeting #zettelkasten #idea #draft
|
380
|
+
|
381
|
+
## 💭 Today's Ideas
|
382
|
+
|
383
|
+
- Started using Zettelkasten
|
384
|
+
- Quick capture of ideas is crucial
|
385
|
+
- Focus on recording first, organizing later
|
386
|
+
|
387
|
+
## 🔗 Potential Connections
|
388
|
+
|
389
|
+
- [[learning_methods]]
|
390
|
+
- [[knowledge_management_systems]]
|
391
|
+
|
392
|
+
## 📋 Next Actions
|
393
|
+
|
394
|
+
- [ ] Research this idea further #todo
|
395
|
+
- [ ] Find related literature #todo
|
396
|
+
|
397
|
+
---
|
398
|
+
|
399
|
+
*This is a sample file. Feel free to edit or delete.*
|
400
|
+
`,
|
401
|
+
'02_LiteratureNote/sample_literature.md': `# Literature Note Sample
|
402
|
+
|
403
|
+
Tags: #literature #source/book #zettelkasten #method #notes
|
404
|
+
|
405
|
+
## 📚 Book Information
|
406
|
+
|
407
|
+
- **Title**: How to Take Smart Notes
|
408
|
+
- **Author**: Sönke Ahrens
|
409
|
+
- **Year**: 2017
|
410
|
+
- **Date Read**: ${new Date().toISOString().split('T')[0]}
|
411
|
+
|
412
|
+
## 📝 Key Points
|
413
|
+
|
414
|
+
### Zettelkasten Principles
|
415
|
+
|
416
|
+
1. **One Idea, One Note**
|
417
|
+
- Each note focuses on a single idea
|
418
|
+
- Complex concepts are split into multiple notes
|
419
|
+
|
420
|
+
2. **Write in Your Own Words**
|
421
|
+
- Not just copy-paste, but express understanding
|
422
|
+
- Make it understandable when revisiting later
|
423
|
+
|
424
|
+
3. **Create Links**
|
425
|
+
- Explicitly create relationships between notes
|
426
|
+
- Enables emergence of new insights
|
427
|
+
|
428
|
+
## 🤔 My Reflections
|
429
|
+
|
430
|
+
I realized that Zettelkasten is not just a note-taking tool, but a thinking tool.
|
431
|
+
The important thing is... #concept #important
|
432
|
+
|
433
|
+
## 💡 Action Items
|
434
|
+
|
435
|
+
- [ ] Try the Zettelkasten system for a week #todo
|
436
|
+
- [ ] Think about how to measure effectiveness #todo
|
437
|
+
|
438
|
+
## 🔗 Related Notes
|
439
|
+
|
440
|
+
- [[001_zettelkasten_principles]]
|
441
|
+
- [[note_taking_methods]]
|
442
|
+
|
443
|
+
---
|
444
|
+
|
445
|
+
*This is a sample file. Replace with your actual reading notes.*
|
446
|
+
`,
|
447
|
+
'03_PermanentNote/sample_permanent.md': `# Permanent Note Sample
|
448
|
+
|
449
|
+
ID: 001
|
450
|
+
Created: ${new Date().toISOString().split('T')[0]}
|
451
|
+
Tags: #permanent #concept #knowledge-management #connection #important
|
452
|
+
|
453
|
+
## 🎯 The Importance of "Connections" in Knowledge Management
|
454
|
+
|
455
|
+
The most important aspect of knowledge management systems is not individual pieces of information, but their **connections**.
|
456
|
+
|
457
|
+
### Why Connections Matter
|
458
|
+
|
459
|
+
1. **Creation of New Insights**
|
460
|
+
|
461
|
+
- Unexpected discoveries emerge when knowledge from different fields connects
|
462
|
+
- Creativity comes from new combinations of existing elements
|
463
|
+
|
464
|
+
2. **Deepening Understanding**
|
465
|
+
|
466
|
+
- Knowledge becomes more deeply understood when connected to related concepts
|
467
|
+
- Enables understanding knowledge within context
|
468
|
+
|
469
|
+
3. **Memory Enhancement**
|
470
|
+
|
471
|
+
- Connected information is more memorable than isolated information
|
472
|
+
- Increases retrieval cues
|
473
|
+
|
474
|
+
### Practical Methods
|
475
|
+
|
476
|
+
- When creating new notes, consider connections to at least 3 existing notes
|
477
|
+
- Regularly review links between notes to discover new connections
|
478
|
+
- Use structure notes to create knowledge maps by theme
|
479
|
+
|
480
|
+
## 🔗 Related Notes
|
481
|
+
|
482
|
+
- [[creativity_and_connection]]
|
483
|
+
- [[memory_techniques]]
|
484
|
+
- [[zettelkasten_principles]]
|
485
|
+
|
486
|
+
## 📚 References
|
487
|
+
|
488
|
+
- Ahrens, S. (2017). How to Take Smart Notes
|
489
|
+
- [[literature_note_smart_notes]]
|
490
|
+
|
491
|
+
---
|
492
|
+
|
493
|
+
*This knowledge is expressed entirely in my own words.*
|
494
|
+
`,
|
495
|
+
'04_StructureNote/sample_structure.md': `# Structure Note: What is Zettelkasten
|
496
|
+
|
497
|
+
Created: ${new Date().toISOString().split('T')[0]}
|
498
|
+
Tags: #structure #zettelkasten #index #map #overview
|
499
|
+
|
500
|
+
## 🗺️ Topic Overview
|
501
|
+
|
502
|
+
This note serves as an entry point for the "Zettelkasten" theme.
|
503
|
+
It provides navigation to all related notes.
|
504
|
+
|
505
|
+
## 📖 Core Concepts
|
506
|
+
|
507
|
+
### Zettelkasten Principles
|
508
|
+
|
509
|
+
- [[001_zettelkasten_principles]] - Basic philosophy
|
510
|
+
- [[connection_importance]] - Importance of connections
|
511
|
+
- [[atomic_notes]] - Atomic note concept
|
512
|
+
|
513
|
+
### Practical Methods
|
514
|
+
|
515
|
+
- [[daily_note_routine]] - Daily note-taking habits
|
516
|
+
- [[note_linking_strategies]] - Linking strategies
|
517
|
+
- [[review_process]] - Regular review process
|
518
|
+
|
519
|
+
## 📚 References and Sources
|
520
|
+
|
521
|
+
### Literature Notes
|
522
|
+
|
523
|
+
- [[literature_note_smart_notes]] - "How to Take Smart Notes"
|
524
|
+
- [[literature_note_second_brain]] - "Building a Second Brain"
|
525
|
+
|
526
|
+
### Fleeting Notes
|
527
|
+
|
528
|
+
- [[fleeting_zettelkasten_discovery]] - First impressions of Zettelkasten
|
529
|
+
- [[fleeting_digital_vs_analog]] - Digital vs Analog considerations
|
530
|
+
|
531
|
+
## 🔍 Related Topics
|
532
|
+
|
533
|
+
- [[knowledge_management]] - Broader knowledge management context
|
534
|
+
- [[creative_thinking]] - Connection to creative thinking
|
535
|
+
- [[learning_systems]] - Learning systems in general
|
536
|
+
|
537
|
+
## 📊 Progress Status
|
538
|
+
|
539
|
+
- ✅ Understanding basic concepts
|
540
|
+
- 🔄 Exploring practical methods
|
541
|
+
- ⭕ Building personal system (future goal)
|
542
|
+
|
543
|
+
---
|
544
|
+
|
545
|
+
*This structure note will be updated regularly, and new connections will be made as notes are added.*
|
546
|
+
`
|
547
|
+
};
|
548
|
+
}
|
549
|
+
}
|
550
|
+
|
551
|
+
module.exports = {
|
552
|
+
createZettelkasten
|
553
|
+
};
|
package/package.json
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
{
|
2
|
+
"name": "zettelkasten-starter",
|
3
|
+
"version": "1.0.0",
|
4
|
+
"description": "Zettelkasten-Starter is an ultimately simple library for a zettelkasten beginner",
|
5
|
+
"main": "index.js",
|
6
|
+
"bin": {
|
7
|
+
"create-zettelkasten": "bin/create-zettelkasten.js"
|
8
|
+
},
|
9
|
+
"scripts": {
|
10
|
+
"test": "jest",
|
11
|
+
"test:watch": "jest --watch",
|
12
|
+
"test:coverage": "jest --coverage"
|
13
|
+
},
|
14
|
+
"keywords": [
|
15
|
+
"zettelkasten",
|
16
|
+
"note-taking",
|
17
|
+
"knowledge-management",
|
18
|
+
"pkm",
|
19
|
+
"markdown",
|
20
|
+
"cli"
|
21
|
+
],
|
22
|
+
"author": "Enokisan",
|
23
|
+
"license": "MIT",
|
24
|
+
"repository": {
|
25
|
+
"type": "git",
|
26
|
+
"url": "git+https://github.com/Enokisan/zettelkasten-starter.git"
|
27
|
+
},
|
28
|
+
"homepage": "https://github.com/Enokisan/zettelkasten-starter#readme",
|
29
|
+
"bugs": {
|
30
|
+
"url": "https://github.com/Enokisan/zettelkasten-starter/issues"
|
31
|
+
},
|
32
|
+
"dependencies": {
|
33
|
+
"commander": "^9.5.0",
|
34
|
+
"fs-extra": "^11.2.0"
|
35
|
+
},
|
36
|
+
"devDependencies": {
|
37
|
+
"jest": "^29.7.0",
|
38
|
+
"@types/jest": "^29.5.5"
|
39
|
+
},
|
40
|
+
"engines": {
|
41
|
+
"node": ">=14.0.0"
|
42
|
+
},
|
43
|
+
"jest": {
|
44
|
+
"testEnvironment": "node",
|
45
|
+
"collectCoverageFrom": [
|
46
|
+
"lib/**/*.js",
|
47
|
+
"bin/**/*.js",
|
48
|
+
"!node_modules/**"
|
49
|
+
],
|
50
|
+
"coverageDirectory": "coverage",
|
51
|
+
"coverageReporters": [
|
52
|
+
"text",
|
53
|
+
"lcov",
|
54
|
+
"html"
|
55
|
+
]
|
56
|
+
}
|
57
|
+
}
|