@pennyfarthing/core 8.0.4 → 9.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 (141) hide show
  1. package/README.md +3 -3
  2. package/package.json +3 -3
  3. package/pennyfarthing-dist/agents/README.md +1 -1
  4. package/pennyfarthing-dist/agents/dev.md +1 -1
  5. package/pennyfarthing-dist/agents/handoff.md +1 -1
  6. package/pennyfarthing-dist/agents/reviewer-preflight.md +1 -1
  7. package/pennyfarthing-dist/agents/reviewer.md +21 -4
  8. package/pennyfarthing-dist/agents/sm-setup.md +3 -3
  9. package/pennyfarthing-dist/agents/sm.md +11 -1
  10. package/pennyfarthing-dist/agents/tea.md +1 -1
  11. package/pennyfarthing-dist/agents/testing-runner.md +3 -3
  12. package/pennyfarthing-dist/commands/architect.md +3 -1
  13. package/pennyfarthing-dist/commands/continue-session.md +2 -2
  14. package/pennyfarthing-dist/commands/dev.md +3 -1
  15. package/pennyfarthing-dist/commands/devops.md +3 -1
  16. package/pennyfarthing-dist/commands/health-check.md +3 -1
  17. package/pennyfarthing-dist/commands/new-work.md +23 -0
  18. package/pennyfarthing-dist/commands/orchestrator.md +3 -1
  19. package/pennyfarthing-dist/commands/parallel-work.md +6 -4
  20. package/pennyfarthing-dist/commands/pm.md +3 -1
  21. package/pennyfarthing-dist/commands/prime.md +18 -22
  22. package/pennyfarthing-dist/commands/reviewer.md +3 -1
  23. package/pennyfarthing-dist/commands/set-theme.md +1 -1
  24. package/pennyfarthing-dist/commands/sm.md +3 -1
  25. package/pennyfarthing-dist/commands/sprint.md +13 -4
  26. package/pennyfarthing-dist/commands/tea.md +3 -1
  27. package/pennyfarthing-dist/commands/tech-writer.md +3 -1
  28. package/pennyfarthing-dist/commands/ux-designer.md +3 -1
  29. package/pennyfarthing-dist/commands/work.md +4 -2
  30. package/pennyfarthing-dist/guides/agent-behavior.md +36 -257
  31. package/pennyfarthing-dist/personas/themes/rome.yaml +11 -11
  32. package/pennyfarthing-dist/scripts/core/agent-session.sh +7 -0
  33. package/pennyfarthing-dist/scripts/core/check-context.sh +140 -226
  34. package/pennyfarthing-dist/scripts/core/handoff-marker.sh +13 -2
  35. package/pennyfarthing-dist/scripts/git/worktree-manager.sh +4 -1
  36. package/pennyfarthing-dist/scripts/health/drift-detection.sh +1 -7
  37. package/pennyfarthing-dist/scripts/hooks/post-merge.sh +4 -11
  38. package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +3 -8
  39. package/pennyfarthing-dist/scripts/hooks/pre-push.sh +3 -3
  40. package/pennyfarthing-dist/scripts/jira/create-jira-epic.sh +1 -7
  41. package/pennyfarthing-dist/scripts/jira/create-jira-story.sh +2 -8
  42. package/pennyfarthing-dist/scripts/jira/jira-reconcile.sh +2 -8
  43. package/pennyfarthing-dist/scripts/lib/find-root.sh +17 -45
  44. package/pennyfarthing-dist/scripts/maintenance/sidecar-health.sh +1 -7
  45. package/pennyfarthing-dist/scripts/sprint/archive-story.sh +2 -8
  46. package/pennyfarthing-dist/scripts/sprint/available-stories.sh +2 -8
  47. package/pennyfarthing-dist/scripts/sprint/check-story.sh +2 -8
  48. package/pennyfarthing-dist/scripts/sprint/get-epic-field.sh +2 -8
  49. package/pennyfarthing-dist/scripts/sprint/get-story-field.sh +2 -8
  50. package/pennyfarthing-dist/scripts/sprint/list-future.sh +2 -8
  51. package/pennyfarthing-dist/scripts/sprint/new-sprint.sh +2 -8
  52. package/pennyfarthing-dist/scripts/sprint/promote-epic.sh +2 -8
  53. package/pennyfarthing-dist/scripts/sprint/sprint-info.sh +2 -8
  54. package/pennyfarthing-dist/scripts/tests/test-character-voice.sh +2 -1
  55. package/pennyfarthing-dist/scripts/workflow/finish-story.sh +4 -9
  56. package/pennyfarthing-dist/scripts/workflow/fix-session-phase.sh +2 -8
  57. package/pennyfarthing-dist/scripts/workflow/list-workflows.sh +2 -8
  58. package/pennyfarthing-dist/scripts/workflow/phase-owner.sh +1 -7
  59. package/pennyfarthing-dist/scripts/workflow/resume-workflow.sh +2 -8
  60. package/pennyfarthing-dist/scripts/workflow/show-workflow.sh +2 -8
  61. package/pennyfarthing-dist/scripts/workflow/start-workflow.sh +2 -8
  62. package/pennyfarthing-dist/scripts/workflow/workflow-status.sh +2 -8
  63. package/pennyfarthing-dist/skills/dev-patterns/SKILL.md +1 -1
  64. package/pennyfarthing-dist/skills/jira/SKILL.md +48 -24
  65. package/pennyfarthing-dist/skills/sprint/scripts/sync-epic-jira.sh +7 -0
  66. package/pennyfarthing-dist/skills/sprint/skill.md +30 -30
  67. package/pennyfarthing-dist/workflows/patch.yaml +68 -0
  68. package/pennyfarthing_scripts/__pycache__/__init__.cpython-314.pyc +0 -0
  69. package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
  70. package/pennyfarthing_scripts/cli.py +168 -0
  71. package/pennyfarthing_scripts/common/__pycache__/__init__.cpython-314.pyc +0 -0
  72. package/pennyfarthing_scripts/common/__pycache__/config.cpython-314.pyc +0 -0
  73. package/pennyfarthing_scripts/common/__pycache__/output.cpython-314.pyc +0 -0
  74. package/pennyfarthing_scripts/context.py +414 -0
  75. package/pennyfarthing_scripts/patch_mode.py +449 -0
  76. package/pennyfarthing_scripts/prime/__pycache__/__init__.cpython-314.pyc +0 -0
  77. package/pennyfarthing_scripts/prime/__pycache__/cli.cpython-314.pyc +0 -0
  78. package/pennyfarthing_scripts/prime/__pycache__/loader.cpython-314.pyc +0 -0
  79. package/pennyfarthing_scripts/prime/__pycache__/models.cpython-314.pyc +0 -0
  80. package/pennyfarthing_scripts/prime/__pycache__/persona.cpython-314.pyc +0 -0
  81. package/pennyfarthing_scripts/prime/__pycache__/session.cpython-314.pyc +0 -0
  82. package/pennyfarthing_scripts/prime/__pycache__/tiers.cpython-314.pyc +0 -0
  83. package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
  84. package/pennyfarthing_scripts/prime/cli.py +209 -1
  85. package/pennyfarthing_scripts/prime/models.py +9 -0
  86. package/pennyfarthing_scripts/prime/persona.py +41 -0
  87. package/pennyfarthing_scripts/prime/tiers.py +201 -0
  88. package/pennyfarthing_scripts/sprint/__pycache__/__init__.cpython-314.pyc +0 -0
  89. package/pennyfarthing_scripts/sprint/__pycache__/__main__.cpython-314.pyc +0 -0
  90. package/pennyfarthing_scripts/sprint/__pycache__/archive.cpython-314.pyc +0 -0
  91. package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
  92. package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
  93. package/pennyfarthing_scripts/sprint/__pycache__/status.cpython-314.pyc +0 -0
  94. package/pennyfarthing_scripts/sprint/__pycache__/work.cpython-314.pyc +0 -0
  95. package/pennyfarthing_scripts/sprint/cli.py +144 -84
  96. package/pennyfarthing_scripts/tests/test_patch_mode.py +830 -0
  97. package/pennyfarthing_scripts/tests/test_tiers.py +1090 -0
  98. package/pennyfarthing_scripts/tests/test_token_counting.py +559 -0
  99. package/pennyfarthing_scripts/tests/test_workflow_check.py +341 -0
  100. package/pennyfarthing_scripts/workflow.py +104 -0
  101. package/pennyfarthing-dist/scripts/hooks/__pycache__/question_reflector_check.cpython-314.pyc +0 -0
  102. package/pennyfarthing_scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  103. package/pennyfarthing_scripts/__pycache__/config.cpython-314.pyc +0 -0
  104. package/pennyfarthing_scripts/__pycache__/jira.cpython-314.pyc +0 -0
  105. package/pennyfarthing_scripts/__pycache__/jira_epic_creation.cpython-314.pyc +0 -0
  106. package/pennyfarthing_scripts/__pycache__/jira_sync.cpython-314.pyc +0 -0
  107. package/pennyfarthing_scripts/__pycache__/jira_sync_story.cpython-314.pyc +0 -0
  108. package/pennyfarthing_scripts/__pycache__/sprint.cpython-314.pyc +0 -0
  109. package/pennyfarthing_scripts/__pycache__/workflow.cpython-311.pyc +0 -0
  110. package/pennyfarthing_scripts/__pycache__/workflow.cpython-314.pyc +0 -0
  111. package/pennyfarthing_scripts/brownfield/__pycache__/__init__.cpython-314.pyc +0 -0
  112. package/pennyfarthing_scripts/brownfield/__pycache__/__main__.cpython-314.pyc +0 -0
  113. package/pennyfarthing_scripts/brownfield/__pycache__/cli.cpython-314.pyc +0 -0
  114. package/pennyfarthing_scripts/brownfield/__pycache__/discover.cpython-314.pyc +0 -0
  115. package/pennyfarthing_scripts/git/__pycache__/__init__.cpython-314.pyc +0 -0
  116. package/pennyfarthing_scripts/git/__pycache__/create_branches.cpython-314.pyc +0 -0
  117. package/pennyfarthing_scripts/git/__pycache__/status_all.cpython-314.pyc +0 -0
  118. package/pennyfarthing_scripts/jira/__pycache__/__init__.cpython-314.pyc +0 -0
  119. package/pennyfarthing_scripts/jira/__pycache__/__main__.cpython-314.pyc +0 -0
  120. package/pennyfarthing_scripts/jira/__pycache__/bidirectional.cpython-314.pyc +0 -0
  121. package/pennyfarthing_scripts/jira/__pycache__/claim.cpython-314.pyc +0 -0
  122. package/pennyfarthing_scripts/jira/__pycache__/cli.cpython-314.pyc +0 -0
  123. package/pennyfarthing_scripts/jira/__pycache__/client.cpython-314.pyc +0 -0
  124. package/pennyfarthing_scripts/jira/__pycache__/compat.cpython-314.pyc +0 -0
  125. package/pennyfarthing_scripts/jira/__pycache__/epic.cpython-314.pyc +0 -0
  126. package/pennyfarthing_scripts/jira/__pycache__/mappings.cpython-314.pyc +0 -0
  127. package/pennyfarthing_scripts/jira/__pycache__/models.cpython-314.pyc +0 -0
  128. package/pennyfarthing_scripts/jira/__pycache__/story.cpython-314.pyc +0 -0
  129. package/pennyfarthing_scripts/jira/__pycache__/sync.cpython-314.pyc +0 -0
  130. package/pennyfarthing_scripts/preflight/__pycache__/__init__.cpython-314.pyc +0 -0
  131. package/pennyfarthing_scripts/preflight/__pycache__/__main__.cpython-314.pyc +0 -0
  132. package/pennyfarthing_scripts/preflight/__pycache__/cli.cpython-314.pyc +0 -0
  133. package/pennyfarthing_scripts/preflight/__pycache__/finish.cpython-314.pyc +0 -0
  134. package/pennyfarthing_scripts/prime/__pycache__/__main__.cpython-314.pyc +0 -0
  135. package/pennyfarthing_scripts/sprint/__pycache__/validator.cpython-314.pyc +0 -0
  136. package/pennyfarthing_scripts/tests/__pycache__/__init__.cpython-314.pyc +0 -0
  137. package/pennyfarthing_scripts/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
  138. package/pennyfarthing_scripts/tests/__pycache__/test_brownfield.cpython-314-pytest-9.0.2.pyc +0 -0
  139. package/pennyfarthing_scripts/tests/__pycache__/test_git_utils.cpython-314-pytest-9.0.2.pyc +0 -0
  140. package/pennyfarthing_scripts/tests/__pycache__/test_prime.cpython-314-pytest-9.0.2.pyc +0 -0
  141. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_validator.cpython-314-pytest-9.0.2.pyc +0 -0
