@meza/adr-tools 2.0.2 → 2.0.4

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 (124) hide show
  1. package/dist/index.js +8 -1
  2. package/dist/index.js.map +1 -1
  3. package/dist/types/index.d.ts.map +1 -1
  4. package/dist/types/version.d.ts +1 -1
  5. package/dist/version.js +1 -1
  6. package/package.json +21 -9
  7. package/.adr-dir +0 -1
  8. package/.editorconfig +0 -13
  9. package/.gitattributes +0 -40
  10. package/.github/renovate.json +0 -4
  11. package/.github/stale.yml +0 -18
  12. package/.github/workflows/ci-pr.yml +0 -48
  13. package/.github/workflows/ci.yml +0 -67
  14. package/.github/workflows/security.yml +0 -49
  15. package/.releaserc.json +0 -49
  16. package/.yarnrc.yml +0 -8
  17. package/AGENTS.engineer.md +0 -236
  18. package/AGENTS.md +0 -11
  19. package/AGENTS.reviewer.md +0 -115
  20. package/CONTRIBUTING.md +0 -102
  21. package/biome.json +0 -28
  22. package/dist/index.test.js +0 -197
  23. package/dist/index.test.js.map +0 -1
  24. package/dist/inject-version.test.js +0 -27
  25. package/dist/inject-version.test.js.map +0 -1
  26. package/dist/lib/adr.test.js +0 -319
  27. package/dist/lib/adr.test.js.map +0 -1
  28. package/dist/lib/config.test.js +0 -60
  29. package/dist/lib/config.test.js.map +0 -1
  30. package/dist/lib/links.test.js +0 -66
  31. package/dist/lib/links.test.js.map +0 -1
  32. package/dist/lib/manipulator-errors.test.js +0 -21
  33. package/dist/lib/manipulator-errors.test.js.map +0 -1
  34. package/dist/lib/manipulator.test.js +0 -96
  35. package/dist/lib/manipulator.test.js.map +0 -1
  36. package/dist/lib/numbering.test.js +0 -39
  37. package/dist/lib/numbering.test.js.map +0 -1
  38. package/dist/lib/opening.test.js +0 -81
  39. package/dist/lib/opening.test.js.map +0 -1
  40. package/dist/lib/prompt.test.js +0 -33
  41. package/dist/lib/prompt.test.js.map +0 -1
  42. package/dist/lib/template.test.js +0 -62
  43. package/dist/lib/template.test.js.map +0 -1
  44. package/dist/types/index.test.d.ts +0 -2
  45. package/dist/types/index.test.d.ts.map +0 -1
  46. package/dist/types/inject-version.test.d.ts +0 -2
  47. package/dist/types/inject-version.test.d.ts.map +0 -1
  48. package/dist/types/lib/adr.test.d.ts +0 -2
  49. package/dist/types/lib/adr.test.d.ts.map +0 -1
  50. package/dist/types/lib/config.test.d.ts +0 -2
  51. package/dist/types/lib/config.test.d.ts.map +0 -1
  52. package/dist/types/lib/links.test.d.ts +0 -2
  53. package/dist/types/lib/links.test.d.ts.map +0 -1
  54. package/dist/types/lib/manipulator-errors.test.d.ts +0 -2
  55. package/dist/types/lib/manipulator-errors.test.d.ts.map +0 -1
  56. package/dist/types/lib/manipulator.test.d.ts +0 -2
  57. package/dist/types/lib/manipulator.test.d.ts.map +0 -1
  58. package/dist/types/lib/numbering.test.d.ts +0 -2
  59. package/dist/types/lib/numbering.test.d.ts.map +0 -1
  60. package/dist/types/lib/opening.test.d.ts +0 -2
  61. package/dist/types/lib/opening.test.d.ts.map +0 -1
  62. package/dist/types/lib/prompt.test.d.ts +0 -2
  63. package/dist/types/lib/prompt.test.d.ts.map +0 -1
  64. package/dist/types/lib/template.test.d.ts +0 -2
  65. package/dist/types/lib/template.test.d.ts.map +0 -1
  66. package/doc/adr/.adr-sequence.lock +0 -1
  67. package/doc/adr/0001-record-architecture-decisions.md +0 -21
  68. package/doc/adr/0002-using-heavy-e2e-tests.md +0 -20
  69. package/doc/adr/0003-esm.md +0 -34
  70. package/doc/adr/0004-gate-editor-opening-behind---open-and---open-with.md +0 -55
  71. package/doc/adr/decisions.md +0 -6
  72. package/lefthook.yml +0 -14
  73. package/scripts/inject-version.mjs +0 -34
  74. package/src/index.test.ts +0 -222
  75. package/src/index.ts +0 -261
  76. package/src/inject-version.test.ts +0 -31
  77. package/src/lib/adr.test.ts +0 -389
  78. package/src/lib/adr.ts +0 -384
  79. package/src/lib/config.test.ts +0 -69
  80. package/src/lib/config.ts +0 -36
  81. package/src/lib/links.test.ts +0 -74
  82. package/src/lib/links.ts +0 -34
  83. package/src/lib/manipulator-errors.test.ts +0 -22
  84. package/src/lib/manipulator.test.ts +0 -109
  85. package/src/lib/manipulator.ts +0 -98
  86. package/src/lib/numbering.test.ts +0 -45
  87. package/src/lib/numbering.ts +0 -25
  88. package/src/lib/opening.test.ts +0 -96
  89. package/src/lib/prompt.test.ts +0 -42
  90. package/src/lib/prompt.ts +0 -14
  91. package/src/lib/template.test.ts +0 -74
  92. package/src/lib/template.ts +0 -22
  93. package/src/templates/init.md +0 -21
  94. package/src/templates/template.md +0 -19
  95. package/src/types/environment.d.ts +0 -14
  96. package/src/version.ts +0 -1
  97. package/tests/.adr-dir +0 -1
  98. package/tests/__snapshots__/generate-graph.e2e.test.ts.snap +0 -39
  99. package/tests/__snapshots__/init-adr-repository.e2e.test.ts.snap +0 -51
  100. package/tests/__snapshots__/linking-records.e2e.test.ts.snap +0 -155
  101. package/tests/__snapshots__/new-adr.e2e.test.ts.snap +0 -54
  102. package/tests/__snapshots__/superseding-records.e2e.test.ts.snap +0 -122
  103. package/tests/__snapshots__/toc-prefixing.e2e.test.ts.snap +0 -9
  104. package/tests/__snapshots__/use-template-override.e2e.test.ts.snap +0 -17
  105. package/tests/edit-on-create.e2e.test.ts +0 -90
  106. package/tests/fake-editor +0 -5
  107. package/tests/fake-editor.cmd +0 -4
  108. package/tests/fake-visual +0 -5
  109. package/tests/fake-visual.cmd +0 -4
  110. package/tests/funny-characters.e2e.test.ts +0 -51
  111. package/tests/generate-graph.e2e.test.ts +0 -49
  112. package/tests/helpers/adr-cli.ts +0 -24
  113. package/tests/init-adr-repository.e2e.test.ts +0 -58
  114. package/tests/linking-records.e2e.test.ts +0 -72
  115. package/tests/list-adrs.e2e.test.ts +0 -60
  116. package/tests/new-adr.e2e.test.ts +0 -62
  117. package/tests/open-with.e2e.test.ts +0 -53
  118. package/tests/superseding-records.e2e.test.ts +0 -64
  119. package/tests/toc-prefixing.e2e.test.ts +0 -43
  120. package/tests/use-template-override.e2e.test.ts +0 -52
  121. package/tests/work-form-other-directories.e2e.test.ts +0 -48
  122. package/tsconfig.json +0 -55
  123. package/vitest.config.e2e.ts +0 -18
  124. package/vitest.config.ts +0 -30
