@pageai/ralph-loop 1.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 (120) hide show
  1. package/.agent/PROMPT.md +58 -0
  2. package/.agent/STEERING.md +3 -0
  3. package/.agent/logs/LOG.md +13 -0
  4. package/.agent/prd/.gitkeep +0 -0
  5. package/.agent/screenshots/.gitkeep +0 -0
  6. package/.agent/skills/component-refactoring/SKILL.md +247 -0
  7. package/.agent/skills/component-refactoring/references/complexity-patterns.md +485 -0
  8. package/.agent/skills/component-refactoring/references/component-splitting.md +419 -0
  9. package/.agent/skills/component-refactoring/references/hook-extraction.md +317 -0
  10. package/.agent/skills/e2e-tester/SKILL.md +595 -0
  11. package/.agent/skills/frontend-code-review/SKILL.md +73 -0
  12. package/.agent/skills/frontend-code-review/references/code-quality.md +28 -0
  13. package/.agent/skills/frontend-code-review/references/performance.md +36 -0
  14. package/.agent/skills/frontend-testing/SKILL.md +316 -0
  15. package/.agent/skills/frontend-testing/assets/component-test.template.tsx +293 -0
  16. package/.agent/skills/frontend-testing/assets/hook-test.template.ts +207 -0
  17. package/.agent/skills/frontend-testing/assets/utility-test.template.ts +154 -0
  18. package/.agent/skills/frontend-testing/references/async-testing.md +345 -0
  19. package/.agent/skills/frontend-testing/references/checklist.md +188 -0
  20. package/.agent/skills/frontend-testing/references/common-patterns.md +449 -0
  21. package/.agent/skills/frontend-testing/references/mocking.md +289 -0
  22. package/.agent/skills/frontend-testing/references/workflow.md +265 -0
  23. package/.agent/skills/prd-creator/JSON.md +613 -0
  24. package/.agent/skills/prd-creator/PRD.md +196 -0
  25. package/.agent/skills/prd-creator/SKILL.md +143 -0
  26. package/.agent/skills/skill-creator/SKILL.md +355 -0
  27. package/.agent/skills/skill-creator/references/output-patterns.md +86 -0
  28. package/.agent/skills/skill-creator/references/workflows.md +28 -0
  29. package/.agent/skills/skill-creator/scripts/init_skill.py +300 -0
  30. package/.agent/skills/skill-creator/scripts/package_skill.py +110 -0
  31. package/.agent/skills/vercel-react-best-practices/AGENTS.md +2249 -0
  32. package/.agent/skills/vercel-react-best-practices/SKILL.md +125 -0
  33. package/.agent/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  34. package/.agent/skills/vercel-react-best-practices/rules/advanced-use-latest.md +49 -0
  35. package/.agent/skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
  36. package/.agent/skills/vercel-react-best-practices/rules/async-defer-await.md +80 -0
  37. package/.agent/skills/vercel-react-best-practices/rules/async-dependencies.md +36 -0
  38. package/.agent/skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
  39. package/.agent/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
  40. package/.agent/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +59 -0
  41. package/.agent/skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
  42. package/.agent/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
  43. package/.agent/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
  44. package/.agent/skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
  45. package/.agent/skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
  46. package/.agent/skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
  47. package/.agent/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +82 -0
  48. package/.agent/skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
  49. package/.agent/skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
  50. package/.agent/skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
  51. package/.agent/skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
  52. package/.agent/skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
  53. package/.agent/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
  54. package/.agent/skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
  55. package/.agent/skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
  56. package/.agent/skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
  57. package/.agent/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
  58. package/.agent/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
  59. package/.agent/skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
  60. package/.agent/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  61. package/.agent/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
  62. package/.agent/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
  63. package/.agent/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
  64. package/.agent/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
  65. package/.agent/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
  66. package/.agent/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
  67. package/.agent/skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
  68. package/.agent/skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
  69. package/.agent/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
  70. package/.agent/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  71. package/.agent/skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
  72. package/.agent/skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
  73. package/.agent/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
  74. package/.agent/skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
  75. package/.agent/skills/vercel-react-best-practices/rules/server-cache-react.md +26 -0
  76. package/.agent/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +79 -0
  77. package/.agent/skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
  78. package/.agent/skills/vitest-best-practices/AGENTS.md +84 -0
  79. package/.agent/skills/vitest-best-practices/SKILL.md +130 -0
  80. package/.agent/skills/vitest-best-practices/references/aaa-pattern.md +260 -0
  81. package/.agent/skills/vitest-best-practices/references/assertions.md +393 -0
  82. package/.agent/skills/vitest-best-practices/references/async-testing.md +454 -0
  83. package/.agent/skills/vitest-best-practices/references/error-handling.md +382 -0
  84. package/.agent/skills/vitest-best-practices/references/organization.md +212 -0
  85. package/.agent/skills/vitest-best-practices/references/parameterized-tests.md +297 -0
  86. package/.agent/skills/vitest-best-practices/references/performance.md +528 -0
  87. package/.agent/skills/vitest-best-practices/references/snapshot-testing.md +483 -0
  88. package/.agent/skills/vitest-best-practices/references/test-doubles.md +499 -0
  89. package/.agent/skills/vitest-best-practices/references/vitest-features.md +529 -0
  90. package/.agent/skills/web-design-guidelines/SKILL.md +39 -0
  91. package/.agent/tasks/.gitkeep +0 -0
  92. package/.agent/tasks.json +1 -0
  93. package/.claude/agents/code-reviewer.md +172 -0
  94. package/.claude/commands/aw.md +50 -0
  95. package/.claude/hooks/play-sound.js +87 -0
  96. package/.claude/hooks/pre-tool-use.js +40 -0
  97. package/.claude/settings.json +54 -0
  98. package/.claude/settings.local.json +13 -0
  99. package/.mcp.json +31 -0
  100. package/AGENTS.md +44 -0
  101. package/CLAUDE.md +1 -0
  102. package/README.md +236 -0
  103. package/bin/cli.js +156 -0
  104. package/bin/lib/copy.js +149 -0
  105. package/bin/lib/display.js +137 -0
  106. package/package.json +65 -0
  107. package/ralph.sh +333 -0
  108. package/scripts/lib/args.sh +44 -0
  109. package/scripts/lib/cleanup.sh +53 -0
  110. package/scripts/lib/constants.sh +25 -0
  111. package/scripts/lib/display.sh +196 -0
  112. package/scripts/lib/logging.sh +30 -0
  113. package/scripts/lib/notify.sh +41 -0
  114. package/scripts/lib/output.sh +147 -0
  115. package/scripts/lib/preflight.sh +57 -0
  116. package/scripts/lib/preview.sh +77 -0
  117. package/scripts/lib/promise.sh +76 -0
  118. package/scripts/lib/spinner.sh +85 -0
  119. package/scripts/lib/terminal.sh +57 -0
  120. package/scripts/lib/timing.sh +223 -0
