@marcomigozzi/trailhead 0.14.0 → 0.15.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 (92) hide show
  1. package/README.it.md +118 -30
  2. package/README.md +107 -29
  3. package/bin/base-preflight-docs.test.js +65 -0
  4. package/bin/execute-split-docs.test.js +146 -0
  5. package/bin/isolation-answer-docs.test.js +41 -0
  6. package/bin/isolation-copy-docs.test.js +28 -0
  7. package/bin/lib/codex-projection.js +1 -1
  8. package/bin/lib/codex-projection.test.js +41 -0
  9. package/bin/report-status-docs.test.js +195 -0
  10. package/bin/resolve-helpers-docs.test.js +36 -0
  11. package/bin/scope-confirm-skip-docs.test.js +70 -0
  12. package/bin/trailhead.js +3 -3
  13. package/bin/trailhead.test.js +341 -1
  14. package/bin/uat-setup-docs.test.js +65 -0
  15. package/package.json +1 -1
  16. package/plugins/trailhead/.claude-plugin/plugin.json +1 -1
  17. package/plugins/trailhead/agents/trailhead-code-review.md +15 -2
  18. package/plugins/trailhead/agents/trailhead-codebase-map.md +20 -1
  19. package/plugins/trailhead/agents/trailhead-debug.md +20 -1
  20. package/plugins/trailhead/agents/trailhead-executor.md +29 -2
  21. package/plugins/trailhead/agents/trailhead-fix.md +29 -2
  22. package/plugins/trailhead/agents/trailhead-plan.md +20 -1
  23. package/plugins/trailhead/agents/trailhead-research.md +22 -3
  24. package/plugins/trailhead/agents/trailhead-verify.md +15 -2
  25. package/plugins/trailhead/hooks/lib/base-preflight.test.js +513 -0
  26. package/plugins/trailhead/hooks/lib/commit-message-check.js +1 -1
  27. package/plugins/trailhead/hooks/lib/commit-message-check.test.js +3 -2
  28. package/plugins/trailhead/hooks/lib/commit-msg-module-repo.test.js +116 -0
  29. package/plugins/trailhead/hooks/lib/commit-msg-sync.test.js +100 -9
  30. package/plugins/trailhead/hooks/lib/dashboard.test.js +409 -0
  31. package/plugins/trailhead/hooks/lib/fixtures/commit-msg-legacy-js +104 -0
  32. package/plugins/trailhead/hooks/lib/isolation-answer.test.js +258 -0
  33. package/plugins/trailhead/hooks/lib/isolation-copy.test.js +298 -0
  34. package/plugins/trailhead/hooks/lib/issue-body.test.js +470 -0
  35. package/plugins/trailhead/hooks/lib/report-status.test.js +366 -0
  36. package/plugins/trailhead/hooks/lib/shell-scan.js +36 -1
  37. package/plugins/trailhead/hooks/lib/shell-scan.test.js +14 -0
  38. package/plugins/trailhead/hooks/lib/uat-backfill.test.js +150 -0
  39. package/plugins/trailhead/hooks/lib/uat-setup.test.js +340 -0
  40. package/plugins/trailhead/hooks/lib/work-state.test.js +45 -0
  41. package/plugins/trailhead/hooks/mod/README.md +38 -6
  42. package/plugins/trailhead/hooks/mod/map-pane-view.ts +168 -15
  43. package/plugins/trailhead/hooks/mod/map-pane.tsx +275 -106
  44. package/plugins/trailhead/hooks/mod/register.tsx +15 -2
  45. package/plugins/trailhead/hooks/mod/work-state-view.ts +17 -0
  46. package/plugins/trailhead/hooks/trailhead-install-guard.js +169 -34
  47. package/plugins/trailhead/hooks/trailhead-install-guard.test.js +180 -1
  48. package/plugins/trailhead/hooks/trailhead-search-guard.js +43 -7
  49. package/plugins/trailhead/hooks/trailhead-search-guard.test.js +66 -2
  50. package/plugins/trailhead/skills/_shared/choices.md +4 -2
  51. package/plugins/trailhead/skills/_shared/configuration-reference.md +15 -3
  52. package/plugins/trailhead/skills/_shared/load-first.md +3 -3
  53. package/plugins/trailhead/skills/_shared/multi-map.md +2 -2
  54. package/plugins/trailhead/skills/_shared/session-handoff.md +1 -1
  55. package/plugins/trailhead/skills/_shared/substrate-commands.md +22 -11
  56. package/plugins/trailhead/skills/_shared/substrate.md +6 -4
  57. package/plugins/trailhead/skills/_shared/teamwork.md +16 -7
  58. package/plugins/trailhead/skills/_shared/techniques/acceptance-testing.md +17 -9
  59. package/plugins/trailhead/skills/_shared/techniques/code-review.md +1 -1
  60. package/plugins/trailhead/skills/_shared/techniques/codebase-map.md +1 -1
  61. package/plugins/trailhead/skills/_shared/techniques/debug.md +2 -0
  62. package/plugins/trailhead/skills/_shared/techniques/research.md +4 -2
  63. package/plugins/trailhead/skills/_shared/techniques/verify.md +1 -1
  64. package/plugins/trailhead/skills/_shared/techniques.md +12 -2
  65. package/plugins/trailhead/skills/trailhead/SKILL.md +1 -1
  66. package/plugins/trailhead/skills/trailhead-capture/SKILL.md +1 -1
  67. package/plugins/trailhead/skills/trailhead-capture/references/capture.md +2 -2
  68. package/plugins/trailhead/skills/trailhead-chart/SKILL.md +1 -1
  69. package/plugins/trailhead/skills/trailhead-chart/references/charting.md +1 -1
  70. package/plugins/trailhead/skills/trailhead-manage/SKILL.md +1 -1
  71. package/plugins/trailhead/skills/trailhead-manage/references/auditing.md +1 -1
  72. package/plugins/trailhead/skills/trailhead-view/SKILL.md +8 -6
  73. package/plugins/trailhead/skills/trailhead-work/SKILL.md +14 -12
  74. package/plugins/trailhead/skills/trailhead-work/references/auto.md +4 -4
  75. package/plugins/trailhead/skills/trailhead-work/references/executor-brief.md +29 -0
  76. package/plugins/trailhead/skills/trailhead-work/references/ticket-engines.md +26 -10
  77. package/plugins/trailhead/templates/trailhead-base-preflight.js +305 -0
  78. package/plugins/trailhead/templates/trailhead-commit-msg +38 -102
  79. package/plugins/trailhead/templates/trailhead-commit-msg-sync.js +58 -27
  80. package/plugins/trailhead/templates/trailhead-commit-msg.cjs +114 -0
  81. package/plugins/trailhead/templates/trailhead-dashboard.js +340 -0
  82. package/plugins/trailhead/templates/trailhead-isolation-answer.js +227 -0
  83. package/plugins/trailhead/templates/trailhead-isolation-copy.js +239 -0
  84. package/plugins/trailhead/templates/trailhead-issue-body.js +459 -0
  85. package/plugins/trailhead/templates/trailhead-report-status.js +274 -0
  86. package/plugins/trailhead/templates/trailhead-uat-backfill.js +114 -0
  87. package/plugins/trailhead/templates/trailhead-uat-setup.js +385 -0
  88. package/plugins/trailhead/templates/trailhead-work-state.js +25 -7
  89. package/plugins/trailhead/tests/map-pane-ui.test.ts +566 -19
  90. package/plugins/trailhead/tests/map-pane-view.test.ts +304 -16
  91. package/plugins/trailhead/tests/work-state-view.test.ts +25 -1
  92. package/plugins/trailhead/types/index.d.ts +37 -3
