@nan0web/release 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.
Files changed (79) hide show
  1. package/.datasets/README.dataset.jsonl +19 -0
  2. package/.editorconfig +20 -0
  3. package/CHANGELOG.md +6 -0
  4. package/CONTRIBUTING.md +42 -0
  5. package/LICENSE +15 -0
  6. package/README.md +284 -0
  7. package/bin/commands/InitCommand.js +10 -0
  8. package/bin/lib/functions.js +240 -0
  9. package/bin/lib/functions.test.js +138 -0
  10. package/bin/lib/temp/pnpm-lock.yaml +0 -0
  11. package/bin/nan0release.js +57 -0
  12. package/bin/release.js +12 -0
  13. package/bin/release.test.js +73 -0
  14. package/bin/temp/package.json +1 -0
  15. package/docs/system.uk.md +413 -0
  16. package/docs/uk/README.md +284 -0
  17. package/legacy/PrepareCommand.js +154 -0
  18. package/legacy/PrepareCommand.test.js +189 -0
  19. package/legacy/changelog.js +446 -0
  20. package/package.json +43 -0
  21. package/releases/1/0/v1.0.0/models/Person.js +26 -0
  22. package/releases/1/0/v1.0.0/models/Person.test.js +44 -0
  23. package/releases/1/0/v1.0.0/models/company.js +11 -0
  24. package/releases/1/0/v1.0.0/models/members.js +14 -0
  25. package/releases/1/0/v1.0.0/models/members.test.js +21 -0
  26. package/releases/1/0/v1.0.0/models/teams/DevTeam.js +6 -0
  27. package/releases/1/0/v1.0.0/models/teams/DevTeam.test.js +15 -0
  28. package/releases/1/0/v1.0.0/models/teams/index.js +5 -0
  29. package/releases/1/0/v1.0.0/release.js +12 -0
  30. package/releases/1/0/v1.0.0/release.md +121 -0
  31. package/releases/1/0/v1.0.0/release.test.js +36 -0
  32. package/releases/1/0/v1.0.0/test/YaRaSLove/git-native.test.js +14 -0
  33. package/releases/current.js +3 -0
  34. package/releases/index.js +11 -0
  35. package/src/README.md.js +468 -0
  36. package/src/Release/Document.js +110 -0
  37. package/src/Release/MarkdownToTest.js +137 -0
  38. package/src/Release/MarkdownToTest.test.js +60 -0
  39. package/src/Release/Person.js +29 -0
  40. package/src/Release.js +132 -0
  41. package/src/ReleaseCLI.js +122 -0
  42. package/src/Team.js +35 -0
  43. package/src/architecture/ProjectManagementAsCode.js +264 -0
  44. package/src/architecture/ProjectManagementAsCode.test.js +128 -0
  45. package/src/co/AppCommandMessage.js +22 -0
  46. package/src/co/AppCommandOptions.js +27 -0
  47. package/src/commands/ChatCommand.js +56 -0
  48. package/src/commands/HostCommand.js +63 -0
  49. package/src/commands/InitCommand.js +65 -0
  50. package/src/commands/ListCommand.js +56 -0
  51. package/src/commands/PublishCommand.js +144 -0
  52. package/src/commands/SealCommand.js +51 -0
  53. package/src/commands/ServeCommand.js +53 -0
  54. package/src/commands/ShowCommand.js +58 -0
  55. package/src/commands/SubCommand.js +21 -0
  56. package/src/commands/ValidateCommand.js +66 -0
  57. package/src/db/ReleaseDB.js +54 -0
  58. package/system.md +571 -0
  59. package/tsconfig.json +23 -0
  60. package/types/Release/Document.d.ts +31 -0
  61. package/types/Release/MarkdownToTest.d.ts +47 -0
  62. package/types/Release/Person.d.ts +17 -0
  63. package/types/Release.d.ts +89 -0
  64. package/types/ReleaseCLI.d.ts +33 -0
  65. package/types/Team.d.ts +27 -0
  66. package/types/architecture/ProjectManagementAsCode.d.ts +159 -0
  67. package/types/co/AppCommandMessage.d.ts +12 -0
  68. package/types/co/AppCommandOptions.d.ts +23 -0
  69. package/types/commands/ChatCommand.d.ts +20 -0
  70. package/types/commands/HostCommand.d.ts +16 -0
  71. package/types/commands/InitCommand.d.ts +20 -0
  72. package/types/commands/ListCommand.d.ts +21 -0
  73. package/types/commands/PublishCommand.d.ts +49 -0
  74. package/types/commands/SealCommand.d.ts +21 -0
  75. package/types/commands/ServeCommand.d.ts +17 -0
  76. package/types/commands/ShowCommand.d.ts +19 -0
  77. package/types/commands/SubCommand.d.ts +8 -0
  78. package/types/commands/ValidateCommand.d.ts +19 -0
  79. package/types/db/ReleaseDB.d.ts +30 -0
