@kennethsolomon/shipkit 3.7.0 → 3.8.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 +5 -0
- package/package.json +1 -1
- package/skills/sk:fast-track/SKILL.md +80 -0
- package/skills/sk:gates/SKILL.md +97 -0
- package/skills/sk:retro/SKILL.md +124 -0
- package/skills/sk:reverse-doc/SKILL.md +116 -0
- package/skills/sk:scope-check/SKILL.md +93 -0
- package/skills/sk:setup-claude/SKILL.md +53 -0
- package/skills/sk:setup-claude/scripts/apply_setup_claude.py +206 -6
- package/skills/sk:setup-claude/templates/.claude/agents/e2e-tester.md +46 -0
- package/skills/sk:setup-claude/templates/.claude/agents/linter.md +53 -0
- package/skills/sk:setup-claude/templates/.claude/agents/perf-auditor.md +43 -0
- package/skills/sk:setup-claude/templates/.claude/agents/security-auditor.md +47 -0
- package/skills/sk:setup-claude/templates/.claude/agents/test-runner.md +42 -0
- package/skills/sk:setup-claude/templates/.claude/rules/api.md.template +14 -0
- package/skills/sk:setup-claude/templates/.claude/rules/frontend.md.template +15 -0
- package/skills/sk:setup-claude/templates/.claude/rules/laravel.md.template +15 -0
- package/skills/sk:setup-claude/templates/.claude/rules/react.md.template +14 -0
- package/skills/sk:setup-claude/templates/.claude/rules/tests.md.template +16 -0
- package/skills/sk:setup-claude/templates/.claude/settings.json.template +76 -0
- package/skills/sk:setup-claude/templates/.claude/statusline.sh +50 -0
- package/skills/sk:setup-claude/templates/hooks/log-agent.sh +24 -0
- package/skills/sk:setup-claude/templates/hooks/pre-compact.sh +44 -0
- package/skills/sk:setup-claude/templates/hooks/session-start.sh +53 -0
- package/skills/sk:setup-claude/templates/hooks/session-stop.sh +33 -0
- package/skills/sk:setup-claude/templates/hooks/validate-commit.sh +81 -0
- package/skills/sk:setup-claude/templates/hooks/validate-push.sh +43 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Claude Code statusline: Shows persistent status in CLI
|
|
3
|
+
# Receives JSON on stdin with context_window, model info
|
|
4
|
+
# Outputs a single formatted line
|
|
5
|
+
|
|
6
|
+
INPUT=$(cat)
|
|
7
|
+
|
|
8
|
+
# Parse context and model — use jq if available
|
|
9
|
+
if command -v jq >/dev/null 2>&1; then
|
|
10
|
+
MODEL=$(echo "$INPUT" | jq -r '.model // "unknown"' 2>/dev/null)
|
|
11
|
+
CTX_USED=$(echo "$INPUT" | jq -r '.context_window.used // 0' 2>/dev/null)
|
|
12
|
+
CTX_TOTAL=$(echo "$INPUT" | jq -r '.context_window.total // 1' 2>/dev/null)
|
|
13
|
+
else
|
|
14
|
+
MODEL="unknown"
|
|
15
|
+
CTX_USED=0
|
|
16
|
+
CTX_TOTAL=1
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
# Calculate context percentage
|
|
20
|
+
if [ "$CTX_TOTAL" -gt 0 ] 2>/dev/null; then
|
|
21
|
+
CTX_PCT=$((CTX_USED * 100 / CTX_TOTAL))
|
|
22
|
+
else
|
|
23
|
+
CTX_PCT=0
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# Branch
|
|
27
|
+
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "none")
|
|
28
|
+
|
|
29
|
+
# Current workflow step
|
|
30
|
+
STEP="—"
|
|
31
|
+
if [ -f "tasks/workflow-status.md" ]; then
|
|
32
|
+
NEXT_LINE=$(grep -E ">>\s*next\s*<<" "tasks/workflow-status.md" 2>/dev/null | head -1)
|
|
33
|
+
if [ -n "$NEXT_LINE" ]; then
|
|
34
|
+
# Extract step number and name from table row
|
|
35
|
+
STEP_NUM=$(echo "$NEXT_LINE" | grep -oE '^\|[[:space:]]*[0-9]+' | grep -oE '[0-9]+')
|
|
36
|
+
STEP_NAME=$(echo "$NEXT_LINE" | sed 's/.*| *>> next << *|.*//' | sed 's/|.*//;s/^ *//;s/ *$//')
|
|
37
|
+
if [ -n "$STEP_NUM" ]; then
|
|
38
|
+
STEP="Step ${STEP_NUM}"
|
|
39
|
+
fi
|
|
40
|
+
fi
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# Task name from todo.md
|
|
44
|
+
TASK="—"
|
|
45
|
+
if [ -f "tasks/todo.md" ]; then
|
|
46
|
+
TASK=$(head -1 "tasks/todo.md" 2>/dev/null | sed 's/^# TODO.*— //' | cut -c1-40)
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
# Output single line
|
|
50
|
+
echo "[${CTX_PCT}%] ${MODEL} | ${STEP} | ${BRANCH} | ${TASK}"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Claude Code SubagentStart hook: Log agent invocations for audit trail
|
|
3
|
+
# Tracks which agents are being used and when
|
|
4
|
+
#
|
|
5
|
+
# Input schema (SubagentStart):
|
|
6
|
+
# { "agent_id": "agent-abc123", "agent_name": "linter", ... }
|
|
7
|
+
|
|
8
|
+
INPUT=$(cat)
|
|
9
|
+
|
|
10
|
+
# Parse agent name
|
|
11
|
+
if command -v jq >/dev/null 2>&1; then
|
|
12
|
+
AGENT_NAME=$(echo "$INPUT" | jq -r '.agent_name // "unknown"' 2>/dev/null)
|
|
13
|
+
else
|
|
14
|
+
AGENT_NAME=$(echo "$INPUT" | grep -oE '"agent_name"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/"agent_name"[[:space:]]*:[[:space:]]*"//;s/"$//')
|
|
15
|
+
[ -z "$AGENT_NAME" ] && AGENT_NAME="unknown"
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
|
19
|
+
|
|
20
|
+
# Append to audit log (create tasks/ dir if needed)
|
|
21
|
+
mkdir -p tasks 2>/dev/null
|
|
22
|
+
echo "$TIMESTAMP | Agent invoked: $AGENT_NAME" >> "tasks/agent-audit.log" 2>/dev/null
|
|
23
|
+
|
|
24
|
+
exit 0
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Claude Code PreCompact hook: Preserve session state before context compression
|
|
3
|
+
# Outputs critical state so Claude retains it after compaction
|
|
4
|
+
|
|
5
|
+
echo "=== Pre-Compaction State Snapshot ==="
|
|
6
|
+
|
|
7
|
+
# Workflow status
|
|
8
|
+
if [ -f "tasks/workflow-status.md" ]; then
|
|
9
|
+
echo ""
|
|
10
|
+
echo "--- workflow-status.md ---"
|
|
11
|
+
cat "tasks/workflow-status.md" 2>/dev/null | head -30
|
|
12
|
+
TOTAL_LINES=$(wc -l < "tasks/workflow-status.md" 2>/dev/null | tr -d ' ')
|
|
13
|
+
if [ "$TOTAL_LINES" -gt 30 ]; then
|
|
14
|
+
echo " ... ($TOTAL_LINES total lines)"
|
|
15
|
+
fi
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
# Git status
|
|
19
|
+
echo ""
|
|
20
|
+
echo "--- Uncommitted Changes ---"
|
|
21
|
+
STAGED=$(git diff --cached --name-only 2>/dev/null)
|
|
22
|
+
UNSTAGED=$(git diff --name-only 2>/dev/null)
|
|
23
|
+
UNTRACKED=$(git ls-files --others --exclude-standard 2>/dev/null)
|
|
24
|
+
|
|
25
|
+
[ -n "$STAGED" ] && echo "Staged:" && echo "$STAGED" | sed 's/^/ /'
|
|
26
|
+
[ -n "$UNSTAGED" ] && echo "Unstaged:" && echo "$UNSTAGED" | sed 's/^/ /'
|
|
27
|
+
[ -n "$UNTRACKED" ] && echo "Untracked:" && echo "$UNTRACKED" | sed 's/^/ /'
|
|
28
|
+
|
|
29
|
+
if [ -z "$STAGED" ] && [ -z "$UNSTAGED" ] && [ -z "$UNTRACKED" ]; then
|
|
30
|
+
echo " (clean)"
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# Log compaction event
|
|
34
|
+
if [ -d "tasks" ]; then
|
|
35
|
+
echo "" >> "tasks/progress.md" 2>/dev/null
|
|
36
|
+
echo "### [$(date +%Y-%m-%d)] Context compaction occurred at $(date +%H:%M:%S)" >> "tasks/progress.md" 2>/dev/null
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
echo ""
|
|
40
|
+
echo "--- Recovery ---"
|
|
41
|
+
echo "Read tasks/workflow-status.md to restore current step."
|
|
42
|
+
echo "Read tasks/progress.md for recent work."
|
|
43
|
+
echo "==================================="
|
|
44
|
+
exit 0
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Claude Code SessionStart hook: Load project context at session start
|
|
3
|
+
# Outputs context that Claude sees when a session begins
|
|
4
|
+
|
|
5
|
+
echo "=== ShipKit — Session Context ==="
|
|
6
|
+
|
|
7
|
+
# Current branch
|
|
8
|
+
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
|
|
9
|
+
if [ -n "$BRANCH" ]; then
|
|
10
|
+
echo "Branch: $BRANCH"
|
|
11
|
+
echo ""
|
|
12
|
+
echo "Recent commits:"
|
|
13
|
+
git log --oneline -5 2>/dev/null | while read -r line; do
|
|
14
|
+
echo " $line"
|
|
15
|
+
done
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
# Current workflow step from workflow-status.md
|
|
19
|
+
if [ -f "tasks/workflow-status.md" ]; then
|
|
20
|
+
echo ""
|
|
21
|
+
NEXT_STEP=$(grep -E ">>\s*next\s*<<" "tasks/workflow-status.md" 2>/dev/null | head -1)
|
|
22
|
+
if [ -n "$NEXT_STEP" ]; then
|
|
23
|
+
echo "Workflow: $NEXT_STEP"
|
|
24
|
+
else
|
|
25
|
+
echo "Workflow: all steps complete or not started"
|
|
26
|
+
fi
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# Tech debt count
|
|
30
|
+
if [ -f "tasks/tech-debt.md" ]; then
|
|
31
|
+
TOTAL=$(grep -c "^### \[" "tasks/tech-debt.md" 2>/dev/null || echo 0)
|
|
32
|
+
RESOLVED=$(grep -c "^Resolved:" "tasks/tech-debt.md" 2>/dev/null || echo 0)
|
|
33
|
+
UNRESOLVED=$((TOTAL - RESOLVED))
|
|
34
|
+
if [ "$UNRESOLVED" -gt 0 ]; then
|
|
35
|
+
echo "Tech Debt: $UNRESOLVED unresolved item(s)"
|
|
36
|
+
fi
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
# Code health (skip on large codebases to keep session start fast)
|
|
40
|
+
if [ -d "src" ]; then
|
|
41
|
+
FILE_COUNT=$(find src/ -type f 2>/dev/null | head -1001 | wc -l | tr -d ' ')
|
|
42
|
+
if [ "$FILE_COUNT" -le 1000 ]; then
|
|
43
|
+
TODO_COUNT=$(grep -r "TODO" src/ 2>/dev/null | wc -l | tr -d ' ')
|
|
44
|
+
FIXME_COUNT=$(grep -r "FIXME" src/ 2>/dev/null | wc -l | tr -d ' ')
|
|
45
|
+
if [ "$TODO_COUNT" -gt 0 ] || [ "$FIXME_COUNT" -gt 0 ]; then
|
|
46
|
+
echo ""
|
|
47
|
+
echo "Code health: ${TODO_COUNT} TODOs, ${FIXME_COUNT} FIXMEs in src/"
|
|
48
|
+
fi
|
|
49
|
+
fi
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
echo "==================================="
|
|
53
|
+
exit 0
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Claude Code Stop hook: Log session accomplishments
|
|
3
|
+
# Runs when a Claude Code session ends
|
|
4
|
+
|
|
5
|
+
# Only log if tasks/ directory exists (ShipKit project)
|
|
6
|
+
if [ ! -d "tasks" ]; then
|
|
7
|
+
exit 0
|
|
8
|
+
fi
|
|
9
|
+
|
|
10
|
+
TIMESTAMP=$(date +%Y-%m-%d\ %H:%M:%S)
|
|
11
|
+
|
|
12
|
+
# Get current branch
|
|
13
|
+
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
|
|
14
|
+
|
|
15
|
+
# Count commits in this session (last hour)
|
|
16
|
+
RECENT_COMMITS=$(git log --since="1 hour ago" --oneline 2>/dev/null | wc -l | tr -d ' ')
|
|
17
|
+
|
|
18
|
+
# Get current workflow step
|
|
19
|
+
CURRENT_STEP=""
|
|
20
|
+
if [ -f "tasks/workflow-status.md" ]; then
|
|
21
|
+
CURRENT_STEP=$(grep -E ">>\s*next\s*<<" "tasks/workflow-status.md" 2>/dev/null | head -1 | sed 's/.*| //' | sed 's/ |.*//')
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# Append session end to progress.md
|
|
25
|
+
{
|
|
26
|
+
echo ""
|
|
27
|
+
echo "### [$TIMESTAMP] Session ended"
|
|
28
|
+
echo "- Branch: $BRANCH"
|
|
29
|
+
echo "- Commits this session: $RECENT_COMMITS"
|
|
30
|
+
[ -n "$CURRENT_STEP" ] && echo "- Next step: $CURRENT_STEP"
|
|
31
|
+
} >> "tasks/progress.md" 2>/dev/null
|
|
32
|
+
|
|
33
|
+
exit 0
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Claude Code PreToolUse hook: Validates git commit commands
|
|
3
|
+
# Receives JSON on stdin with tool_input.command
|
|
4
|
+
# Exit 0 = allow, Exit 2 = block (stderr shown to Claude)
|
|
5
|
+
#
|
|
6
|
+
# Validates: conventional commit format, debug statements, hardcoded secrets
|
|
7
|
+
|
|
8
|
+
INPUT=$(cat)
|
|
9
|
+
|
|
10
|
+
# Parse command — use jq if available, fall back to grep
|
|
11
|
+
if command -v jq >/dev/null 2>&1; then
|
|
12
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
|
|
13
|
+
else
|
|
14
|
+
COMMAND=$(echo "$INPUT" | grep -oE '"command"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/"command"[[:space:]]*:[[:space:]]*"//;s/"$//')
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
# Only process git commit commands
|
|
18
|
+
if ! echo "$COMMAND" | grep -qE '^git[[:space:]]+commit'; then
|
|
19
|
+
exit 0
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
WARNINGS=""
|
|
23
|
+
|
|
24
|
+
# Extract commit message from -m flag
|
|
25
|
+
COMMIT_MSG=$(echo "$COMMAND" | grep -oE '\-m[[:space:]]+"[^"]*"' | sed 's/-m[[:space:]]*"//;s/"$//' || echo "")
|
|
26
|
+
if [ -z "$COMMIT_MSG" ]; then
|
|
27
|
+
COMMIT_MSG=$(echo "$COMMAND" | grep -oE "\-m[[:space:]]+'[^']*'" | sed "s/-m[[:space:]]*'//;s/'$//" || echo "")
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# Validate conventional commit format (type(scope): message or type: message)
|
|
31
|
+
if [ -n "$COMMIT_MSG" ]; then
|
|
32
|
+
if ! echo "$COMMIT_MSG" | grep -qE '^(feat|fix|docs|refactor|chore|test|style|perf|ci|build|revert)(\([a-zA-Z0-9_-]+\))?(!)?:[[:space:]].+'; then
|
|
33
|
+
WARNINGS="$WARNINGS\nCOMMIT: Message does not follow conventional commit format: type(scope): message"
|
|
34
|
+
fi
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# Check staged files for debug statements and secrets
|
|
38
|
+
STAGED=$(git diff --cached --name-only 2>/dev/null)
|
|
39
|
+
if [ -n "$STAGED" ]; then
|
|
40
|
+
# Read full staged diff once (avoids repeated git subprocess calls per file)
|
|
41
|
+
FULL_DIFF=$(git diff --cached 2>/dev/null)
|
|
42
|
+
|
|
43
|
+
while IFS= read -r file; do
|
|
44
|
+
if [ -f "$file" ]; then
|
|
45
|
+
FILE_DIFF=$(echo "$FULL_DIFF" | sed -n "/^diff --git a\/$file /,/^diff --git /p")
|
|
46
|
+
# JavaScript/TypeScript debug
|
|
47
|
+
if echo "$file" | grep -qE '\.(js|ts|jsx|tsx)$'; then
|
|
48
|
+
if echo "$FILE_DIFF" | grep -E '^\+.*console\.(log|debug|warn)\(' | grep -qv '//.*console'; then
|
|
49
|
+
WARNINGS="$WARNINGS\nDEBUG: $file has console.log/debug/warn in staged changes"
|
|
50
|
+
fi
|
|
51
|
+
if echo "$FILE_DIFF" | grep -qE '^\+.*debugger'; then
|
|
52
|
+
WARNINGS="$WARNINGS\nDEBUG: $file has debugger statement in staged changes"
|
|
53
|
+
fi
|
|
54
|
+
fi
|
|
55
|
+
# PHP debug
|
|
56
|
+
if echo "$file" | grep -qE '\.php$'; then
|
|
57
|
+
if echo "$FILE_DIFF" | grep -qE '^\+.*(dd\(|dump\(|var_dump\(|print_r\()'; then
|
|
58
|
+
WARNINGS="$WARNINGS\nDEBUG: $file has dd/dump/var_dump in staged changes"
|
|
59
|
+
fi
|
|
60
|
+
fi
|
|
61
|
+
# Python debug
|
|
62
|
+
if echo "$file" | grep -qE '\.py$'; then
|
|
63
|
+
if echo "$FILE_DIFF" | grep -qE '^\+.*(breakpoint\(\)|pdb\.set_trace\(\))'; then
|
|
64
|
+
WARNINGS="$WARNINGS\nDEBUG: $file has breakpoint/pdb in staged changes"
|
|
65
|
+
fi
|
|
66
|
+
fi
|
|
67
|
+
fi
|
|
68
|
+
done <<< "$STAGED"
|
|
69
|
+
|
|
70
|
+
# Check for potential hardcoded secrets (reuse FULL_DIFF)
|
|
71
|
+
if echo "$FULL_DIFF" | grep -qE '^\+.*(PRIVATE_KEY|SECRET_KEY|API_KEY|PASSWORD|TOKEN)[[:space:]]*=.*[a-zA-Z0-9]{16,}'; then
|
|
72
|
+
WARNINGS="$WARNINGS\nSECRET: Staged changes may contain hardcoded secrets. Review before committing."
|
|
73
|
+
fi
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
# Print warnings (non-blocking) and allow commit
|
|
77
|
+
if [ -n "$WARNINGS" ]; then
|
|
78
|
+
echo -e "=== Commit Validation Warnings ===$WARNINGS\n================================" >&2
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
exit 0
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Claude Code PreToolUse hook: Warns on push to protected branches
|
|
3
|
+
# Receives JSON on stdin with tool_input.command
|
|
4
|
+
# Exit 0 = allow (warnings only), Exit 2 = block
|
|
5
|
+
#
|
|
6
|
+
# Warns on: push to main/master/production/release, force push
|
|
7
|
+
|
|
8
|
+
INPUT=$(cat)
|
|
9
|
+
|
|
10
|
+
# Parse command
|
|
11
|
+
if command -v jq >/dev/null 2>&1; then
|
|
12
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
|
|
13
|
+
else
|
|
14
|
+
COMMAND=$(echo "$INPUT" | grep -oE '"command"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/"command"[[:space:]]*:[[:space:]]*"//;s/"$//')
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
# Only process git push commands
|
|
18
|
+
if ! echo "$COMMAND" | grep -qE '^git[[:space:]]+push'; then
|
|
19
|
+
exit 0
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
WARNINGS=""
|
|
23
|
+
|
|
24
|
+
# Check for protected branch names
|
|
25
|
+
for branch in main master production release; do
|
|
26
|
+
if echo "$COMMAND" | grep -qE "(origin[[:space:]]+${branch}|[[:space:]]${branch}$)"; then
|
|
27
|
+
WARNINGS="$WARNINGS\nPROTECTED: Pushing to '${branch}' — this is a protected branch. Confirm this is intentional."
|
|
28
|
+
fi
|
|
29
|
+
done
|
|
30
|
+
|
|
31
|
+
# Check for force push
|
|
32
|
+
if echo "$COMMAND" | grep -qE '\-\-force([[:space:]]|$)|\-f([[:space:]]|$)'; then
|
|
33
|
+
WARNINGS="$WARNINGS\nFORCE: Using --force push. This rewrites remote history and is destructive."
|
|
34
|
+
fi
|
|
35
|
+
if echo "$COMMAND" | grep -qE '\-\-force-with-lease'; then
|
|
36
|
+
WARNINGS="$WARNINGS\nFORCE: Using --force-with-lease. Safer than --force but still rewrites history."
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
if [ -n "$WARNINGS" ]; then
|
|
40
|
+
echo -e "=== Push Validation Warnings ===$WARNINGS\n================================" >&2
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
exit 0
|