@@ -16,7 +16,10 @@ const os = require('os');
16
16
 
17
17
  const scriptPath = path.resolve(__dirname, '..', '..', 'templates', 'trailhead-commit-msg-sync.js');
18
18
  const templatePath = path.resolve(__dirname, '..', '..', 'templates', 'trailhead-commit-msg');
19
+ const cjsTemplatePath = path.resolve(__dirname, '..', '..', 'templates', 'trailhead-commit-msg.cjs');
19
20
  const oldFixturePath = path.join(__dirname, 'fixtures', 'commit-msg-a319156');
21
+ // Hook JS monofile pre-#257 (extensionless CommonJS), da migrare al nuovo layout.
22
+ const legacyJsFixturePath = path.join(__dirname, 'fixtures', 'commit-msg-legacy-js');
20
23
 
21
24
  const {
22
25
  resolveHooksTarget,
@@ -29,7 +32,10 @@ let passed = 0;
29
32
  const ok = (name, cond) => { assert.ok(cond, name); passed++; };
30
33
 
31
34
  const templateText = fs.readFileSync(templatePath, 'utf8');
35
+ const cjsText = fs.readFileSync(cjsTemplatePath, 'utf8');
32
36
  const oldHookText = fs.readFileSync(oldFixturePath, 'utf8');
37
+ const legacyJsText = fs.readFileSync(legacyJsFixturePath, 'utf8');
38
+ const CJS_NAME = 'trailhead-commit-msg.cjs';
33
39
 
34
40
  function mktmp() {
35
41
  return fs.mkdtempSync(path.join(os.tmpdir(), 'trailhead-commit-msg-sync-test-'));
@@ -38,6 +44,9 @@ function mktmp() {
38
44
  // --- isTrailheadHook() unit cases -------------------------------------------
39
45
  ok('isTrailheadHook: current template is recognised', isTrailheadHook(templateText));
40
46
  ok('isTrailheadHook: old (a319156) hook is recognised', isTrailheadHook(oldHookText));
47
+ ok('isTrailheadHook: legacy single-file JS hook is recognised', isTrailheadHook(legacyJsText));
48
+ ok('isTrailheadHook: wrapper header is on a "#" line within the first 5', isTrailheadHook(templateText));
49
+ ok('isTrailheadHook: wrapper is a sh script', templateText.startsWith('#!/bin/sh\n'));
41
50
  ok('isTrailheadHook: plain foreign script is not', !isTrailheadHook('#!/bin/sh\necho hi\n'));
42
51
  ok('isTrailheadHook: mention outside the first 5 lines, not line-start, is not',
43
52
  !isTrailheadHook('#!/bin/sh\necho hi\necho more\necho stuff\necho again\n# trailhead commit-msg hook (git). fake\n'));
@@ -55,13 +64,14 @@ function modeOf(p) { return fs.statSync(p).mode & 0o777; }
55
64
  // absent -> installed, 0755
56
65
  {
57
66
  const dir = mktmp();
58
- const result = syncCommitMsgHook({ hooksDir: dir, templateText, hooksPathSet: false, fs });
67
+ const result = syncCommitMsgHook({ hooksDir: dir, templateText, hooksPathSet: false, cjsText, fs });
59
68
  const hookPath = path.join(dir, 'commit-msg');
60
69
  ok('syncCommitMsgHook: absent -> installed', result.status === 'installed');
61
70
  ok('syncCommitMsgHook: absent -> hook path returned', result.hook === hookPath);
62
71
  ok('syncCommitMsgHook: absent -> file written', fs.existsSync(hookPath));
63
72
  ok('syncCommitMsgHook: absent -> content matches template', fs.readFileSync(hookPath, 'utf8') === templateText);
64
73
  ok('syncCommitMsgHook: absent -> mode 0755', modeOf(hookPath) === 0o755);
74
+ ok('syncCommitMsgHook: absent -> .cjs written beside it', fs.readFileSync(path.join(dir, CJS_NAME), 'utf8') === cjsText);
65
75
  fs.rmSync(dir, { recursive: true, force: true });
66
76
  }
67
77
 
@@ -71,21 +81,77 @@ function modeOf(p) { return fs.statSync(p).mode & 0o777; }
71
81
  const hookPath = path.join(dir, 'commit-msg');
72
82
  fs.writeFileSync(hookPath, oldHookText);
73
83
  fs.chmodSync(hookPath, 0o755);
74
- const result = syncCommitMsgHook({ hooksDir: dir, templateText, hooksPathSet: false, fs });
84
+ const result = syncCommitMsgHook({ hooksDir: dir, templateText, hooksPathSet: false, cjsText, fs });
75
85
  ok('syncCommitMsgHook: old -> upgraded', result.status === 'upgraded');
76
86
  ok('syncCommitMsgHook: old -> bytes now match template', fs.readFileSync(hookPath, 'utf8') === templateText);
77
87
  ok('syncCommitMsgHook: old -> mode 0755', modeOf(hookPath) === 0o755);
78
88
  fs.rmSync(dir, { recursive: true, force: true });
79
89
  }
80
90
 
91
+ // legacy single-file JS hook -> upgraded to wrapper + .cjs
92
+ {
93
+ const dir = mktmp();
94
+ const hookPath = path.join(dir, 'commit-msg');
95
+ fs.writeFileSync(hookPath, legacyJsText);
96
+ fs.chmodSync(hookPath, 0o755);
97
+ const result = syncCommitMsgHook({ hooksDir: dir, templateText, cjsText, hooksPathSet: false, fs });
98
+ ok('syncCommitMsgHook: legacy JS -> upgraded', result.status === 'upgraded');
99
+ ok('syncCommitMsgHook: legacy JS -> wrapper now matches template', fs.readFileSync(hookPath, 'utf8') === templateText);
100
+ ok('syncCommitMsgHook: legacy JS -> .cjs written', fs.readFileSync(path.join(dir, CJS_NAME), 'utf8') === cjsText);
101
+ ok('syncCommitMsgHook: legacy JS -> wrapper mode 0755', modeOf(hookPath) === 0o755);
102
+ const again = syncCommitMsgHook({ hooksDir: dir, templateText, cjsText, hooksPathSet: false, fs });
103
+ ok('syncCommitMsgHook: legacy JS -> second run is current', again.status === 'current');
104
+ ok('syncCommitMsgHook: legacy JS -> no temp files left', fs.readdirSync(dir).sort().join(',') === `commit-msg,${CJS_NAME}`);
105
+ fs.rmSync(dir, { recursive: true, force: true });
106
+ }
107
+
108
+ // current wrapper but outdated .cjs -> upgraded, .cjs rewritten
109
+ {
110
+ const dir = mktmp();
111
+ const hookPath = path.join(dir, 'commit-msg');
112
+ fs.writeFileSync(hookPath, templateText);
113
+ fs.chmodSync(hookPath, 0o755);
114
+ fs.writeFileSync(path.join(dir, CJS_NAME), '// stale\n');
115
+ const result = syncCommitMsgHook({ hooksDir: dir, templateText, cjsText, hooksPathSet: false, fs });
116
+ ok('syncCommitMsgHook: outdated .cjs -> upgraded', result.status === 'upgraded');
117
+ ok('syncCommitMsgHook: outdated .cjs -> rewritten', fs.readFileSync(path.join(dir, CJS_NAME), 'utf8') === cjsText);
118
+ fs.rmSync(dir, { recursive: true, force: true });
119
+ }
120
+
121
+ // current wrapper but .cjs missing -> upgraded, .cjs restored
122
+ {
123
+ const dir = mktmp();
124
+ const hookPath = path.join(dir, 'commit-msg');
125
+ fs.writeFileSync(hookPath, templateText);
126
+ fs.chmodSync(hookPath, 0o755);
127
+ const result = syncCommitMsgHook({ hooksDir: dir, templateText, cjsText, hooksPathSet: false, fs });
128
+ ok('syncCommitMsgHook: missing .cjs -> upgraded', result.status === 'upgraded');
129
+ ok('syncCommitMsgHook: missing .cjs -> restored', fs.readFileSync(path.join(dir, CJS_NAME), 'utf8') === cjsText);
130
+ fs.rmSync(dir, { recursive: true, force: true });
131
+ }
132
+
133
+ // outdated wrapper (older sh wrapper text) + current .cjs -> upgraded wrapper
134
+ {
135
+ const dir = mktmp();
136
+ const hookPath = path.join(dir, 'commit-msg');
137
+ fs.writeFileSync(hookPath, '#!/bin/sh\n# trailhead commit-msg hook (git). older wrapper\nexit 0\n');
138
+ fs.chmodSync(hookPath, 0o755);
139
+ fs.writeFileSync(path.join(dir, CJS_NAME), cjsText);
140
+ const result = syncCommitMsgHook({ hooksDir: dir, templateText, cjsText, hooksPathSet: false, fs });
141
+ ok('syncCommitMsgHook: outdated wrapper -> upgraded', result.status === 'upgraded');
142
+ ok('syncCommitMsgHook: outdated wrapper -> now matches template', fs.readFileSync(hookPath, 'utf8') === templateText);
143
+ fs.rmSync(dir, { recursive: true, force: true });
144
+ }
145
+
81
146
  // CRLF current -> current, untouched
82
147
  {
83
148
  const dir = mktmp();
84
149
  const hookPath = path.join(dir, 'commit-msg');
85
150
  const crlfTemplate = templateText.replace(/\n/g, '\r\n');
151
+ fs.writeFileSync(path.join(dir, CJS_NAME), cjsText);
86
152
  fs.writeFileSync(hookPath, crlfTemplate);
87
153
  fs.chmodSync(hookPath, 0o755);
88
- const result = syncCommitMsgHook({ hooksDir: dir, templateText, hooksPathSet: false, fs });
154
+ const result = syncCommitMsgHook({ hooksDir: dir, templateText, hooksPathSet: false, cjsText, fs });
89
155
  ok('syncCommitMsgHook: CRLF current -> current', result.status === 'current');
90
156
  ok('syncCommitMsgHook: CRLF current -> untouched bytes', fs.readFileSync(hookPath, 'utf8') === crlfTemplate);
91
157
  fs.rmSync(dir, { recursive: true, force: true });
@@ -96,8 +162,9 @@ function modeOf(p) { return fs.statSync(p).mode & 0o777; }
96
162
  const dir = mktmp();
97
163
  const hookPath = path.join(dir, 'commit-msg');
98
164
  fs.writeFileSync(hookPath, templateText);
165
+ fs.writeFileSync(path.join(dir, CJS_NAME), cjsText);
99
166
  fs.chmodSync(hookPath, 0o644);
100
- const result = syncCommitMsgHook({ hooksDir: dir, templateText, hooksPathSet: false, fs });
167
+ const result = syncCommitMsgHook({ hooksDir: dir, templateText, hooksPathSet: false, cjsText, fs });
101
168
  ok('syncCommitMsgHook: current 0644 -> status current', result.status === 'current');
102
169
  ok('syncCommitMsgHook: current 0644 -> mode fixed to 0755', modeOf(hookPath) === 0o755);
103
170
  fs.rmSync(dir, { recursive: true, force: true });
@@ -110,10 +177,11 @@ function modeOf(p) { return fs.statSync(p).mode & 0o777; }
110
177
  const foreign = '#!/bin/sh\necho hi\n';
111
178
  fs.writeFileSync(hookPath, foreign);
112
179
  fs.chmodSync(hookPath, 0o755);
113
- const result = syncCommitMsgHook({ hooksDir: dir, templateText, hooksPathSet: false, fs });
180
+ const result = syncCommitMsgHook({ hooksDir: dir, templateText, hooksPathSet: false, cjsText, fs });
114
181
  ok('syncCommitMsgHook: foreign shell -> foreign', result.status === 'foreign');
115
182
  ok('syncCommitMsgHook: foreign shell -> bytes untouched', fs.readFileSync(hookPath, 'utf8') === foreign);
116
183
  ok('syncCommitMsgHook: foreign shell -> mode untouched', modeOf(hookPath) === 0o755);
184
+ ok('syncCommitMsgHook: foreign shell -> no .cjs written', !fs.existsSync(path.join(dir, CJS_NAME)));
117
185
  fs.rmSync(dir, { recursive: true, force: true });
118
186
  }
119
187
 
@@ -124,7 +192,7 @@ function modeOf(p) { return fs.statSync(p).mode & 0o777; }
124
192
  const foreign = '#!/bin/sh\necho "trailhead commit-msg hook (git). not really"\nexit 0\n';
125
193
  fs.writeFileSync(hookPath, foreign);
126
194
  fs.chmodSync(hookPath, 0o755);
127
- const result = syncCommitMsgHook({ hooksDir: dir, templateText, hooksPathSet: false, fs });
195
+ const result = syncCommitMsgHook({ hooksDir: dir, templateText, hooksPathSet: false, cjsText, fs });
128
196
  ok('syncCommitMsgHook: foreign wrapper mentioning phrase -> foreign', result.status === 'foreign');
129
197
  ok('syncCommitMsgHook: foreign wrapper -> bytes untouched', fs.readFileSync(hookPath, 'utf8') === foreign);
130
198
  fs.rmSync(dir, { recursive: true, force: true });
@@ -138,10 +206,11 @@ function modeOf(p) { return fs.statSync(p).mode & 0o777; }
138
206
  const targetContent = '#!/bin/sh\necho real\n';
139
207
  fs.writeFileSync(targetPath, targetContent);
140
208
  fs.symlinkSync(targetPath, hookPath);
141
- const result = syncCommitMsgHook({ hooksDir: dir, templateText, hooksPathSet: false, fs });
209
+ const result = syncCommitMsgHook({ hooksDir: dir, templateText, hooksPathSet: false, cjsText, fs });
142
210
  ok('syncCommitMsgHook: symlink -> foreign', result.status === 'foreign');
143
211
  ok('syncCommitMsgHook: symlink -> still a symlink', fs.lstatSync(hookPath).isSymbolicLink());
144
212
  ok('syncCommitMsgHook: symlink target untouched', fs.readFileSync(targetPath, 'utf8') === targetContent);
213
+ ok('syncCommitMsgHook: symlink -> no .cjs written', !fs.existsSync(path.join(dir, CJS_NAME)));
145
214
  fs.rmSync(dir, { recursive: true, force: true });
146
215
  }
147
216
 
@@ -149,9 +218,9 @@ function modeOf(p) { return fs.statSync(p).mode & 0o777; }
149
218
  {
150
219
  const dir = mktmp();
151
220
  const hookPath = path.join(dir, 'commit-msg');
152
- const result = syncCommitMsgHook({ hooksDir: dir, templateText, hooksPathSet: true, fs });
221
+ const result = syncCommitMsgHook({ hooksDir: dir, templateText, hooksPathSet: true, cjsText, fs });
153
222
  ok('syncCommitMsgHook: absent + hooksPathSet -> skipped', result.status === 'skipped');
154
- ok('syncCommitMsgHook: absent + hooksPathSet -> nothing created', !fs.existsSync(hookPath));
223
+ ok('syncCommitMsgHook: absent + hooksPathSet -> nothing created', !fs.existsSync(hookPath) && !fs.existsSync(path.join(dir, CJS_NAME)));
155
224
  fs.rmSync(dir, { recursive: true, force: true });
156
225
  }
157
226
 
@@ -226,6 +295,7 @@ function assertSingleJsonLine(out, label) {
226
295
  const hookPath = path.join(dir, '.git', 'hooks', 'commit-msg');
227
296
  ok('e2e fresh repo: hook file exists', fs.existsSync(hookPath));
228
297
  ok('e2e fresh repo: hook is executable', (modeOf(hookPath) & 0o111) !== 0);
298
+ ok('e2e fresh repo: .cjs installed beside it', fs.readFileSync(path.join(dir, '.git', 'hooks', CJS_NAME), 'utf8') === cjsText);
229
299
 
230
300
  const out2 = runScript(['--repo', dir], { env: gitEnv() });
231
301
  const parsed2 = assertSingleJsonLine(out2, 'e2e fresh repo (2nd run)');
@@ -246,6 +316,24 @@ function assertSingleJsonLine(out, label) {
246
316
  const parsed = assertSingleJsonLine(out, 'e2e seeded old hook');
247
317
  ok('e2e seeded old hook: status upgraded', parsed.status === 'upgraded');
248
318
  ok('e2e seeded old hook: content now matches template', fs.readFileSync(hookPath, 'utf8') === templateText);
319
+ ok('e2e seeded old hook: .cjs installed', fs.readFileSync(path.join(hooksDir, CJS_NAME), 'utf8') === cjsText);
320
+ fs.rmSync(dir, { recursive: true, force: true });
321
+ }
322
+
323
+ // seeded legacy single-file JS hook -> upgraded to wrapper + .cjs
324
+ {
325
+ const dir = mktmp();
326
+ execFileSync('git', ['init', '-q'], { cwd: dir, env: gitEnv() });
327
+ const hooksDir = path.join(dir, '.git', 'hooks');
328
+ fs.mkdirSync(hooksDir, { recursive: true });
329
+ const hookPath = path.join(hooksDir, 'commit-msg');
330
+ fs.writeFileSync(hookPath, legacyJsText);
331
+ fs.chmodSync(hookPath, 0o755);
332
+ const out = runScript(['--repo', dir], { env: gitEnv() });
333
+ const parsed = assertSingleJsonLine(out, 'e2e seeded legacy JS hook');
334
+ ok('e2e seeded legacy JS hook: status upgraded', parsed.status === 'upgraded');
335
+ ok('e2e seeded legacy JS hook: wrapper installed', fs.readFileSync(hookPath, 'utf8') === templateText);
336
+ ok('e2e seeded legacy JS hook: .cjs installed', fs.readFileSync(path.join(hooksDir, CJS_NAME), 'utf8') === cjsText);
249
337
  fs.rmSync(dir, { recursive: true, force: true });
250
338
  }
251
339
 
@@ -263,6 +351,7 @@ function assertSingleJsonLine(out, label) {
263
351
  const parsed = assertSingleJsonLine(out, 'e2e seeded foreign hook');
264
352
  ok('e2e seeded foreign hook: status foreign', parsed.status === 'foreign');
265
353
  ok('e2e seeded foreign hook: bytes untouched', fs.readFileSync(hookPath, 'utf8') === foreign);
354
+ ok('e2e seeded foreign hook: no .cjs written', !fs.existsSync(path.join(hooksDir, CJS_NAME)));
266
355
  fs.rmSync(dir, { recursive: true, force: true });
267
356
  }
268
357
 
@@ -293,6 +382,7 @@ function assertSingleJsonLine(out, label) {
293
382
  const parsed = assertSingleJsonLine(out, 'e2e core.hooksPath old hook');
294
383
  ok('e2e core.hooksPath old hook: status upgraded', parsed.status === 'upgraded');
295
384
  ok('e2e core.hooksPath old hook: upgraded in .githooks', fs.readFileSync(hookPath, 'utf8') === templateText);
385
+ ok('e2e core.hooksPath old hook: .cjs lands in .githooks', fs.readFileSync(path.join(githooksDir, CJS_NAME), 'utf8') === cjsText);
296
386
  fs.rmSync(dir, { recursive: true, force: true });
297
387
  }
298
388
 
@@ -311,6 +401,7 @@ function assertSingleJsonLine(out, label) {
311
401
  const parsed = assertSingleJsonLine(out, 'e2e core.hooksPath foreign hook');
312
402
  ok('e2e core.hooksPath foreign hook: status foreign', parsed.status === 'foreign');
313
403
  ok('e2e core.hooksPath foreign hook: untouched', fs.readFileSync(hookPath, 'utf8') === foreign);
404
+ ok('e2e core.hooksPath foreign hook: no .cjs written', !fs.existsSync(path.join(githooksDir, CJS_NAME)));
314
405
  fs.rmSync(dir, { recursive: true, force: true });
315
406
  }
316
407