@@ -0,0 +1,284 @@
1
+ # @nan0web/release
2
+
3
+ | Назва пакету | [Статус](https://github.com/nan0web/monorepo/blob/main/system.md#написання-сценаріїв) | Документація | Покриття тестами | Особливості | Версія npm |
4
+ |--------------|----------------------------------------------------------------------------------------|---------------|------------------|-------------|------------|
5
+ | [@nan0web/release](https://github.com/nan0web/release/) |🟢 `97.1%` | 🧪 [Англійською 🏴󠁧󠁢󠁥󠁮󠁧󠁿](https://github.com/nan0web/release/blob/main/README.md)<br />[Українською 🇺🇦](https://github.com/nan0web/release/blob/main/docs/uk/README.md) |🟡 `84.2%` | ✅ d.ts 📜 system.md 🕹️ playground | — |
6
+
7
+ Керування проєктами як код, орієнтоване на Git, підписане GPG та кероване тестами.
8
+
9
+ На відміну від традиційних інструментів керування проєктами, які потребують постійної синхронізації чи ручних оновлень,
10
+ `@nan0web/release` забезпечує структуру проєкту, де:
11
+ - Завдання представлені як тести у файлах `.test.js`.
12
+ - Статус відстежується через результати виконання тестів.
13
+ - Ролі в команді та погодження зберігаються у структурованих файлах.
14
+ - Всі зміни підписані, версійовані та компонуємі.
15
+
16
+ Це гарантує:
17
+ - Чітке, автоматизоване відстеження проєкту.
18
+ - Незмінні нотатки релізів і рефлексії після них.
19
+ - Нульову неоднозначність – завдання не існують, якщо не протестовані.
20
+
21
+ ## Встановлення
22
+
23
+ Як встановити за допомогою pnpm?
24
+ ```bash
25
+ pnpm add @nan0web/release
26
+ ```
27
+
28
+ Як встановити за допомогою npm?
29
+ ```bash
30
+ npm install @nan0web/release
31
+ ```
32
+
33
+ Як встановити за допомогою yarn?
34
+ ```bash
35
+ yarn add @nan0web/release
36
+ ```
37
+
38
+ ## Використання CLI
39
+
40
+ Почніть із ініціалізації нового релізу:
41
+
42
+ Як ініціалізувати нову версію релізу?
43
+ ```bash
44
+ release init v1.0.0
45
+ ```
46
+
47
+ const cli = new ReleaseCLI()
48
+ Показати деталі релізу:
49
+
50
+ Як показати інформацію про реліз?
51
+ ```bash
52
+ release show [--full]
53
+ ```
54
+
55
+ const release = new Release({
56
+ version: "v1.0.0",
57
+ createdAt: new Date("2025-08-20")
58
+ })
59
+ console.info(release.version) // v1.0.0
60
+ Список усіх релізів у проєкті:
61
+
62
+ Як отримати список усіх релізів?
63
+ ```bash
64
+ release list [--json]
65
+ ```
66
+
67
+ const cli = new ReleaseCLI()
68
+ Додати повідомлення до поточного релізу:
69
+
70
+ Як написати повідомлення до релізу?
71
+ ```bash
72
+ release chat write --user alice "Проблема з пайплайном збірки"
73
+ ```
74
+
75
+ const cli = new ReleaseCLI()
76
+ Запустити статичний сервер для перегляду релізів:
77
+
78
+ Як запустити інтерфейс релізів?
79
+ ```bash
80
+ release host [--webui] [--port 3000]
81
+ ```
82
+
83
+ const cli = new ReleaseCLI()
84
+ Обслуговувати всі файли релізу для локального перегляду:
85
+
86
+ Як обслуговувати статичні ресурси релізу?
87
+ ```bash
88
+ release serve [--port 8080]
89
+ ```
90
+
91
+ const cli = new ReleaseCLI()
92
+ Перевірити завдання та цілісність релізу:
93
+
94
+ Як перевірити завдання релізу?
95
+ ```bash
96
+ release validate [--ignore-fail]
97
+ ```
98
+
99
+ const release = new Release({
100
+ version: "v1.0.0",
101
+ createdAt: new Date("2025-08-20")
102
+ })
103
+ Завершити реліз з рефлексією:
104
+
105
+ Як завершити (запечатати) реліз?
106
+ ```bash
107
+ release seal [--message "Усі основні API стабільні і протестовані"]
108
+ ```
109
+
110
+ const cli = new ReleaseCLI()
111
+ ## Основні концепції
112
+
113
+ ### 1. Реліз як об’єкт
114
+
115
+ Об’єкт `Release` включає:
116
+ - `version`: ідентифікатор релізу (vX.Y.Z).
117
+ - `createdAt`, `startAt`, `planAt`, `completeAt`: часові мітки.
118
+ - `document`: розібраний markdown-документ із структурною орієнтацією.
119
+
120
+ Усі властивості типізовані та автоматично перевіряються через JSDoc та runtime-аналіз.
121
+
122
+ Як створити об’єкт Release?
123
+ ```js
124
+ import { Release } from '@nan0web/release'
125
+ const release = new Release({
126
+ version: "v1.0.0",
127
+ createdAt: "2025-08-20T10:00:00Z"
128
+ })
129
+ console.info(release.version) // ← v1.0.0
130
+ console.info(release.createdAt instanceof Date) // ← true
131
+
132
+ ```
133
+ ### 2. Розпізнавання документа релізу
134
+
135
+ `ReleaseDocument` розширює `@nan0web/markdown`:
136
+ - Структурний аналіз markdown-файлу нотаток релізу
137
+ - Видобування розділів і завдань
138
+ - Аналіз команд і ролей із frontmatter чи коду
139
+
140
+ Як перетворити markdown-нотатки релізу у структуровані дані?
141
+ ```js
142
+ import { ReleaseDocument } from '@nan0web/release'
143
+ const md = `# v1.0.0 - 2025-08-20
144
+
145
+ ## Огляд
146
+ Цей реліз включає поліпшення інтерфейсу та стабілізацію API.
147
+
148
+ ### Завдання
149
+ ### Done **Реалізувати основний дизайн інтерфейсу** [ui.core-design]
150
+ ### InProgress **Виправити проблеми адаптивності на мобільних пристроях** [ui.mobile-fixes]`
151
+
152
+ const doc = ReleaseDocument.from(md)
153
+ console.info(doc.version) // ← v1.0.0
154
+ console.info(doc.date instanceof Date) // ← true
155
+ console.info(doc.document.children instanceof Array) // ← true
156
+
157
+ ```
158
+ ### 3. Структури персони та команди
159
+
160
+ Об’єкт `Person` включає:
161
+ - `name`: HumanName
162
+ - `gender`: HumanGender
163
+ - `contacts`: масив HumanContact
164
+
165
+ Як створити об’єкт Person із типізованими властивостями?
166
+ ```js
167
+ import { Person } from '@nan0web/release'
168
+ const person = new Person({
169
+ name: ["Alice", "Developer"],
170
+ gender: "female",
171
+ contacts: ["mailto:alice@example.com"]
172
+ })
173
+ console.info(person.name.firstName) // ← Alice
174
+ console.info(person.contacts.length >= 0) // ← true
175
+
176
+ ```
177
+ ## Архітектура: керування проєктами як код
178
+
179
+ Ця архітектура розглядає завдання як тести, ролі — як класи, а прогрес — як підтверджені результати.
180
+
181
+ ### Управління завданнями
182
+
183
+ Завдання фіксуються як `taskId` → `testFilePath` в `ProjectManagement`.
184
+ Статус визначається через запуск наборів тестів `node:test`.
185
+
186
+ Як зареєструвати та перевірити завдання за допомогою тестів?
187
+ ```js
188
+ import { ProjectManagement } from '@nan0web/release'
189
+ const pm = new ProjectManagement()
190
+ pm.registerTask("task-1", "./tests/task1.test.js")
191
+ pm.registerTask("task-2", "./tests/task2.test.js")
192
+
193
+ const mockResults = {
194
+ passed: ["task-1"],
195
+ failed: [],
196
+ pending: ["task-2"]
197
+ }
198
+
199
+ pm.validateProjectState = async () => mockResults
200
+ const results = await pm.validateProjectState()
201
+
202
+ console.info(results.passed.includes("task-1")) // ← true
203
+ console.info(results.pending.includes("task-2")) // ← true
204
+
205
+ ```
206
+ ### Обробка релізу
207
+
208
+ `ReleaseManager` координує релізи і перевіряє готовність.
209
+ Він гарантує:
210
+ - Всі зареєстровані тести проходять
211
+ - Версія правильно збільшується
212
+ - Git-тег встановлюється, якщо всі перевірки успішні
213
+
214
+ Як виконати реліз після перевірки?
215
+ ```js
216
+ import { ReleaseManager, ProjectManagement } from '@nan0web/release'
217
+ const pm = new ProjectManagement()
218
+ const rm = new ReleaseManager(pm)
219
+
220
+ rm.calculateVersion = () => "v1.0.1"
221
+ rm.publish = async () => true
222
+
223
+ const result = await rm.executeRelease("patch")
224
+ console.info(result.version) // ← v1.0.1
225
+ console.info(result.published) // ← true
226
+
227
+ ```
228
+ ### Інтеграція з CHANGELOG
229
+
230
+ Через `ChangelogTaskManager` можна:
231
+ - Видобувати означення завдань з записів змін
232
+ - Генерувати відповідні тести
233
+ - Автоматизувати перевірку завдань у CI
234
+
235
+ Як розібрати changelog і видобути завдання?
236
+ ```js
237
+ import { ChangelogTaskManager } from '@nan0web/release'
238
+ const ctm = new ChangelogTaskManager()
239
+ const changelog = `# Зміни
240
+
241
+ ## [1.0.0] - 2025-08-20
242
+ ### Added
243
+ - Створено основну структуру [core.init]
244
+ - Додано підтримку нотаток релізу [docs.release-notes]`
245
+
246
+ const tasks = ctm.parseChangelog(changelog)
247
+ console.info(Array.isArray(tasks)) // ← true
248
+ console.info(tasks.length >= 0) // ← true
249
+
250
+ ```
251
+ ## Особливості Java•Script
252
+
253
+ Використовує JSDoc і .d.ts для автозаповнення
254
+
255
+ ## Демонстрація CLI
256
+
257
+ Спробуйте пакет у терміналі:
258
+
259
+ Як запустити демо playground?
260
+ ```bash
261
+ git clone https://github.com/nan0web/release.git
262
+ cd release
263
+ pnpm install
264
+ pnpm run playground
265
+ ```
266
+
267
+ ## Статус проєкту
268
+
269
+ Перевірити готовність проєкту можна так:
270
+
271
+ Як перевірити статус перед публікацією?
272
+ ```bash
273
+ npm test
274
+ npm run test:coverage
275
+ npm run test:status
276
+ ```
277
+
278
+ ## Внески
279
+
280
+ Як зробити внесок? — [перегляньте тут](./CONTRIBUTING.md)
281
+
282
+ ## Ліцензія
283
+
284
+ Як застосувати ліцензію ISC? — [перегляньте тут](./LICENSE)
@@ -0,0 +1,154 @@
1
+ import { execSync } from 'node:child_process'
2
+ import DB from '@nan0web/db-fs'
3
+ import Logger from '@nan0web/log'
4
+ import { Changelog } from '@nan0web/changelog'
5
+ import { Command } from '@nan0web/co'
6
+
7
+ /**
8
+ * @typedef {Object} PrepareCommandOpts
9
+ * @property {boolean} laconic
10
+ * @property {boolean} descriptive
11
+ * @property {string} template
12
+ */
13
+ /**
14
+ * Prepare command class
15
+ */
16
+ class PrepareCommand extends Command {
17
+ /** @type {Logger} */
18
+ logger
19
+ /** @type {PrepareCommandOpts} */
20
+ opts
21
+ /** @type {string} */
22
+ cwd
23
+ /** @type {DB} */
24
+ db
25
+
26
+ constructor(options = {}) {
27
+ const {
28
+ logger = new Logger(),
29
+ cwd = ".",
30
+ } = options
31
+ super(options)
32
+ this.logger = Logger.from(logger)
33
+ this.cwd = String(cwd)
34
+ this.options = new Map([
35
+ ["laconic", [false, "Generate laconic changelog"]],
36
+ ["descriptive", [false, "Generate descriptive changelog"]],
37
+ ["template", [String, "Template file or message for a changelog"]],
38
+ ])
39
+ this.db = new DB({ cwd: this.cwd })
40
+ }
41
+
42
+ /**
43
+ * Run the prepare command
44
+ * @param {string[]} args - Command arguments
45
+ * @returns {Promise<void>}
46
+ */
47
+ async run(args) {
48
+ let content = await this.db.loadDocument('CHANGELOG.md', '')
49
+
50
+ const changelog = new Changelog()
51
+ changelog.parse(content)
52
+ changelog.init()
53
+ const latest = changelog.getLatestVersion()
54
+ const versions = changelog.getVersions()
55
+
56
+ // Get git changes
57
+ const changes = this.getGitChanges()
58
+
59
+ if (changes.length > 0) {
60
+ const promptContent = await this.generateChangelogPrompt(changes)
61
+ await this.db.writeDocument("me.md", String(promptContent))
62
+ }
63
+
64
+ // Save updated changelog
65
+ await this.db.saveDocument('CHANGELOG.md', String(changelog.document))
66
+ }
67
+
68
+ /**
69
+ * Get list of new and modified files from git
70
+ * @returns {string[]} - Array of file paths
71
+ */
72
+ getGitChanges() {
73
+ try {
74
+ // Get uncommitted changes
75
+ const statusOutput = execSync('git status --porcelain', {
76
+ cwd: this.cwd,
77
+ encoding: 'utf8'
78
+ })
79
+
80
+ const changes = statusOutput
81
+ .split('\n')
82
+ .filter(line => line.trim() !== '')
83
+ .map(line => {
84
+ const parts = line.trim().split(/\s+/)
85
+ return parts[1]
86
+ })
87
+
88
+ return changes
89
+ } catch (e) {
90
+ this.logger.warn('Git repository not found or git command failed')
91
+ return []
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Generate changelog prompt from file changes
97
+ * @param {string[]} changes - Array of changed file paths
98
+ * @returns {Promise<string>} - Prompt content
99
+ */
100
+ async generateChangelogPrompt(changes) {
101
+ const fileStats = []
102
+ for (const filePath of changes) {
103
+ try {
104
+ const stat = await this.db.statDocument(filePath)
105
+ fileStats.push({ path: filePath, isFile: stat.isFile })
106
+ } catch {
107
+ fileStats.push({ path: filePath, isFile: false })
108
+ }
109
+ }
110
+
111
+ const files = fileStats
112
+ .filter(item => item.isFile)
113
+ .map(item => item.path)
114
+
115
+ if (files.length === 0) {
116
+ return ''
117
+ }
118
+
119
+ const promptLines = [
120
+ "Based on the following changed files, please provide changelog entries:",
121
+ ]
122
+
123
+ const diffOutput = execSync('git diff --no-color HEAD', {
124
+ cwd: this.cwd,
125
+ encoding: 'utf8'
126
+ })
127
+
128
+ if (diffOutput.trim() !== '') {
129
+ promptLines.push('\n```diff\n' + diffOutput.trim() + '\n```')
130
+ }
131
+
132
+ const newFiles = execSync('git ls-files --others --exclude-standard', {
133
+ cwd: this.cwd,
134
+ encoding: 'utf8'
135
+ }).split('\n').filter(line => line.trim() !== '')
136
+
137
+ for (const file of newFiles) {
138
+ try {
139
+ const content = await this.db.loadDocumentAs(".txt", file)
140
+ promptLines.push(`\n#### \`${file}\``)
141
+ promptLines.push('```')
142
+ promptLines.push(content)
143
+ promptLines.push('```')
144
+ } catch {
145
+ // ignore files that can't be read
146
+ this.logger.warn("Could not read file", file)
147
+ }
148
+ }
149
+
150
+ return promptLines.join('\n')
151
+ }
152
+ }
153
+
154
+ export default PrepareCommand
@@ -0,0 +1,189 @@
1
+ import { describe, it } from 'node:test'
2
+ import { strict as assert } from 'node:assert'
3
+ import { execSync } from 'node:child_process'
4
+ import { mkdirSync, writeFileSync, existsSync, rmSync } from 'node:fs'
5
+ import { resolve } from 'node:path'
6
+ import DB from '@nan0web/db-fs'
7
+ import Logger from '@nan0web/log'
8
+ import PrepareCommand from './PrepareCommand.js'
9
+
10
+ describe('PrepareCommand', () => {
11
+ it('should initialize CHANGELOG.md if it is empty', async () => {
12
+ const testDir = resolve('test_data/empty_changelog')
13
+
14
+ // Remove directory if exists
15
+ if (existsSync(testDir)) {
16
+ rmSync(testDir, { recursive: true })
17
+ }
18
+
19
+ mkdirSync(testDir, { recursive: true })
20
+
21
+ // Initialize git repo
22
+ try {
23
+ execSync('git init', { cwd: testDir })
24
+ execSync('git config user.name "Test User"', { cwd: testDir })
25
+ execSync('git config user.email "test@example.com"', { cwd: testDir })
26
+ } catch (e) {
27
+ assert.fail('Failed to initialize git repository: ' + e.message)
28
+ }
29
+
30
+ // Create empty CHANGELOG.md
31
+ writeFileSync(resolve(testDir, 'CHANGELOG.md'), '')
32
+
33
+ // Add and commit
34
+ try {
35
+ execSync('git add CHANGELOG.md', { cwd: testDir })
36
+ execSync('git commit -m "Initial commit"', { cwd: testDir })
37
+ } catch (e) {
38
+ // Ignore if nothing to commit
39
+ }
40
+
41
+ const command = new PrepareCommand({
42
+ cwd: testDir,
43
+ logger: new Logger({ silent: true })
44
+ })
45
+
46
+ try {
47
+ await command.run([])
48
+ } catch (e) {
49
+ assert.fail('Command failed: ' + e.message)
50
+ }
51
+
52
+ const db = new DB({ cwd: testDir })
53
+ const changelogContent = await db.loadDocument('CHANGELOG.md', '')
54
+ assert.ok(changelogContent.includes('# Changelog'), 'CHANGELOG.md should be initialized with header')
55
+ })
56
+
57
+ it('should generate changelog prompt with new and modified files', async () => {
58
+ const testDir = resolve('test_data/changes')
59
+
60
+ // Remove directory if exists
61
+ if (existsSync(testDir)) {
62
+ rmSync(testDir, { recursive: true })
63
+ }
64
+
65
+ mkdirSync(testDir, { recursive: true })
66
+
67
+ // Initialize git repo
68
+ try {
69
+ execSync('git init', { cwd: testDir })
70
+ execSync('git config user.name "Test User"', { cwd: testDir })
71
+ execSync('git config user.email "test@example.com"', { cwd: testDir })
72
+ // Create initial commit
73
+ writeFileSync(resolve(testDir, 'initial.txt'), 'initial content')
74
+ execSync('git add initial.txt', { cwd: testDir })
75
+ execSync('git commit -m "Initial commit"', { cwd: testDir })
76
+ } catch (e) {
77
+ assert.fail('Failed to initialize git repository: ' + e.message)
78
+ }
79
+
80
+ // Create initial files
81
+ writeFileSync(resolve(testDir, 'CHANGELOG.md'), '# Changelog\n')
82
+ writeFileSync(resolve(testDir, 'existing-file.txt'), 'existing content\n')
83
+
84
+ // Add and commit initial files
85
+ try {
86
+ execSync('git add CHANGELOG.md existing-file.txt', { cwd: testDir })
87
+ execSync('git commit -m "Add initial files"', { cwd: testDir })
88
+ } catch (e) {
89
+ // Ignore if nothing to commit
90
+ }
91
+
92
+ // Make changes
93
+ writeFileSync(resolve(testDir, 'new-file.txt'), 'new content\n')
94
+ writeFileSync(resolve(testDir, 'existing-file.txt'), 'existing content\nmodified content\n')
95
+
96
+ const command = new PrepareCommand({
97
+ cwd: testDir,
98
+ logger: new Logger({ silent: true })
99
+ })
100
+
101
+ try {
102
+ await command.run([])
103
+ } catch (err) {
104
+ assert.fail('Command failed: ' + err.message)
105
+ }
106
+
107
+ // Test passes if no exception is thrown
108
+ assert.ok(true)
109
+ })
110
+
111
+ it('should include recent changelog entries in the prompt', async () => {
112
+ const testDir = resolve('test_data/recent_entries')
113
+
114
+ // Remove directory if exists
115
+ if (existsSync(testDir)) {
116
+ rmSync(testDir, { recursive: true })
117
+ }
118
+
119
+ mkdirSync(testDir, { recursive: true })
120
+
121
+ // Initialize git repo
122
+ try {
123
+ execSync('git init', { cwd: testDir })
124
+ execSync('git config user.name "Test User"', { cwd: testDir })
125
+ execSync('git config user.email "test@example.com"', { cwd: testDir })
126
+ // Create initial commit
127
+ writeFileSync(resolve(testDir, 'initial.txt'), 'initial content')
128
+ execSync('git add initial.txt', { cwd: testDir })
129
+ execSync('git commit -m "Initial commit"', { cwd: testDir })
130
+ } catch (e) {
131
+ assert.fail('Failed to initialize git repository: ' + e.message)
132
+ }
133
+
134
+ // Create directories
135
+ mkdirSync(resolve(testDir, 'src'), { recursive: true })
136
+
137
+ // Create CHANGELOG.md with existing releases
138
+ const changelogContent = `# Changelog
139
+
140
+ ## [1.2.0] - 2024-01-03
141
+ ### Added
142
+ - New feature Y
143
+
144
+ ## [1.1.0] - 2024-01-02
145
+ ### Changed
146
+ - Improved performance of module X
147
+
148
+ ## [1.0.0] - 2024-01-01
149
+ ### Added
150
+ - Initial release
151
+ - Core functionality implemented
152
+ `
153
+ writeFileSync(resolve(testDir, 'CHANGELOG.md'), changelogContent)
154
+
155
+ // Add and commit CHANGELOG.md
156
+ try {
157
+ execSync('git add CHANGELOG.md', { cwd: testDir })
158
+ execSync('git commit -m "Add changelog with entries"', { cwd: testDir })
159
+ } catch (e) {
160
+ // Ignore if nothing to commit
161
+ }
162
+
163
+ // Make changes
164
+ writeFileSync(resolve(testDir, 'src/new-feature.js'), 'console.log("new feature")\n')
165
+
166
+ const command = new PrepareCommand({
167
+ cwd: testDir,
168
+ logger: new Logger({ silent: true })
169
+ })
170
+
171
+ try {
172
+ await command.run([])
173
+ } catch (err) {
174
+ assert.fail('Command failed: ' + err.message)
175
+ }
176
+
177
+ const db = new DB({ cwd: testDir })
178
+ const promptContent = await db.loadDocument('me.md', '')
179
+
180
+ // Check that recent entries are included in the prompt
181
+ assert.ok(promptContent.includes('## [1.2.0] - 2024-01-03'), 'Prompt should include most recent entry')
182
+ assert.ok(promptContent.includes('## [1.1.0] - 2024-01-02'), 'Prompt should include second recent entry')
183
+ assert.ok(promptContent.includes('## [1.0.0] - 2024-01-01'), 'Prompt should include third recent entry')
184
+
185
+ // Check that the prompt contains the instruction about using these entries as examples
186
+ assert.ok(promptContent.includes('Based on the following changed files, please provide changelog entries:'),
187
+ 'Prompt should contain instructions for generating changelog entries')
188
+ })
189
+ })