@pennyfarthing/core 8.1.0 → 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.
- package/README.md +3 -3
- package/package.json +3 -3
- package/pennyfarthing-dist/agents/README.md +1 -1
- package/pennyfarthing-dist/agents/dev.md +1 -1
- package/pennyfarthing-dist/agents/handoff.md +1 -1
- package/pennyfarthing-dist/agents/reviewer-preflight.md +1 -1
- package/pennyfarthing-dist/agents/sm-setup.md +3 -3
- package/pennyfarthing-dist/agents/sm.md +1 -1
- package/pennyfarthing-dist/agents/tea.md +1 -1
- package/pennyfarthing-dist/agents/testing-runner.md +3 -3
- package/pennyfarthing-dist/commands/architect.md +2 -0
- package/pennyfarthing-dist/commands/continue-session.md +2 -2
- package/pennyfarthing-dist/commands/dev.md +2 -0
- package/pennyfarthing-dist/commands/devops.md +2 -0
- package/pennyfarthing-dist/commands/health-check.md +2 -0
- package/pennyfarthing-dist/commands/new-work.md +23 -0
- package/pennyfarthing-dist/commands/orchestrator.md +2 -0
- package/pennyfarthing-dist/commands/parallel-work.md +4 -2
- package/pennyfarthing-dist/commands/pm.md +2 -0
- package/pennyfarthing-dist/commands/reviewer.md +2 -0
- package/pennyfarthing-dist/commands/sm.md +2 -0
- package/pennyfarthing-dist/commands/tea.md +2 -0
- package/pennyfarthing-dist/commands/tech-writer.md +2 -0
- package/pennyfarthing-dist/commands/ux-designer.md +2 -0
- package/pennyfarthing-dist/commands/work.md +2 -0
- package/pennyfarthing-dist/guides/agent-behavior.md +29 -264
- package/pennyfarthing-dist/scripts/core/agent-session.sh +7 -0
- package/pennyfarthing-dist/scripts/core/check-context.sh +140 -226
- package/pennyfarthing-dist/scripts/core/handoff-marker.sh +13 -2
- package/pennyfarthing-dist/scripts/git/worktree-manager.sh +4 -1
- package/pennyfarthing-dist/scripts/health/drift-detection.sh +1 -7
- package/pennyfarthing-dist/scripts/hooks/post-merge.sh +4 -11
- package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +3 -8
- package/pennyfarthing-dist/scripts/hooks/pre-push.sh +3 -3
- package/pennyfarthing-dist/scripts/jira/create-jira-epic.sh +1 -7
- package/pennyfarthing-dist/scripts/jira/create-jira-story.sh +2 -8
- package/pennyfarthing-dist/scripts/jira/jira-reconcile.sh +2 -8
- package/pennyfarthing-dist/scripts/lib/find-root.sh +17 -45
- package/pennyfarthing-dist/scripts/maintenance/sidecar-health.sh +1 -7
- package/pennyfarthing-dist/scripts/sprint/archive-story.sh +2 -8
- package/pennyfarthing-dist/scripts/sprint/available-stories.sh +2 -8
- package/pennyfarthing-dist/scripts/sprint/check-story.sh +2 -8
- package/pennyfarthing-dist/scripts/sprint/get-epic-field.sh +2 -8
- package/pennyfarthing-dist/scripts/sprint/get-story-field.sh +2 -8
- package/pennyfarthing-dist/scripts/sprint/list-future.sh +2 -8
- package/pennyfarthing-dist/scripts/sprint/new-sprint.sh +2 -8
- package/pennyfarthing-dist/scripts/sprint/promote-epic.sh +2 -8
- package/pennyfarthing-dist/scripts/sprint/sprint-info.sh +2 -8
- package/pennyfarthing-dist/scripts/tests/test-character-voice.sh +2 -1
- package/pennyfarthing-dist/scripts/workflow/finish-story.sh +4 -9
- package/pennyfarthing-dist/scripts/workflow/fix-session-phase.sh +2 -8
- package/pennyfarthing-dist/scripts/workflow/list-workflows.sh +2 -8
- package/pennyfarthing-dist/scripts/workflow/phase-owner.sh +1 -7
- package/pennyfarthing-dist/scripts/workflow/resume-workflow.sh +2 -8
- package/pennyfarthing-dist/scripts/workflow/show-workflow.sh +2 -8
- package/pennyfarthing-dist/scripts/workflow/start-workflow.sh +2 -8
- package/pennyfarthing-dist/scripts/workflow/workflow-status.sh +2 -8
- package/pennyfarthing-dist/skills/dev-patterns/SKILL.md +1 -1
- package/pennyfarthing-dist/skills/jira/SKILL.md +48 -24
- package/pennyfarthing-dist/skills/sprint/scripts/sync-epic-jira.sh +7 -0
- package/pennyfarthing-dist/skills/sprint/skill.md +30 -30
- package/pennyfarthing-dist/workflows/patch.yaml +68 -0
- package/pennyfarthing_scripts/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/common/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/common/__pycache__/config.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/common/__pycache__/output.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/context.py +414 -0
- package/pennyfarthing_scripts/patch_mode.py +449 -0
- package/pennyfarthing_scripts/prime/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/loader.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/persona.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/session.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/tiers.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/cli.py +201 -0
- package/pennyfarthing_scripts/prime/models.py +9 -0
- package/pennyfarthing_scripts/prime/persona.py +41 -0
- package/pennyfarthing_scripts/prime/tiers.py +201 -0
- package/pennyfarthing_scripts/sprint/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/archive.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/status.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/work.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/tests/test_patch_mode.py +830 -0
- package/pennyfarthing_scripts/tests/test_tiers.py +1090 -0
- package/pennyfarthing_scripts/tests/test_token_counting.py +559 -0
- package/pennyfarthing-dist/scripts/hooks/__pycache__/question_reflector_check.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/config.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira_bidirectional_sync.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira_epic_creation.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira_sync.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira_sync_story.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/output.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/sprint.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/workflow.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/brownfield/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/brownfield/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/brownfield/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/brownfield/__pycache__/discover.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/create_branches.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/status_all.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/bidirectional.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/claim.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/client.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/compat.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/epic.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/mappings.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/story.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/sync.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/preflight/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/preflight/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/preflight/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/preflight/__pycache__/finish.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/validator.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/story/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/story/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/story/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/story/__pycache__/create.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/story/__pycache__/size.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/story/__pycache__/template.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_brownfield.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_cli_modules.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_common.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_git_utils.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_jira_package.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_package_structure.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_prime.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_sprint_package.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_sprint_validator.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_story_package.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_workflow_check.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_workflow_cli.cpython-314-pytest-9.0.2.pyc +0 -0
|
@@ -2,15 +2,55 @@
|
|
|
2
2
|
# check-context.sh - Check current Claude Code context usage
|
|
3
3
|
# Returns: percentage and recommendation for handoff
|
|
4
4
|
#
|
|
5
|
+
# This is a thin wrapper around pennyfarthing_scripts/context.py
|
|
6
|
+
#
|
|
5
7
|
# Usage:
|
|
6
8
|
# ./check-context.sh # Output env vars (most recent transcript)
|
|
7
9
|
# ./check-context.sh --human # Human-readable output
|
|
8
10
|
# ./check-context.sh --session <id> # Check specific session transcript
|
|
9
|
-
# SESSION_ID=<id> ./check-context.sh # Alternative: session via env var
|
|
10
11
|
# eval $(./check-context.sh) # Load vars into shell
|
|
11
12
|
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
|
|
15
|
+
# Find project root (where pennyfarthing_scripts lives)
|
|
16
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
|
|
17
|
+
|
|
18
|
+
# Resolve symlinks to find actual location
|
|
19
|
+
REAL_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}" 2>/dev/null || echo "${BASH_SOURCE[0]}")"
|
|
20
|
+
REAL_DIR="$(cd "$(dirname "$REAL_SCRIPT")" && pwd -P)"
|
|
21
|
+
|
|
22
|
+
# Determine if we're in pennyfarthing-dist (3 levels up to package root)
|
|
23
|
+
# or in node_modules (need to find project root differently)
|
|
24
|
+
if [[ "$REAL_DIR" == *"/pennyfarthing-dist/scripts/core" ]]; then
|
|
25
|
+
# In pennyfarthing source or node_modules/@pennyfarthing/core
|
|
26
|
+
PACKAGE_ROOT="${REAL_DIR%/pennyfarthing-dist/scripts/core}"
|
|
27
|
+
elif [[ "$REAL_DIR" == *"/node_modules/"* ]]; then
|
|
28
|
+
# In node_modules, walk up to find project root
|
|
29
|
+
PACKAGE_ROOT="${REAL_DIR}"
|
|
30
|
+
while [[ "$PACKAGE_ROOT" != "/" ]] && [[ ! -d "$PACKAGE_ROOT/pennyfarthing_scripts" ]]; do
|
|
31
|
+
PACKAGE_ROOT="$(dirname "$PACKAGE_ROOT")"
|
|
32
|
+
done
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
# Try to find pennyfarthing_scripts
|
|
36
|
+
PYTHON_MODULE=""
|
|
37
|
+
if [[ -f "$PACKAGE_ROOT/pennyfarthing_scripts/context.py" ]]; then
|
|
38
|
+
PYTHON_MODULE="$PACKAGE_ROOT/pennyfarthing_scripts/context.py"
|
|
39
|
+
elif [[ -f "${PROJECT_ROOT:-}/pennyfarthing_scripts/context.py" ]]; then
|
|
40
|
+
PYTHON_MODULE="${PROJECT_ROOT}/pennyfarthing_scripts/context.py"
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# If Python module exists, use it
|
|
44
|
+
if [[ -n "$PYTHON_MODULE" ]]; then
|
|
45
|
+
exec python3 "$PYTHON_MODULE" "$@"
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
# Fallback: inline implementation for environments without the Python module
|
|
49
|
+
# This ensures backwards compatibility
|
|
50
|
+
|
|
12
51
|
# Parse command line arguments
|
|
13
52
|
HUMAN_MODE=false
|
|
53
|
+
EXPLICIT_SESSION=""
|
|
14
54
|
while [[ $# -gt 0 ]]; do
|
|
15
55
|
case "$1" in
|
|
16
56
|
--human)
|
|
@@ -18,7 +58,7 @@ while [[ $# -gt 0 ]]; do
|
|
|
18
58
|
shift
|
|
19
59
|
;;
|
|
20
60
|
--session)
|
|
21
|
-
|
|
61
|
+
EXPLICIT_SESSION="$2"
|
|
22
62
|
shift 2
|
|
23
63
|
;;
|
|
24
64
|
*)
|
|
@@ -28,253 +68,127 @@ while [[ $# -gt 0 ]]; do
|
|
|
28
68
|
done
|
|
29
69
|
|
|
30
70
|
# Derive Claude project path from current directory
|
|
31
|
-
# Claude Code stores transcripts at ~/.claude/projects/<path-with-dashes>
|
|
32
|
-
# The path format is: -Users-name-Projects-project (leading dash, all slashes become dashes)
|
|
33
71
|
PROJECT_DIR="${PROJECT_ROOT:-$(pwd)}"
|
|
34
72
|
CLAUDE_PROJECT_PATH="$HOME/.claude/projects/$(echo "$PROJECT_DIR" | tr '/' '-')"
|
|
35
73
|
|
|
36
|
-
# Default
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
import
|
|
49
|
-
|
|
50
|
-
imminent_threshold = $DEFAULT_IMMINENT_THRESHOLD
|
|
51
|
-
warning_threshold = $DEFAULT_WARNING_THRESHOLD
|
|
52
|
-
critical_threshold = $DEFAULT_CRITICAL_THRESHOLD
|
|
53
|
-
max_tokens = $DEFAULT_MAX_TOKENS
|
|
54
|
-
tirepump_threshold = $DEFAULT_TIREPUMP_THRESHOLD
|
|
55
|
-
permission_mode = 'manual' # Default to manual
|
|
56
|
-
relay_mode = False # MSSCI-12395: Independent auto-handoff toggle
|
|
57
|
-
|
|
58
|
-
# First try .pennyfarthing/config.local.yaml (preferred location)
|
|
74
|
+
# Default config
|
|
75
|
+
WARNING_THRESHOLD=60
|
|
76
|
+
CRITICAL_THRESHOLD=85
|
|
77
|
+
MAX_TOKENS=200000
|
|
78
|
+
TIREPUMP_THRESHOLD=60
|
|
79
|
+
PERMISSION_MODE="manual"
|
|
80
|
+
RELAY_MODE="false"
|
|
81
|
+
|
|
82
|
+
# Load config from .pennyfarthing/config.local.yaml if available
|
|
83
|
+
CONFIG_FILE="${PROJECT_DIR}/.pennyfarthing/config.local.yaml"
|
|
84
|
+
if [[ -f "$CONFIG_FILE" ]] && command -v python3 &>/dev/null; then
|
|
85
|
+
eval "$(python3 -c "
|
|
86
|
+
import yaml
|
|
59
87
|
try:
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if 'permission_mode' in config['workflow']:
|
|
74
|
-
permission_mode = config['workflow']['permission_mode']
|
|
75
|
-
# MSSCI-12395: relay_mode for auto-handoff (independent of permission_mode)
|
|
76
|
-
if 'relay_mode' in config['workflow']:
|
|
77
|
-
relay_mode = config['workflow']['relay_mode'] == True
|
|
78
|
-
except:
|
|
79
|
-
# Fallback to settings.local.json (legacy location)
|
|
80
|
-
try:
|
|
81
|
-
with open('$SETTINGS_FILE', 'r') as f:
|
|
82
|
-
settings = json.load(f)
|
|
83
|
-
if 'context_budget' in settings:
|
|
84
|
-
cb = settings['context_budget']
|
|
85
|
-
imminent_threshold = cb.get('imminent_threshold', imminent_threshold)
|
|
86
|
-
warning_threshold = cb.get('warning_threshold', warning_threshold)
|
|
87
|
-
critical_threshold = cb.get('critical_threshold', critical_threshold)
|
|
88
|
-
max_tokens = cb.get('max_tokens', max_tokens)
|
|
89
|
-
tirepump_threshold = cb.get('tirepump_threshold', tirepump_threshold)
|
|
90
|
-
if 'workflow' in settings:
|
|
91
|
-
if 'permission_mode' in settings['workflow']:
|
|
92
|
-
permission_mode = settings['workflow']['permission_mode']
|
|
93
|
-
# MSSCI-12395: relay_mode for auto-handoff (independent of permission_mode)
|
|
94
|
-
if 'relay_mode' in settings['workflow']:
|
|
95
|
-
relay_mode = settings['workflow']['relay_mode'] == True
|
|
96
|
-
except:
|
|
97
|
-
pass
|
|
98
|
-
|
|
99
|
-
print(f'IMMINENT_THRESHOLD={imminent_threshold}')
|
|
100
|
-
print(f'WARNING_THRESHOLD={warning_threshold}')
|
|
101
|
-
print(f'CRITICAL_THRESHOLD={critical_threshold}')
|
|
102
|
-
print(f'MAX_TOKENS={max_tokens}')
|
|
103
|
-
print(f'TIREPUMP_THRESHOLD={tirepump_threshold}')
|
|
104
|
-
print(f'PERMISSION_MODE={permission_mode}')
|
|
105
|
-
print(f'RELAY_MODE={str(relay_mode).lower()}')
|
|
106
|
-
" 2>/dev/null)
|
|
88
|
+
with open('$CONFIG_FILE') as f:
|
|
89
|
+
c = yaml.safe_load(f) or {}
|
|
90
|
+
cb = c.get('context_budget', {})
|
|
91
|
+
wf = c.get('workflow', {})
|
|
92
|
+
print(f'WARNING_THRESHOLD={cb.get(\"warning_threshold\", 60)}')
|
|
93
|
+
print(f'CRITICAL_THRESHOLD={cb.get(\"critical_threshold\", 85)}')
|
|
94
|
+
print(f'MAX_TOKENS={cb.get(\"max_tokens\", 200000)}')
|
|
95
|
+
print(f'TIREPUMP_THRESHOLD={cb.get(\"tirepump_threshold\", 60)}')
|
|
96
|
+
print(f'PERMISSION_MODE={wf.get(\"permission_mode\", \"manual\")}')
|
|
97
|
+
print(f'RELAY_MODE={str(wf.get(\"relay_mode\", False)).lower()}')
|
|
98
|
+
except: pass
|
|
99
|
+
" 2>/dev/null)" || true
|
|
100
|
+
fi
|
|
107
101
|
|
|
108
|
-
#
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
WARNING_THRESHOLD=$DEFAULT_WARNING_THRESHOLD
|
|
112
|
-
CRITICAL_THRESHOLD=$DEFAULT_CRITICAL_THRESHOLD
|
|
113
|
-
MAX_TOKENS=$DEFAULT_MAX_TOKENS
|
|
114
|
-
TIREPUMP_THRESHOLD=$DEFAULT_TIREPUMP_THRESHOLD
|
|
102
|
+
# Find transcript with stale SESSION_ID handling
|
|
103
|
+
find_most_recent() {
|
|
104
|
+
ls -t "$CLAUDE_PROJECT_PATH"/*.jsonl 2>/dev/null | grep -v "agent-" | head -1
|
|
115
105
|
}
|
|
116
106
|
|
|
117
|
-
|
|
118
|
-
if [ -n "$
|
|
119
|
-
|
|
120
|
-
TRANSCRIPT="
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
107
|
+
TRANSCRIPT=""
|
|
108
|
+
if [[ -n "$EXPLICIT_SESSION" ]]; then
|
|
109
|
+
TRANSCRIPT="$CLAUDE_PROJECT_PATH/${EXPLICIT_SESSION}.jsonl"
|
|
110
|
+
[[ ! -f "$TRANSCRIPT" ]] && { echo "CONTEXT_ERROR=session_not_found"; exit 1; }
|
|
111
|
+
elif [[ -n "${SESSION_ID:-}" ]]; then
|
|
112
|
+
CANDIDATE="$CLAUDE_PROJECT_PATH/${SESSION_ID}.jsonl"
|
|
113
|
+
if [[ -f "$CANDIDATE" ]]; then
|
|
114
|
+
NOW=$(date +%s)
|
|
115
|
+
MOD_TIME=$(stat -f %m "$CANDIDATE" 2>/dev/null || stat -c %Y "$CANDIDATE" 2>/dev/null || echo 0)
|
|
116
|
+
AGE=$((NOW - MOD_TIME))
|
|
117
|
+
if [[ "$AGE" -lt 60 ]]; then
|
|
118
|
+
TRANSCRIPT="$CANDIDATE"
|
|
124
119
|
else
|
|
125
|
-
|
|
126
|
-
echo "CONTEXT_SESSION=$SESSION_ID"
|
|
120
|
+
TRANSCRIPT=$(find_most_recent)
|
|
127
121
|
fi
|
|
128
|
-
|
|
122
|
+
else
|
|
123
|
+
TRANSCRIPT=$(find_most_recent)
|
|
129
124
|
fi
|
|
130
125
|
else
|
|
131
|
-
|
|
132
|
-
TRANSCRIPT=$(ls -t "$CLAUDE_PROJECT_PATH"/*.jsonl 2>/dev/null | grep -v "agent-" | head -1)
|
|
133
|
-
if [ -z "$TRANSCRIPT" ]; then
|
|
134
|
-
if [ "$HUMAN_MODE" = "true" ]; then
|
|
135
|
-
echo "⚠️ Context: unknown (no transcript found)"
|
|
136
|
-
else
|
|
137
|
-
echo "CONTEXT_ERROR=no_transcript"
|
|
138
|
-
fi
|
|
139
|
-
exit 1
|
|
140
|
-
fi
|
|
126
|
+
TRANSCRIPT=$(find_most_recent)
|
|
141
127
|
fi
|
|
142
128
|
|
|
143
|
-
|
|
144
|
-
# Baseline = system prompt overhead, cached per session
|
|
145
|
-
# Usable = current - baseline (what the user's conversation has consumed)
|
|
146
|
-
RESULT=$(python3 -c "
|
|
147
|
-
import sys
|
|
148
|
-
import json
|
|
149
|
-
|
|
150
|
-
warning_threshold = $WARNING_THRESHOLD
|
|
151
|
-
max_tokens = $MAX_TOKENS
|
|
152
|
-
permission_mode = '$PERMISSION_MODE'
|
|
153
|
-
relay_mode = '$RELAY_MODE' == 'true' # MSSCI-12395: Independent auto-handoff toggle
|
|
154
|
-
tirepump_threshold = $TIREPUMP_THRESHOLD # Threshold for TirePump auto-handoff (configurable)
|
|
129
|
+
[[ -z "$TRANSCRIPT" ]] && { echo "CONTEXT_ERROR=no_transcript"; exit 1; }
|
|
155
130
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
for line in
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
# Usable tokens = what user's conversation has consumed
|
|
184
|
-
usable_tokens = last_total - baseline
|
|
185
|
-
|
|
186
|
-
# Available capacity = max minus baseline overhead
|
|
187
|
-
available_capacity = max_tokens - baseline
|
|
188
|
-
|
|
189
|
-
# Usable percent = conversation usage as % of available capacity
|
|
190
|
-
usable_pct = (usable_tokens / available_capacity * 100) if available_capacity > 0 else 0
|
|
191
|
-
|
|
192
|
-
# Total percent (for backwards compatibility)
|
|
193
|
-
total_pct = (last_total / max_tokens) * 100
|
|
131
|
+
# Parse transcript
|
|
132
|
+
RESULT=$(python3 -c "
|
|
133
|
+
import json, os
|
|
134
|
+
from pathlib import Path
|
|
135
|
+
|
|
136
|
+
with open('$TRANSCRIPT') as f:
|
|
137
|
+
first_total = last_total = None
|
|
138
|
+
for line in f:
|
|
139
|
+
try:
|
|
140
|
+
data = json.loads(line.strip())
|
|
141
|
+
if 'message' in data and 'usage' in data['message']:
|
|
142
|
+
u = data['message']['usage']
|
|
143
|
+
total = u.get('input_tokens',0) + u.get('cache_read_input_tokens',0) + u.get('cache_creation_input_tokens',0)
|
|
144
|
+
if first_total is None: first_total = total
|
|
145
|
+
last_total = total
|
|
146
|
+
except: continue
|
|
147
|
+
|
|
148
|
+
if last_total:
|
|
149
|
+
baseline = first_total or 0
|
|
150
|
+
usable = last_total - baseline
|
|
151
|
+
available = $MAX_TOKENS - baseline
|
|
152
|
+
usable_pct = int((usable / available * 100) if available > 0 else 0)
|
|
153
|
+
total_pct = int((last_total / $MAX_TOKENS) * 100)
|
|
154
|
+
status = 'HIGH' if usable_pct > $WARNING_THRESHOLD else 'OK'
|
|
155
|
+
relay = '$RELAY_MODE' == 'true'
|
|
156
|
+
tirepump = (relay or '$PERMISSION_MODE' == 'turbo') and usable_pct > $TIREPUMP_THRESHOLD
|
|
157
|
+
is_cyclist = os.environ.get('CYCLIST') == '1' or Path('$PROJECT_DIR/packages/cyclist/.cyclist-port').exists()
|
|
194
158
|
|
|
195
|
-
# Output all values
|
|
196
159
|
print(f'CONTEXT_TOKENS={last_total}')
|
|
197
|
-
print(f'CONTEXT_PERCENT={total_pct
|
|
160
|
+
print(f'CONTEXT_PERCENT={total_pct}')
|
|
198
161
|
print(f'CONTEXT_BASELINE={baseline}')
|
|
199
|
-
print(f'CONTEXT_USABLE_TOKENS={
|
|
200
|
-
print(f'CONTEXT_USABLE_PERCENT={usable_pct
|
|
201
|
-
print(f'CONTEXT_AVAILABLE={
|
|
202
|
-
print(f'
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
if
|
|
206
|
-
|
|
207
|
-
else:
|
|
208
|
-
print('CONTEXT_STATUS=OK')
|
|
209
|
-
|
|
210
|
-
# RELAY_MODE: Output for handoff-marker.sh to use
|
|
211
|
-
print(f'RELAY_MODE={str(relay_mode).lower()}')
|
|
212
|
-
|
|
213
|
-
# HANDOFF_MODE: 'auto' if relay_mode enabled, 'ask' otherwise
|
|
214
|
-
# MSSCI-12395: relay_mode controls autohandoff independent of context level
|
|
215
|
-
if relay_mode:
|
|
216
|
-
print('HANDOFF_MODE=auto')
|
|
217
|
-
else:
|
|
218
|
-
print('HANDOFF_MODE=ask')
|
|
219
|
-
|
|
220
|
-
# TirePump: Use CONTEXT_CLEAR (clear + load next agent) when:
|
|
221
|
-
# 1. relay_mode is true (auto-handoff enabled) - MSSCI-12395
|
|
222
|
-
# 2. context > 60% (tirepump_threshold)
|
|
223
|
-
# This enables continuous autonomous runs without manual intervention
|
|
224
|
-
# Legacy: also support permission_mode == 'turbo' for backwards compatibility
|
|
225
|
-
use_tirepump = (relay_mode or permission_mode == 'turbo') and usable_pct > tirepump_threshold
|
|
226
|
-
print(f'USE_TIREPUMP={str(use_tirepump).lower()}')
|
|
227
|
-
|
|
228
|
-
# Cyclist detection: Multiple methods for robustness
|
|
229
|
-
# 1. CYCLIST env var set to '1' (Electron mode - Cyclist spawns Claude)
|
|
230
|
-
# 2. .cyclist-port file exists (Web mode - Claude connects to running Cyclist)
|
|
231
|
-
import os
|
|
232
|
-
from pathlib import Path
|
|
233
|
-
is_cyclist = os.environ.get('CYCLIST', '') == '1'
|
|
234
|
-
if not is_cyclist:
|
|
235
|
-
# Check for .cyclist-port file in packages/cyclist directory
|
|
236
|
-
# This indicates Cyclist is running in web mode
|
|
237
|
-
project_dir = os.environ.get('CYCLIST_PROJECT_DIR', os.getcwd())
|
|
238
|
-
port_file = Path(project_dir) / 'packages' / 'cyclist' / '.cyclist-port'
|
|
239
|
-
if not port_file.exists():
|
|
240
|
-
# Also check cwd in case we're already in cyclist dir
|
|
241
|
-
port_file = Path(os.getcwd()) / '.cyclist-port'
|
|
242
|
-
is_cyclist = port_file.exists()
|
|
162
|
+
print(f'CONTEXT_USABLE_TOKENS={usable}')
|
|
163
|
+
print(f'CONTEXT_USABLE_PERCENT={usable_pct}')
|
|
164
|
+
print(f'CONTEXT_AVAILABLE={available}')
|
|
165
|
+
print(f'CONTEXT_STATUS={status}')
|
|
166
|
+
print(f'PERMISSION_MODE=$PERMISSION_MODE')
|
|
167
|
+
print(f'RELAY_MODE=$RELAY_MODE')
|
|
168
|
+
print(f'HANDOFF_MODE={\"auto\" if relay else \"ask\"}')
|
|
169
|
+
print(f'USE_TIREPUMP={str(tirepump).lower()}')
|
|
243
170
|
print(f'IS_CYCLIST={str(is_cyclist).lower()}')
|
|
171
|
+
if usable_pct >= $CRITICAL_THRESHOLD:
|
|
172
|
+
print('CONTEXT_WARNING=Critical')
|
|
173
|
+
print(\"CONTEXT_RECOMMENDATION='checkpoint and handoff recommended'\")
|
|
174
|
+
elif usable_pct >= $WARNING_THRESHOLD:
|
|
175
|
+
print('CONTEXT_WARNING=High')
|
|
176
|
+
print(\"CONTEXT_RECOMMENDATION='consider handoff soon'\")
|
|
244
177
|
" 2>/dev/null)
|
|
245
178
|
|
|
246
|
-
if [ "$HUMAN_MODE"
|
|
179
|
+
if [[ "$HUMAN_MODE" == "true" ]]; then
|
|
247
180
|
eval "$RESULT"
|
|
248
|
-
if [ "$USE_TIREPUMP"
|
|
249
|
-
echo "🔄 Context: ${CONTEXT_USABLE_PERCENT}% used (${CONTEXT_USABLE_TOKENS} of ${CONTEXT_AVAILABLE} available) - TIREPUMP
|
|
250
|
-
elif [ "$CONTEXT_STATUS"
|
|
251
|
-
echo "⚠️ Context: ${CONTEXT_USABLE_PERCENT}% used (${CONTEXT_USABLE_TOKENS} of ${CONTEXT_AVAILABLE} available) -
|
|
181
|
+
if [[ "${USE_TIREPUMP:-}" == "true" ]]; then
|
|
182
|
+
echo "🔄 Context: ${CONTEXT_USABLE_PERCENT}% used (${CONTEXT_USABLE_TOKENS} of ${CONTEXT_AVAILABLE} available) - TIREPUMP"
|
|
183
|
+
elif [[ "${CONTEXT_STATUS:-}" == "HIGH" ]]; then
|
|
184
|
+
echo "⚠️ Context: ${CONTEXT_USABLE_PERCENT}% used (${CONTEXT_USABLE_TOKENS} of ${CONTEXT_AVAILABLE} available) - HIGH"
|
|
252
185
|
else
|
|
253
|
-
echo "✅ Context: ${CONTEXT_USABLE_PERCENT}% used (${CONTEXT_USABLE_TOKENS} of ${CONTEXT_AVAILABLE} available)"
|
|
254
|
-
fi
|
|
255
|
-
echo " Overhead: ${CONTEXT_BASELINE} tokens (system prompt + tools)"
|
|
256
|
-
echo " Mode: ${PERMISSION_MODE}"
|
|
257
|
-
|
|
258
|
-
# Output warning messages at configurable thresholds (use usable percent)
|
|
259
|
-
if [ -n "$CONTEXT_USABLE_PERCENT" ]; then
|
|
260
|
-
if [ "$CONTEXT_USABLE_PERCENT" -ge "$CRITICAL_THRESHOLD" ] 2>/dev/null; then
|
|
261
|
-
echo "CONTEXT_WARNING: Critical (${CONTEXT_USABLE_PERCENT}%) - checkpoint and handoff recommended"
|
|
262
|
-
elif [ "$CONTEXT_USABLE_PERCENT" -ge "$WARNING_THRESHOLD" ] 2>/dev/null; then
|
|
263
|
-
echo "CONTEXT_WARNING: High (${CONTEXT_USABLE_PERCENT}%) - consider handoff soon"
|
|
264
|
-
fi
|
|
186
|
+
echo "✅ Context: ${CONTEXT_USABLE_PERCENT:-?}% used (${CONTEXT_USABLE_TOKENS:-?} of ${CONTEXT_AVAILABLE:-?} available)"
|
|
265
187
|
fi
|
|
188
|
+
echo " Overhead: ${CONTEXT_BASELINE:-?} tokens (system prompt + tools)"
|
|
189
|
+
echo " Mode: ${PERMISSION_MODE:-manual}"
|
|
190
|
+
[[ "${CONTEXT_WARNING:-}" == "Critical" ]] && echo "CONTEXT_WARNING: Critical - checkpoint and handoff recommended"
|
|
191
|
+
[[ "${CONTEXT_WARNING:-}" == "High" ]] && echo "CONTEXT_WARNING: High - consider handoff soon"
|
|
266
192
|
else
|
|
267
193
|
echo "$RESULT"
|
|
268
|
-
|
|
269
|
-
# Also output warnings in non-human mode for scripting (use usable percent)
|
|
270
|
-
eval "$RESULT" 2>/dev/null || true
|
|
271
|
-
if [ -n "$CONTEXT_USABLE_PERCENT" ]; then
|
|
272
|
-
if [ "$CONTEXT_USABLE_PERCENT" -ge "$CRITICAL_THRESHOLD" ] 2>/dev/null; then
|
|
273
|
-
echo "CONTEXT_WARNING=Critical"
|
|
274
|
-
echo "CONTEXT_RECOMMENDATION='checkpoint and handoff recommended'"
|
|
275
|
-
elif [ "$CONTEXT_USABLE_PERCENT" -ge "$WARNING_THRESHOLD" ] 2>/dev/null; then
|
|
276
|
-
echo "CONTEXT_WARNING=High"
|
|
277
|
-
echo "CONTEXT_RECOMMENDATION='consider handoff soon'"
|
|
278
|
-
fi
|
|
279
|
-
fi
|
|
280
194
|
fi
|
|
@@ -62,12 +62,23 @@ AGENT_COMMAND:
|
|
|
62
62
|
---
|
|
63
63
|
EOF
|
|
64
64
|
elif [[ "$IS_CYCLIST" != "true" ]]; then
|
|
65
|
-
# Not in Cyclist - no marker
|
|
65
|
+
# Not in Cyclist - no marker, include context info
|
|
66
|
+
# Get context percentage for warning
|
|
67
|
+
CONTEXT_PCT="${CONTEXT_USABLE_PERCENT:-unknown}"
|
|
68
|
+
if [[ "$CONTEXT_PCT" != "unknown" ]] && [[ "$CONTEXT_PCT" -ge 60 ]] 2>/dev/null; then
|
|
69
|
+
CONTEXT_WARNING=" (context: ${CONTEXT_PCT}% - consider /clear before continuing)"
|
|
70
|
+
elif [[ "$CONTEXT_PCT" != "unknown" ]]; then
|
|
71
|
+
CONTEXT_WARNING=" (context: ${CONTEXT_PCT}%)"
|
|
72
|
+
else
|
|
73
|
+
CONTEXT_WARNING=""
|
|
74
|
+
fi
|
|
66
75
|
cat <<EOF
|
|
67
76
|
---
|
|
68
77
|
AGENT_COMMAND:
|
|
69
78
|
marker: ""
|
|
70
|
-
fallback: "Run \`/${NEXT_AGENT}\` to continue"
|
|
79
|
+
fallback: "Run \`/${NEXT_AGENT}\` to continue${CONTEXT_WARNING}"
|
|
80
|
+
relay_mode: ${RELAY_MODE}
|
|
81
|
+
context_percent: ${CONTEXT_PCT}
|
|
71
82
|
---
|
|
72
83
|
EOF
|
|
73
84
|
elif [[ "$RELAY_MODE" != "true" ]]; then
|
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
set -e
|
|
10
10
|
|
|
11
|
+
# Self-locate: derive paths from this script's position
|
|
12
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd -P)"
|
|
13
|
+
|
|
11
14
|
# Load environment
|
|
12
15
|
if [ -f "$PROJECT_ROOT/.env" ]; then
|
|
13
16
|
set -a; source "$PROJECT_ROOT/.env"; set +a
|
|
@@ -20,7 +23,7 @@ fi
|
|
|
20
23
|
|
|
21
24
|
# Source repo utilities (handles repos.yaml or legacy env vars)
|
|
22
25
|
REPO_UTILS_LAZY=1 # Don't auto-load, we'll do it after validation
|
|
23
|
-
source "$
|
|
26
|
+
source "$SCRIPT_DIR/../misc/repo-utils.sh"
|
|
24
27
|
|
|
25
28
|
WORKTREE_ROOT="${WORKTREE_ROOT:-$PROJECT_ROOT/worktrees}"
|
|
26
29
|
WORKTREE_PORT_OFFSET="${WORKTREE_PORT_OFFSET:-100}"
|
|
@@ -22,13 +22,7 @@ while [[ $# -gt 0 ]]; do
|
|
|
22
22
|
done
|
|
23
23
|
|
|
24
24
|
# Find project root
|
|
25
|
-
|
|
26
|
-
d="$PWD"
|
|
27
|
-
while [[ ! -d "$d/.pennyfarthing" ]] && [[ "$d" != "/" ]]; do
|
|
28
|
-
d="$(dirname "$d")"
|
|
29
|
-
done
|
|
30
|
-
PROJECT_ROOT="$d"
|
|
31
|
-
fi
|
|
25
|
+
source "$(dirname "${BASH_SOURCE[0]}")/../lib/find-root.sh"
|
|
32
26
|
|
|
33
27
|
ARCHIVE_DIR="$PROJECT_ROOT/sprint/archive"
|
|
34
28
|
SESSION_DIR="$PROJECT_ROOT/.session/archive"
|
|
@@ -14,18 +14,11 @@
|
|
|
14
14
|
|
|
15
15
|
set -uo pipefail
|
|
16
16
|
|
|
17
|
-
# Self-locate
|
|
18
|
-
|
|
19
|
-
source "$
|
|
20
|
-
|
|
21
|
-
# Initialize paths
|
|
22
|
-
PROJECT_ROOT="$(find_project_root 2>/dev/null || echo "")"
|
|
23
|
-
if [[ -z "$PROJECT_ROOT" ]]; then
|
|
24
|
-
# Not in a pennyfarthing project, silently exit
|
|
25
|
-
exit 0
|
|
26
|
-
fi
|
|
17
|
+
# Self-locate (resolve symlink first for .git/hooks/ symlinks)
|
|
18
|
+
REAL_SCRIPT="$(readlink -f "${BASH_SOURCE[0]:-$0}" 2>/dev/null || realpath "${BASH_SOURCE[0]:-$0}" 2>/dev/null || echo "${BASH_SOURCE[0]:-$0}")"
|
|
19
|
+
source "$(dirname "$REAL_SCRIPT")/../lib/find-root.sh"
|
|
27
20
|
|
|
28
|
-
|
|
21
|
+
# PROJECT_ROOT is now set by find-root.sh
|
|
29
22
|
SESSION_DIR="$PROJECT_ROOT/.session"
|
|
30
23
|
SPRINT_FILE="$PROJECT_ROOT/sprint/current-sprint.yaml"
|
|
31
24
|
|
|
@@ -12,14 +12,9 @@
|
|
|
12
12
|
|
|
13
13
|
set -uo pipefail
|
|
14
14
|
|
|
15
|
-
# Find project root
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if [[ "$SCRIPT_DIR" == *".git/hooks"* ]]; then
|
|
19
|
-
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
20
|
-
else
|
|
21
|
-
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
|
|
22
|
-
fi
|
|
15
|
+
# Find project root (resolve symlink first for .git/hooks/ symlinks)
|
|
16
|
+
REAL_SCRIPT="$(readlink -f "${BASH_SOURCE[0]:-$0}" 2>/dev/null || realpath "${BASH_SOURCE[0]:-$0}" 2>/dev/null || echo "${BASH_SOURCE[0]:-$0}")"
|
|
17
|
+
source "$(dirname "$REAL_SCRIPT")/../lib/find-root.sh"
|
|
23
18
|
|
|
24
19
|
# =============================================================================
|
|
25
20
|
# Check 1: Branch Protection
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
|
|
10
10
|
set -uo pipefail
|
|
11
11
|
|
|
12
|
-
# Self-locate
|
|
13
|
-
|
|
14
|
-
if ! source "$
|
|
12
|
+
# Self-locate (resolve symlink first for .git/hooks/ symlinks)
|
|
13
|
+
REAL_SCRIPT="$(readlink -f "${BASH_SOURCE[0]:-$0}" 2>/dev/null || realpath "${BASH_SOURCE[0]:-$0}" 2>/dev/null || echo "${BASH_SOURCE[0]:-$0}")"
|
|
14
|
+
if ! source "$(dirname "$REAL_SCRIPT")/../lib/find-root.sh" 2>/dev/null; then
|
|
15
15
|
exit 0
|
|
16
16
|
fi
|
|
17
17
|
|
|
@@ -19,13 +19,7 @@ if [[ -z "$EPIC_ID" ]]; then
|
|
|
19
19
|
fi
|
|
20
20
|
|
|
21
21
|
# Find project root
|
|
22
|
-
|
|
23
|
-
d="$PWD"
|
|
24
|
-
while [[ ! -d "$d/.pennyfarthing" ]] && [[ "$d" != "/" ]]; do
|
|
25
|
-
d="$(dirname "$d")"
|
|
26
|
-
done
|
|
27
|
-
PROJECT_ROOT="$d"
|
|
28
|
-
fi
|
|
22
|
+
source "$(dirname "${BASH_SOURCE[0]}")/../lib/find-root.sh"
|
|
29
23
|
|
|
30
24
|
SPRINT_FILE="$PROJECT_ROOT/sprint/current-sprint.yaml"
|
|
31
25
|
SCRIPTS_DIR="$PROJECT_ROOT/.pennyfarthing/scripts"
|
|
@@ -14,14 +14,8 @@ if [[ -z "$EPIC_JIRA_KEY" || -z "$STORY_ID" ]]; then
|
|
|
14
14
|
exit 1
|
|
15
15
|
fi
|
|
16
16
|
|
|
17
|
-
#
|
|
18
|
-
|
|
19
|
-
d="$PWD"
|
|
20
|
-
while [[ ! -d "$d/.pennyfarthing" ]] && [[ "$d" != "/" ]]; do
|
|
21
|
-
d="$(dirname "$d")"
|
|
22
|
-
done
|
|
23
|
-
PROJECT_ROOT="$d"
|
|
24
|
-
fi
|
|
17
|
+
# Find project root
|
|
18
|
+
source "$(dirname "${BASH_SOURCE[0]}")/../lib/find-root.sh"
|
|
25
19
|
|
|
26
20
|
SPRINT_FILE="$PROJECT_ROOT/sprint/current-sprint.yaml"
|
|
27
21
|
JIRA_PROJECT="${JIRA_PROJECT_KEY:-MSSCI}"
|
|
@@ -13,14 +13,8 @@
|
|
|
13
13
|
|
|
14
14
|
set -euo pipefail
|
|
15
15
|
|
|
16
|
-
#
|
|
17
|
-
|
|
18
|
-
d="$PWD"
|
|
19
|
-
while [[ ! -d "$d/.pennyfarthing" ]] && [[ "$d" != "/" ]]; do
|
|
20
|
-
d="$(dirname "$d")"
|
|
21
|
-
done
|
|
22
|
-
PROJECT_ROOT="$d"
|
|
23
|
-
fi
|
|
16
|
+
# Find project root
|
|
17
|
+
source "$(dirname "${BASH_SOURCE[0]}")/../lib/find-root.sh"
|
|
24
18
|
|
|
25
19
|
SPRINT_FILE="$PROJECT_ROOT/sprint/current-sprint.yaml"
|
|
26
20
|
FIX_MODE="${1:-}"
|