@@ -0,0 +1,85 @@
1
+ #!/bin/bash
2
+ # Spinner module for ralph.sh
3
+ # Animated spinner display with step tracking
4
+ # Dependencies: constants.sh, timing.sh, terminal.sh
5
+
6
+ # Spinner characters
7
+ SPINNER='โ ‹โ ™โ นโ ธโ ผโ ดโ ฆโ งโ ‡โ '
8
+ SPINNER_PID=""
9
+ CURRENT_STEP="Thinking"
10
+
11
+ # Spinner function - runs in background, reads step from STEP_FILE
12
+ # Usage: start_spinner; do_work; stop_spinner
13
+ start_spinner() {
14
+ echo "Thinking" > "$STEP_FILE"
15
+ echo "" > "$PREVIEW_LINE_FILE"
16
+
17
+ (
18
+ # Spinner chars as array for proper unicode handling
19
+ local -a SPIN_CHARS=(โ ‹ โ ™ โ น โ ธ โ ผ โ ด โ ฆ โ ง โ ‡ โ )
20
+ local i=0
21
+ local spin_len=${#SPIN_CHARS[@]}
22
+ local term_width=$(tput cols 2>/dev/null || echo 80)
23
+ local preview_max=$((term_width - 6)) # Leave room for " > "
24
+ while true; do
25
+ local step=$(cat "$STEP_FILE" 2>/dev/null || echo "Thinking")
26
+ local preview=$(cat "$PREVIEW_LINE_FILE" 2>/dev/null || echo "")
27
+ local char="${SPIN_CHARS[$i]}"
28
+ # Truncate preview if needed
29
+ if [ ${#preview} -gt $preview_max ]; then
30
+ preview="${preview:0:$((preview_max - 3))}..."
31
+ fi
32
+ # Line 1: spinner + step, Line 2: dimmed preview
33
+ printf "\r\033[K ${C}%s${R} %s" "$char" "$step"
34
+ if [ -n "$preview" ]; then
35
+ printf "\n\033[K ${D}โ–ธ %s${R}\033[A" "$preview"
36
+ fi
37
+ i=$(( (i + 1) % spin_len ))
38
+ sleep 0.1
39
+ done
40
+ ) &
41
+ SPINNER_PID=$!
42
+ }
43
+
44
+ # Stop the spinner
45
+ stop_spinner() {
46
+ if [ -n "$SPINNER_PID" ] && kill -0 "$SPINNER_PID" 2>/dev/null; then
47
+ kill "$SPINNER_PID" 2>/dev/null
48
+ wait "$SPINNER_PID" 2>/dev/null || true
49
+ # Clear both lines (spinner and preview)
50
+ printf "\r\033[K\n\033[K\033[A\r"
51
+ fi
52
+ SPINNER_PID=""
53
+ }
54
+
55
+ # Update spinner step based on output line and record step timing
56
+ update_spinner_step() {
57
+ local line="$1"
58
+ local detected=$(detect_step "$line")
59
+ if [ -n "$detected" ]; then
60
+ echo "$detected" > "$STEP_FILE"
61
+
62
+ # Clean step name (remove trailing spaces) for timing tracking
63
+ local clean_step=$(echo "$detected" | sed 's/ *$//')
64
+
65
+ # Record timing if step changed
66
+ if [ "$clean_step" != "$CURRENT_STEP_NAME" ]; then
67
+ record_step_time "$clean_step"
68
+ fi
69
+ fi
70
+ }
71
+
72
+ # Update the preview line shown under the spinner
73
+ update_preview_line() {
74
+ local line="$1"
75
+ # Skip empty lines or lines that are just whitespace
76
+ [ -z "$line" ] && return
77
+ [[ "$line" =~ ^[[:space:]]*$ ]] && return
78
+ # Sanitize: replace newlines/tabs with spaces, collapse multiple spaces
79
+ # This prevents multi-line content from breaking cursor positioning
80
+ line=$(echo "$line" | tr '\n\t\r' ' ' | sed 's/ */ /g; s/^ *//; s/ *$//')
81
+ # Skip if sanitization resulted in empty string
82
+ [ -z "$line" ] && return
83
+ # Write to preview file (spinner reads this)
84
+ echo "$line" > "$PREVIEW_LINE_FILE"
85
+ }
@@ -0,0 +1,57 @@
1
+ #!/bin/bash
2
+ # Terminal module for ralph.sh
3
+ # ANSI support detection and terminal width utilities
4
+ # Dependencies: constants.sh
5
+
6
+ # Check if terminal supports ANSI escape sequences
7
+ check_ansi_support() {
8
+ # Check if $TERM is set and not dumb
9
+ if [ -z "$TERM" ] || [ "$TERM" = "dumb" ]; then
10
+ ANSI_SUPPORTED=false
11
+ return
12
+ fi
13
+ # Check for known terminal types that don't support ANSI
14
+ case "$TERM" in
15
+ dumb|unknown|network)
16
+ ANSI_SUPPORTED=false
17
+ return
18
+ ;;
19
+ esac
20
+ # Check if stdout is a terminal
21
+ if [ ! -t 1 ]; then
22
+ ANSI_SUPPORTED=false
23
+ return
24
+ fi
25
+ ANSI_SUPPORTED=true
26
+ }
27
+
28
+ # Get terminal width (default to 80 if unavailable)
29
+ get_terminal_width() {
30
+ if command -v tput &> /dev/null; then
31
+ local width=$(tput cols 2>/dev/null)
32
+ if [ -n "$width" ] && [ "$width" -gt 0 ]; then
33
+ echo "$width"
34
+ return
35
+ fi
36
+ fi
37
+ # Fallback to stty
38
+ if command -v stty &> /dev/null; then
39
+ local width=$(stty size 2>/dev/null | cut -d' ' -f2)
40
+ if [ -n "$width" ] && [ "$width" -gt 0 ]; then
41
+ echo "$width"
42
+ return
43
+ fi
44
+ fi
45
+ echo "80"
46
+ }
47
+
48
+ # Truncate line to fit terminal width
49
+ truncate_line() {
50
+ local line="$1"
51
+ local max_width="$2"
52
+ if [ ${#line} -gt $max_width ]; then
53
+ echo "${line:0:$((max_width - 3))}..."
54
+ else
55
+ echo "$line"
56
+ fi
57
+ }
@@ -0,0 +1,223 @@
1
+ #!/bin/bash
2
+ # Timing module for ralph.sh
3
+ # Step timing tracking and duration formatting
4
+ # Dependencies: constants.sh
5
+ #
6
+ # Steps: Thinking, Reading code, Implementing, Writing tests, Testing, Linting,
7
+ # Typechecking, Committing
8
+
9
+ # Step timing tracking (using indexed arrays for bash 3.x compatibility)
10
+ STEP_NAMES=("Thinking" "Reading code" "Implementing" "Writing tests" "Testing" "Linting" "Typechecking" "Committing")
11
+ ITERATION_STEP_VALUES=(0 0 0 0 0 0 0 0) # Step times for current iteration
12
+ SESSION_STEP_VALUES=(0 0 0 0 0 0 0 0) # Accumulated step times across all iterations
13
+ CURRENT_STEP_NAME="" # Name of current step being timed
14
+ CURRENT_STEP_START=0 # Timestamp when current step started
15
+
16
+ # Get step emoji by name (bash 3.x compatible)
17
+ get_step_emoji() {
18
+ case "$1" in
19
+ "Thinking") echo "๐Ÿค”" ;;
20
+ "Reading code") echo "๐Ÿ“–" ;;
21
+ "Implementing") echo "โšก" ;;
22
+ "Writing tests") echo "โœ๏ธ" ;;
23
+ "Testing") echo "๐Ÿงช" ;;
24
+ "Linting") echo "๐Ÿงน" ;;
25
+ "Typechecking") echo "๐Ÿ“" ;;
26
+ "Committing") echo "๐Ÿ“ฆ" ;;
27
+ *) echo "" ;;
28
+ esac
29
+ }
30
+
31
+ # Get step index by name (-1 if not found)
32
+ get_step_index() {
33
+ local name="$1"
34
+ for i in "${!STEP_NAMES[@]}"; do
35
+ if [ "${STEP_NAMES[$i]}" = "$name" ]; then
36
+ echo "$i"
37
+ return
38
+ fi
39
+ done
40
+ echo "-1"
41
+ }
42
+
43
+ # Format duration in seconds to MM:SS or HH:MM:SS
44
+ format_duration() {
45
+ local seconds=$1
46
+ local hours=$((seconds / 3600))
47
+ local mins=$(((seconds % 3600) / 60))
48
+ local secs=$((seconds % 60))
49
+
50
+ if [ $hours -gt 0 ]; then
51
+ printf "%02d:%02d:%02d" $hours $mins $secs
52
+ else
53
+ printf "%02d:%02d" $mins $secs
54
+ fi
55
+ }
56
+
57
+ # Format delta with color coding (stock market style)
58
+ # Returns: colored string with +/- prefix, or empty if first iteration
59
+ format_delta() {
60
+ local current=$1
61
+ local previous=$2
62
+
63
+ # Skip if first iteration (no previous time)
64
+ if [ "$previous" -eq 0 ]; then
65
+ echo ""
66
+ return
67
+ fi
68
+
69
+ local delta=$((current - previous))
70
+
71
+ if [ $delta -lt 0 ]; then
72
+ # Faster (green with - prefix)
73
+ local abs_delta=$((-delta))
74
+ printf "${GR}-%ds${R}" $abs_delta
75
+ elif [ $delta -gt 0 ]; then
76
+ # Slower (red with + prefix)
77
+ printf "${RD}+%ds${R}" $delta
78
+ else
79
+ # Same time
80
+ printf "~"
81
+ fi
82
+ }
83
+
84
+ # Record time spent in current step when transitioning to a new step
85
+ # Usage: record_step_time "new_step_name"
86
+ # Pass empty string to finalize the last step without starting a new one
87
+ record_step_time() {
88
+ local new_step="$1"
89
+
90
+ # If we have a current step, record its elapsed time
91
+ if [ -n "$CURRENT_STEP_NAME" ] && [ "$CURRENT_STEP_START" -gt 0 ]; then
92
+ local now=$(date +%s)
93
+ local elapsed=$((now - CURRENT_STEP_START))
94
+
95
+ # Only record if there's actual time (more than 0 seconds)
96
+ if [ "$elapsed" -gt 0 ]; then
97
+ local idx=$(get_step_index "$CURRENT_STEP_NAME")
98
+ if [ "$idx" -ge 0 ]; then
99
+ # Add to iteration step times
100
+ local current_time=${ITERATION_STEP_VALUES[$idx]}
101
+ ITERATION_STEP_VALUES[$idx]=$((current_time + elapsed))
102
+
103
+ # Add to session step times
104
+ local session_time=${SESSION_STEP_VALUES[$idx]}
105
+ SESSION_STEP_VALUES[$idx]=$((session_time + elapsed))
106
+ fi
107
+ fi
108
+ fi
109
+
110
+ # Start timing the new step (if provided)
111
+ if [ -n "$new_step" ]; then
112
+ CURRENT_STEP_NAME="$new_step"
113
+ CURRENT_STEP_START=$(date +%s)
114
+ else
115
+ # No new step - clear tracking
116
+ CURRENT_STEP_NAME=""
117
+ CURRENT_STEP_START=0
118
+ fi
119
+ }
120
+
121
+ # Format step duration for display
122
+ # Usage: format_step_duration 45 # -> "45s"
123
+ # Usage: format_step_duration 125 # -> "02:05"
124
+ format_step_duration() {
125
+ local seconds=$1
126
+ if [ "$seconds" -lt 60 ]; then
127
+ echo "${seconds}s"
128
+ else
129
+ local mins=$((seconds / 60))
130
+ local secs=$((seconds % 60))
131
+ printf "%02d:%02d" $mins $secs
132
+ fi
133
+ }
134
+
135
+ # Format step times as inline display string
136
+ # Usage: format_step_times "ITERATION" or "SESSION"
137
+ # Returns: "โ”‚ ๐Ÿงช Testing: 45s โ”‚ ๐Ÿงน Linting: 12s" (sorted by duration descending)
138
+ format_step_times() {
139
+ local mode="$1"
140
+ local output=""
141
+ local sorted_steps=()
142
+
143
+ # Build array of "duration:step_name" for sorting
144
+ for i in "${!STEP_NAMES[@]}"; do
145
+ local step="${STEP_NAMES[$i]}"
146
+ local time
147
+ if [ "$mode" = "SESSION" ]; then
148
+ time=${SESSION_STEP_VALUES[$i]}
149
+ else
150
+ time=${ITERATION_STEP_VALUES[$i]}
151
+ fi
152
+ if [ "$time" -gt 0 ]; then
153
+ sorted_steps+=("$time:$step")
154
+ fi
155
+ done
156
+
157
+ # Sort by duration descending (reverse numeric sort on first field)
158
+ if [ ${#sorted_steps[@]} -gt 0 ]; then
159
+ IFS=$'\n' sorted_steps=($(sort -t: -k1 -rn <<<"${sorted_steps[*]}"))
160
+ unset IFS
161
+ fi
162
+
163
+ # Build output string
164
+ for item in "${sorted_steps[@]}"; do
165
+ local time="${item%%:*}"
166
+ local step="${item#*:}"
167
+ local emoji=$(get_step_emoji "$step")
168
+ local formatted=$(format_step_duration $time)
169
+
170
+ if [ -n "$output" ]; then
171
+ output="$output ${C}โ”‚${R} "
172
+ fi
173
+ output="$output$emoji $step: ${Y}$formatted${R}"
174
+ done
175
+
176
+ echo "$output"
177
+ }
178
+
179
+ # Initialize iteration step times at start of new iteration
180
+ # Usage: init_iteration_step_times
181
+ init_iteration_step_times() {
182
+ # Clear iteration step times (reset all to 0)
183
+ ITERATION_STEP_VALUES=(0 0 0 0 0 0 0 0)
184
+
185
+ # Start timing with "Thinking" as default initial step
186
+ CURRENT_STEP_NAME="Thinking"
187
+ CURRENT_STEP_START=$(date +%s)
188
+ }
189
+
190
+ # Display session step totals at end of run
191
+ # Usage: display_session_step_totals
192
+ display_session_step_totals() {
193
+ local step_output=$(format_step_times "SESSION")
194
+
195
+ if [ -n "$step_output" ]; then
196
+ echo -e " ๐Ÿ“Š Session totals: $step_output"
197
+ fi
198
+ }
199
+
200
+ # Detect current step from output line
201
+ # Returns: step name based on output patterns
202
+ detect_step() {
203
+ local line="$1"
204
+
205
+ # Check patterns in order of specificity
206
+ if echo "$line" | grep -qiE "(git commit|committing)"; then
207
+ echo "Committing "
208
+ elif echo "$line" | grep -qiE "(npm test|jest|vitest|testing|test.*pass|test.*fail)"; then
209
+ echo "Testing "
210
+ elif echo "$line" | grep -qiE "(eslint|lint|prettier|formatting)"; then
211
+ echo "Linting "
212
+ elif echo "$line" | grep -qiE "(npm run typecheck|tsc|typescript|typecheck)"; then
213
+ echo "Typechecking "
214
+ elif echo "$line" | grep -qiE "(\.test\.|\.spec\.|test file|writing test)"; then
215
+ echo "Writing tests "
216
+ elif echo "$line" | grep -qiE "(write|edit|creating|updating|modifying).*\.(ts|js|sh|json|md)"; then
217
+ echo "Implementing "
218
+ elif echo "$line" | grep -qiE "(read|glob|grep|searching|finding|looking)"; then
219
+ echo "Reading code "
220
+ else
221
+ echo ""
222
+ fi
223
+ }