@pennyfarthing/core 9.4.0 → 10.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 (60) hide show
  1. package/dist/cli/commands/cyclist.d.ts +1 -1
  2. package/dist/cli/commands/cyclist.d.ts.map +1 -1
  3. package/dist/cli/commands/cyclist.js +15 -25
  4. package/dist/cli/commands/cyclist.js.map +1 -1
  5. package/dist/cli/commands/cyclist.test.js +12 -8
  6. package/dist/cli/commands/cyclist.test.js.map +1 -1
  7. package/dist/cli/commands/doctor.d.ts.map +1 -1
  8. package/dist/cli/commands/doctor.js +149 -15
  9. package/dist/cli/commands/doctor.js.map +1 -1
  10. package/dist/cli/commands/e2e-fresh-install.test.d.ts +19 -0
  11. package/dist/cli/commands/e2e-fresh-install.test.d.ts.map +1 -0
  12. package/dist/cli/commands/e2e-fresh-install.test.js +395 -0
  13. package/dist/cli/commands/e2e-fresh-install.test.js.map +1 -0
  14. package/dist/cli/commands/e2e-upgrade.test.d.ts +22 -0
  15. package/dist/cli/commands/e2e-upgrade.test.d.ts.map +1 -0
  16. package/dist/cli/commands/e2e-upgrade.test.js +562 -0
  17. package/dist/cli/commands/e2e-upgrade.test.js.map +1 -0
  18. package/dist/cli/commands/init-consolidation.test.d.ts +17 -0
  19. package/dist/cli/commands/init-consolidation.test.d.ts.map +1 -0
  20. package/dist/cli/commands/init-consolidation.test.js +217 -0
  21. package/dist/cli/commands/init-consolidation.test.js.map +1 -0
  22. package/dist/cli/commands/init.js +18 -15
  23. package/dist/cli/commands/init.js.map +1 -1
  24. package/dist/cli/commands/persona-config-consolidation.test.d.ts +18 -0
  25. package/dist/cli/commands/persona-config-consolidation.test.d.ts.map +1 -0
  26. package/dist/cli/commands/persona-config-consolidation.test.js +230 -0
  27. package/dist/cli/commands/persona-config-consolidation.test.js.map +1 -0
  28. package/dist/cli/commands/theme.js +2 -2
  29. package/dist/cli/commands/theme.js.map +1 -1
  30. package/dist/cli/commands/uninstall.d.ts.map +1 -1
  31. package/dist/cli/commands/uninstall.js +2 -1
  32. package/dist/cli/commands/uninstall.js.map +1 -1
  33. package/dist/cli/commands/update-consolidation.test.d.ts +19 -0
  34. package/dist/cli/commands/update-consolidation.test.d.ts.map +1 -0
  35. package/dist/cli/commands/update-consolidation.test.js +572 -0
  36. package/dist/cli/commands/update-consolidation.test.js.map +1 -0
  37. package/dist/cli/commands/update.d.ts +25 -0
  38. package/dist/cli/commands/update.d.ts.map +1 -1
  39. package/dist/cli/commands/update.js +128 -11
  40. package/dist/cli/commands/update.js.map +1 -1
  41. package/dist/cli/utils/manifest.d.ts +3 -3
  42. package/dist/cli/utils/manifest.d.ts.map +1 -1
  43. package/dist/cli/utils/manifest.js +21 -12
  44. package/dist/cli/utils/manifest.js.map +1 -1
  45. package/dist/cli/utils/settings-consolidation.test.d.ts +13 -0
  46. package/dist/cli/utils/settings-consolidation.test.d.ts.map +1 -0
  47. package/dist/cli/utils/settings-consolidation.test.js +227 -0
  48. package/dist/cli/utils/settings-consolidation.test.js.map +1 -0
  49. package/dist/cli/utils/settings.d.ts +11 -0
  50. package/dist/cli/utils/settings.d.ts.map +1 -1
  51. package/dist/cli/utils/settings.js +59 -6
  52. package/dist/cli/utils/settings.js.map +1 -1
  53. package/dist/cli/utils/themes.d.ts +3 -3
  54. package/dist/cli/utils/themes.d.ts.map +1 -1
  55. package/dist/cli/utils/themes.js +14 -12
  56. package/dist/cli/utils/themes.js.map +1 -1
  57. package/dist/cli/utils/themes.test.d.ts +2 -2
  58. package/dist/cli/utils/themes.test.js +29 -13
  59. package/dist/cli/utils/themes.test.js.map +1 -1
  60. package/package.json +1 -1