@@ -1,61 +1,33 @@
1
1
  #!/usr/bin/env bash
2
- # Shared utility: Find project root via BASH_SOURCE self-location
3
- #
4
- # Core principle: Scripts derive paths from their own location, not from $PWD.
5
- # A script in pennyfarthing-dist/scripts/<category>/ is always 3 levels below
6
- # the package root. This works regardless of where the user invoked it from.
2
+ # Shared utility: Find project root by walking up to .pennyfarthing/
7
3
  #
8
4
  # Usage:
9
- # SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
10
5
  # source "$SCRIPT_DIR/../lib/find-root.sh"
11
6
  # # PROJECT_ROOT is now set
7
+ #
8
+ # Or simply:
9
+ # source /path/to/find-root.sh
10
+ # # PROJECT_ROOT is now set
12
11
 
13
- # Require SCRIPT_DIR to be set by caller
14
- if [[ -z "${SCRIPT_DIR:-}" ]]; then
15
- echo "Error: SCRIPT_DIR must be set before sourcing find-root.sh" >&2
16
- echo "Add this before sourcing:" >&2
17
- echo ' SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"' >&2
18
- exit 1
19
- fi
20
-
21
- # Allow explicit override via PENNYFARTHING_ROOT (avoids conflict with orchestrator's PROJECT_ROOT)
22
- if [[ -n "${PENNYFARTHING_ROOT:-}" ]]; then
23
- PROJECT_ROOT="$PENNYFARTHING_ROOT"
12
+ # Allow explicit override
13
+ if [[ -n "${PROJECT_ROOT:-}" ]]; then
24
14
  export PROJECT_ROOT
