@pennyfarthing/core 11.2.2 → 11.3.2

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 (168) hide show
  1. package/README.md +3 -3
  2. package/package.json +1 -1
  3. package/packages/core/dist/cli/commands/doctor-legacy.test.js +2 -2
  4. package/packages/core/dist/cli/commands/doctor-legacy.test.js.map +1 -1
  5. package/packages/core/dist/cli/commands/doctor.d.ts +63 -0
  6. package/packages/core/dist/cli/commands/doctor.d.ts.map +1 -1
  7. package/packages/core/dist/cli/commands/doctor.js +280 -43
  8. package/packages/core/dist/cli/commands/doctor.js.map +1 -1
  9. package/packages/core/dist/cli/commands/init.d.ts +12 -0
  10. package/packages/core/dist/cli/commands/init.d.ts.map +1 -1
  11. package/packages/core/dist/cli/commands/init.js +45 -0
  12. package/packages/core/dist/cli/commands/init.js.map +1 -1
  13. package/packages/core/dist/cli/commands/pyproject-install.test.d.ts +19 -0
  14. package/packages/core/dist/cli/commands/pyproject-install.test.d.ts.map +1 -0
  15. package/packages/core/dist/cli/commands/pyproject-install.test.js +261 -0
  16. package/packages/core/dist/cli/commands/pyproject-install.test.js.map +1 -0
  17. package/packages/core/dist/cli/commands/update-consolidation.test.js +14 -6
  18. package/packages/core/dist/cli/commands/update-consolidation.test.js.map +1 -1
  19. package/packages/core/dist/cli/commands/update.d.ts.map +1 -1
  20. package/packages/core/dist/cli/commands/update.js +5 -1
  21. package/packages/core/dist/cli/commands/update.js.map +1 -1
  22. package/packages/core/dist/cli/index.js +2 -0
  23. package/packages/core/dist/cli/index.js.map +1 -1
  24. package/packages/core/dist/cli/utils/python.d.ts +1 -0
  25. package/packages/core/dist/cli/utils/python.d.ts.map +1 -1
  26. package/packages/core/dist/cli/utils/python.js +22 -1
  27. package/packages/core/dist/cli/utils/python.js.map +1 -1
  28. package/packages/core/dist/cli/utils/settings-hook-migration.test.d.ts +17 -0
  29. package/packages/core/dist/cli/utils/settings-hook-migration.test.d.ts.map +1 -0
  30. package/packages/core/dist/cli/utils/settings-hook-migration.test.js +382 -0
  31. package/packages/core/dist/cli/utils/settings-hook-migration.test.js.map +1 -0
  32. package/packages/core/dist/cli/utils/settings-pf-wrapper.test.d.ts +16 -0
  33. package/packages/core/dist/cli/utils/settings-pf-wrapper.test.d.ts.map +1 -0
  34. package/packages/core/dist/cli/utils/settings-pf-wrapper.test.js +377 -0
  35. package/packages/core/dist/cli/utils/settings-pf-wrapper.test.js.map +1 -0
  36. package/packages/core/dist/cli/utils/settings.d.ts.map +1 -1
  37. package/packages/core/dist/cli/utils/settings.js +15 -2
  38. package/packages/core/dist/cli/utils/settings.js.map +1 -1
  39. package/packages/core/dist/server/paths.d.ts.map +1 -1
  40. package/packages/core/dist/server/paths.js +6 -0
  41. package/packages/core/dist/server/paths.js.map +1 -1
  42. package/packages/core/dist/server/settings.d.ts.map +1 -1
  43. package/packages/core/dist/server/settings.js +5 -0
  44. package/packages/core/dist/server/settings.js.map +1 -1
  45. package/packages/core/dist/workflow/tandem-workflow-templates.test.js +7 -5
  46. package/packages/core/dist/workflow/tandem-workflow-templates.test.js.map +1 -1
  47. package/packages/core/dist/workflow/workflow-graph-validation.d.ts +65 -0
  48. package/packages/core/dist/workflow/workflow-graph-validation.d.ts.map +1 -0
  49. package/packages/core/dist/workflow/workflow-graph-validation.js +190 -0
  50. package/packages/core/dist/workflow/workflow-graph-validation.js.map +1 -0
  51. package/packages/core/dist/workflow/workflow-graph-validation.test.d.ts +18 -0
  52. package/packages/core/dist/workflow/workflow-graph-validation.test.d.ts.map +1 -0
  53. package/packages/core/dist/workflow/workflow-graph-validation.test.js +706 -0
  54. package/packages/core/dist/workflow/workflow-graph-validation.test.js.map +1 -0
  55. package/packages/core/dist/workflow/workflow-migration.test.js +6 -5
  56. package/packages/core/dist/workflow/workflow-migration.test.js.map +1 -1
  57. package/pennyfarthing-dist/agents/dev.md +4 -2
  58. package/pennyfarthing-dist/agents/devops.md +2 -10
  59. package/pennyfarthing-dist/agents/reviewer-preflight.md +4 -5
  60. package/pennyfarthing-dist/agents/sm.md +4 -17
  61. package/pennyfarthing-dist/commands/pf-health-check.md +30 -11
  62. package/pennyfarthing-dist/gates/{confidence-sm.md → confidence.md} +16 -17
  63. package/pennyfarthing-dist/gates/dev-exit.md +75 -0
  64. package/pennyfarthing-dist/gates/merge-ready.md +49 -0
  65. package/pennyfarthing-dist/gates/release-ready.md +95 -0
  66. package/pennyfarthing-dist/gates/reviewer-preflight-check.md +90 -0
  67. package/pennyfarthing-dist/gates/sm-setup-exit.md +82 -0
  68. package/pennyfarthing-dist/guides/agent-behavior.md +88 -30
  69. package/pennyfarthing-dist/guides/gates.md +7 -2
  70. package/pennyfarthing-dist/scripts/lib/find-root.sh +5 -0
  71. package/pennyfarthing-dist/scripts/lib/run-pf.sh +7 -0
  72. package/pennyfarthing-dist/skills/pf-settings/skill.md +42 -0
  73. package/pennyfarthing-dist/skills/skill-registry.yaml +15 -0
  74. package/pennyfarthing-dist/templates/pyproject.toml +27 -0
  75. package/pennyfarthing-dist/workflows/bdd-tandem.yaml +7 -3
  76. package/pennyfarthing-dist/workflows/bdd.yaml +7 -3
  77. package/pennyfarthing-dist/workflows/installation-check/steps/step-01-foundation.md +77 -0
  78. package/pennyfarthing-dist/workflows/installation-check/steps/step-02-commands.md +82 -0
  79. package/pennyfarthing-dist/workflows/installation-check/steps/step-03-hooks.md +121 -0
  80. package/pennyfarthing-dist/workflows/installation-check/steps/step-04-scripts.md +83 -0
  81. package/pennyfarthing-dist/workflows/installation-check/steps/step-05-layout.md +81 -0
  82. package/pennyfarthing-dist/workflows/installation-check/steps/step-06-legacy.md +94 -0
  83. package/pennyfarthing-dist/workflows/installation-check/steps/step-07-tools.md +80 -0
  84. package/pennyfarthing-dist/workflows/installation-check/steps/step-08-summary.md +99 -0
  85. package/pennyfarthing-dist/workflows/installation-check/workflow.yaml +47 -0
  86. package/pennyfarthing-dist/workflows/tdd-tandem.yaml +7 -3
  87. package/pennyfarthing-dist/workflows/tdd.yaml +7 -3
  88. package/pennyfarthing-dist/workflows/trivial.yaml +7 -3
  89. package/pennyfarthing_scripts/__pycache__/__init__.cpython-314.pyc +0 -0
  90. package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
  91. package/pennyfarthing_scripts/__pycache__/context.cpython-314.pyc +0 -0
  92. package/pennyfarthing_scripts/bc/__pycache__/cli.cpython-314.pyc +0 -0
  93. package/pennyfarthing_scripts/bc/__pycache__/focus.cpython-314.pyc +0 -0
  94. package/pennyfarthing_scripts/bc/__pycache__/split.cpython-314.pyc +0 -0
  95. package/pennyfarthing_scripts/bc/cli.py +21 -0
  96. package/pennyfarthing_scripts/bc/focus.py +1 -0
  97. package/pennyfarthing_scripts/bc/split.py +52 -0
  98. package/pennyfarthing_scripts/bikerack/__pycache__/audit_log_panel.cpython-314.pyc +0 -0
  99. package/pennyfarthing_scripts/bikerack/__pycache__/base_panel.cpython-314.pyc +0 -0
  100. package/pennyfarthing_scripts/bikerack/__pycache__/changed_panel.cpython-314.pyc +0 -0
  101. package/pennyfarthing_scripts/bikerack/__pycache__/context_meter_footer.cpython-314.pyc +0 -0
  102. package/pennyfarthing_scripts/bikerack/__pycache__/debug_panel.cpython-314.pyc +0 -0
  103. package/pennyfarthing_scripts/bikerack/__pycache__/diffs_panel.cpython-314.pyc +0 -0
  104. package/pennyfarthing_scripts/bikerack/__pycache__/events.cpython-314.pyc +0 -0
  105. package/pennyfarthing_scripts/bikerack/__pycache__/launcher.cpython-314.pyc +0 -0
  106. package/pennyfarthing_scripts/bikerack/__pycache__/portrait_resolver.cpython-314.pyc +0 -0
  107. package/pennyfarthing_scripts/bikerack/__pycache__/progress_panel.cpython-314.pyc +0 -0
  108. package/pennyfarthing_scripts/bikerack/__pycache__/sprint_panel.cpython-314.pyc +0 -0
  109. package/pennyfarthing_scripts/bikerack/__pycache__/tui.cpython-314.pyc +0 -0
  110. package/pennyfarthing_scripts/bikerack/__pycache__/ws_client.cpython-314.pyc +0 -0
  111. package/pennyfarthing_scripts/bikerack/context_meter_footer.py +53 -3
  112. package/pennyfarthing_scripts/bikerack/tui.py +202 -8
  113. package/pennyfarthing_scripts/bmad/__init__.py +1 -0
  114. package/pennyfarthing_scripts/bmad/__pycache__/__init__.cpython-314.pyc +0 -0
  115. package/pennyfarthing_scripts/bmad/__pycache__/cli.cpython-314.pyc +0 -0
  116. package/pennyfarthing_scripts/bmad/__pycache__/parser.cpython-314.pyc +0 -0
  117. package/pennyfarthing_scripts/bmad/__pycache__/sync.cpython-314.pyc +0 -0
  118. package/pennyfarthing_scripts/bmad/__pycache__/test_parser.cpython-314-pytest-9.0.2.pyc +0 -0
  119. package/pennyfarthing_scripts/bmad/__pycache__/test_sync.cpython-314-pytest-9.0.2.pyc +0 -0
  120. package/pennyfarthing_scripts/bmad/cli.py +197 -0
  121. package/pennyfarthing_scripts/bmad/importer.py +200 -0
  122. package/pennyfarthing_scripts/bmad/parser.py +233 -0
  123. package/pennyfarthing_scripts/bmad/sync.py +464 -0
  124. package/pennyfarthing_scripts/bmad/test_parser.py +253 -0
  125. package/pennyfarthing_scripts/bmad/test_sync.py +223 -0
  126. package/pennyfarthing_scripts/cli.py +10 -0
  127. package/pennyfarthing_scripts/common/__pycache__/config.cpython-314.pyc +0 -0
  128. package/pennyfarthing_scripts/consultation/__pycache__/cli.cpython-314.pyc +0 -0
  129. package/pennyfarthing_scripts/handoff/__pycache__/cli.cpython-314.pyc +0 -0
  130. package/pennyfarthing_scripts/handoff/__pycache__/complete_phase.cpython-314.pyc +0 -0
  131. package/pennyfarthing_scripts/handoff/__pycache__/gate_file.cpython-314.pyc +0 -0
  132. package/pennyfarthing_scripts/handoff/__pycache__/gate_runner.cpython-314.pyc +0 -0
  133. package/pennyfarthing_scripts/handoff/__pycache__/marker.cpython-314.pyc +0 -0
  134. package/pennyfarthing_scripts/handoff/__pycache__/resolve_gate.cpython-314.pyc +0 -0
  135. package/pennyfarthing_scripts/hooks/__pycache__/__init__.cpython-314.pyc +0 -0
  136. package/pennyfarthing_scripts/hooks/__pycache__/bell_mode.cpython-314.pyc +0 -0
  137. package/pennyfarthing_scripts/hooks/__pycache__/cli.cpython-314.pyc +0 -0
  138. package/pennyfarthing_scripts/hooks/__pycache__/context_breaker.cpython-314.pyc +0 -0
  139. package/pennyfarthing_scripts/hooks/__pycache__/context_warning.cpython-314.pyc +0 -0
  140. package/pennyfarthing_scripts/hooks/__pycache__/cyclist_pretooluse.cpython-314.pyc +0 -0
  141. package/pennyfarthing_scripts/hooks/__pycache__/pre_edit_check.cpython-314.pyc +0 -0
  142. package/pennyfarthing_scripts/hooks/__pycache__/reflector_check.cpython-314.pyc +0 -0
  143. package/pennyfarthing_scripts/hooks/__pycache__/schema_validation.cpython-314.pyc +0 -0
  144. package/pennyfarthing_scripts/hooks/__pycache__/session_start.cpython-314.pyc +0 -0
  145. package/pennyfarthing_scripts/hooks/__pycache__/session_stop.cpython-314.pyc +0 -0
  146. package/pennyfarthing_scripts/hooks/__pycache__/sprint_yaml_validation.cpython-314.pyc +0 -0
  147. package/pennyfarthing_scripts/hooks/__pycache__/statusline.cpython-314.pyc +0 -0
  148. package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
  149. package/pennyfarthing_scripts/settings/__init__.py +0 -0
  150. package/pennyfarthing_scripts/settings/__pycache__/__init__.cpython-314.pyc +0 -0
  151. package/pennyfarthing_scripts/settings/__pycache__/cli.cpython-314.pyc +0 -0
  152. package/pennyfarthing_scripts/settings/__pycache__/settings.cpython-314.pyc +0 -0
  153. package/pennyfarthing_scripts/settings/cli.py +55 -0
  154. package/pennyfarthing_scripts/settings/settings.py +98 -0
  155. package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
  156. package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
  157. package/pennyfarthing_scripts/sprint/__pycache__/story_update.cpython-314.pyc +0 -0
  158. package/pennyfarthing_scripts/tests/__pycache__/test_confidence_sm_evaluation.cpython-314-pytest-9.0.2.pyc +0 -0
  159. package/pennyfarthing_scripts/tests/__pycache__/test_confidence_sm_gate.cpython-314-pytest-9.0.2.pyc +0 -0
  160. package/pennyfarthing_scripts/tests/__pycache__/test_gate_file_resolution.cpython-314-pytest-9.0.2.pyc +0 -0
  161. package/pennyfarthing_scripts/tests/__pycache__/test_gate_runner.cpython-314-pytest-9.0.2.pyc +0 -0
  162. package/pennyfarthing_scripts/tests/__pycache__/test_resolve_gate_file_field.cpython-314-pytest-9.0.2.pyc +0 -0
  163. package/pennyfarthing_scripts/tests/__pycache__/test_workflow_list_team.cpython-314.pyc +0 -0
  164. package/pennyfarthing_scripts/tests/test_confidence_sm_gate.py +17 -16
  165. package/pennyfarthing_scripts/tests/test_resolve_gate_file_field.py +45 -47
  166. package/pennyfarthing_scripts/tests/test_workflow_list_team.py +0 -4
  167. package/pennyfarthing_scripts/workflow/__pycache__/cli.cpython-314.pyc +0 -0
  168. package/pennyfarthing_scripts/workflow/__pycache__/state.cpython-314.pyc +0 -0
