@objctp/opencode-shell-routines 1.2.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 (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +114 -0
  3. package/agents/shell-architect.md +88 -0
  4. package/agents/shell-expert.md +60 -0
  5. package/commands/shell-audit.md +47 -0
  6. package/commands/shell-batch-exec.md +48 -0
  7. package/commands/shell-new.md +57 -0
  8. package/commands/shell-routines-setup.md +66 -0
  9. package/commands/shell-test-run.md +46 -0
  10. package/opencode.json +19 -0
  11. package/package.json +34 -0
  12. package/plugins/shell-hooks.ts +150 -0
  13. package/scripts/lib-batch.sh +297 -0
  14. package/scripts/lib-common.sh +332 -0
  15. package/skills/shell-batch-operations/SKILL.md +97 -0
  16. package/skills/shell-batch-operations/assets/batch-template.sh +124 -0
  17. package/skills/shell-batch-operations/examples/data-pipeline.sh +157 -0
  18. package/skills/shell-batch-operations/examples/file-batch.sh +140 -0
  19. package/skills/shell-batch-operations/references/decision-tree.md +53 -0
  20. package/skills/shell-best-practices/SKILL.md +313 -0
  21. package/skills/shell-best-practices/assets/library.sh +142 -0
  22. package/skills/shell-best-practices/assets/minimal.sh +54 -0
  23. package/skills/shell-best-practices/assets/posix.sh +180 -0
  24. package/skills/shell-best-practices/assets/standard.sh +203 -0
  25. package/skills/shell-best-practices/references/patterns.md +386 -0
  26. package/skills/shell-best-practices/references/security.md +195 -0
  27. package/skills/shell-debugging/SKILL.md +115 -0
  28. package/skills/shell-debugging/examples/debug-session.md +165 -0
  29. package/skills/shell-debugging/references/debugging-guide.md +336 -0
  30. package/skills/shell-profiling/SKILL.md +154 -0
  31. package/skills/shell-profiling/examples/profile-session.md +225 -0
  32. package/skills/shell-profiling/references/optimisation-patterns.md +373 -0
  33. package/skills/shell-profiling/references/profiling-tools.md +318 -0
  34. package/skills/shell-profiling/scripts/bench.sh +82 -0
  35. package/skills/shell-profiling/scripts/trace-aggregate.sh +34 -0
  36. package/skills/shell-review/SKILL.md +61 -0
  37. package/skills/shell-review/examples/sample-review.md +42 -0
  38. package/skills/shell-review/references/guidelines.md +48 -0
  39. package/skills/shell-review/references/review-template.md +56 -0
  40. package/skills/shell-security/SKILL.md +128 -0
  41. package/skills/shell-security/examples/dangerous-command-review.md +231 -0
  42. package/skills/shell-security/examples/secure-script-example.sh +317 -0
  43. package/skills/shell-security/references/dangerous-commands.md +561 -0
  44. package/skills/shell-security/references/security-patterns.md +30 -0
  45. package/skills/shell-security/references/sensitive-files.md +525 -0
  46. package/skills/shell-security/scripts/security-audit.sh +208 -0
  47. package/skills/shell-test/SKILL.md +237 -0
  48. package/skills/shell-test/examples/test-example.md +74 -0
  49. package/skills/shell-test/references/advanced-patterns.md +52 -0
  50. package/skills/shell-test/references/assertions.md +184 -0
  51. package/skills/shell-test/references/test-template.md +60 -0
  52. package/skills/shell-test/scripts/public-coverage.sh +93 -0
@@ -0,0 +1,165 @@
1
+ # Example: Debugging an Unbound Variable Error
2
+
3
+ ## The Failing Script
4
+
5
+ ```bash
6
+ #!/usr/bin/env bash
7
+ # deploy.sh - Deploy application to environment
8
+ set -euo pipefail
9
+
10
+ ENV="${1:-}"
11
+ DRY_RUN="${2:-false}"
12
+
13
+ validate_env() {
14
+ local env="$1"
15
+ case "$env" in
16
+ staging|production) ;;
17
+ *)
18
+ echo "Error: invalid environment '$env'" >&2
19
+ return 1
20
+ ;;
21
+ esac
22
+ }
23
+
24
+ build_image() {
25
+ local tag="$1"
26
+ docker build -t "$tag" .
27
+ }
28
+
29
+ push_image() {
30
+ local tag="$1"
31
+ docker push "$tag"
32
+ }
33
+
34
+ deploy() {
35
+ local env="$1"
36
+ local tag="myapp:${COMMIT_HASH}"
37
+ build_image "$tag"
38
+ push_image "$tag"
39
+ kubectl set image "deployment/myapp-${env}" "myapp=${tag}"
40
+ }
41
+
42
+ # Main
43
+ validate_env "$ENV"
44
+
45
+ if [[ "$DRY_RUN" == "true" ]]; then
46
+ echo "DRY RUN: would deploy to $ENV"
47
+ exit 0
48
+ fi
49
+
50
+ deploy "$ENV"
51
+ echo "Deployed to $ENV"
52
+ ```
53
+
54
+ ## Step 1: Observe the Error
55
+
56
+ ```bash
57
+ $ bash deploy.sh staging
58
+ deploy.sh: line 42: COMMIT_HASH: unbound variable
59
+ ```
60
+
61
+ The script exits with an unbound variable error. The message tells us the variable name (`COMMIT_HASH`) and the line number (42, inside `deploy()`).
62
+
63
+ Exit code:
64
+
65
+ ```bash
66
+ $ echo $?
67
+ 1
68
+ ```
69
+
70
+ ## Step 2: Syntax Check
71
+
72
+ Before investigating further, confirm the script has no syntax errors:
73
+
74
+ ```bash
75
+ $ bash -n deploy.sh
76
+ # No output -- syntax is valid
77
+ ```
78
+
79
+ The problem is not a syntax issue. It is a runtime error caused by `set -u` (nounset) combined with a missing variable.
80
+
81
+ ## Step 3: Enable Trace Mode
82
+
83
+ Add `set -x` to see the execution flow leading to the failure:
84
+
85
+ ```bash
86
+ $ bash -x deploy.sh staging
87
+ + ENV=staging
88
+ + DRY_RUN=false
89
+ + validate_env staging
90
+ + local env=staging
91
+ + case staging in
92
+ + return 0
93
+ + [[ false == \t\r\u\e ]]
94
+ + deploy staging
95
+ + local env=staging
96
+ + local tag=myapp:
97
+ deploy.sh: line 42: COMMIT_HASH: unbound variable
98
+ ```
99
+
100
+ The trace shows `tag=myapp:` -- the `COMMIT_HASH` variable is empty at expansion time, and because of `set -u`, bash treats it as an error rather than expanding to an empty string.
101
+
102
+ The root cause is clear: `COMMIT_HASH` is never set in the script. It was presumably meant to be injected from the environment or derived from git.
103
+
104
+ ## Step 4: ShellCheck for Additional Issues
105
+
106
+ ```bash
107
+ $ shellcheck deploy.sh
108
+
109
+ In deploy.sh line 42:
110
+ local tag="myapp:${COMMIT_HASH}"
111
+ ^---------^ SC2153: Possible misspelling: COMMIT_HASH (not declared)
112
+ ```
113
+
114
+ ShellCheck confirms the variable is undeclared. It also catches the missing guard.
115
+
116
+ ## Step 5: Apply the Fix
117
+
118
+ The variable should either be derived or have a sensible default:
119
+
120
+ ```bash
121
+ # Option A: Derive from git
122
+ COMMIT_HASH="${COMMIT_HASH:-$(git rev-parse --short HEAD 2>/dev/null || echo unknown)}"
123
+
124
+ # Option B: Require it explicitly
125
+ if [[ -z "${COMMIT_HASH:-}" ]]; then
126
+ echo "Error: COMMIT_HASH must be set" >&2
127
+ exit 1
128
+ fi
129
+ ```
130
+
131
+ After applying option A near the top of the script:
132
+
133
+ ```bash
134
+ #!/usr/bin/env bash
135
+ # deploy.sh - Deploy application to environment
136
+ set -euo pipefail
137
+
138
+ ENV="${1:-}"
139
+ DRY_RUN="${2:-false}"
140
+ COMMIT_HASH="${COMMIT_HASH:-$(git rev-parse --short HEAD 2>/dev/null || echo unknown)}"
141
+ ```
142
+
143
+ ## Step 6: Verify the Fix
144
+
145
+ ```bash
146
+ $ bash deploy.sh staging true
147
+ DRY RUN: would deploy to staging
148
+
149
+ $ bash -x deploy.sh staging true
150
+ + ENV=staging
151
+ + DRY_RUN=true
152
+ ++ git rev-parse --short HEAD
153
+ + COMMIT_HASH=a1b2c3d
154
+ + validate_env staging
155
+ + local env=staging
156
+ + case staging in
157
+ + return 0
158
+ + [[ true == \t\r\u\e ]]
159
+ + echo 'DRY RUN: would deploy to staging'
160
+ DRY RUN: would deploy to staging
161
+ + exit 0
162
+ ```
163
+
164
+ The script now resolves `COMMIT_HASH` from git and completes without errors.
165
+
@@ -0,0 +1,336 @@
1
+ # Shell Script Debugging Guide
2
+
3
+ ## Quick Reference
4
+
5
+ ### Enable Debug Output
6
+ ```bash
7
+ # Print each command before execution
8
+ set -x
9
+
10
+ # Print script lines as read
11
+ set -v
12
+
13
+ # Show function calls in trace
14
+ set -o functrace
15
+ ```
16
+
17
+ ### Disable Debug Output
18
+ ```bash
19
+ set +x
20
+ set +v
21
+ set +o functrace
22
+ ```
23
+
24
+ ### Syntax Check
25
+ ```bash
26
+ # Parse without executing
27
+ bash -n script.sh
28
+
29
+ # Check all .sh files
30
+ for f in *.sh; do bash -n "$f"; done
31
+ ```
32
+
33
+ ## Debugging Checklist
34
+
35
+ Use this checklist when troubleshooting:
36
+
37
+ ### Environment Issues
38
+ - [ ] Correct shell? (`echo $SHELL`, `ps -p $$`)
39
+ - [ ] Bash version compatible? (`bash --version`)
40
+ - [ ] Required tools installed? (`command -v toolname`)
41
+ - [ ] PATH correct? (`echo $PATH`)
42
+ - [ ] Environment variables set? (`env | grep VAR`)
43
+
44
+ ### Syntax Issues
45
+ - [ ] Run `bash -n script.sh` for syntax errors
46
+ - [ ] Check paired quotes: `'` and `"`
47
+ - [ ] Check paired brackets: `(`, `[`, `{`, `((`, `[[`
48
+ - [ ] Check line continuation: `\` at end of lines
49
+ - [ ] No Windows line endings? (`file script.sh`)
50
+
51
+ ### Variable Issues
52
+ - [ ] Variable defined before use?
53
+ - [ ] Variable scope correct? (`local` vs global)
54
+ - [ ] Proper quoting? `"$var"` not `$var`
55
+ - [ ] Default value needed? `${var:-default}`
56
+ - [ ] Indirect reference correct? `${!varname}`
57
+
58
+ ### Logic Issues
59
+ - [ ] Correct comparison operator?
60
+ - Strings: `[[ "$a" == "$b" ]]` or `[ "$a" = "$b" ]`
61
+ - Numbers: `(( a == b ))` or `[ "$a" -eq "$b" ]`
62
+ - [ ] Test returns expected value? `echo $?`
63
+ - [ ] Exit codes correct? 0=success, non-zero=failure
64
+ - [ ] Pipeline error handled? `set -o pipefail`
65
+
66
+ ### File Issues
67
+ - [ ] File exists? `[[ -f "$file" ]]`
68
+ - [ ] Readable? `[[ -r "$file" ]]`
69
+ - [ ] Correct path? Absolute vs relative
70
+ - [ ] Permission problems? `ls -l "$file"`
71
+
72
+ ## Common Patterns
73
+
74
+ ### Debug Function
75
+
76
+ ```bash
77
+ debug() {
78
+ if [[ "${DEBUG:-0}" == "1" ]]; then
79
+ echo "[DEBUG] $*" >&2
80
+ fi
81
+ }
82
+
83
+ # Usage
84
+ DEBUG=1
85
+ debug "Variable value: $var"
86
+ ```
87
+
88
+ ### Debug Section
89
+
90
+ ```bash
91
+ # Enable debug for a section only
92
+ debug_section() {
93
+ local PS4='+ ${BASH_SOURCE}:${LINENO}: '
94
+ set -x
95
+ # Problematic code here
96
+ "$@"
97
+ set +x
98
+ }
99
+
100
+ debug_section your_function arg1 arg2
101
+ ```
102
+
103
+ ### Trace Variable Changes
104
+
105
+ ```bash
106
+ # Monitor variable assignment
107
+ # Note: eval is used here for diagnostic purposes only.
108
+ # Do not copy this pattern into production scripts.
109
+ trace_var() {
110
+ local var_name="$1"
111
+ local old_value="${!var_name}"
112
+ local new_value="$2"
113
+
114
+ echo "[TRACE] $var_name: '$old_value' -> '$new_value'" >&2
115
+ eval "$var_name='$new_value'"
116
+ }
117
+
118
+ # Usage
119
+ trace_var myvar "new value"
120
+ ```
121
+
122
+ ## Debugging Specific Issues
123
+
124
+ ### "Command not found"
125
+
126
+ **Symptoms**: Script fails with `command: not found`
127
+
128
+ **Checklist**:
129
+ 1. Verify command spelling
130
+ 2. Check if command exists: `which command` or `command -v command`
131
+ 3. Verify PATH: `echo $PATH`
132
+ 4. Check if alias/function shadowing: `type command`
133
+
134
+ **Solution**:
135
+ ```bash
136
+ # Use absolute path
137
+ /usr/bin/python3 script.py
138
+
139
+ # Or verify command exists first
140
+ if ! command -v python3 >/dev/null 2>&1; then
141
+ echo "Error: python3 not found" >&2
142
+ exit 1
143
+ fi
144
+ ```
145
+
146
+ ### "Unbound variable"
147
+
148
+ **Symptoms**: Script fails with `unbound variable` when using `set -u`
149
+
150
+ **Checklist**:
151
+ 1. Variable used before definition?
152
+ 2. Empty array causing issue?
153
+ 3. Conditional variable not set?
154
+
155
+ **Solution**:
156
+ ```bash
157
+ # Provide default value
158
+ echo "${var:-default}"
159
+
160
+ # Or allow empty
161
+ echo "${var:-}"
162
+
163
+ # Or check first
164
+ if [[ -n "${var:-}" ]]; then
165
+ echo "$var"
166
+ fi
167
+ ```
168
+
169
+ ### Pipeline fails silently
170
+
171
+ **Symptoms**: Error in middle of pipeline doesn't cause exit
172
+
173
+ **Solution**:
174
+ ```bash
175
+ # Enable pipefail
176
+ set -euo pipefail
177
+
178
+ # Or capture pipeline status
179
+ command1 | command2
180
+ pipeline_status=(${PIPESTATUS[@]})
181
+ if [[ ${pipeline_status[0]} -ne 0 ]]; then
182
+ echo "command1 failed" >&2
183
+ fi
184
+ ```
185
+
186
+ ### Subshell loses variables
187
+
188
+ **Symptoms**: Variables set in pipeline/subshell not available after
189
+
190
+ **Solution**:
191
+ ```bash
192
+ # BAD - var lost
193
+ echo "data" | while read line; do
194
+ var="$line"
195
+ done
196
+ echo "$var" # Empty
197
+
198
+ # GOOD - var preserved
199
+ while IFS= read -r line; do
200
+ var="$line"
201
+ done < <(echo "data")
202
+ echo "$var" # Works
203
+ ```
204
+
205
+ ### Whitespace breaking arguments
206
+
207
+ **Symptoms**: Filename with spaces causing errors
208
+
209
+ **Solution**:
210
+ ```bash
211
+ # BAD
212
+ for file in *.txt; do
213
+ mv $file /dest/
214
+ done
215
+
216
+ # GOOD
217
+ for file in *.txt; do
218
+ mv "$file" /dest/
219
+ done
220
+
221
+ # GOOD for find
222
+ find . -name "*.txt" -print0 | while IFS= read -r -d '' file; do
223
+ mv "$file" /dest/
224
+ done
225
+ ```
226
+
227
+ ### "Wrong branch" in && || chain
228
+
229
+ **Symptoms**: The `||` fallback command runs unexpectedly, even when the initial command succeeded
230
+
231
+ **Checklist**:
232
+ 1. Is there a `cmd1 && cmd2 || cmd3` pattern?
233
+ 2. Did `cmd2` fail even though `cmd1` succeeded?
234
+ 3. The `||` triggers on ANY failure in the chain, not just `cmd1`
235
+
236
+ **Solution**:
237
+ ```bash
238
+ # BAD - if cmd2 fails, cmd3 runs regardless of cmd1
239
+ deploy && healthcheck || rollback
240
+
241
+ # GOOD - use explicit if/else
242
+ if deploy && healthcheck; then
243
+ echo "OK"
244
+ else
245
+ rollback
246
+ fi
247
+ ```
248
+
249
+ ### Numeric comparison gives wrong result
250
+
251
+ **Symptoms**: A numeric comparison behaves unexpectedly (e.g., "9 is less than 7")
252
+
253
+ **Checklist**:
254
+ 1. Does the comparison use `>` or `<` inside `[[ ]]`?
255
+ 2. `>` inside `[[ ]]` is lexicographic, not numeric: `"9" < "7"` as strings
256
+ 3. Does the comparison use `-gt`/`-lt` in `(( ))`? Those are wrong — `(( ))` uses `>`/`<`
257
+
258
+ **Solution**:
259
+ ```bash
260
+ # BAD - lexicographic comparison
261
+ [[ $count > 7 ]] # "9" is NOT greater than "7" as strings
262
+
263
+ # GOOD - arithmetic context
264
+ (( count > 7 ))
265
+
266
+ # GOOD - or test operators inside [[ ]]
267
+ [[ $count -gt 7 ]]
268
+ ```
269
+
270
+ ## Advanced Debugging
271
+
272
+ ### Custom PS4 for Better Traces
273
+
274
+ ```bash
275
+ # Custom trace prompt
276
+ export PS4='+ [${BASH_SOURCE}:${LINENO}] ${FUNCNAME[0]:-main}: '
277
+
278
+ set -x
279
+ # Your code here
280
+ set +x
281
+ ```
282
+
283
+ ### Logging All Calls
284
+
285
+ ```bash
286
+ # Log every function call
287
+ declare -A call_log
288
+ call_count() {
289
+ local func="${FUNCNAME[1]}"
290
+ ((call_log[$func]++)) || true
291
+ echo "Call $func: ${call_log[$func]}" >&2
292
+ }
293
+
294
+ trap 'call_count' DEBUG
295
+
296
+ # ... script code ...
297
+
298
+ trap - DEBUG # Disable
299
+ ```
300
+
301
+ ### Timing Execution
302
+
303
+ ```bash
304
+ # Time a section
305
+ start=$(date +%s.%N)
306
+ # ... code ...
307
+ end=$(date +%s.%N)
308
+ duration=$(echo "$end - $start" | bc)
309
+ echo "Took: $duration seconds"
310
+ ```
311
+
312
+ > For comprehensive performance profiling with PS4 timing, BASH_XTRACEFD, strace analysis, and benchmarking workflows, see the `shell-profiling` skill.
313
+
314
+ ## ShellCheck Quick Reference
315
+
316
+ Common ShellCheck warnings and fixes:
317
+
318
+ | SC Code | Meaning | Fix |
319
+ |---------|---------|-----|
320
+ | SC2086 | Double quote to prevent globbing | Use `"$var"` |
321
+ | SC2039 | In POSIX sh | Replace bashism |
322
+ | SC2164 | Use `cd ... || exit` | Add error handling |
323
+ | SC2155 | Declare and assign separately (masks return value) | Declare, then assign: `local x; x="$(cmd)"` |
324
+ | SC2002 | Useless use of cat | Pass the file directly: `grep pattern file` |
325
+ | SC2046 | Quote to prevent word splitting | `"$(cmd)"` |
326
+ | SC1091 | File not found | Fix path or disable |
327
+ | SC2206 | Word splitting when filling an array | `read -ra arr <<< "$s"` or `mapfile -t arr` |
328
+
329
+ Run ShellCheck:
330
+ ```bash
331
+ shellcheck script.sh
332
+ # Or with specific format
333
+ shellcheck -f gcc script.sh
334
+ # Or ignore specific rules
335
+ shellcheck -e SC2039 script.sh
336
+ ```
@@ -0,0 +1,154 @@
1
+ ---
2
+ name: shell-profiling
3
+ description: Profile a slow-but-correct bash script: measure a baseline, trace with xtrace timing to find the hotspot, apply shell-specific optimisations, and benchmark the result. Use when a script works correctly but runs too slowly, or to measure/compare execution speed ("profile this script", "why is my script slow", "find the bottleneck"). For runtime errors use shell-debugging; for quality review use shell-review.
4
+ allowed-tools:
5
+ - Read
6
+ - Grep
7
+ - Glob
8
+ - Bash
9
+ argument-hint: [script-path]
10
+ ---
11
+
12
+ # Shell Profiling Skill
13
+
14
+ Guides systematic performance profiling of bash scripts to identify bottlenecks and apply targeted optimisations.
15
+
16
+ ## Scope
17
+
18
+ This skill handles **performance problems** -- scripts that work correctly but run too slowly. It covers:
19
+
20
+ - Timing measurement at multiple granularities (whole-script, per-section, per-line)
21
+ - Xtrace-based profiling with microsecond-precision timestamps
22
+ - Syscall and library-call analysis
23
+ - Statistical benchmarking and comparison
24
+ - Shell-specific optimisation patterns (subshells, builtins, I/O, loops, string processing)
25
+
26
+ This skill does **not** cover:
27
+
28
+ - Runtime failures or error diagnosis -- use `shell-debugging`
29
+ - General code quality or style -- use `shell-review`
30
+ - Writing standards or scaffolding -- use `shell-best-practices`
31
+
32
+ ## Workflow
33
+
34
+ The profiling cycle is: **measure the original, instrument a temporary copy to diagnose, discard the copy, apply fixes to the original, benchmark the result.** Never leave profiling instrumentation in the target script.
35
+
36
+ ### 1. Define Target
37
+
38
+ Before profiling, decide what acceptable performance looks like (e.g., "under 5 seconds" or "twice as fast"). This prevents over-optimisation and gives a clear stopping condition.
39
+
40
+ ### 2. Measure baseline
41
+
42
+ Run the original script with `time` to establish a baseline. No modifications to the script:
43
+
44
+ ```bash
45
+ time bash script.sh args
46
+ ```
47
+
48
+ For per-section granularity, use `EPOCHREALTIME` inside the script (add temporarily, remove after measurement). See `references/profiling-tools.md` for all timing methods and their trade-offs.
49
+
50
+ ### 3. Trace with Timing
51
+
52
+ Create a temporary copy of the script and add xtrace instrumentation to it. The original script remains untouched:
53
+
54
+ ```bash
55
+ cp script.sh /tmp/script.profiling.sh
56
+ ```
57
+
58
+ Insert after the shebang and `set` lines in the copy:
59
+
60
+ ```bash
61
+ exec 42>/tmp/trace.log
62
+ BASH_XTRACEFD=42
63
+ PS4='+ ${EPOCHREALTIME} ${BASH_SOURCE}:${LINENO} ${FUNCNAME[0]:-main} '
64
+ set -x
65
+ ```
66
+
67
+ Run the instrumented copy, then discard it. See `references/profiling-tools.md` for BASH_XTRACEFD setup and capture patterns.
68
+
69
+ ### 4. Identify Hotspot
70
+
71
+ Process the trace output to find the lines consuming the most time. Use `scripts/trace-aggregate.sh` for deterministic aggregation:
72
+
73
+ ```bash
74
+ scripts/trace-aggregate.sh /tmp/trace.log
75
+ ```
76
+
77
+ Output shows cumulative time per source location with call counts, sorted by descending time.
78
+
79
+ Look for locations with the highest cumulative time and call counts, particularly tight loops, subshell invocations, or external command calls.
80
+
81
+ ### 5. Deep-dive
82
+
83
+ When the hotspot involves system calls or I/O, use deeper analysis tools. These run against the original script without modification:
84
+
85
+ ```bash
86
+ # Syscall summary (Linux)
87
+ strace -c -f bash script.sh
88
+
89
+ # Detailed resource usage
90
+ /usr/bin/time -v bash script.sh
91
+ ```
92
+
93
+ See `references/profiling-tools.md` for platform-specific alternatives (macOS: `dtruss`, `sample`, Instruments).
94
+
95
+ ### 6. Apply Optimisation
96
+
97
+ Apply fixes to the **original script** (not the instrumented copy). Consult `references/optimisation-patterns.md` for shell-specific fixes:
98
+
99
+ - Subshell elimination -- replace `$(cmd)` with parameter expansion
100
+ - Builtin selection -- use bash builtins over external commands
101
+ - I/O reduction -- batch reads, redirect once, avoid unnecessary pipes
102
+ - Loop tuning -- process substitution, `lastpipe`, pre-allocated arrays
103
+ - String processing -- single-pass awk vs multi-tool pipelines
104
+
105
+ ### 7. Benchmark
106
+
107
+ Measure the improvement statistically against the original baseline:
108
+
109
+ ```bash
110
+ # With hyperfine (recommended)
111
+ hyperfine 'bash script_before.sh' 'bash script_after.sh'
112
+
113
+ # With bench.sh (no hyperfine required)
114
+ scripts/bench.sh -r 10 -w 1 -- bash script_before.sh
115
+ scripts/bench.sh -r 10 -w 1 -- bash script_after.sh
116
+ ```
117
+
118
+ Discard the first run (warm-up), compare median not mean.
119
+
120
+ If the result still misses the target set in step 1, return to step 3 with the next hotspot; stop once the target is met or all viable patterns are exhausted.
121
+
122
+ ### 8. Report
123
+
124
+ Present a before/after comparison:
125
+
126
+ - Baseline timing vs optimised timing
127
+ - Percentage improvement
128
+ - Which patterns were applied
129
+ - Any trade-offs introduced (readability, portability)
130
+
131
+ Clean up temporary files (`/tmp/trace.log`, `/tmp/script.profiling.sh`).
132
+
133
+ ## References
134
+
135
+ - `references/profiling-tools.md` -- Comprehensive catalogue of timing, tracing, syscall, and benchmarking tools with syntax examples, output interpretation, and platform availability
136
+ - `references/optimisation-patterns.md` -- Shell-specific performance patterns with before/after code snippets, covering subshells, builtins, I/O, loops, and string processing
137
+
138
+ Always read all references, scripts, and examples before producing output.
139
+
140
+ ## Scripts
141
+
142
+ - `scripts/trace-aggregate.sh` -- Aggregates xtrace timestamps into cumulative per-line timings with call counts. Usage: `trace-aggregate.sh <trace-file> [top-n]`
143
+ - `scripts/bench.sh` -- Manual benchmark harness with warm-up runs, median/min/max/spread statistics. Usage: `bench.sh [-r runs] [-w warmup] [--] <command...>`
144
+
145
+ ## Examples
146
+
147
+ - `examples/profile-session.md` -- End-to-end walkthrough: profiling a slow log-processing script from baseline measurement through hotspot identification, optimisation, and benchmarking
148
+
149
+ ## Integration
150
+
151
+ - **`shell-debugging`** -- For scripts that produce errors or incorrect output (profiling assumes the script works correctly)
152
+ - **`shell-best-practices`** -- General writing standards, quoting, error handling (apply alongside performance optimisations)
153
+ - **`shell-review`** -- Quality assessment after optimisation is complete
154
+ - **`shell-architect`** agent -- Architectural decisions affecting performance (batch vs individual processing, parallelism strategy, data flow)