25
15
  return 0 2>/dev/null || exit 0
26
16
  fi
27
17
 
28
- # Resolve SCRIPT_DIR (follow symlinks to real location)
29
- _real_script_dir="$(cd "$SCRIPT_DIR" && pwd -P)"
30
-
31
- # Derive package root from script location
32
- # Scripts can be in:
33
- # - pennyfarthing-dist/scripts/<category>/ (npm package or framework dev)
34
- # - .pennyfarthing/scripts/<category>/ (consumer project after init)
35
- if [[ "$_real_script_dir" == */pennyfarthing-dist/scripts/* ]]; then
36
- # Extract everything before /pennyfarthing-dist/scripts/
37
- _pkg_root="${_real_script_dir%/pennyfarthing-dist/scripts/*}"
18
+ # Walk up from PWD looking for .pennyfarthing/
19
+ _d="$PWD"
20
+ while [[ ! -d "$_d/.pennyfarthing" ]] && [[ "$_d" != "/" ]]; do
21
+ _d="$(dirname "$_d")"
22
+ done
38
23
 
39
- # Determine context: framework dev or consumer?
40
- # Check node_modules FIRST - if we're in node_modules, we're always a consumer
41
- if [[ "$_pkg_root" == */node_modules/* ]]; then
42
- # Consumer context: walk up from node_modules to project
43
- PROJECT_ROOT="${_pkg_root%/node_modules/*}"
44
- elif [[ -d "$_pkg_root/pennyfarthing-dist" && ! -L "$_pkg_root/pennyfarthing-dist" ]]; then
45
- # Framework context: package root IS project root
46
- PROJECT_ROOT="$_pkg_root"
47
- else
48
- PROJECT_ROOT="$_pkg_root"
49
- fi
50
- elif [[ "$_real_script_dir" == */.pennyfarthing/scripts/* ]]; then
51
- # Consumer project context: scripts copied to .pennyfarthing/scripts/
52
- # Extract everything before /.pennyfarthing/scripts/
53
- PROJECT_ROOT="${_real_script_dir%/.pennyfarthing/scripts/*}"
24
+ if [[ -d "$_d/.pennyfarthing" ]]; then
25
+ PROJECT_ROOT="$_d"
54
26
  else
55
- echo "Error: Script not in expected location (pennyfarthing-dist/scripts/ or .pennyfarthing/scripts/)" >&2
56
- echo "SCRIPT_DIR=$_real_script_dir" >&2
27
+ echo "Error: Could not find .pennyfarthing/ directory" >&2
28
+ echo "Are you in a Pennyfarthing-enabled project?" >&2
57
29
  exit 1
58
30
  fi
59
31
 
60
- unset _real_script_dir _pkg_root
32
+ unset _d
61
33
  export PROJECT_ROOT
@@ -12,13 +12,7 @@
12
12
  set -euo pipefail
13
13
 
14
14
  # Find project root
15
- if [[ -z "${PROJECT_ROOT:-}" ]]; then
16
- d="$PWD"
17
- while [[ ! -d "$d/.pennyfarthing" ]] && [[ "$d" != "/" ]]; do
18
- d="$(dirname "$d")"
19
- done
20
- PROJECT_ROOT="$d"
21
- fi
15
+ source "$(dirname "${BASH_SOURCE[0]}")/../lib/find-root.sh"
22
16
 
23
17
  SIDECAR_DIR="$PROJECT_ROOT/.pennyfarthing/sidecars"
24
18
  FIX_MODE="${1:-}"
@@ -40,14 +40,8 @@ if [[ -z "$STORY_ID" ]]; then
40
40
  exit 1
41
41
  fi
42
42
 
43
- # PROJECT_ROOT should be set by find-root.sh, but find it if not
44
- if [[ -z "${PROJECT_ROOT:-}" ]]; then
45
- d="$PWD"
46
- while [[ ! -d "$d/.pennyfarthing" ]] && [[ "$d" != "/" ]]; do
47
- d="$(dirname "$d")"
48
- done
49
- PROJECT_ROOT="$d"
50
- fi
43
+ # Find project root
44
+ source "$(dirname "${BASH_SOURCE[0]}")/../lib/find-root.sh"
51
45
 
52
46
  SPRINT_FILE="$PROJECT_ROOT/sprint/current-sprint.yaml"
53
47
 
@@ -6,14 +6,8 @@
6
6
 
7
7
  set -euo pipefail
8
8
 
9
- # PROJECT_ROOT should be set by find-root.sh, but find it if not
10
- if [[ -z "${PROJECT_ROOT:-}" ]]; then
11
- d="$PWD"
12
- while [[ ! -d "$d/.pennyfarthing" ]] && [[ "$d" != "/" ]]; do
13
- d="$(dirname "$d")"
14
- done
15
- PROJECT_ROOT="$d"
16
- fi
9
+ # Find project root
10
+ source "$(dirname "${BASH_SOURCE[0]}")/../lib/find-root.sh"
17
11
 
18
12
  SPRINT_FILE="$PROJECT_ROOT/sprint/current-sprint.yaml"
19
13
 
@@ -18,14 +18,8 @@ if [[ -z "$ID" ]]; then
18
18
  exit 1
19
19
  fi
20
20
 
21
- # PROJECT_ROOT should be set by find-root.sh
22
- if [[ -z "${PROJECT_ROOT:-}" ]]; then
23
- d="$PWD"
24
- while [[ ! -d "$d/.pennyfarthing" ]] && [[ "$d" != "/" ]]; do
25
- d="$(dirname "$d")"
26
- done
27
- PROJECT_ROOT="$d"
28
- fi
21
+ # Find project root
22
+ source "$(dirname "${BASH_SOURCE[0]}")/../lib/find-root.sh"
29
23
 
30
24
  SPRINT_FILE="$PROJECT_ROOT/sprint/current-sprint.yaml"
31
25
 
@@ -32,14 +32,8 @@ if [[ ! "$EPIC_ID" =~ ^epic- ]]; then
32
32
  EPIC_ID="epic-$EPIC_ID"
33
33
  fi
34
34
 
35
- # PROJECT_ROOT should be set by find-root.sh
36
- if [[ -z "${PROJECT_ROOT:-}" ]]; then
37
- d="$PWD"
38
- while [[ ! -d "$d/.pennyfarthing" ]] && [[ "$d" != "/" ]]; do
39
- d="$(dirname "$d")"
40
- done
41
- PROJECT_ROOT="$d"
42
- fi
35
+ # Find project root
36
+ source "$(dirname "${BASH_SOURCE[0]}")/../lib/find-root.sh"
43
37
 
44
38
  SPRINT_FILE="$PROJECT_ROOT/sprint/current-sprint.yaml"
45
39
 
@@ -27,14 +27,8 @@ if [[ -z "$STORY_ID" || -z "$FIELD" ]]; then
27
27
  exit 1
28
28
  fi
29
29
 
30
- # PROJECT_ROOT should be set by find-root.sh
31
- if [[ -z "${PROJECT_ROOT:-}" ]]; then
32
- d="$PWD"
33
- while [[ ! -d "$d/.pennyfarthing" ]] && [[ "$d" != "/" ]]; do
34
- d="$(dirname "$d")"
35
- done
36
- PROJECT_ROOT="$d"
37
- fi
30
+ # Find project root
31
+ source "$(dirname "${BASH_SOURCE[0]}")/../lib/find-root.sh"
38
32
 
39
33
  SPRINT_FILE="$PROJECT_ROOT/sprint/current-sprint.yaml"
40
34
 
@@ -7,14 +7,8 @@
7
7
 
8
8
  set -euo pipefail
9
9
 
10
- # PROJECT_ROOT should be set by find-root.sh, but find it if not
11
- if [[ -z "${PROJECT_ROOT:-}" ]]; then
12
- d="$PWD"
13
- while [[ ! -d "$d/.pennyfarthing" ]] && [[ "$d" != "/" ]]; do
14
- d="$(dirname "$d")"
15
- done
16
- PROJECT_ROOT="$d"
17
- fi
10
+ # Find project root
11
+ source "$(dirname "${BASH_SOURCE[0]}")/../lib/find-root.sh"
18
12
 
19
13
  FUTURE_FILE="$PROJECT_ROOT/sprint/future.yaml"
20
14
 
@@ -27,14 +27,8 @@ if [[ -z "$SPRINT_YYWW" || -z "$JIRA_ID" || -z "$START_DATE" || -z "$END_DATE" |
27
27
  exit 1
28
28
  fi
29
29
 
30
- # PROJECT_ROOT should be set by find-root.sh, but find it if not
31
- if [[ -z "${PROJECT_ROOT:-}" ]]; then
32
- d="$PWD"
33
- while [[ ! -d "$d/.pennyfarthing" ]] && [[ "$d" != "/" ]]; do
34
- d="$(dirname "$d")"
35
- done
36
- PROJECT_ROOT="$d"
37
- fi
30
+ # Find project root
31
+ source "$(dirname "${BASH_SOURCE[0]}")/../lib/find-root.sh"
38
32
  TEMPLATE_FILE="$PROJECT_ROOT/sprint/sprint-template.yaml"
39
33
  SPRINT_FILE="$PROJECT_ROOT/sprint/current-sprint.yaml"
40
34
  ARCHIVE_FILE="$PROJECT_ROOT/sprint/archive/sprint-${SPRINT_YYWW}-completed.yaml"
@@ -21,14 +21,8 @@ if [[ -z "$EPIC_ID" ]]; then
21
21
  exit 1
22
22
  fi
23
23
 
24
- # PROJECT_ROOT should be set by find-root.sh, but find it if not
25
- if [[ -z "${PROJECT_ROOT:-}" ]]; then
26
- d="$PWD"
27
- while [[ ! -d "$d/.pennyfarthing" ]] && [[ "$d" != "/" ]]; do
28
- d="$(dirname "$d")"
29
- done
30
- PROJECT_ROOT="$d"
31
- fi
24
+ # Find project root
25
+ source "$(dirname "${BASH_SOURCE[0]}")/../lib/find-root.sh"
32
26
 
33
27
  FUTURE_FILE="$PROJECT_ROOT/sprint/future.yaml"
34
28
  SPRINT_FILE="$PROJECT_ROOT/sprint/current-sprint.yaml"
@@ -6,14 +6,8 @@
6
6
 
7
7
  set -euo pipefail
8
8
 
9
- # PROJECT_ROOT should be set by find-root.sh
10
- if [[ -z "${PROJECT_ROOT:-}" ]]; then
11
- d="$PWD"
12
- while [[ ! -d "$d/.pennyfarthing" ]] && [[ "$d" != "/" ]]; do
13
- d="$(dirname "$d")"
14
- done
15
- PROJECT_ROOT="$d"
16
- fi
9
+ # Find project root
10
+ source "$(dirname "${BASH_SOURCE[0]}")/../lib/find-root.sh"
17
11
 
18
12
  SPRINT_FILE="$PROJECT_ROOT/sprint/current-sprint.yaml"
19
13
 
@@ -28,8 +28,9 @@ mkdir -p "$TEST_DIR/scripts"
28
28
 
29
29
  # Copy core scripts (including agent-session.sh)
30
30
  mkdir -p "$TEST_DIR/scripts/core"
31
+ mkdir -p "$TEST_DIR/scripts/lib"
31
32
  cp "$PROJECT_ROOT/scripts/core/agent-session.sh" "$TEST_DIR/scripts/core/"
32
- cp "$PROJECT_ROOT/scripts/core/run.sh" "$TEST_DIR/scripts/"
33
+ cp "$PROJECT_ROOT/scripts/lib/find-root.sh" "$TEST_DIR/scripts/lib/"
33
34
 
34
35
  # Create minimal theme file
35
36
  cat > "$TEST_DIR/.pennyfarthing/personas/themes/test-theme.yaml" << 'EOF'
@@ -40,14 +40,8 @@ if [[ -z "$STORY_ID" ]]; then
40
40
  exit 1
41
41
  fi
42
42
 
43
- # PROJECT_ROOT should be set by find-root.sh, but find it if not
44
- if [[ -z "${PROJECT_ROOT:-}" ]]; then
45
- d="$PWD"
46
- while [[ ! -d "$d/.pennyfarthing" ]] && [[ "$d" != "/" ]]; do
47
- d="$(dirname "$d")"
48
- done
49
- PROJECT_ROOT="$d"
50
- fi
43
+ # Find project root
44
+ source "$(dirname "${BASH_SOURCE[0]}")/../lib/find-root.sh"
51
45
 
52
46
  SESSION_FILE="$PROJECT_ROOT/.session/${STORY_ID}-session.md"
53
47
  SPRINT_FILE="$PROJECT_ROOT/sprint/current-sprint.yaml"
@@ -63,7 +57,8 @@ if [[ ! -f "$SESSION_FILE" ]]; then
63
57
  fi
64
58
 
65
59
  # Extract metadata from session file (handle both "**Jira:**" and "- **Jira:**" formats)
66
- JIRA_KEY=$(grep -E '\*\*Jira:\*\*' "$SESSION_FILE" | sed 's/.*\*\*Jira:\*\* //' | tr -d ' ' || echo "")
60
+ # Also handle markdown link format: [MSSCI-12721](https://...)
61
+ JIRA_KEY=$(grep -E '\*\*Jira:\*\*' "$SESSION_FILE" | sed 's/.*\*\*Jira:\*\* //' | sed 's/\[//' | sed 's/\].*//' | tr -d ' ' || echo "")
67
62
  # Extract branch - strip any trailing annotations like "(pushed)"
68
63
  BRANCH=$(grep -E '\*\*Branch:\*\*' "$SESSION_FILE" | sed 's/.*\*\*Branch:\*\* //' | sed 's/ *(.*//' | tr -d ' ' || echo "")
69
64
 
@@ -46,14 +46,8 @@ if [[ -z "$STORY_ID" ]] || [[ -z "$TARGET_PHASE" ]]; then
46
46
  exit 1
47
47
  fi
48
48
 
49
- # PROJECT_ROOT should be set by find-root.sh, but find it if not
50
- if [[ -z "${PROJECT_ROOT:-}" ]]; then
51
- d="$PWD"
52
- while [[ ! -d "$d/.pennyfarthing" ]] && [[ "$d" != "/" ]]; do
53
- d="$(dirname "$d")"
54
- done
55
- PROJECT_ROOT="$d"
56
- fi
49
+ # Find project root
50
+ source "$(dirname "${BASH_SOURCE[0]}")/../lib/find-root.sh"
57
51
 
58
52
  # Try multiple session file naming patterns
59
53
  SESSION_FILE=""
@@ -7,14 +7,8 @@
7
7
 
8
8
  set -euo pipefail
9
9
 
10
- # PROJECT_ROOT should be set by find-root.sh, but find it if not
11
- if [[ -z "${PROJECT_ROOT:-}" ]]; then
12
- d="$PWD"
13
- while [[ ! -d "$d/.pennyfarthing" ]] && [[ "$d" != "/" ]]; do
14
- d="$(dirname "$d")"
15
- done
16
- PROJECT_ROOT="$d"
17
- fi
10
+ # Find project root
11
+ source "$(dirname "${BASH_SOURCE[0]}")/../lib/find-root.sh"
18
12
 
19
13
  WORKFLOWS_DIR="$PROJECT_ROOT/.pennyfarthing/workflows"
20
14
 
@@ -14,13 +14,7 @@ if [[ -z "$WORKFLOW" || -z "$PHASE" ]]; then
14
14
  fi
15
15
 
16
16
  # Find project root
17
- if [[ -z "${PROJECT_ROOT:-}" ]]; then
18
- d="$PWD"
19
- while [[ ! -d "$d/.pennyfarthing" ]] && [[ "$d" != "/" ]]; do
20
- d="$(dirname "$d")"
21
- done
22
- PROJECT_ROOT="$d"
23
- fi
17
+ source "$(dirname "${BASH_SOURCE[0]}")/../lib/find-root.sh"
24
18
 
25
19
  WORKFLOW_FILE="$PROJECT_ROOT/.pennyfarthing/workflows/${WORKFLOW}.yaml"
26
20
 
@@ -6,14 +6,8 @@
6
6
 
7
7
  set -euo pipefail
8
8
 
9
- # PROJECT_ROOT should be set by find-root.sh, but find it if not
10
- if [[ -z "${PROJECT_ROOT:-}" ]]; then
11
- d="$PWD"
12
- while [[ ! -d "$d/.pennyfarthing" ]] && [[ "$d" != "/" ]]; do
13
- d="$(dirname "$d")"
14
- done
15
- PROJECT_ROOT="$d"
16
- fi
9
+ # Find project root
10
+ source "$(dirname "${BASH_SOURCE[0]}")/../lib/find-root.sh"
17
11
 
18
12
  WORKFLOWS_DIR="$PROJECT_ROOT/.pennyfarthing/workflows"
19
13
  SESSION_DIR="$PROJECT_ROOT/.session"
@@ -8,14 +8,8 @@
8
8
 
9
9
  set -euo pipefail
10
10
 
11
- # PROJECT_ROOT should be set by find-root.sh, but find it if not
12
- if [[ -z "${PROJECT_ROOT:-}" ]]; then
13
- d="$PWD"
14
- while [[ ! -d "$d/.pennyfarthing" ]] && [[ "$d" != "/" ]]; do
15
- d="$(dirname "$d")"
16
- done
17
- PROJECT_ROOT="$d"
18
- fi
11
+ # Find project root
12
+ source "$(dirname "${BASH_SOURCE[0]}")/../lib/find-root.sh"
19
13
 
20
14
  WORKFLOWS_DIR="$PROJECT_ROOT/.pennyfarthing/workflows"
21
15
  SESSION_DIR="$PROJECT_ROOT/.session"
@@ -7,14 +7,8 @@
7
7
 
8
8
  set -euo pipefail
9
9
 
10
- # PROJECT_ROOT should be set by find-root.sh, but find it if not
11
- if [[ -z "${PROJECT_ROOT:-}" ]]; then
12
- d="$PWD"
13
- while [[ ! -d "$d/.pennyfarthing" ]] && [[ "$d" != "/" ]]; do
14
- d="$(dirname "$d")"
15
- done
16
- PROJECT_ROOT="$d"
17
- fi
10
+ # Find project root
11
+ source "$(dirname "${BASH_SOURCE[0]}")/../lib/find-root.sh"
18
12
 
19
13
  WORKFLOWS_DIR="$PROJECT_ROOT/.pennyfarthing/workflows"
20
14
  SESSION_DIR="$PROJECT_ROOT/.session"
@@ -6,14 +6,8 @@
6
6
 
7
7
  set -euo pipefail
8
8
 
9
- # PROJECT_ROOT should be set by find-root.sh, but find it if not
10
- if [[ -z "${PROJECT_ROOT:-}" ]]; then
11
- d="$PWD"
12
- while [[ ! -d "$d/.pennyfarthing" ]] && [[ "$d" != "/" ]]; do
13
- d="$(dirname "$d")"
14
- done
15
- PROJECT_ROOT="$d"
16
- fi
9
+ # Find project root
10
+ source "$(dirname "${BASH_SOURCE[0]}")/../lib/find-root.sh"
17
11
 
18
12
  WORKFLOWS_DIR="$PROJECT_ROOT/.pennyfarthing/workflows"
19
13
  SESSION_DIR="$PROJECT_ROOT/.session"
@@ -381,7 +381,7 @@ TaskOutput tool:
381
381
  Use the background task tracking utilities to manage session file entries:
382
382
 
383
383
  ```bash
384
- source $CLAUDE_PROJECT_DIR/scripts/utils/background-tasks.sh
384
+ source $CLAUDE_PROJECT_DIR/.pennyfarthing/scripts/lib/background-tasks.sh
385
385
  SESSION_FILE="$CLAUDE_PROJECT_DIR/.session/${STORY_ID}-session.md"
386
386
 
387
387
  # After spawning background task, record it:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: jira
3
3
  description: Jira CLI commands for sprint management. Use when viewing, assigning, or updating Jira issues from the command line.
4
- args: "[view|claim|move|assign|create|sync|reconcile|link|search|sprint]"
4
+ args: "[view|check|claim|move|assign|create|sync|reconcile|link|search|sprint]"
5
5
  ---
6
6
 
7
7
  # /jira - Jira Issue Management
@@ -34,36 +34,60 @@ jira issue view MSSCI-12038 --raw
34
34
 
35
35
  ---
36
36
 
37
- ### `/jira claim <issue-key> [--claim]`
37
+ ### `/jira check <issue-key>`
38
38
 
39
- Check availability and claim a story for work.
39
+ Check if a story is available to claim (not assigned to someone else).
40
40
 
41
41
  <run>
42
- .pennyfarthing/scripts jira/jira-claim-story.sh <issue-key> [--claim]
42
+ .pennyfarthing/scripts/jira/jira-claim-story.sh <issue-key>
43
43
  </run>
44
44
 
45
45
  <args>
46
46
  | Arg | Required | Description |
47
47
  |-----|----------|-------------|
48
48
  | `issue-key` | Yes | Jira key `MSSCI-XXXXX` or story key `35-7-name` |
49
- | `--claim` | No | Actually claim (assign + move to In Progress) |
50
49
  </args>
51
50
 
52
51
  <exit-codes>
53
52
  | Code | Meaning |
54
53
  |------|---------|
55
- | `0` | Available or successfully claimed |
54
+ | `0` | Available (unassigned or assigned to you) |
56
55
  | `1` | Assigned to someone else |
57
56
  | `2` | Not found or not synced |
58
57
  | `3` | Error (CLI not installed, etc.) |
59
58
  </exit-codes>
60
59
 
61
60
  <example>
62
- # Check if available
63
- .pennyfarthing/scripts jira/jira-claim-story.sh MSSCI-12038
61
+ .pennyfarthing/scripts/jira/jira-claim-story.sh MSSCI-12038
62
+ </example>
63
+
64
+ ---
65
+
66
+ ### `/jira claim <issue-key>`
67
+
68
+ Claim a story for work (assign to self + move to In Progress).
69
+
70
+ <run>
71
+ .pennyfarthing/scripts/jira/jira-claim-story.sh <issue-key> --claim
72
+ </run>
73
+
74
+ <args>
75
+ | Arg | Required | Description |
76
+ |-----|----------|-------------|
77
+ | `issue-key` | Yes | Jira key `MSSCI-XXXXX` or story key `35-7-name` |
78
+ </args>
64
79
 
65
- # Claim it (assign to self + In Progress)
66
- .pennyfarthing/scripts jira/jira-claim-story.sh MSSCI-12038 --claim
80
+ <exit-codes>
81
+ | Code | Meaning |
82
+ |------|---------|
83
+ | `0` | Successfully claimed |
84
+ | `1` | Assigned to someone else (BLOCKED) |
85
+ | `2` | Not found or not synced |
86
+ | `3` | Error (CLI not installed, etc.) |
87
+ </exit-codes>
88
+
89
+ <example>
90
+ .pennyfarthing/scripts/jira/jira-claim-story.sh MSSCI-12038 --claim
67
91
  </example>
68
92
 
69
93
  ---
@@ -135,7 +159,7 @@ jira issue assign MSSCI-12038 x --project MSSCI
135
159
  Create a single Jira story under an epic from sprint YAML.
136
160
 
137
161
  <run>
138
- .pennyfarthing/scripts jira/create-jira-story.sh <epic-key> <story-id>
162
+ .pennyfarthing/scripts/jira/create-jira-story.sh <epic-key> <story-id>
139
163
  </run>
140
164
 
141
165
  <args>
@@ -150,7 +174,7 @@ Creating a single story that's missing from Jira but exists in sprint YAML.
150
174
  </when>
151
175
 
152
176
  <example>
153
- .pennyfarthing/scripts jira/create-jira-story.sh MSSCI-12077 MSSCI-12066
177
+ .pennyfarthing/scripts/jira/create-jira-story.sh MSSCI-12077 MSSCI-12066
154
178
  </example>
155
179
 
156
180
  <output>
@@ -169,7 +193,7 @@ Creating a single story that's missing from Jira but exists in sprint YAML.
169
193
  Create a Jira epic and all its child stories from sprint YAML.
170
194
 
171
195
  <run>
172
- .pennyfarthing/scripts jira/create-jira-epic.sh <epic-id> [--dry-run]
196
+ .pennyfarthing/scripts/jira/create-jira-epic.sh <epic-id> [--dry-run]
173
197
  </run>
174
198
 
175
199
  <args>
@@ -181,10 +205,10 @@ Create a Jira epic and all its child stories from sprint YAML.
181
205
 
182
206
  <example>
183
207
  # Preview what would be created
184
- .pennyfarthing/scripts jira/create-jira-epic.sh epic-41 --dry-run
208
+ .pennyfarthing/scripts/jira/create-jira-epic.sh epic-41 --dry-run
185
209
 
186
210
  # Create epic and stories
187
- .pennyfarthing/scripts jira/create-jira-epic.sh epic-41
211
+ .pennyfarthing/scripts/jira/create-jira-epic.sh epic-41
188
212
  </example>
189
213
 
190
214
  <output>
@@ -202,7 +226,7 @@ Create a Jira epic and all its child stories from sprint YAML.
202
226
  Sync an epic and its stories from sprint YAML to Jira.
203
227
 
204
228
  <run>
205
- .pennyfarthing/scripts jira/sync-epic-jira.sh <epic-id> [options]
229
+ .pennyfarthing/scripts/jira/sync-epic-jira.sh <epic-id> [options]
206
230
  </run>
207
231
 
208
232
  <args>
@@ -217,13 +241,13 @@ Sync an epic and its stories from sprint YAML to Jira.
217
241
 
218
242
  <example>
219
243
  # Show sync status
220
- .pennyfarthing/scripts jira/sync-epic-jira.sh MSSCI-11952
244
+ .pennyfarthing/scripts/jira/sync-epic-jira.sh MSSCI-11952
221
245
 
222
246
  # Preview changes
223
- .pennyfarthing/scripts jira/sync-epic-jira.sh MSSCI-11952 --dry-run
247
+ .pennyfarthing/scripts/jira/sync-epic-jira.sh MSSCI-11952 --dry-run
224
248
 
225
249
  # Full sync
226
- .pennyfarthing/scripts jira/sync-epic-jira.sh MSSCI-11952 --all
250
+ .pennyfarthing/scripts/jira/sync-epic-jira.sh MSSCI-11952 --all
227
251
  </example>
228
252
 
229
253
  <output>
@@ -240,7 +264,7 @@ Sync an epic and its stories from sprint YAML to Jira.
240
264
  Generate a reconciliation report comparing sprint YAML against Jira.
241
265
 
242
266
  <run>
243
- .pennyfarthing/scripts jira/jira-reconcile.sh [--fix]
267
+ .pennyfarthing/scripts/jira/jira-reconcile.sh [--fix]
244
268
  </run>
245
269
 
246
270
  <args>
@@ -251,10 +275,10 @@ Generate a reconciliation report comparing sprint YAML against Jira.
251
275
 
252
276
  <example>
253
277
  # Report only
254
- .pennyfarthing/scripts jira/jira-reconcile.sh
278
+ .pennyfarthing/scripts/jira/jira-reconcile.sh
255
279
 
256
280
  # Report and fix
257
- .pennyfarthing/scripts jira/jira-reconcile.sh --fix
281
+ .pennyfarthing/scripts/jira/jira-reconcile.sh --fix
258
282
  </example>
259
283
 
260
284
  <output>
@@ -402,8 +426,8 @@ Sprint field reference:
402
426
  | Command | Script/Action |
403
427
  |---------|---------------|
404
428
  | `/jira view MSSCI-XXX` | `jira issue view MSSCI-XXX` |
405
- | `/jira claim MSSCI-XXX` | `jira-claim-story.sh MSSCI-XXX` |
406
- | `/jira claim MSSCI-XXX --claim` | `jira-claim-story.sh MSSCI-XXX --claim` |
429
+ | `/jira check MSSCI-XXX` | `jira-claim-story.sh MSSCI-XXX` |
430
+ | `/jira claim MSSCI-XXX` | `jira-claim-story.sh MSSCI-XXX --claim` |
407
431
  | `/jira move MSSCI-XXX "Done"` | `jira issue move MSSCI-XXX "Done" -p MSSCI` |
408
432
  | `/jira assign MSSCI-XXX "user"` | `jira issue assign MSSCI-XXX "user" -p MSSCI` |
409
433
  | `/jira create story E-KEY S-ID` | `create-jira-story.sh E-KEY S-ID` |
@@ -10,6 +10,13 @@
10
10
 
11
11
  set -euo pipefail
12
12
 
13
+ # Find package root (where pennyfarthing_scripts lives) from script location
14
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
15
+ PACKAGE_ROOT="$(cd "$SCRIPT_DIR/../../../.." && pwd -P)"
16
+
17
+ # Set PYTHONPATH so Python can find pennyfarthing_scripts
18
+ export PYTHONPATH="${PACKAGE_ROOT}:${PYTHONPATH:-}"
19
+
13
20
  EPIC_ID="${1:-}"
14
21
 
15
22
  if [[ -z "$EPIC_ID" ]]; then