@@ -37,6 +37,17 @@ export function findLocalPyproject(nodeModulesPath) {
37
37
  }
38
38
  catch { /* ignore read errors */ }
39
39
  }
40
+ // Check .pennyfarthing/pyproject.toml (auto-generated by init)
41
+ const pfCandidate = join(dir, '.pennyfarthing', 'pyproject.toml');
42
+ if (existsSync(pfCandidate)) {
43
+ try {
44
+ const content = readFileSync(pfCandidate, 'utf8');
45
+ if (content.includes('pennyfarthing-scripts') || content.includes('pennyfarthing_scripts')) {
46
+ return pfCandidate;
47
+ }
48
+ }
49
+ catch { /* ignore read errors */ }
50
+ }
40
51
  const parent = dirname(dir);
41
52
  if (parent === dir)
42
53
  break;
@@ -46,16 +57,26 @@ export function findLocalPyproject(nodeModulesPath) {
46
57
  }
47
58
  /**
48
59
  * Check if the `pf` CLI is already installed and working.
60
+ * Checks both the global `pf` binary and `uv run pf` (local project resolution).
49
61
  * Returns the version string if installed, null otherwise.
50
62
  */
51
63
  export function getPfVersion() {
64
+ // Strategy 1: Check for global pf binary
52
65
  try {
53
66
  const result = spawnSync('pf', ['--version'], { encoding: 'utf8', stdio: 'pipe' });
54
67
  if (result.status === 0) {
55
68
  return result.stdout?.trim() || 'unknown';
56
69
  }
57
70
  }
58
- catch { /* not installed */ }
71
+ catch { /* not installed globally */ }
72
+ // Strategy 2: Check for pf via uv run (local project resolution)
73
+ try {
74
+ const result = spawnSync('uv', ['run', 'pf', '--version'], { encoding: 'utf8', stdio: 'pipe' });
75
+ if (result.status === 0) {
76
+ return `${result.stdout?.trim() || 'unknown'} (via uv run)`;
77
+ }
78
+ }
79
+ catch { /* uv not available or pf not resolvable */ }
59
80
  return null;
60
81
  }