@@ -0,0 +1,395 @@
1
+ /**
2
+ * E2E test for MSSCI-14373: Fresh repo install
3
+ *
4
+ * This test runs `pennyfarthing init --force` in a fresh temp directory
5
+ * with a simulated node_modules tree (symlinked to the real pennyfarthing-dist),
6
+ * then validates the complete installation using `pennyfarthing doctor --json`.
7
+ *
8
+ * Acceptance Criteria:
9
+ * AC1: Test creates a fresh empty git repo in a temp directory
10
+ * AC2: Runs `pennyfarthing init` in that repo
11
+ * AC3: Validates the installation with `pennyfarthing doctor` (should pass)
12
+ * AC4: Exercises the just scripts: dev, test, build
13
+ * AC5: Iteratively adjusts and restarts if scripts fail
14
+ * AC6: Cleans up the temp repo afterward
15
+ *
16
+ * Run with: cd packages/core && npm run build && npm test
17
+ */
18
+ import { describe, it, beforeEach, afterEach } from 'node:test';
19
+ import assert from 'node:assert';
20
+ import { existsSync, mkdirSync, rmSync, readFileSync, lstatSync, readdirSync, statSync, symlinkSync, writeFileSync, } from 'fs';
21
+ import { join, resolve, dirname } from 'path';
22
+ import { tmpdir } from 'os';
23
+ import { execSync, spawnSync } from 'child_process';
24
+ import { fileURLToPath } from 'url';
25
+ const __filename = fileURLToPath(import.meta.url);
26
+ const __dirname = dirname(__filename);
27
+ // Path to the CLI binary (compiled JS)
28
+ const CLI_BIN = resolve(__dirname, '../../../bin/pennyfarthing.js');
29
+ // Path to the real pennyfarthing-dist (for creating fake node_modules)
30
+ // From dist/cli/commands/ → packages/core/dist/cli/commands/ → 5 levels up to repo root
31
+ const REAL_PENNYFARTHING_DIST = resolve(__dirname, '../../../../../pennyfarthing-dist');
32
+ // ─── Helpers ───────────────────────────────────────────────────────
33
+ function createTestDir() {
34
+ const dir = join(tmpdir(), `pf-e2e-fresh-${Date.now()}-${Math.random().toString(36).slice(2)}`);
35
+ mkdirSync(dir, { recursive: true });
36
+ return dir;
37
+ }
38
+ /**
39
+ * Create a fake node_modules tree that symlinks to the real pennyfarthing-dist.
40
+ * This simulates what `npm install pennyfarthing` would produce.
41
+ */
42
+ function setupNodeModules(testDir) {
43
+ const targetDir = join(testDir, 'node_modules/@pennyfarthing/core');
44
+ mkdirSync(targetDir, { recursive: true });
45
+ // Symlink to the real pennyfarthing-dist so init can find templates, commands, etc.
46
+ symlinkSync(REAL_PENNYFARTHING_DIST, join(targetDir, 'pennyfarthing-dist'));
47
+ }
48
+ /**
49
+ * Initialize a git repo in the test directory so hooks can be installed.
50
+ */
51
+ function initGitRepo(testDir) {
52
+ execSync('git init', { cwd: testDir, stdio: 'pipe' });
53
+ // Create an initial commit so git is fully initialized
54
+ writeFileSync(join(testDir, '.gitkeep'), '');
55
+ execSync('git add .gitkeep && git commit -m "init"', {
56
+ cwd: testDir,
57
+ stdio: 'pipe',
58
+ env: {
59
+ ...process.env,
60
+ GIT_AUTHOR_NAME: 'Test',
61
+ GIT_AUTHOR_EMAIL: 'test@test.com',
62
+ GIT_COMMITTER_NAME: 'Test',
63
+ GIT_COMMITTER_EMAIL: 'test@test.com',
64
+ },
65
+ });
66
+ }
67
+ /**
68
+ * Run the pennyfarthing CLI in the test directory.
69
+ */
70
+ function runCLI(testDir, args) {
71
+ const result = spawnSync('node', [CLI_BIN, ...args], {
72
+ cwd: testDir,
73
+ stdio: 'pipe',
74
+ timeout: 30000,
75
+ env: {
76
+ ...process.env,
77
+ // Ensure we don't pick up the real project's node_modules
78
+ NODE_PATH: '',
79
+ // Disable chalk/ANSI colors so --json output is parseable
80
+ FORCE_COLOR: '0',
81
+ NO_COLOR: '1',
82
+ },
83
+ });
84
+ return {
85
+ stdout: result.stdout?.toString() || '',
86
+ stderr: result.stderr?.toString() || '',
87
+ status: result.status,
88
+ };
89
+ }
90
+ /**
91
+ * Extract JSON array from doctor --json output.
92
+ * Doctor outputs header text (Pennyfarthing Health Check, etc.) before the JSON
93
+ * when --json is used. We need to extract just the JSON array.
94
+ */
95
+ function extractDoctorJson(stdout) {
96
+ // Find the first '[' which starts the JSON array
97
+ const jsonStart = stdout.indexOf('[');
98
+ if (jsonStart === -1) {
99
+ throw new Error(`No JSON array found in doctor output: ${stdout.slice(0, 200)}`);
100
+ }
101
+ return JSON.parse(stdout.slice(jsonStart));
102
+ }
103
+ // ─── Constants for verification ───────────────────────────────────
104
+ const CORE_AGENTS = [
105
+ 'dev', 'tea', 'sm', 'reviewer', 'architect',
106
+ 'pm', 'tech-writer', 'ux-designer', 'devops', 'orchestrator'
107
+ ];
108
+ const DIRECTORY_SYMLINKS = [
109
+ { name: 'agents', link: '.pennyfarthing/agents' },
110
+ { name: 'guides', link: '.pennyfarthing/guides' },
111
+ { name: 'output-styles', link: '.pennyfarthing/output-styles' },
112
+ { name: 'personas', link: '.pennyfarthing/personas' },
113
+ { name: 'scripts', link: '.pennyfarthing/scripts' },
114
+ { name: 'workflows', link: '.pennyfarthing/workflows' },
115
+ ];
116
+ // ─── Test Suites ──────────────────────────────────────────────────
117
+ describe('MSSCI-14373: E2E fresh repo install', () => {
118
+ let testDir;
119
+ beforeEach(() => {
120
+ testDir = createTestDir();
121
+ setupNodeModules(testDir);
122
+ initGitRepo(testDir);
123
+ });
124
+ afterEach(() => {
125
+ // AC6: Cleans up the temp repo afterward
126
+ if (existsSync(testDir)) {
127
+ rmSync(testDir, { recursive: true, force: true });
128
+ }
129
+ });
130
+ // ─── AC1: Create fresh git repo ────────────────────────────────
131
+ describe('AC1: Test creates a fresh empty git repo in a temp directory', () => {
132
+ it('should have a valid git repo in the test directory', () => {
133
+ assert.ok(existsSync(join(testDir, '.git')), 'Test dir should be a git repo');
134
+ });
135
+ it('should have node_modules with pennyfarthing symlink', () => {
136
+ const distPath = join(testDir, 'node_modules/@pennyfarthing/core/pennyfarthing-dist');
137
+ assert.ok(existsSync(distPath), 'Fake node_modules should contain pennyfarthing-dist');
138
+ assert.ok(lstatSync(distPath).isSymbolicLink(), 'pennyfarthing-dist should be a symlink');
139
+ });
140
+ });
141
+ // ─── AC2: Run pennyfarthing init ───────────────────────────────
142
+ describe('AC2: Runs pennyfarthing init in that repo', () => {
143
+ it('should complete init --force without error', () => {
144
+ const result = runCLI(testDir, ['init', '--force']);
145
+ assert.strictEqual(result.status, 0, `Init should exit 0, got ${result.status}.\nStdout: ${result.stdout}\nStderr: ${result.stderr}`);
146
+ });
147
+ it('should create all required directories', () => {
148
+ runCLI(testDir, ['init', '--force']);
149
+ const expectedDirs = [
150
+ '.claude',
151
+ '.pennyfarthing',
152
+ '.pennyfarthing/project/commands',
153
+ '.pennyfarthing/project/skills',
154
+ '.pennyfarthing/project/docs',
155
+ '.pennyfarthing/project/hooks',
156
+ 'sprint',
157
+ '.pennyfarthing/sidecars',
158
+ '.session',
159
+ ];
160
+ for (const dir of expectedDirs) {
161
+ assert.ok(existsSync(join(testDir, dir)), `Directory ${dir} should exist after init`);
162
+ }
163
+ });
164
+ it('should create .pennyfarthing/ symlinks to node_modules', () => {
165
+ runCLI(testDir, ['init', '--force']);
166
+ for (const { name, link } of DIRECTORY_SYMLINKS) {
167
+ const linkPath = join(testDir, link);
168
+ assert.ok(existsSync(linkPath), `Symlink ${link} should exist`);
169
+ assert.ok(lstatSync(linkPath).isSymbolicLink(), `${link} should be a symlink (not a copy)`);
170
+ }
171
+ });
172
+ it('should copy commands as real directory (not symlink)', () => {
173
+ runCLI(testDir, ['init', '--force']);
174
+ const commandsDir = join(testDir, '.claude/commands');
175
+ assert.ok(existsSync(commandsDir), '.claude/commands should exist');
176
+ assert.ok(!lstatSync(commandsDir).isSymbolicLink(), '.claude/commands should be a real directory (not a symlink)');
177
+ // Should have at least some command files
178
+ const files = readdirSync(commandsDir);
179
+ assert.ok(files.length > 0, '.claude/commands should contain files');
180
+ assert.ok(files.some(f => f.endsWith('.md')), '.claude/commands should contain .md files');
181
+ });
182
+ it('should copy skills as real directory (not symlink)', () => {
183
+ runCLI(testDir, ['init', '--force']);
184
+ const skillsDir = join(testDir, '.claude/skills');
185
+ assert.ok(existsSync(skillsDir), '.claude/skills should exist');
186
+ assert.ok(!lstatSync(skillsDir).isSymbolicLink(), '.claude/skills should be a real directory (not a symlink)');
187
+ // Should have at least one skill subdirectory
188
+ const entries = readdirSync(skillsDir);
189
+ assert.ok(entries.length > 0, '.claude/skills should contain skill directories');
190
+ });
191
+ it('should create agent sidecars for all 10 core agents', () => {
192
+ runCLI(testDir, ['init', '--force']);
193
+ const sidecarFiles = ['patterns.md', 'gotchas.md', 'decisions.md'];
194
+ for (const agent of CORE_AGENTS) {
195
+ const sidecarDir = join(testDir, `.pennyfarthing/sidecars/${agent}`);
196
+ assert.ok(existsSync(sidecarDir), `Sidecar dir for ${agent} should exist`);
197
+ for (const file of sidecarFiles) {
198
+ assert.ok(existsSync(join(sidecarDir, file)), `Sidecar ${file} for ${agent} should exist`);
199
+ }
200
+ }
201
+ });
202
+ it('should install git hooks', () => {
203
+ runCLI(testDir, ['init', '--force']);
204
+ const hooks = ['pre-commit', 'pre-push', 'post-merge'];
205
+ for (const hook of hooks) {
206
+ const hookPath = join(testDir, '.git/hooks', hook);
207
+ assert.ok(existsSync(hookPath), `Git hook ${hook} should be installed`);
208
+ // Check executable
209
+ const stats = statSync(hookPath);
210
+ const isExecutable = (stats.mode & 0o111) !== 0;
211
+ assert.ok(isExecutable, `Git hook ${hook} should be executable`);
212
+ // Check it's a pennyfarthing hook
213
+ const content = readFileSync(hookPath, 'utf8');
214
+ assert.ok(content.includes('pennyfarthing'), `Git hook ${hook} should contain pennyfarthing marker`);
215
+ }
216
+ });
217
+ it('should generate template files under .pennyfarthing/', () => {
218
+ runCLI(testDir, ['init', '--force']);
219
+ const expectedTemplateFiles = [
220
+ '.pennyfarthing/persona-config.yaml',
221
+ '.pennyfarthing/preferences.yaml',
222
+ '.pennyfarthing/project/docs/agent-scopes.yaml',
223
+ '.pennyfarthing/project/pennyfarthing-settings.yaml',
224
+ '.pennyfarthing/project/hooks/setup-env.sh',
225
+ ];
226
+ for (const file of expectedTemplateFiles) {
227
+ assert.ok(existsSync(join(testDir, file)), `Template file ${file} should exist after init`);
228
+ }
229
+ });
230
+ it('should generate shared-context.md at .claude/project/docs/', () => {
231
+ runCLI(testDir, ['init', '--force']);
232
+ assert.ok(existsSync(join(testDir, '.claude/project/docs/shared-context.md')), 'shared-context.md should be at .claude/project/docs/ (user-owned file)');
233
+ });
234
+ it('should make setup-env.sh executable', () => {
235
+ runCLI(testDir, ['init', '--force']);
236
+ const setupEnvPath = join(testDir, '.pennyfarthing/project/hooks/setup-env.sh');
237
+ assert.ok(existsSync(setupEnvPath), 'setup-env.sh should exist');
238
+ const stats = statSync(setupEnvPath);
239
+ const isExecutable = (stats.mode & 0o111) !== 0;
240
+ assert.ok(isExecutable, 'setup-env.sh should be executable');
241
+ });
242
+ it('should write manifest at .pennyfarthing/manifest.json', () => {
243
+ runCLI(testDir, ['init', '--force']);
244
+ const manifestPath = join(testDir, '.pennyfarthing/manifest.json');
245
+ assert.ok(existsSync(manifestPath), 'Manifest should exist at .pennyfarthing/');
246
+ const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
247
+ assert.strictEqual(manifest.installationType, 'symlink', 'Install type should be symlink');
248
+ assert.ok(manifest.version, 'Manifest should have a version');
249
+ assert.ok(manifest.projectName, 'Manifest should have a project name');
250
+ assert.ok(manifest.nodeModulesPath.includes('pennyfarthing-dist'), 'Manifest should reference node_modules path');
251
+ // managedPaths should include .pennyfarthing/* paths (not .claude/agents etc.)
252
+ assert.ok(manifest.managedPaths.includes('.claude/commands'), 'managedPaths should include .claude/commands');
253
+ assert.ok(manifest.managedPaths.includes('.claude/skills'), 'managedPaths should include .claude/skills');
254
+ assert.ok(manifest.managedPaths.some((p) => p.startsWith('.pennyfarthing/')), 'managedPaths should include .pennyfarthing/ paths');
255
+ assert.ok(!manifest.managedPaths.includes('.claude/agents'), 'managedPaths should NOT include legacy .claude/agents');
256
+ });
257
+ it('should NOT write manifest at .claude/manifest.json', () => {
258
+ runCLI(testDir, ['init', '--force']);
259
+ assert.ok(!existsSync(join(testDir, '.claude/manifest.json')), 'Manifest should NOT be at .claude/manifest.json (legacy location)');
260
+ });
261
+ it('should create settings.local.json with symlink', () => {
262
+ runCLI(testDir, ['init', '--force']);
263
+ const pennyfarthingSettings = join(testDir, '.pennyfarthing/settings.local.json');
264
+ const claudeSettings = join(testDir, '.claude/settings.local.json');
265
+ assert.ok(existsSync(pennyfarthingSettings), 'settings.local.json should exist at .pennyfarthing/');
266
+ assert.ok(existsSync(claudeSettings), '.claude/settings.local.json should exist');
267
+ assert.ok(lstatSync(claudeSettings).isSymbolicLink(), '.claude/settings.local.json should be a symlink to .pennyfarthing/');
268
+ // Verify settings content has required hooks
269
+ const settings = JSON.parse(readFileSync(pennyfarthingSettings, 'utf8'));
270
+ assert.ok(settings.hooks, 'Settings should have hooks');
271
+ assert.ok(settings.hooks.SessionStart, 'Settings should have SessionStart hooks');
272
+ assert.ok(settings.hooks.PostToolUse, 'Settings should have PostToolUse hooks');
273
+ assert.ok(settings.hooks.Stop, 'Settings should have Stop hooks');
274
+ assert.ok(settings.hooks.PreToolUse, 'Settings should have PreToolUse hooks');
275
+ });
276
+ it('should update .gitignore with pennyfarthing entries', () => {
277
+ runCLI(testDir, ['init', '--force']);
278
+ const gitignorePath = join(testDir, '.gitignore');
279
+ assert.ok(existsSync(gitignorePath), '.gitignore should exist');
280
+ const content = readFileSync(gitignorePath, 'utf8');
281
+ assert.ok(content.includes('# Pennyfarthing runtime'), '.gitignore should contain Pennyfarthing section');
282
+ assert.ok(content.includes('.session/*'), '.gitignore should exclude .session/');
283
+ assert.ok(content.includes('.claude/settings.local.json'), '.gitignore should exclude settings.local.json');
284
+ });
285
+ });
286
+ // ─── AC3: Validate with doctor ─────────────────────────────────
287
+ describe('AC3: Validates the installation with pennyfarthing doctor', () => {
288
+ it('should pass doctor check with no failures', () => {
289
+ // First run init
290
+ const initResult = runCLI(testDir, ['init', '--force']);
291
+ assert.strictEqual(initResult.status, 0, 'Init should succeed first');
292
+ // Then run doctor
293
+ const doctorResult = runCLI(testDir, ['doctor', '--json']);
294
+ assert.strictEqual(doctorResult.status, 0, `Doctor should exit 0 (no failures).\nStdout: ${doctorResult.stdout}\nStderr: ${doctorResult.stderr}`);
295
+ // Parse JSON output and verify no failures
296
+ const results = extractDoctorJson(doctorResult.stdout);
297
+ const failures = results.filter((r) => r.status === 'fail');
298
+ assert.strictEqual(failures.length, 0, `Doctor should have 0 failures, got ${failures.length}: ${JSON.stringify(failures, null, 2)}`);
299
+ });
300
+ it('doctor should show manifest as passing', () => {
301
+ runCLI(testDir, ['init', '--force']);
302
+ const doctorResult = runCLI(testDir, ['doctor', '--json']);
303
+ const results = extractDoctorJson(doctorResult.stdout);
304
+ const manifestCheck = results.find((r) => r.name === 'manifest/exists');
305
+ assert.ok(manifestCheck, 'Doctor should check manifest');
306
+ assert.strictEqual(manifestCheck.status, 'pass', 'Manifest check should pass');
307
+ });
308
+ it('doctor should show directory symlinks as passing', () => {
309
+ runCLI(testDir, ['init', '--force']);
310
+ const doctorResult = runCLI(testDir, ['doctor', '--json']);
311
+ const results = extractDoctorJson(doctorResult.stdout);
312
+ // Check the 6 directory symlinks (agents, guides, output-styles, personas, scripts, workflows)
313
+ // Note: commands and skills are intentionally copied (not symlinked),
314
+ // so doctor may warn about them — that's a separate doctor issue.
315
+ const directorySymlinkNames = ['agents', 'guides', 'output-styles', 'personas', 'scripts', 'workflows'];
316
+ for (const name of directorySymlinkNames) {
317
+ const check = results.find((r) => r.name === `symlink/${name}`);
318
+ assert.ok(check, `Doctor should check symlink/${name}`);
319
+ assert.strictEqual(check.status, 'pass', `Symlink check symlink/${name} should pass, got ${check.status}: ${check.detail}`);
320
+ }
321
+ });
322
+ it('doctor should show settings.local.json as passing', () => {
323
+ runCLI(testDir, ['init', '--force']);
324
+ const doctorResult = runCLI(testDir, ['doctor', '--json']);
325
+ const results = extractDoctorJson(doctorResult.stdout);
326
+ const settingsCheck = results.find((r) => r.name === 'settings.local.json');
327
+ assert.ok(settingsCheck, 'Doctor should check settings.local.json');
328
+ assert.strictEqual(settingsCheck.status, 'pass', 'Settings check should pass');
329
+ });
330
+ it('doctor should show session-start hook as passing', () => {
331
+ runCLI(testDir, ['init', '--force']);
332
+ const doctorResult = runCLI(testDir, ['doctor', '--json']);
333
+ const results = extractDoctorJson(doctorResult.stdout);
334
+ const hookCheck = results.find((r) => r.name === 'settings/session-start-hook');
335
+ assert.ok(hookCheck, 'Doctor should check session-start hook');
336
+ assert.strictEqual(hookCheck.status, 'pass', 'Session-start hook check should pass');
337
+ });
338
+ });
339
+ // ─── AC6: Cleanup verification ─────────────────────────────────
340
+ describe('AC6: Cleans up the temp repo afterward', () => {
341
+ it('test cleanup in afterEach removes the temp directory', () => {
342
+ // This test verifies that the cleanup function works by
343
+ // checking that a previously-created testDir no longer exists
344
+ // after cleanup. Since afterEach runs after each test, we
345
+ // validate the pattern is in place.
346
+ assert.ok(existsSync(testDir), 'Test dir should exist during test');
347
+ // afterEach will clean up - verified by not leaking temp dirs
348
+ });
349
+ });
350
+ // ─── Idempotency ───────────────────────────────────────────────
351
+ describe('Idempotency: Running init twice should not break things', () => {
352
+ it('should succeed when run twice on same directory', () => {
353
+ const result1 = runCLI(testDir, ['init', '--force']);
354
+ assert.strictEqual(result1.status, 0, 'First init should succeed');
355
+ const result2 = runCLI(testDir, ['init', '--force']);
356
+ assert.strictEqual(result2.status, 0, 'Second init should succeed');
357
+ // Doctor should still pass after double-init
358
+ const doctorResult = runCLI(testDir, ['doctor', '--json']);
359
+ assert.strictEqual(doctorResult.status, 0, 'Doctor should pass after double init');
360
+ const results = extractDoctorJson(doctorResult.stdout);
361
+ const failures = results.filter((r) => r.status === 'fail');
362
+ assert.strictEqual(failures.length, 0, 'No doctor failures after double init');
363
+ });
364
+ });
365
+ // ─── Edge cases ────────────────────────────────────────────────
366
+ describe('Edge cases', () => {
367
+ it('should fail gracefully without node_modules', () => {
368
+ // Create a fresh dir without node_modules
369
+ const bareDir = createTestDir();
370
+ initGitRepo(bareDir);
371
+ try {
372
+ const result = runCLI(bareDir, ['init', '--force']);
373
+ assert.notStrictEqual(result.status, 0, 'Init should fail without node_modules');
374
+ }
375
+ finally {
376
+ rmSync(bareDir, { recursive: true, force: true });
377
+ }
378
+ });
379
+ it('should work without .git directory (no hooks installed)', () => {
380
+ // Create dir without git init
381
+ const nonGitDir = createTestDir();
382
+ setupNodeModules(nonGitDir);
383
+ try {
384
+ const result = runCLI(nonGitDir, ['init', '--force']);
385
+ assert.strictEqual(result.status, 0, 'Init should succeed without .git');
386
+ // Verify no hooks dir crash
387
+ assert.ok(existsSync(join(nonGitDir, '.pennyfarthing/manifest.json')), 'Manifest should still be created');
388
+ }
389
+ finally {
390
+ rmSync(nonGitDir, { recursive: true, force: true });
391
+ }
392
+ });
393
+ });
394
+ });
395
+ //# sourceMappingURL=e2e-fresh-install.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"e2e-fresh-install.test.js","sourceRoot":"","sources":["../../../src/cli/commands/e2e-fresh-install.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * E2E test for MSSCI-14374: Existing repo upgrade
3
+ *
4
+ * This test sets up an old-style installation with files in legacy .claude/
5
+ * locations, runs `pennyfarthing update` to migrate, and validates that:
6
+ * - Files are migrated to .pennyfarthing/
7
+ * - Symlinks exist for Claude Code compatibility
8
+ * - Doctor passes after migration
9
+ * - Temp directory is cleaned up
10
+ *
11
+ * Acceptance Criteria:
12
+ * AC1: Test creates a temp directory with old-style install layout (.claude/ based)
13
+ * AC2: Test runs `pennyfarthing update` against the old layout
14
+ * AC3: Test validates files migrated to .pennyfarthing/
15
+ * AC4: Test validates symlinks exist for Claude Code compatibility
16
+ * AC5: Test runs doctor and confirms it passes
17
+ * AC6: Test cleans up temp directory afterward
18
+ *
19
+ * Run with: cd packages/core && npm run build && npm test
20
+ */
21
+ export {};
22
+ //# sourceMappingURL=e2e-upgrade.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"e2e-upgrade.test.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/e2e-upgrade.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG"}