@meza/adr-tools 1.0.12 → 2.0.1

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 (111) hide show
  1. package/.github/renovate.json +2 -1
  2. package/.github/workflows/ci-pr.yml +2 -25
  3. package/.github/workflows/ci.yml +12 -46
  4. package/.releaserc.json +18 -8
  5. package/AGENTS.engineer.md +236 -0
  6. package/AGENTS.md +11 -0
  7. package/AGENTS.reviewer.md +115 -0
  8. package/CONTRIBUTING.md +102 -0
  9. package/README.md +16 -4
  10. package/biome.json +18 -139
  11. package/dist/index.js +164 -81
  12. package/dist/index.js.map +1 -1
  13. package/dist/index.test.js +189 -0
  14. package/dist/index.test.js.map +1 -0
  15. package/dist/inject-version.test.js +27 -0
  16. package/dist/inject-version.test.js.map +1 -0
  17. package/dist/lib/adr.js +132 -27
  18. package/dist/lib/adr.js.map +1 -1
  19. package/dist/lib/adr.test.js +308 -0
  20. package/dist/lib/adr.test.js.map +1 -0
  21. package/dist/lib/config.js +3 -2
  22. package/dist/lib/config.js.map +1 -1
  23. package/dist/lib/config.test.js +60 -0
  24. package/dist/lib/config.test.js.map +1 -0
  25. package/dist/lib/links.test.js +5 -4
  26. package/dist/lib/links.test.js.map +1 -1
  27. package/dist/lib/manipulator-errors.test.js +21 -0
  28. package/dist/lib/manipulator-errors.test.js.map +1 -0
  29. package/dist/lib/manipulator.test.js +21 -1
  30. package/dist/lib/manipulator.test.js.map +1 -1
  31. package/dist/lib/numbering.js +1 -1
  32. package/dist/lib/numbering.js.map +1 -1
  33. package/dist/lib/numbering.test.js +7 -0
  34. package/dist/lib/numbering.test.js.map +1 -1
  35. package/dist/lib/opening.test.js +81 -0
  36. package/dist/lib/opening.test.js.map +1 -0
  37. package/dist/lib/prompt.js +1 -1
  38. package/dist/lib/prompt.js.map +1 -1
  39. package/dist/lib/template.test.js +62 -0
  40. package/dist/lib/template.test.js.map +1 -0
  41. package/dist/types/index.d.ts +25 -0
  42. package/dist/types/index.d.ts.map +1 -1
  43. package/dist/types/index.test.d.ts +2 -0
  44. package/dist/types/index.test.d.ts.map +1 -0
  45. package/dist/types/inject-version.test.d.ts +2 -0
  46. package/dist/types/inject-version.test.d.ts.map +1 -0
  47. package/dist/types/lib/adr.d.ts +15 -0
  48. package/dist/types/lib/adr.d.ts.map +1 -1
  49. package/dist/types/lib/adr.test.d.ts +2 -0
  50. package/dist/types/lib/adr.test.d.ts.map +1 -0
  51. package/dist/types/lib/config.d.ts.map +1 -1
  52. package/dist/types/lib/config.test.d.ts +2 -0
  53. package/dist/types/lib/config.test.d.ts.map +1 -0
  54. package/dist/types/lib/manipulator-errors.test.d.ts +2 -0
  55. package/dist/types/lib/manipulator-errors.test.d.ts.map +1 -0
  56. package/dist/types/lib/opening.test.d.ts +2 -0
  57. package/dist/types/lib/opening.test.d.ts.map +1 -0
  58. package/dist/types/lib/prompt.d.ts.map +1 -1
  59. package/dist/types/lib/template.test.d.ts +2 -0
  60. package/dist/types/lib/template.test.d.ts.map +1 -0
  61. package/dist/types/version.d.ts +1 -1
  62. package/dist/types/version.d.ts.map +1 -1
  63. package/dist/version.js +1 -1
  64. package/dist/version.js.map +1 -1
  65. package/doc/adr/.adr-sequence.lock +1 -1
  66. package/doc/adr/0001-record-architecture-decisions.md +21 -0
  67. package/doc/adr/0002-using-heavy-e2e-tests.md +20 -0
  68. package/doc/adr/0003-esm.md +34 -0
  69. package/doc/adr/0004-gate-editor-opening-behind---open-and---open-with.md +55 -0
  70. package/doc/adr/decisions.md +4 -1
  71. package/lefthook.yml +14 -0
  72. package/package.json +24 -26
  73. package/scripts/inject-version.mjs +34 -0
  74. package/src/index.test.ts +212 -0
  75. package/src/index.ts +229 -108
  76. package/src/inject-version.test.ts +31 -0
  77. package/src/lib/adr.test.ts +376 -0
  78. package/src/lib/adr.ts +173 -27
  79. package/src/lib/config.test.ts +69 -0
  80. package/src/lib/config.ts +3 -2
  81. package/src/lib/links.test.ts +8 -4
  82. package/src/lib/manipulator-errors.test.ts +22 -0
  83. package/src/lib/manipulator.test.ts +25 -1
  84. package/src/lib/numbering.test.ts +8 -0
  85. package/src/lib/numbering.ts +1 -1
  86. package/src/lib/opening.test.ts +96 -0
  87. package/src/lib/prompt.ts +1 -1
  88. package/src/lib/template.test.ts +74 -0
  89. package/src/version.ts +1 -2
  90. package/tests/edit-on-create.e2e.test.ts +47 -16
  91. package/tests/fake-editor.cmd +2 -0
  92. package/tests/fake-visual.cmd +2 -0
  93. package/tests/funny-characters.e2e.test.ts +12 -11
  94. package/tests/generate-graph.e2e.test.ts +13 -17
  95. package/tests/helpers/adr-cli.ts +24 -0
  96. package/tests/init-adr-repository.e2e.test.ts +7 -6
  97. package/tests/linking-records.e2e.test.ts +14 -13
  98. package/tests/list-adrs.e2e.test.ts +26 -19
  99. package/tests/new-adr.e2e.test.ts +10 -9
  100. package/tests/open-with.e2e.test.ts +53 -0
  101. package/tests/superseding-records.e2e.test.ts +11 -10
  102. package/tests/toc-prefixing.e2e.test.ts +10 -9
  103. package/tests/use-template-override.e2e.test.ts +9 -8
  104. package/tests/work-form-other-directories.e2e.test.ts +10 -8
  105. package/vitest.config.e2e.ts +8 -1
  106. package/vitest.config.ts +20 -4
  107. package/.github/workflows/sync-deps-to-main.yml +0 -25
  108. package/.github/workflows/sync-to-deps.yml +0 -26
  109. package/.husky/commit-msg +0 -4
  110. package/CHANGELOG.md +0 -121
  111. package/scripts/inject-version.sh +0 -4