61
82
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"python.js","sourceRoot":"","sources":["../../../src/cli/utils/python.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"python.js","sourceRoot":"","sources":["../../../src/cli/utils/python.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Tests for Story 117-2: Generate hook commands with pf.sh wrapper path, not bare pf
3
+ *
4
+ * Bug: migrateHookPaths() only migrates .sh file hooks to pf.sh commands.
5
+ * It does NOT migrate bare `pf hooks X` commands to `pf.sh hooks X`.
6
+ * Additionally, mergeSettingsLocalJson() detects bare `pf hooks X` as
7
+ * "already configured" and skips them, leaving broken commands in place.
8
+ *
9
+ * Acceptance Criteria:
10
+ * AC1: Hook generation uses $CLAUDE_PROJECT_DIR/.pennyfarthing/scripts/core/pf.sh wrapper path
11
+ * AC2: Generated settings.local.json hooks work without pf being in PATH
12
+ * AC3: Existing hook functionality preserved
13
+ *
14
+ * Run with: cd packages/core && npm run build && npm test
15
+ */
16
+ export {};
17
+ //# sourceMappingURL=settings-hook-migration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-hook-migration.test.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/settings-hook-migration.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}
@@ -0,0 +1,382 @@
1
+ /**
2
+ * Tests for Story 117-2: Generate hook commands with pf.sh wrapper path, not bare pf
3
+ *
4
+ * Bug: migrateHookPaths() only migrates .sh file hooks to pf.sh commands.
5
+ * It does NOT migrate bare `pf hooks X` commands to `pf.sh hooks X`.
6
+ * Additionally, mergeSettingsLocalJson() detects bare `pf hooks X` as
7
+ * "already configured" and skips them, leaving broken commands in place.
8
+ *
9
+ * Acceptance Criteria:
10
+ * AC1: Hook generation uses $CLAUDE_PROJECT_DIR/.pennyfarthing/scripts/core/pf.sh wrapper path
11
+ * AC2: Generated settings.local.json hooks work without pf being in PATH
12
+ * AC3: Existing hook functionality preserved
13
+ *
14
+ * Run with: cd packages/core && npm run build && npm test
15
+ */
16
+ import { describe, it, beforeEach, afterEach } from 'node:test';
17
+ import assert from 'node:assert';
18
+ import { existsSync, mkdirSync, writeFileSync, rmSync, readFileSync, } from 'fs';
19
+ import { join } from 'path';
20
+ import { tmpdir } from 'os';
21
+ import { migrateHookPaths, mergeSettingsLocalJson, LEGACY_HOOK_MIGRATIONS, } from './settings.js';
22
+ // =============================================================================
23
+ // Helpers
24
+ // =============================================================================
25
+ const PF_SH_PREFIX = '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh';
26
+ function makeHookEntry(command, matcher) {
27
+ return {
28
+ ...(matcher !== undefined ? { matcher } : {}),
29
+ hooks: [{ type: 'command', command }],
30
+ };
31
+ }
32
+ // =============================================================================
33
+ // AC1: PF_SH constant and template use wrapper path
34
+ // =============================================================================
35
+ describe('117-2 AC1: Hook generation uses pf.sh wrapper path', () => {
36
+ it('should have PF_SH prefix in all LEGACY_HOOK_MIGRATIONS values', () => {
37
+ for (const [shName, pfCommand] of Object.entries(LEGACY_HOOK_MIGRATIONS)) {
38
+ assert.ok(pfCommand.includes('.pennyfarthing/scripts/core/pf.sh'), `Migration for ${shName} should use pf.sh wrapper, got: ${pfCommand}`);
39
+ }
40
+ });
41
+ it('should use $CLAUDE_PROJECT_DIR in all LEGACY_HOOK_MIGRATIONS values', () => {
42
+ for (const [shName, pfCommand] of Object.entries(LEGACY_HOOK_MIGRATIONS)) {
43
+ assert.ok(pfCommand.includes('$CLAUDE_PROJECT_DIR'), `Migration for ${shName} should use $CLAUDE_PROJECT_DIR, got: ${pfCommand}`);
44
+ }
45
+ });
46
+ });
47
+ // =============================================================================
48
+ // AC2: migrateHookPaths migrates bare `pf` to `pf.sh` wrapper
49
+ // =============================================================================
50
+ describe('117-2 AC2: migrateHookPaths migrates bare pf commands', () => {
51
+ it('should migrate bare "pf hooks session-start" to pf.sh wrapper', () => {
52
+ const hooks = [
53
+ makeHookEntry('pf hooks session-start'),
54
+ ];
55
+ const migrated = migrateHookPaths(hooks);
56
+ assert.ok(migrated, 'Should report migration occurred');
57
+ assert.ok(hooks[0].hooks[0].command.includes('pf.sh hooks session-start'), `Should migrate to pf.sh wrapper, got: ${hooks[0].hooks[0].command}`);
58
+ assert.ok(hooks[0].hooks[0].command.includes('$CLAUDE_PROJECT_DIR'), `Should include $CLAUDE_PROJECT_DIR, got: ${hooks[0].hooks[0].command}`);
59
+ });
60
+ it('should migrate bare "pf hooks reflector-check" to pf.sh wrapper', () => {
61
+ const hooks = [
62
+ makeHookEntry('pf hooks reflector-check'),
63
+ ];
64
+ const migrated = migrateHookPaths(hooks);
65
+ assert.ok(migrated, 'Should report migration occurred');
66
+ assert.strictEqual(hooks[0].hooks[0].command, `${PF_SH_PREFIX} hooks reflector-check`, 'Should use full pf.sh wrapper path');
67
+ });
68
+ it('should migrate bare "pf hooks bell-mode" to pf.sh wrapper', () => {
69
+ const hooks = [
70
+ makeHookEntry('pf hooks bell-mode'),
71
+ ];
72
+ const migrated = migrateHookPaths(hooks);
73
+ assert.ok(migrated, 'Should report migration occurred');
74
+ assert.strictEqual(hooks[0].hooks[0].command, `${PF_SH_PREFIX} hooks bell-mode`);
75
+ });
76
+ it('should migrate bare "pf hooks context-breaker" to pf.sh wrapper', () => {
77
+ const hooks = [
78
+ makeHookEntry('pf hooks context-breaker'),
79
+ ];
80
+ const migrated = migrateHookPaths(hooks);
81
+ assert.ok(migrated, 'Should report migration occurred');
82
+ assert.strictEqual(hooks[0].hooks[0].command, `${PF_SH_PREFIX} hooks context-breaker`);
83
+ });
84
+ it('should migrate bare "pf hooks statusline" to pf.sh wrapper', () => {
85
+ const hooks = [
86
+ makeHookEntry('pf hooks statusline'),
87
+ ];
88
+ const migrated = migrateHookPaths(hooks);
89
+ assert.ok(migrated, 'Should report migration occurred');
90
+ assert.strictEqual(hooks[0].hooks[0].command, `${PF_SH_PREFIX} hooks statusline`);
91
+ });
92
+ it('should migrate all known bare pf hook commands', () => {
93
+ const bareCommands = [
94
+ 'pf hooks session-start',
95
+ 'pf hooks session-stop',
96
+ 'pf hooks reflector-check',
97
+ 'pf hooks pre-edit-check',
98
+ 'pf hooks context-warning',
99
+ 'pf hooks context-breaker',
100
+ 'pf hooks cyclist-pretooluse',
101
+ 'pf hooks schema-validation',
102
+ 'pf hooks bell-mode',
103
+ 'pf hooks sprint-yaml',
104
+ 'pf hooks statusline',
105
+ ];
106
+ for (const bareCmd of bareCommands) {
107
+ const hooks = [makeHookEntry(bareCmd)];
108
+ const migrated = migrateHookPaths(hooks);
109
+ const subcommand = bareCmd.replace('pf hooks ', '');
110
+ assert.ok(migrated, `Should migrate "${bareCmd}"`);
111
+ assert.strictEqual(hooks[0].hooks[0].command, `${PF_SH_PREFIX} hooks ${subcommand}`, `"${bareCmd}" should become "${PF_SH_PREFIX} hooks ${subcommand}"`);
112
+ }
113
+ });
114
+ it('should migrate multiple bare pf commands in same hook array', () => {
115
+ const hooks = [
116
+ makeHookEntry('pf hooks session-start'),
117
+ makeHookEntry('pf hooks bell-mode'),
118
+ makeHookEntry('pf hooks context-breaker'),
119
+ ];
120
+ const migrated = migrateHookPaths(hooks);
121
+ assert.ok(migrated, 'Should report migration occurred');
122
+ assert.strictEqual(hooks[0].hooks[0].command, `${PF_SH_PREFIX} hooks session-start`);
123
+ assert.strictEqual(hooks[1].hooks[0].command, `${PF_SH_PREFIX} hooks bell-mode`);
124
+ assert.strictEqual(hooks[2].hooks[0].command, `${PF_SH_PREFIX} hooks context-breaker`);
125
+ });
126
+ it('should NOT migrate commands that already use pf.sh wrapper', () => {
127
+ const hooks = [
128
+ makeHookEntry(`${PF_SH_PREFIX} hooks session-start`),
129
+ ];
130
+ const migrated = migrateHookPaths(hooks);
131
+ assert.ok(!migrated, 'Should NOT report migration for already-correct commands');
132
+ assert.strictEqual(hooks[0].hooks[0].command, `${PF_SH_PREFIX} hooks session-start`, 'Should leave correct commands unchanged');
133
+ });
134
+ it('should NOT migrate non-pf commands', () => {
135
+ const hooks = [
136
+ makeHookEntry('custom-hook.sh'),
137
+ makeHookEntry('.pennyfarthing/project/hooks/setup-env.sh'),
138
+ ];
139
+ const migrated = migrateHookPaths(hooks);
140
+ assert.ok(!migrated, 'Should NOT migrate non-pf commands');
141
+ assert.strictEqual(hooks[0].hooks[0].command, 'custom-hook.sh');
142
+ assert.strictEqual(hooks[1].hooks[0].command, '.pennyfarthing/project/hooks/setup-env.sh');
143
+ });
144
+ it('should preserve matcher when migrating bare pf commands', () => {
145
+ const hooks = [
146
+ makeHookEntry('pf hooks sprint-yaml', 'Edit|Write'),
147
+ ];
148
+ const migrated = migrateHookPaths(hooks);
149
+ assert.ok(migrated, 'Should report migration occurred');
150
+ assert.strictEqual(hooks[0].matcher, 'Edit|Write', 'Should preserve matcher');
151
+ assert.strictEqual(hooks[0].hooks[0].command, `${PF_SH_PREFIX} hooks sprint-yaml`);
152
+ });
153
+ });
154
+ // =============================================================================
155
+ // AC2: mergeSettingsLocalJson replaces bare pf commands during merge
156
+ // =============================================================================
157
+ describe('117-2 AC2: mergeSettingsLocalJson fixes bare pf commands', () => {
158
+ let testDir;
159
+ let assetsPath;
160
+ beforeEach(() => {
161
+ testDir = join(tmpdir(), `pf-hook-migration-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
162
+ const pennyfarthingDir = join(testDir, '.pennyfarthing');
163
+ assetsPath = join(testDir, 'assets');
164
+ mkdirSync(join(testDir, '.claude'), { recursive: true });
165
+ mkdirSync(pennyfarthingDir, { recursive: true });
166
+ mkdirSync(join(assetsPath, 'templates'), { recursive: true });
167
+ // Template with correct pf.sh wrapper paths
168
+ writeFileSync(join(assetsPath, 'templates/settings.local.json.template'), JSON.stringify({
169
+ hooks: {
170
+ SessionStart: [
171
+ {
172
+ hooks: [{
173
+ type: 'command',
174
+ command: `${PF_SH_PREFIX} hooks session-start`,
175
+ }],
176
+ },
177
+ ],
178
+ Stop: [
179
+ {
180
+ hooks: [{
181
+ type: 'command',
182
+ command: `${PF_SH_PREFIX} hooks reflector-check`,
183
+ }],
184
+ },
185
+ ],
186
+ PostToolUse: [
187
+ {
188
+ hooks: [{
189
+ type: 'command',
190
+ command: `${PF_SH_PREFIX} hooks bell-mode`,
191
+ }],
192
+ },
193
+ ],
194
+ PreToolUse: [
195
+ {
196
+ matcher: 'Edit|Write|Bash|Task',
197
+ hooks: [{
198
+ type: 'command',
199
+ command: `${PF_SH_PREFIX} hooks context-breaker`,
200
+ }],
201
+ },
202
+ ],
203
+ },
204
+ statusLine: {
205
+ type: 'command',
206
+ command: `${PF_SH_PREFIX} hooks statusline`,
207
+ },
208
+ }, null, 2));
209
+ });
210
+ afterEach(() => {
211
+ if (existsSync(testDir)) {
212
+ rmSync(testDir, { recursive: true, force: true });
213
+ }
214
+ });
215
+ it('should migrate bare pf SessionStart hooks to pf.sh wrapper during merge', async () => {
216
+ // Consumer has bare `pf hooks` commands from old installation
217
+ const existingSettings = {
218
+ hooks: {
219
+ SessionStart: [
220
+ {
221
+ hooks: [{
222
+ type: 'command',
223
+ command: 'pf hooks session-start',
224
+ }],
225
+ },
226
+ ],
227
+ },
228
+ };
229
+ writeFileSync(join(testDir, '.pennyfarthing/settings.local.json'), JSON.stringify(existingSettings, null, 2));
230
+ await mergeSettingsLocalJson(testDir, assetsPath, {});
231
+ const result = JSON.parse(readFileSync(join(testDir, '.pennyfarthing/settings.local.json'), 'utf8'));
232
+ // The bare `pf hooks session-start` should be migrated to use pf.sh wrapper
233
+ const sessionStartCommands = result.hooks.SessionStart
234
+ .flatMap((e) => e.hooks?.map(h => h.command) ?? []);
235
+ const hasBareCommand = sessionStartCommands.some((cmd) => cmd === 'pf hooks session-start');
236
+ const hasWrapperCommand = sessionStartCommands.some((cmd) => cmd.includes('pf.sh hooks session-start'));
237
+ assert.ok(!hasBareCommand, 'Bare "pf hooks session-start" should be migrated away');
238
+ assert.ok(hasWrapperCommand, 'Should have pf.sh wrapper command after migration');
239
+ });
240
+ it('should migrate bare pf PostToolUse hooks during merge', async () => {
241
+ const existingSettings = {
242
+ hooks: {
243
+ PostToolUse: [
244
+ {
245
+ matcher: '',
246
+ hooks: [{
247
+ type: 'command',
248
+ command: 'pf hooks bell-mode',
249
+ }],
250
+ },
251
+ ],
252
+ },
253
+ };
254
+ writeFileSync(join(testDir, '.pennyfarthing/settings.local.json'), JSON.stringify(existingSettings, null, 2));
255
+ await mergeSettingsLocalJson(testDir, assetsPath, {});
256
+ const result = JSON.parse(readFileSync(join(testDir, '.pennyfarthing/settings.local.json'), 'utf8'));
257
+ const bellModeCommands = result.hooks.PostToolUse
258
+ .flatMap((e) => e.hooks?.map(h => h.command) ?? []);
259
+ const hasBareCommand = bellModeCommands.some((cmd) => cmd === 'pf hooks bell-mode');
260
+ assert.ok(!hasBareCommand, 'Bare "pf hooks bell-mode" should be migrated');
261
+ });
262
+ it('should migrate bare pf Stop hooks during merge', async () => {
263
+ const existingSettings = {
264
+ hooks: {
265
+ Stop: [
266
+ {
267
+ hooks: [{
268
+ type: 'command',
269
+ command: 'pf hooks reflector-check',
270
+ }],
271
+ },
272
+ {
273
+ hooks: [{
274
+ type: 'command',
275
+ command: 'pf hooks session-stop',
276
+ }],
277
+ },
278
+ ],
279
+ },
280
+ };
281
+ writeFileSync(join(testDir, '.pennyfarthing/settings.local.json'), JSON.stringify(existingSettings, null, 2));
282
+ await mergeSettingsLocalJson(testDir, assetsPath, {});
283
+ const result = JSON.parse(readFileSync(join(testDir, '.pennyfarthing/settings.local.json'), 'utf8'));
284
+ const stopCommands = result.hooks.Stop
285
+ .flatMap((e) => e.hooks?.map(h => h.command) ?? []);
286
+ const hasBareCommand = stopCommands.some((cmd) => cmd.startsWith('pf hooks') && !cmd.includes('pf.sh'));
287
+ assert.ok(!hasBareCommand, 'All bare "pf hooks" Stop commands should be migrated');
288
+ });
289
+ it('should migrate bare pf PreToolUse hooks during merge', async () => {
290
+ const existingSettings = {
291
+ hooks: {
292
+ PreToolUse: [
293
+ {
294
+ matcher: 'Edit|Write',
295
+ hooks: [{
296
+ type: 'command',
297
+ command: 'pf hooks pre-edit-check',
298
+ }],
299
+ },
300
+ {
301
+ matcher: 'Edit|Write|Bash|Task',
302
+ hooks: [{
303
+ type: 'command',
304
+ command: 'pf hooks context-breaker',
305
+ }],
306
+ },
307
+ ],
308
+ },
309
+ };
310
+ writeFileSync(join(testDir, '.pennyfarthing/settings.local.json'), JSON.stringify(existingSettings, null, 2));
311
+ await mergeSettingsLocalJson(testDir, assetsPath, {});
312
+ const result = JSON.parse(readFileSync(join(testDir, '.pennyfarthing/settings.local.json'), 'utf8'));
313
+ const preToolUseCommands = result.hooks.PreToolUse
314
+ .flatMap((e) => e.hooks?.map(h => h.command) ?? []);
315
+ const hasBareCommand = preToolUseCommands.some((cmd) => cmd.startsWith('pf hooks') && !cmd.includes('pf.sh'));
316
+ assert.ok(!hasBareCommand, 'All bare "pf hooks" PreToolUse commands should be migrated');
317
+ });
318
+ it('should migrate bare pf statusLine command during merge', async () => {
319
+ const existingSettings = {
320
+ hooks: {},
321
+ statusLine: {
322
+ type: 'command',
323
+ command: 'pf hooks statusline',
324
+ },
325
+ };
326
+ writeFileSync(join(testDir, '.pennyfarthing/settings.local.json'), JSON.stringify(existingSettings, null, 2));
327
+ await mergeSettingsLocalJson(testDir, assetsPath, {});
328
+ const result = JSON.parse(readFileSync(join(testDir, '.pennyfarthing/settings.local.json'), 'utf8'));
329
+ assert.ok(result.statusLine.command.includes('pf.sh hooks statusline'), `statusLine should use pf.sh wrapper, got: ${result.statusLine.command}`);
330
+ assert.ok(result.statusLine.command !== 'pf hooks statusline', 'Bare "pf hooks statusline" should be migrated');
331
+ });
332
+ });
333
+ // =============================================================================
334
+ // AC3: Existing hook functionality preserved
335
+ // =============================================================================
336
+ describe('117-2 AC3: Existing hook functionality preserved', () => {
337
+ it('should still migrate .sh file hooks to pf.sh wrapper (legacy migration)', () => {
338
+ const hooks = [
339
+ makeHookEntry('session-start.sh'),
340
+ makeHookEntry('bell-mode-hook.sh'),
341
+ ];
342
+ const migrated = migrateHookPaths(hooks);
343
+ assert.ok(migrated, 'Should migrate .sh hooks');
344
+ assert.strictEqual(hooks[0].hooks[0].command, `${PF_SH_PREFIX} hooks session-start`, '.sh file should migrate to pf.sh wrapper');
345
+ assert.strictEqual(hooks[1].hooks[0].command, `${PF_SH_PREFIX} hooks bell-mode`, '.sh file should migrate to pf.sh wrapper');
346
+ });
347
+ it('should still migrate legacy directory paths', () => {
348
+ const hooks = [
349
+ makeHookEntry('.claude/pennyfarthing/scripts/custom-script.sh'),
350
+ ];
351
+ const migrated = migrateHookPaths(hooks);
352
+ assert.ok(migrated, 'Should migrate legacy paths');
353
+ assert.strictEqual(hooks[0].hooks[0].command, '.pennyfarthing/scripts/custom-script.sh', 'Legacy directory path should be migrated');
354
+ });
355
+ it('should handle mixed .sh, bare pf, and pf.sh commands in same array', () => {
356
+ const hooks = [
357
+ makeHookEntry('session-start.sh'), // legacy .sh
358
+ makeHookEntry('pf hooks bell-mode'), // bare pf
359
+ makeHookEntry(`${PF_SH_PREFIX} hooks context-breaker`), // already correct
360
+ makeHookEntry('.pennyfarthing/project/hooks/setup-env.sh'), // custom, leave alone
361
+ ];
362
+ const migrated = migrateHookPaths(hooks);
363
+ assert.ok(migrated, 'Should report migration for .sh and bare pf');
364
+ assert.strictEqual(hooks[0].hooks[0].command, `${PF_SH_PREFIX} hooks session-start`, '.sh file migrated');
365
+ assert.strictEqual(hooks[1].hooks[0].command, `${PF_SH_PREFIX} hooks bell-mode`, 'bare pf migrated');
366
+ assert.strictEqual(hooks[2].hooks[0].command, `${PF_SH_PREFIX} hooks context-breaker`, 'already correct left alone');
367
+ assert.strictEqual(hooks[3].hooks[0].command, '.pennyfarthing/project/hooks/setup-env.sh', 'custom hook left alone');
368
+ });
369
+ it('should not produce duplicate hooks after migration', () => {
370
+ // Consumer has BOTH bare pf and pf.sh for the same hook
371
+ const hooks = [
372
+ makeHookEntry('pf hooks session-start'),
373
+ makeHookEntry(`${PF_SH_PREFIX} hooks session-start`),
374
+ ];
375
+ migrateHookPaths(hooks);
376
+ // After migration, both should point to the same command
377
+ // The deduplication happens at a higher level, but migration should still work
378
+ assert.strictEqual(hooks[0].hooks[0].command, `${PF_SH_PREFIX} hooks session-start`);
379
+ assert.strictEqual(hooks[1].hooks[0].command, `${PF_SH_PREFIX} hooks session-start`);
380
+ });
381
+ });
382
+ //# sourceMappingURL=settings-hook-migration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-hook-migration.test.js","sourceRoot":"","sources":["../../../src/cli/utils/settings-hook-migration.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Tests for Story 117-2: Generate hook commands with pf.sh wrapper path, not bare pf
3
+ *
4
+ * The pf CLI is only available via `uv run` or the pf.sh wrapper script.
5
+ * Consumer installs don't have `pf` on PATH. All generated hook commands
6
+ * must use the wrapper: "$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh
7
+ *
8
+ * Acceptance Criteria:
9
+ * AC1: Hook generation uses pf.sh wrapper path instead of bare `pf`
10
+ * AC2: Existing bare `pf` hooks in settings.local.json are migrated to wrapper path
11
+ * AC3: Tests validate the correct path is generated
12
+ *
13
+ * Run with: cd packages/core && pnpm build && node --test dist/cli/utils/settings-pf-wrapper.test.js
14
+ */
15
+ export {};
16
+ //# sourceMappingURL=settings-pf-wrapper.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-pf-wrapper.test.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/settings-pf-wrapper.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG"}