@@ -1,31 +0,0 @@
1
- import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
2
- import os from 'node:os';
3
- import path from 'node:path';
4
- import { describe, expect, it } from 'vitest';
5
- // @ts-expect-error no module types for script import
6
- import { buildVersionSource, resolveTargetPath, writeVersionFile } from '../scripts/inject-version.mjs';
7
-
8
- describe('inject-version script helpers', () => {
9
- it('builds the version export', () => {
10
- expect(buildVersionSource('1.2.3')).toBe("export const LIB_VERSION = '1.2.3';\n");
11
- });
12
-
13
- it('requires an output path', () => {
14
- expect(() => resolveTargetPath(['node', 'scripts/inject-version.mjs'])).toThrow(
15
- 'Usage: node scripts/inject-version.mjs <output-file>'
16
- );
17
- });
18
-
19
- it('writes the version file', () => {
20
- const tmpDir = mkdtempSync(path.join(os.tmpdir(), 'adr-version-'));
21
- const targetPath = path.join(tmpDir, 'version.ts');
22
-
23
- try {
24
- writeVersionFile(targetPath, '9.9.9');
25
- const contents = readFileSync(targetPath, 'utf8');
26
- expect(contents).toBe("export const LIB_VERSION = '9.9.9';\n");
27
- } finally {
28
- rmSync(tmpDir, { recursive: true, force: true });
29
- }
30
- });
31
- });
@@ -1,389 +0,0 @@
1
- import fs from 'fs/promises';
2
- import open from 'open';
3
- import path from 'path';
4
- import { afterEach, describe, expect, it, vi } from 'vitest';
5
- import { chooseOpenPlan, generateToc, init, link, listAdrs, newAdr } from './adr.js';
6
- import { getDir, workingDir } from './config.js';
7
- import { findMatchingFilesFor, getLinkDetails } from './links.js';
8
- import { getTitleFrom, injectLink, supersede } from './manipulator.js';
9
- import { newNumber } from './numbering.js';
10
- import { askForClarification } from './prompt.js';
11
- import { template } from './template.js';
12
-
13
- vi.mock('fs/promises', () => ({
14
- default: {
15
- readFile: vi.fn(),
16
- writeFile: vi.fn(),
17
- readdir: vi.fn(),
18
- mkdir: vi.fn()
19
- }
20
- }));
21
- vi.mock('open', () => ({ default: vi.fn() }));
22
- vi.mock('./config.js', () => ({
23
- getDir: vi.fn(),
24
- workingDir: vi.fn()
25
- }));
26
- vi.mock('./links.js', () => ({
27
- getLinkDetails: vi.fn(),
28
- findMatchingFilesFor: vi.fn()
29
- }));
30
- vi.mock('./manipulator.js', () => ({
31
- getTitleFrom: vi.fn(),
32
- injectLink: vi.fn(),
33
- supersede: vi.fn()
34
- }));
35
- vi.mock('./numbering.js', () => ({
36
- newNumber: vi.fn()
37
- }));
38
- vi.mock('./prompt.js', () => ({
39
- askForClarification: vi.fn()
40
- }));
41
- vi.mock('./template.js', () => ({
42
- template: vi.fn()
43
- }));
44
-
45
- describe('adr helpers', () => {
46
- const mockReaddir = vi.mocked(fs.readdir) as unknown as {
47
- mockResolvedValueOnce: (value: string[]) => void;
48
- };
49
-
50
- afterEach(() => {
51
- vi.resetAllMocks();
52
- vi.unstubAllEnvs();
53
- });
54
-
55
- it('preserves non-escape backslashes in quoted editor arguments', () => {
56
- expect(chooseOpenPlan({ openWith: String.raw`code "C:\temp\record.md"` })).toEqual({
57
- type: 'app',
58
- name: 'code',
59
- args: [String.raw`C:\temp\record.md`]
60
- });
61
- });
62
-
63
- it('falls back to the default opener for an incomplete editor quote', () => {
64
- vi.stubEnv('EDITOR', '"');
65
- expect(chooseOpenPlan({ open: true })).toEqual({ type: 'default' });
66
- });
67
-
68
- it('generates the toc from adr files', async () => {
69
- vi.mocked(getDir).mockResolvedValueOnce('/repo/doc/adr');
70
- mockReaddir.mockResolvedValueOnce(['0001-first.md', 'notes.txt']);
71
- vi.mocked(fs.readFile).mockResolvedValueOnce('# First ADR');
72
- vi.mocked(getTitleFrom).mockReturnValueOnce('ADR 1: First');
73
-
74
- await generateToc({ prefix: '/p/' });
75
- expect(fs.writeFile).toHaveBeenCalledWith(
76
- path.resolve('/repo/doc/adr', 'decisions.md'),
77
- '# Table of Contents\n\n- [ADR 1: First](/p/0001-first.md)'
78
- );
79
- });
80
-
81
- it('generates the toc without a prefix', async () => {
82
- vi.mocked(getDir).mockResolvedValueOnce('/repo/doc/adr');
83
- mockReaddir.mockResolvedValueOnce(['0001-first.md']);
84
- vi.mocked(fs.readFile).mockResolvedValueOnce('# First ADR');
85
- vi.mocked(getTitleFrom).mockReturnValueOnce('ADR 1: First');
86
-
87
- await generateToc();
88
- expect(fs.writeFile).toHaveBeenCalledWith(
89
- path.resolve('/repo/doc/adr', 'decisions.md'),
90
- '# Table of Contents\n\n- [ADR 1: First](0001-first.md)'
91
- );
92
- });
93
-
94
- it('includes ADRs with more than four digits', async () => {
95
- vi.mocked(getDir).mockResolvedValueOnce('/repo/doc/adr');
96
- mockReaddir.mockResolvedValueOnce(['10000-future.md']);
97
- vi.mocked(fs.readFile).mockResolvedValueOnce('# Future ADR');
98
- vi.mocked(getTitleFrom).mockReturnValueOnce('ADR 10000: Future');
99
-
100
- await generateToc();
101
- expect(fs.writeFile).toHaveBeenCalledWith(
102
- path.resolve('/repo/doc/adr', 'decisions.md'),
103
- '# Table of Contents\n\n- [ADR 10000: Future](10000-future.md)'
104
- );
105
- });
106
-
107
- it('excludes ADRs with fewer than four digits', async () => {
108
- vi.mocked(getDir).mockResolvedValueOnce('/repo/doc/adr');
109
- mockReaddir.mockResolvedValueOnce(['123-short.md', '0001-first.md']);
110
- vi.mocked(fs.readFile).mockResolvedValueOnce('# First ADR');
111
- vi.mocked(getTitleFrom).mockReturnValueOnce('ADR 1: First');
112
-
113
- await generateToc();
114
- expect(fs.writeFile).toHaveBeenCalledWith(
115
- path.resolve('/repo/doc/adr', 'decisions.md'),
116
- '# Table of Contents\n\n- [ADR 1: First](0001-first.md)'
117
- );
118
- });
119
-
120
- it('creates a new adr without opening', async () => {
121
- vi.mocked(newNumber).mockResolvedValueOnce(1);
122
- vi.mocked(template).mockResolvedValueOnce('DATE\nTITLE\nNUMBER\nSTATUS\n');
123
- vi.mocked(getDir).mockResolvedValue('/repo/doc/adr');
124
- mockReaddir.mockResolvedValueOnce([]);
125
-
126
- await newAdr('Example ADR', { open: false, date: 'DATE' });
127
- expect(fs.writeFile).toHaveBeenCalledWith(
128
- path.resolve('/repo/doc/adr', '0001-example-adr.md'),
129
- 'DATE\nExample ADR\n1\nAccepted\n'
130
- );
131
- expect(open).not.toHaveBeenCalled();
132
- });
133
-
134
- it('falls back to error date when the system date is empty', async () => {
135
- const dateSpy = vi.spyOn(Date.prototype, 'toISOString').mockReturnValue('');
136
- vi.mocked(newNumber).mockResolvedValueOnce(1);
137
- vi.mocked(template).mockResolvedValueOnce('DATE\nTITLE\nNUMBER\nSTATUS\n');
138
- vi.mocked(getDir).mockResolvedValue('/repo/doc/adr');
139
- mockReaddir.mockResolvedValueOnce([]);
140
-
141
- await newAdr('Example ADR', { open: false });
142
- expect(fs.writeFile).toHaveBeenCalledWith(
143
- path.resolve('/repo/doc/adr', '0001-example-adr.md'),
144
- 'ERROR\nExample ADR\n1\nAccepted\n'
145
- );
146
- dateSpy.mockRestore();
147
- });
148
-
149
- it('opens adr using open-with when provided', async () => {
150
- vi.mocked(newNumber).mockResolvedValueOnce(1);
151
- vi.mocked(template).mockResolvedValueOnce('DATE\nTITLE\nNUMBER\nSTATUS\n');
152
- vi.mocked(getDir).mockResolvedValue('/repo/doc/adr');
153
- mockReaddir.mockResolvedValueOnce([]);
154
-
155
- await newAdr('Example ADR', { openWith: 'code --wait' });
156
- const adrPath = path.resolve('/repo/doc/adr', '0001-example-adr.md');
157
- expect(open).toHaveBeenCalledWith(adrPath, {
158
- app: { name: 'code', arguments: ['--wait'] },
159
- wait: process.platform === 'win32'
160
- });
161
- });
162
-
163
- it('opens adr with default opener when enabled without an editor', async () => {
164
- vi.mocked(newNumber).mockResolvedValueOnce(1);
165
- vi.mocked(template).mockResolvedValueOnce('DATE\nTITLE\nNUMBER\nSTATUS\n');
166
- vi.mocked(getDir).mockResolvedValue('/repo/doc/adr');
167
- mockReaddir.mockResolvedValueOnce([]);
168
-
169
- await newAdr('Example ADR', { open: true });
170
- expect(open).toHaveBeenCalledWith(path.resolve('/repo/doc/adr', '0001-example-adr.md'), { wait: false });
171
- });
172
-
173
- it('uses open on windows for open-with', async () => {
174
- const originalPlatform = process.platform;
175
- Object.defineProperty(process, 'platform', { value: 'win32' });
176
- vi.mocked(newNumber).mockResolvedValueOnce(1);
177
- vi.mocked(template).mockResolvedValueOnce('DATE\nTITLE\nNUMBER\nSTATUS\n');
178
- vi.mocked(getDir).mockResolvedValue('/repo/doc/adr');
179
- mockReaddir.mockResolvedValueOnce([]);
180
-
181
- try {
182
- await newAdr('Example ADR', { openWith: 'code --wait' });
183
- expect(open).toHaveBeenCalledWith(path.resolve('/repo/doc/adr', '0001-example-adr.md'), {
184
- app: { name: 'code', arguments: ['--wait'] },
185
- wait: true
186
- });
187
- } finally {
188
- Object.defineProperty(process, 'platform', { value: originalPlatform });
189
- }
190
- });
191
-
192
- it('links superseded adrs when provided', async () => {
193
- vi.mocked(newNumber).mockResolvedValueOnce(2);
194
- vi.mocked(template).mockResolvedValueOnce('DATE\nTITLE\nNUMBER\nSTATUS\n');
195
- vi.mocked(getDir).mockResolvedValue('/repo/doc/adr');
196
- mockReaddir.mockResolvedValueOnce([]);
197
- vi.mocked(fs.readFile).mockResolvedValue('# ADR\n');
198
- vi.mocked(getTitleFrom).mockReturnValue('ADR Title');
199
- vi.mocked(getLinkDetails).mockResolvedValueOnce({
200
- link: 'Supersedes',
201
- reverseLink: 'Superseded by',
202
- matches: ['0001-first.md'],
203
- original: '1',
204
- pattern: '1'
205
- });
206
-
207
- await newAdr('Next ADR', { supersedes: ['1'] });
208
- expect(supersede).toHaveBeenCalled();
209
- expect(injectLink).toHaveBeenCalled();
210
- });
211
-
212
- it('asks for clarification when multiple supersede matches exist', async () => {
213
- vi.mocked(newNumber).mockResolvedValueOnce(2);
214
- vi.mocked(template).mockResolvedValueOnce('DATE\nTITLE\nNUMBER\nSTATUS\n');
215
- vi.mocked(getDir).mockResolvedValue('/repo/doc/adr');
216
- mockReaddir.mockResolvedValueOnce([]);
217
- vi.mocked(fs.readFile).mockResolvedValue('# ADR\n');
218
- vi.mocked(getTitleFrom).mockReturnValue('ADR Title');
219
- vi.mocked(getLinkDetails).mockResolvedValueOnce({
220
- link: 'Supersedes',
221
- reverseLink: 'Superseded by',
222
- matches: ['0001-first.md', '0001-other.md'],
223
- original: '1',
224
- pattern: '1'
225
- });
226
- vi.mocked(askForClarification).mockResolvedValue('0001-other.md');
227
-
228
- await newAdr('Next ADR', { supersedes: ['1'] });
229
- expect(askForClarification).toHaveBeenCalled();
230
- });
231
-
232
- it('throws when multiple supersede matches exist in quiet mode', async () => {
233
- vi.mocked(newNumber).mockResolvedValueOnce(2);
234
- vi.mocked(template).mockResolvedValueOnce('DATE\nTITLE\nNUMBER\nSTATUS\n');
235
- vi.mocked(getDir).mockResolvedValue('/repo/doc/adr');
236
- mockReaddir.mockResolvedValueOnce([]);
237
- vi.mocked(getLinkDetails).mockResolvedValueOnce({
238
- link: 'Supersedes',
239
- reverseLink: 'Superseded by',
240
- matches: ['0001-first.md', '0001-other.md'],
241
- original: '1',
242
- pattern: '1'
243
- });
244
-
245
- await expect(newAdr('Next ADR', { supersedes: ['1'], suppressPrompts: true })).rejects.toThrow(
246
- 'Multiple files match the search pattern'
247
- );
248
- });
249
-
250
- it('links related adrs when provided', async () => {
251
- vi.mocked(newNumber).mockResolvedValueOnce(2);
252
- vi.mocked(template).mockResolvedValueOnce('DATE\nTITLE\nNUMBER\nSTATUS\n');
253
- vi.mocked(getDir).mockResolvedValue('/repo/doc/adr');
254
- mockReaddir.mockResolvedValueOnce([]);
255
- vi.mocked(fs.readFile).mockResolvedValue('# ADR\n');
256
- vi.mocked(getTitleFrom).mockReturnValue('ADR Title');
257
- vi.mocked(getLinkDetails).mockResolvedValueOnce({
258
- link: 'Relates to',
259
- reverseLink: 'Related by',
260
- matches: ['0001-first.md'],
261
- original: '1:Relates to:Related by',
262
- pattern: '1'
263
- });
264
-
265
- await newAdr('Next ADR', { links: ['1:Relates to:Related by'] });
266
- expect(injectLink).toHaveBeenCalled();
267
- });
268
-
269
- it('asks for clarification when multiple link matches exist', async () => {
270
- vi.mocked(newNumber).mockResolvedValueOnce(2);
271
- vi.mocked(template).mockResolvedValueOnce('DATE\nTITLE\nNUMBER\nSTATUS\n');
272
- vi.mocked(getDir).mockResolvedValue('/repo/doc/adr');
273
- mockReaddir.mockResolvedValueOnce([]);
274
- vi.mocked(fs.readFile).mockResolvedValue('# ADR\n');
275
- vi.mocked(getTitleFrom).mockReturnValue('ADR Title');
276
- vi.mocked(getLinkDetails).mockResolvedValueOnce({
277
- link: 'Relates to',
278
- reverseLink: 'Related by',
279
- matches: ['0001-first.md', '0001-other.md'],
280
- original: '1:Relates to:Related by',
281
- pattern: '1'
282
- });
283
- vi.mocked(askForClarification).mockResolvedValue('0001-first.md');
284
-
285
- await newAdr('Next ADR', { links: ['1:Relates to:Related by'] });
286
- expect(askForClarification).toHaveBeenCalled();
287
- });
288
-
289
- it('initializes a new adr directory', async () => {
290
- vi.mocked(getDir).mockResolvedValue('/repo/doc/adr');
291
- vi.mocked(workingDir).mockReturnValue('/repo');
292
- vi.mocked(newNumber).mockResolvedValue(1);
293
- vi.mocked(template).mockResolvedValue('DATE\nTITLE\nNUMBER\nSTATUS\n');
294
- mockReaddir.mockResolvedValueOnce([]);
295
-
296
- await init('/repo/doc/adr');
297
- expect(fs.mkdir).toHaveBeenCalledWith('/repo/doc/adr', { recursive: true });
298
- expect(fs.writeFile).toHaveBeenCalledWith(path.join('/repo', '.adr-dir'), path.join('doc', 'adr'));
299
- });
300
-
301
- it('initializes using default directory when none provided', async () => {
302
- vi.mocked(getDir).mockResolvedValue('/repo/doc/adr');
303
- vi.mocked(workingDir).mockReturnValue('/repo');
304
- vi.mocked(newNumber).mockResolvedValue(1);
305
- vi.mocked(template).mockResolvedValue('DATE\nTITLE\nNUMBER\nSTATUS\n');
306
- mockReaddir.mockResolvedValueOnce([]);
307
-
308
- await init();
309
- expect(fs.mkdir).toHaveBeenCalledWith('/repo/doc/adr', { recursive: true });
310
- });
311
-
312
- it('links adrs with explicit command', async () => {
313
- vi.mocked(findMatchingFilesFor).mockResolvedValueOnce(['0001-first.md']);
314
- vi.mocked(getDir).mockResolvedValue('/repo/doc/adr');
315
- vi.mocked(fs.readFile).mockResolvedValue('# ADR\n');
316
- vi.mocked(getTitleFrom).mockReturnValue('ADR Title');
317
- vi.mocked(getLinkDetails).mockResolvedValueOnce({
318
- link: 'Relates to',
319
- reverseLink: 'Related by',
320
- matches: ['0002-second.md'],
321
- original: '2:Relates to:Related by',
322
- pattern: '2'
323
- });
324
-
325
- await link('0001-first.md', 'Relates to', '2', 'Related by');
326
- expect(injectLink).toHaveBeenCalled();
327
- });
328
-
329
- it('asks for clarification when multiple link sources exist', async () => {
330
- vi.mocked(findMatchingFilesFor).mockResolvedValueOnce(['0001-first.md', '0001-second.md']);
331
- vi.mocked(askForClarification).mockResolvedValueOnce('0001-first.md');
332
- vi.mocked(getDir).mockResolvedValue('/repo/doc/adr');
333
- vi.mocked(fs.readFile).mockResolvedValue('# ADR\n');
334
- vi.mocked(getTitleFrom).mockReturnValue('ADR Title');
335
- vi.mocked(getLinkDetails).mockResolvedValueOnce({
336
- link: 'Relates to',
337
- reverseLink: 'Related by',
338
- matches: ['0002-second.md'],
339
- original: '2:Relates to:Related by',
340
- pattern: '2'
341
- });
342
-
343
- await link('0001', 'Relates to', '2', 'Related by');
344
- expect(askForClarification).toHaveBeenCalled();
345
- });
346
-
347
- it('throws when multiple link matches exist in quiet mode', async () => {
348
- vi.mocked(findMatchingFilesFor).mockResolvedValueOnce(['0001-first.md', '0001-second.md']);
349
- await expect(link('0001', 'Relates', '2', 'Related by', { quiet: true })).rejects.toThrow(
350
- 'Multiple files match the search pattern'
351
- );
352
- });
353
-
354
- it('throws when multiple link matches exist in quiet mode for newAdr links', async () => {
355
- vi.mocked(newNumber).mockResolvedValueOnce(2);
356
- vi.mocked(template).mockResolvedValueOnce('DATE\nTITLE\nNUMBER\nSTATUS\n');
357
- vi.mocked(getDir).mockResolvedValue('/repo/doc/adr');
358
- vi.mocked(fs.readdir).mockResolvedValue([]);
359
- vi.mocked(getLinkDetails).mockResolvedValueOnce({
360
- link: 'Relates to',
361
- reverseLink: 'Related by',
362
- matches: ['0001-first.md', '0001-second.md'],
363
- original: '1:Relates to:Related by',
364
- pattern: '1'
365
- });
366
-
367
- await expect(newAdr('Next ADR', { links: ['1:Relates to:Related by'], suppressPrompts: true })).rejects.toThrow(
368
- 'Multiple files match the search pattern'
369
- );
370
- });
371
-
372
- it('lists adr files only', async () => {
373
- vi.mocked(getDir).mockResolvedValue('/repo/doc/adr');
374
- mockReaddir.mockResolvedValueOnce(['0001-first.md', 'notes.txt']);
375
- const result = await listAdrs();
376
- expect(result).toEqual([path.resolve('/repo/doc/adr', '0001-first.md')]);
377
- });
378
-
379
- it('lists adr files sorted by number', async () => {
380
- vi.mocked(getDir).mockResolvedValue('/repo/doc/adr');
381
- mockReaddir.mockResolvedValueOnce(['0010-ten.md', '0002-two.md', 'notes.txt', '0001-one.md']);
382
- const result = await listAdrs();
383
- expect(result).toEqual([
384
- path.resolve('/repo/doc/adr', '0001-one.md'),
385
- path.resolve('/repo/doc/adr', '0002-two.md'),
386
- path.resolve('/repo/doc/adr', '0010-ten.md')
387
- ]);
388
- });
389
- });