@@ -0,0 +1,81 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest';
2
+ import { chooseOpenPlan } from './adr.js';
3
+ describe('chooseOpenPlan', () => {
4
+ afterEach(() => {
5
+ vi.unstubAllEnvs();
6
+ });
7
+ it('returns none when open is false', () => {
8
+ expect(chooseOpenPlan({ open: false })).toEqual({ type: 'none' });
9
+ });
10
+ it('treats open-with as enabling open', () => {
11
+ expect(chooseOpenPlan({ openWith: 'code --wait' })).toEqual({
12
+ type: 'app',
13
+ name: 'code',
14
+ args: ['--wait']
15
+ });
16
+ });
17
+ it('parses quoted arguments', () => {
18
+ expect(chooseOpenPlan({ openWith: 'code "arg with space"' })).toEqual({
19
+ type: 'app',
20
+ name: 'code',
21
+ args: ['arg with space']
22
+ });
23
+ });
24
+ it('unescapes quoted arguments', () => {
25
+ expect(chooseOpenPlan({ openWith: 'code "arg \\"quoted\\""' })).toEqual({
26
+ type: 'app',
27
+ name: 'code',
28
+ args: ['arg "quoted"']
29
+ });
30
+ });
31
+ it('parses single-quoted arguments', () => {
32
+ expect(chooseOpenPlan({ openWith: "code 'arg with space'" })).toEqual({
33
+ type: 'app',
34
+ name: 'code',
35
+ args: ['arg with space']
36
+ });
37
+ });
38
+ it('returns none when open-with is not parseable', () => {
39
+ expect(chooseOpenPlan({ openWith: '"' })).toEqual({ type: 'none' });
40
+ });
41
+ it('returns none when open-with is whitespace', () => {
42
+ expect(chooseOpenPlan({ openWith: ' ' })).toEqual({ type: 'none' });
43
+ });
44
+ it('returns none when open-with is only quoted whitespace', () => {
45
+ expect(chooseOpenPlan({ openWith: '" "' })).toEqual({ type: 'none' });
46
+ });
47
+ it('handles quoted empty command', () => {
48
+ expect(chooseOpenPlan({ openWith: '""', open: true })).toEqual({ type: 'default' });
49
+ });
50
+ it('ignores boolean-like editor values', () => {
51
+ vi.stubEnv('VISUAL', 'true');
52
+ vi.stubEnv('EDITOR', 'false');
53
+ expect(chooseOpenPlan({ open: true })).toEqual({ type: 'default' });
54
+ });
55
+ it('prefers VISUAL over EDITOR when both are set', () => {
56
+ vi.stubEnv('VISUAL', 'code');
57
+ vi.stubEnv('EDITOR', 'vim');
58
+ expect(chooseOpenPlan({ open: true })).toEqual({ type: 'app', name: 'code', args: [] });
59
+ });
60
+ it('uses EDITOR when it is not npm injected', () => {
61
+ vi.stubEnv('EDITOR', 'vim');
62
+ vi.stubEnv('npm_execpath', '');
63
+ vi.stubEnv('npm_config_editor', '');
64
+ expect(chooseOpenPlan({ open: true })).toEqual({ type: 'app', name: 'vim', args: [] });
65
+ });
66
+ it('falls back to default when EDITOR is npm injected', () => {
67
+ vi.stubEnv('npm_execpath', '/usr/bin/npm');
68
+ vi.stubEnv('npm_config_editor', 'vi');
69
+ vi.stubEnv('EDITOR', 'vi');
70
+ vi.stubEnv('VISUAL', '');
71
+ expect(chooseOpenPlan({ open: true })).toEqual({ type: 'default' });
72
+ });
73
+ it('uses EDITOR when npm_config_editor is different', () => {
74
+ vi.stubEnv('npm_execpath', '/usr/bin/npm');
75
+ vi.stubEnv('npm_config_editor', 'code');
76
+ vi.stubEnv('EDITOR', 'vim');
77
+ vi.stubEnv('VISUAL', '');
78
+ expect(chooseOpenPlan({ open: true })).toEqual({ type: 'app', name: 'vim', args: [] });
79
+ });
80
+ });
81
+ //# sourceMappingURL=opening.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opening.test.js","sourceRoot":"src/","sources":["lib/opening.test.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import chalk from 'chalk';
2
- import inquirer from 'inquirer';
3
2
  export const askForClarification = async (searchString, matches) => {
3
+ const { default: inquirer } = await import('inquirer');
4
4
  const selection = await inquirer.prompt([
5
5
  {
6
6
  type: 'list',
@@ -1 +1 @@
1
- {"version":3,"file":"prompt.js","sourceRoot":"src/","sources":["lib/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,QAAQ,MAAM,UAAU,CAAC;AAEhC,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,EAAE,YAAoB,EAAE,OAAiB,EAAE,EAAE;IACnF,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC;QACtC;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,yCAAyC,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG;YAC7E,OAAO,EAAE,OAAO;SACjB;KACF,CAAC,CAAC;IACH,OAAO,SAAS,CAAC,MAAM,CAAC;AAC1B,CAAC,CAAC"}
1
+ {"version":3,"file":"prompt.js","sourceRoot":"src/","sources":["lib/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,EAAE,YAAoB,EAAE,OAAiB,EAAE,EAAE;IACnF,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;IACvD,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC;QACtC;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,yCAAyC,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG;YAC7E,OAAO,EAAE,OAAO;SACjB;KACF,CAAC,CAAC;IACH,OAAO,SAAS,CAAC,MAAM,CAAC;AAC1B,CAAC,CAAC"}
@@ -0,0 +1,62 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
4
+ vi.mock('fs/promises', () => ({
5
+ default: {
6
+ readFile: vi.fn()
7
+ }
8
+ }));
9
+ vi.mock('./config.js', () => ({
10
+ getDir: vi.fn()
11
+ }));
12
+ const mockEnv = (key, value) => {
13
+ if (value === undefined) {
14
+ delete process.env[key];
15
+ return;
16
+ }
17
+ process.env[key] = value;
18
+ };
19
+ describe('template', () => {
20
+ beforeEach(() => {
21
+ vi.resetModules();
22
+ vi.mocked(fs.readFile).mockReset();
23
+ mockEnv('ADR_TEMPLATE', undefined);
24
+ });
25
+ afterEach(() => {
26
+ mockEnv('ADR_TEMPLATE', undefined);
27
+ vi.restoreAllMocks();
28
+ });
29
+ it('uses the explicit template file when provided', async () => {
30
+ const { template } = await import('./template.js');
31
+ vi.mocked(fs.readFile).mockResolvedValueOnce('custom');
32
+ const result = await template('/tmp/custom.md');
33
+ expect(result).toEqual('custom');
34
+ expect(fs.readFile).toHaveBeenCalledWith(path.resolve('/tmp/custom.md'), 'utf8');
35
+ });
36
+ it('uses ADR_TEMPLATE when set', async () => {
37
+ const { template } = await import('./template.js');
38
+ mockEnv('ADR_TEMPLATE', '/tmp/env.md');
39
+ vi.mocked(fs.readFile).mockResolvedValueOnce('env');
40
+ const result = await template();
41
+ expect(result).toEqual('env');
42
+ expect(fs.readFile).toHaveBeenCalledWith(path.resolve('/tmp/env.md'), 'utf8');
43
+ });
44
+ it('uses template from adr directory when available', async () => {
45
+ const { template } = await import('./template.js');
46
+ const { getDir } = await import('./config.js');
47
+ vi.mocked(getDir).mockResolvedValueOnce('/repo/doc/adr');
48
+ vi.mocked(fs.readFile).mockResolvedValueOnce('repo-template');
49
+ const result = await template();
50
+ expect(result).toEqual('repo-template');
51
+ expect(fs.readFile).toHaveBeenCalledWith(path.join('/repo/doc/adr', 'templates/template.md'), 'utf8');
52
+ });
53
+ it('falls back to built-in template when repo template is missing', async () => {
54
+ const { template } = await import('./template.js');
55
+ const { getDir } = await import('./config.js');
56
+ vi.mocked(getDir).mockResolvedValueOnce('/repo/doc/adr');
57
+ vi.mocked(fs.readFile).mockRejectedValueOnce(new Error('missing')).mockResolvedValueOnce('built-in');
58
+ const result = await template();
59
+ expect(result).toEqual('built-in');
60
+ });
61
+ });
62
+ //# sourceMappingURL=template.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template.test.js","sourceRoot":"src/","sources":["lib/template.test.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,28 @@
1
1
  #!/usr/bin/env node
2
+ import fs from 'node:fs/promises';
3
+ import { Command } from 'commander';
4
+ import { generateToc, init, link, listAdrs, newAdr } from './lib/adr.js';
5
+ import { workingDir } from './lib/config.js';
6
+ import { getLinksFrom, getTitleFrom } from './lib/manipulator.js';
7
+ type ProgramDeps = {
8
+ generateToc: typeof generateToc;
9
+ init: typeof init;
10
+ link: typeof link;
11
+ listAdrs: typeof listAdrs;
12
+ newAdr: typeof newAdr;
13
+ workingDir: typeof workingDir;
14
+ readFile: typeof fs.readFile;
15
+ getLinksFrom: typeof getLinksFrom;
16
+ getTitleFrom: typeof getTitleFrom;
17
+ version: string;
18
+ onError: (program: Command, error: Error) => void;
19
+ log: (message: string) => void;
20
+ warn: (message: string) => void;
21
+ };
22
+ export declare const defaultOnError: (program: Command, error: Error) => never;
23
+ export declare const buildProgram: (deps?: ProgramDeps) => Command;
24
+ export declare const run: (argv?: string[], deps?: ProgramDeps) => Promise<Command>;
25
+ export declare const isDirectRun: (argv?: string[], moduleUrl?: string) => boolean;
26
+ export declare const maybeRun: (argv?: string[], deps?: ProgramDeps) => boolean;
2
27
  export {};
3
28
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"src/","sources":["index.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"src/","sources":["index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAIlC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAalE,KAAK,WAAW,GAAG;IACjB,WAAW,EAAE,OAAO,WAAW,CAAC;IAChC,IAAI,EAAE,OAAO,IAAI,CAAC;IAClB,IAAI,EAAE,OAAO,IAAI,CAAC;IAClB,QAAQ,EAAE,OAAO,QAAQ,CAAC;IAC1B,MAAM,EAAE,OAAO,MAAM,CAAC;IACtB,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,QAAQ,EAAE,OAAO,EAAE,CAAC,QAAQ,CAAC;IAC7B,YAAY,EAAE,OAAO,YAAY,CAAC;IAClC,YAAY,EAAE,OAAO,YAAY,CAAC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAClD,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,SAAS,OAAO,EAAE,OAAO,KAAK,UAE5D,CAAC;AAwGF,eAAO,MAAM,YAAY,GAAI,OAAM,WAAyB,YA4F3D,CAAC;AAEF,eAAO,MAAM,GAAG,GAAU,eAAmB,EAAE,OAAM,WAAyB,qBAI7E,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,eAAmB,EAAE,kBAA2B,YAK3E,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,eAAmB,EAAE,OAAO,WAAW,YAO/D,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.test.d.ts","sourceRoot":"src/","sources":["index.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=inject-version.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inject-version.test.d.ts","sourceRoot":"src/","sources":["inject-version.test.ts"],"names":[],"mappings":""}
@@ -1,9 +1,24 @@
1
+ type OpenPlan = {
2
+ type: 'none';
3
+ } | {
4
+ type: 'default';
5
+ } | {
6
+ type: 'app';
7
+ name: string;
8
+ args: string[];
9
+ };
10
+ export declare const chooseOpenPlan: (options?: {
11
+ open?: boolean;
12
+ openWith?: string;
13
+ }) => OpenPlan;
1
14
  interface NewOptions {
2
15
  supersedes?: string[];
3
16
  date?: string | undefined;
4
17
  suppressPrompts?: boolean;
5
18
  template?: string;
6
19
  links?: string[];
20
+ open?: boolean;
21
+ openWith?: string;
7
22
  }
8
23
  export declare const generateToc: (options?: {
9
24
  prefix?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"adr.d.ts","sourceRoot":"src/","sources":["lib/adr.ts"],"names":[],"mappings":"AAaA,UAAU,UAAU;IAClB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AA6GD,eAAO,MAAM,WAAW,GAAU,UAAU;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,kBAc9D,CAAC;AAEF,eAAO,MAAM,MAAM,GAAU,OAAO,MAAM,EAAE,SAAS,UAAU,kBAuC9D,CAAC;AAEF,eAAO,MAAM,IAAI,GAAU,YAAY,MAAM,kBAQ5C,CAAC;AAEF,eAAO,MAAM,IAAI,GACf,QAAQ,MAAM,EACd,MAAM,MAAM,EACZ,QAAQ,MAAM,EACd,aAAa,MAAM,EACnB,UAAU;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,kBAqB9B,CAAC;AAEF,eAAO,MAAM,QAAQ,yBAapB,CAAC"}
1
+ {"version":3,"file":"adr.d.ts","sourceRoot":"src/","sources":["lib/adr.ts"],"names":[],"mappings":"AAuEA,KAAK,QAAQ,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAsEvG,eAAO,MAAM,cAAc,GAAI,UAAU;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,KAAG,QAyBhF,CAAC;AAEF,UAAU,UAAU;IAClB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AA2GD,eAAO,MAAM,WAAW,GAAU,UAAU;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,kBAc9D,CAAC;AAEF,eAAO,MAAM,MAAM,GAAU,OAAO,MAAM,EAAE,SAAS,UAAU,kBAyB9D,CAAC;AAEF,eAAO,MAAM,IAAI,GAAU,YAAY,MAAM,kBAQ5C,CAAC;AAEF,eAAO,MAAM,IAAI,GACf,QAAQ,MAAM,EACd,MAAM,MAAM,EACZ,QAAQ,MAAM,EACd,aAAa,MAAM,EACnB,UAAU;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,kBAqB9B,CAAC;AAEF,eAAO,MAAM,QAAQ,yBAkBpB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=adr.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adr.test.d.ts","sourceRoot":"src/","sources":["lib/adr.test.ts"],"names":[],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"src/","sources":["lib/config.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,UAAU,cAAsB,CAAC;AAyB9C,eAAO,MAAM,MAAM,QAAa,OAAO,CAAC,MAAM,CAI7C,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"src/","sources":["lib/config.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,UAAU,cAAsB,CAAC;AA0B9C,eAAO,MAAM,MAAM,QAAa,OAAO,CAAC,MAAM,CAI7C,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=config.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.test.d.ts","sourceRoot":"src/","sources":["lib/config.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=manipulator-errors.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manipulator-errors.test.d.ts","sourceRoot":"src/","sources":["lib/manipulator-errors.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=opening.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opening.test.d.ts","sourceRoot":"src/","sources":["lib/opening.test.ts"],"names":[],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"prompt.d.ts","sourceRoot":"src/","sources":["lib/prompt.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,mBAAmB,GAAU,cAAc,MAAM,EAAE,SAAS,MAAM,EAAE,iBAUhF,CAAC"}
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"src/","sources":["lib/prompt.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,mBAAmB,GAAU,cAAc,MAAM,EAAE,SAAS,MAAM,EAAE,iBAWhF,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=template.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template.test.d.ts","sourceRoot":"src/","sources":["lib/template.test.ts"],"names":[],"mappings":""}
@@ -1,2 +1,2 @@
1
- export declare const LIB_VERSION = "1.0.12";
1
+ export declare const LIB_VERSION = "2.0.1";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"src/","sources":["version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,WAAW,CAAC"}
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"src/","sources":["version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,UAAU,CAAC"}
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
- export const LIB_VERSION = '1.0.12';
1
+ export const LIB_VERSION = '2.0.1';
2
2
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"src/","sources":["version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC"}
1
+ {"version":3,"file":"version.js","sourceRoot":"src/","sources":["version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC"}
@@ -1 +1 @@
1
- 1
1
+ 4
@@ -0,0 +1,21 @@
1
+ # 1. Record architecture decisions
2
+
3
+ Date: 2022-06-22
4
+
5
+ ## Status
6
+
7
+ Accepted
8
+
9
+ ## Context
10
+
11
+ We need to record the architectural decisions made on this project.
12
+
13
+ ## Decision
14
+
15
+ We will use Architecture Decision Records, as [described by Michael Nygard](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions).
16
+
17
+ ## Consequences
18
+
19
+ See Michael Nygard's article, linked above.
20
+ For a lightweight ADR toolset, see Nat Pryce's [adr-tools](https://github.com/npryce/adr-tools).
21
+ > For a node version of the same tooling, see Meza's [adr-tools](https://github.com/meza/adr-tools).
@@ -0,0 +1,20 @@
1
+ # 2. Using Heavy E2E Tests
2
+
3
+ Date: 2022-06-22
4
+
5
+ ## Status
6
+
7
+ Accepted
8
+
9
+ ## Context
10
+
11
+ The original tool has an [exhaustive test suite](https://github.com/npryce/adr-tools/tree/master/tests) that allows us to make sure that we're backwards compatible.
12
+
13
+ ## Decision
14
+
15
+ We'll be re-implementing those tests for ourselves too. This means that we will be using the original examples,
16
+ expectations and the ethos of invoking the tool with the given examples.
17
+
18
+ ## Consequences
19
+
20
+ The E2E test suite will be very slow to run and it can only be executed sequentially.
@@ -0,0 +1,34 @@
1
+ # 3. ESM
2
+
3
+ Date: 2022-06-29
4
+
5
+ ## Status
6
+
7
+ Accepted
8
+
9
+ ## Context
10
+
11
+ With typescript 4.7 come proper ESM modules.
12
+
13
+ ECMAScript modules are the official standard format to package JavaScript code for reuse. Modules are defined using a variety of import and export statements.
14
+
15
+ ## Decision
16
+
17
+ Since this is a new project, there's no reason to _not_ go with the flow.
18
+
19
+ ## Consequences
20
+
21
+ There's some changes to the way the project is set up.
22
+ Everything is described here, in [Typescript's official documentation](https://devblogs.microsoft.com/typescript/announcing-typescript-4-7-beta/#ecmascript-module-support-in-node-js)
23
+
24
+ However, some highlights:
25
+
26
+ We **must** import code with extensions and `.js` at that (even though the file's actual type is .ts)
27
+
28
+ `__dirname` and `__filename` are no longer available. Use this instead:
29
+ ```typescript
30
+ import path from 'node:path';
31
+ import { fileURLToPath } from 'node:url';
32
+
33
+ const __filename = fileURLToPath(import.meta.url);
34
+ const __dirname = path.dirname(__filename);```
@@ -0,0 +1,55 @@
1
+ # 4. Gate editor opening behind --open and --open-with
2
+
3
+ Date: 2026-01-04
4
+
5
+ ## Status
6
+
7
+ Accepted
8
+
9
+ ## Context
10
+
11
+ This tool historically tries to open a newly created ADR in the user's editor by reading the `VISUAL` and `EDITOR`
12
+ environment variables.
13
+
14
+ In 2025, a common way to run Node CLIs is via `npx`/`npm exec`. npm injects an `EDITOR` value into child processes
15
+ based on npm config (`npm_config_editor`), even when the user did not intend to configure an editor for this tool.
16
+
17
+ This creates two classes of problems:
18
+
19
+ 1. Unwanted side effects. Creating an ADR can unexpectedly launch an editor or attempt to launch a placeholder value.
20
+ 2. Cross-platform friction. Windows shells, quoting rules, and editor invocation are inconsistent, making the default
21
+ "auto open" behavior more fragile than the rest of the CLI.
22
+
23
+ We still want a good "create and open" workflow, but it must be explicit and predictable, and it must work well on
24
+ Windows, macOS, and Linux.
25
+
26
+ ## Decision
27
+
28
+ We will stop automatically opening an editor after `adr new` by default.
29
+
30
+ We will add two flags to the CLI:
31
+
32
+ - `--open`: Open the newly created ADR file after creation.
33
+ - `--open-with <command>`: Open the newly created ADR file using the provided command (optionally including args).
34
+
35
+ When `--open` is supplied, the tool will select an opener in this order:
36
+
37
+ 1. `--open-with <command>` if provided.
38
+ 2. `VISUAL` if it is set by the user and not empty.
39
+ 3. `EDITOR` if it is set by the user and not empty.
40
+ 4. OS default application for the file type.
41
+
42
+ To handle cross-platform differences, we will use a dedicated dependency (`open`) to perform the actual launching
43
+ when possible, rather than building platform-specific process spawning logic in this project.
44
+
45
+ ## Consequences
46
+
47
+ This makes ADR creation deterministic and side-effect free by default, including when invoked via `npx` or within CI.
48
+
49
+ Users who want the "open after create" workflow can still have it, but must opt in via `--open` or explicitly specify
50
+ an opener via `--open-with`.
51
+
52
+ This reduces platform-specific bugs and maintenance burden by relying on a well-maintained cross-platform opener.
53
+
54
+ This is a breaking behavior change for users who relied on implicit editor opening when `VISUAL`/`EDITOR` were set,
55
+ but the new behavior is more predictable and better aligned with modern Node execution environments.
@@ -1,3 +1,6 @@
1
1
  # Table of Contents
2
2
 
3
- - [1. Something about Node.JS](0001-something-about-node-js.md)
3
+ - [1. Record architecture decisions](0001-record-architecture-decisions.md)
4
+ - [2. Using Heavy E2E Tests](0002-using-heavy-e2e-tests.md)
5
+ - [3. ESM](0003-esm.md)
6
+ - [4. Gate editor opening behind --open and --open-with](0004-gate-editor-opening-behind---open-and---open-with.md)
package/lefthook.yml ADDED
@@ -0,0 +1,14 @@
1
+ pre-commit:
2
+ jobs:
3
+ - name: lint
4
+ run: yarn lint
5
+
6
+ pre-push:
7
+ jobs:
8
+ - name: ci
9
+ run: yarn ci
10
+
11
+ commit-msg:
12
+ jobs:
13
+ - name: commitlint
14
+ run: yarn -s commitlint --edit {1}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meza/adr-tools",
3
- "version": "1.0.12",
3
+ "version": "2.0.1",
4
4
  "exports": {
5
5
  ".": {
6
6
  "import": {
@@ -17,23 +17,25 @@
17
17
  "types": "dist/index.d.ts",
18
18
  "private": false,
19
19
  "scripts": {
20
- "prebuild": "./scripts/inject-version.sh ./src/version.ts",
21
- "prepack": "./scripts/inject-version.sh ./src/version.ts && tsc && yarn copy ",
20
+ "prebuild": "node ./scripts/inject-version.mjs ./src/version.ts",
21
+ "prepack": "node ./scripts/inject-version.mjs ./src/version.ts && tsc && yarn copy ",
22
22
  "copy": "copyfiles -u 1 ./src/templates/** ./dist",
23
23
  "build": "tsc && yarn copy",
24
24
  "start": "ts-node src/index.ts",
25
25
  "commit": "cz",
26
- "ci": "npm set editor 'true' && npm-run-all --parallel lint test",
26
+ "commitlint": "commitlint",
27
+ "ci": "npm-run-all lint test",
27
28
  "ci:dev": "exit 0",
28
29
  "clean": "rimraf dist .cache/tsbuildinfo",
29
30
  "clean:all": "yarn clean && rm -rf node_modules .cache",
31
+ "fix:lint": "biome check --fix",
30
32
  "lint:ci": "biome ci --reporter=github",
31
33
  "lint:tsc": "tsc --noEmit",
32
34
  "lint": "npm-run-all --parallel lint:*",
33
- "test": "npm-run-all --parallel test:*",
34
- "test:unit": "vitest",
35
+ "test": "npm-run-all test:unit test:e2e",
36
+ "test:unit": "vitest --coverage",
35
37
  "test:e2e": "vitest --config vitest.config.e2e.ts",
36
- "prepare": "is-ci || husky install",
38
+ "prepare": "is-ci || lefthook install",
37
39
  "report": "exit 0",
38
40
  "semantic-release": "semantic-release",
39
41
  "release": "semantic-release"
@@ -45,51 +47,47 @@
45
47
  "commander": "12.1.0",
46
48
  "core-js": "3.42.0",
47
49
  "inquirer": "9.3.7",
48
- "marked": "4.3.0"
50
+ "marked": "4.3.0",
51
+ "open": "9.1.0"
49
52
  },
50
53
  "commitlint": {
51
54
  "extends": [
52
55
  "@commitlint/config-conventional"
53
56
  ]
54
57
  },
55
- "husky": {
56
- "hooks": {
57
- "post-merge": "install-deps-postmerge",
58
- "pre-push": "yarn ci",
59
- "pre-commit": "yarn lint",
60
- "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
61
- }
62
- },
63
58
  "devDependencies": {
64
- "@biomejs/biome": "1.9.4",
59
+ "@biomejs/biome": "2.3.11",
65
60
  "@commitlint/cli": "19.8.1",
66
61
  "@commitlint/config-conventional": "19.8.1",
67
62
  "@commitlint/prompt-cli": "19.8.1",
68
63
  "@faker-js/faker": "9.8.0",
69
64
  "@meza/adr-tools": "1.0.10",
65
+ "@meza/biome": "^1.1.3",
70
66
  "@meza/tsconfig-base": "1.1.0",
71
67
  "@ryansonshine/commitizen": "4.2.8",
72
68
  "@ryansonshine/cz-conventional-changelog": "3.3.4",
73
- "@semantic-release/changelog": "6.0.3",
74
- "@semantic-release/commit-analyzer": "13.0.1",
75
- "@semantic-release/git": "10.0.1",
76
- "@semantic-release/github": "11.0.2",
77
- "@semantic-release/npm": "12.0.1",
78
- "@semantic-release/release-notes-generator": "14.0.3",
69
+ "@semantic-release/changelog": "^6.0.3",
70
+ "@semantic-release/commit-analyzer": "^13.0.1",
71
+ "@semantic-release/git": "^10.0.1",
72
+ "@semantic-release/github": "^12.0.2",
73
+ "@semantic-release/npm": "^13.1.3",
74
+ "@semantic-release/release-notes-generator": "^14.1.0",
79
75
  "@types/uuid": "10.0.0",
76
+ "@vitest/coverage-v8": "3.1.4",
80
77
  "@vitest/ui": "3.1.4",
78
+ "conventional-changelog-conventionalcommits": "^9.1.0",
81
79
  "copyfiles": "2.4.1",
82
80
  "cross-env": "7.0.3",
83
- "husky": "9.1.7",
84
81
  "install-deps-postmerge": "2.0.1",
85
82
  "is-ci": "4.1.0",
83
+ "lefthook": "2.0.13",
86
84
  "lint-staged": "16.0.0",
87
85
  "mock-cwd": "1.0.0",
88
86
  "npm-run-all2": "8.0.3",
89
87
  "rimraf": "6.0.1",
90
- "semantic-release": "24.2.4",
88
+ "semantic-release": "^25.0.2",
91
89
  "ts-node": "10.9.2",
92
- "tsx": "^4.19.4",
90
+ "tsx": "^4.21.0",
93
91
  "typescript": "5.8.3",
94
92
  "uuid": "11.1.0",
95
93
  "vitest": "3.1.4",
@@ -0,0 +1,34 @@
1
+ import { readFileSync, writeFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
4
+
5
+ export const buildVersionSource = (version) => `export const LIB_VERSION = '${version}';\n`;
6
+
7
+ export const resolveTargetPath = (argv) => {
8
+ const target = argv[2];
9
+ if (!target) {
10
+ throw new Error('Usage: node scripts/inject-version.mjs <output-file>');
11
+ }
12
+ return target;
13
+ };
14
+
15
+ export const writeVersionFile = (target, version) => {
16
+ writeFileSync(resolve(process.cwd(), target), buildVersionSource(version), 'utf8');
17
+ };
18
+
19
+ const readPackageVersion = () =>
20
+ JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version;
21
+
22
+ const run = () => {
23
+ try {
24
+ const target = resolveTargetPath(process.argv);
25
+ writeVersionFile(target, readPackageVersion());
26
+ } catch (error) {
27
+ console.error(error.message);
28
+ process.exit(1);
29
+ }
30
+ };
31
+
32
+ if (pathToFileURL(process.argv[1]).href === import.meta.url) {
33
+ run();
34
+ }