@erclx/canon 5.4.0 → 5.6.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 (54) hide show
  1. package/claude/.claude-plugin/plugin.json +1 -1
  2. package/claude/skills/bash-cli-script/REQUIREMENT.md +10 -9
  3. package/claude/skills/bash-cli-script/SKILL.md +10 -7
  4. package/claude/skills/bash-cli-script/references/template.md +4 -28
  5. package/claude/skills/codebase-layout/REQUIREMENT.md +3 -0
  6. package/claude/skills/codebase-layout/SKILL.md +28 -0
  7. package/claude/skills/codebase-layout/references/adopted.md +6 -0
  8. package/claude/skills/plan-feature/REQUIREMENT.md +4 -0
  9. package/claude/skills/plan-feature/SKILL.md +4 -0
  10. package/claude/skills/read-frames/REQUIREMENT.md +1 -0
  11. package/claude/skills/read-frames/SKILL.md +1 -0
  12. package/claude/skills/teach-workspace/REQUIREMENT.md +3 -0
  13. package/claude/skills/teach-workspace/SKILL.md +6 -5
  14. package/claude/skills/teach-workspace/references/lesson-craft.md +8 -1
  15. package/claude/skills/test-craft/REQUIREMENT.md +4 -1
  16. package/claude/skills/test-craft/SKILL.md +20 -2
  17. package/claude/skills/test-craft/references/adopted.md +3 -1
  18. package/claude/skills/ui-checklist/REQUIREMENT.md +1 -0
  19. package/claude/skills/ui-checklist/SKILL.md +11 -1
  20. package/claude/skills/ux-measure/REQUIREMENT.md +2 -1
  21. package/claude/skills/ux-measure/SKILL.md +3 -1
  22. package/claude/skills/ux-walkthrough/REQUIREMENT.md +3 -0
  23. package/claude/skills/ux-walkthrough/SKILL.md +1 -0
  24. package/docs/agents/architecture-record.md +5 -3
  25. package/docs/agents/context-audit-checks.md +1 -1
  26. package/docs/agents/context-audit.md +3 -3
  27. package/docs/agents/index.md +3 -3
  28. package/docs/agents/skills-audit.md +6 -3
  29. package/docs/agents/teach-authoring.md +3 -3
  30. package/docs/agents/teach.md +11 -5
  31. package/docs/workflow/skill-map.md +1 -2
  32. package/governance/rules/lang/120-bash.md +4 -5
  33. package/package.json +1 -1
  34. package/src/audits/catalog.ts +7 -1
  35. package/src/claude/cases/authoring.ts +0 -5
  36. package/src/claude/skills-audit.ts +81 -1
  37. package/src/commands/claude.ts +33 -0
  38. package/src/commands/context.ts +25 -2
  39. package/src/commands/serve.ts +2 -92
  40. package/src/commands/teach.ts +142 -17
  41. package/src/context/architecture.ts +82 -5
  42. package/src/context/gate.ts +10 -3
  43. package/src/gate/measures.ts +52 -5
  44. package/src/gate/stages.ts +10 -1
  45. package/src/serve/report.ts +110 -0
  46. package/src/teach/nav.ts +12 -1
  47. package/standards/architecture.md +14 -3
  48. package/standards/index.md +1 -0
  49. package/standards/skill-practice.md +51 -0
  50. package/standards/skill.md +3 -1
  51. package/claude/skills/bash-script/REQUIREMENT.md +0 -36
  52. package/claude/skills/bash-script/SKILL.md +0 -100
  53. package/claude/skills/bash-script/references/patterns.md +0 -250
  54. package/claude/skills/bash-script/references/prompts.md +0 -100
@@ -65,6 +65,17 @@ A decision's reasoning stays correct while the numbers it cites move. The anchor
65
65
  - Do not edit a claim in the pass that first anchors it. The anchor states what the claim was measured against, so changing both at once leaves nothing to check the anchor against.
66
66
  - Refresh the anchor whenever the number is re-read, whether or not it moved. A confirmed number and an unread one are the same text without the date.
67
67
 
68
+ ## Revisit condition
69
+
70
+ A decision says why it won and rarely says when that reason stops holding, so an entry can go stale while every word in it stays true. The revisit sentence names the finding that would show the harm the decision prevents has gone or moved.
71
+
72
+ - Close each decision with one sentence of the form `Revisit when <finding>.` Name something a reader could notice, such as a platform release, a measured count crossing a line, or a dependency dropping a behavior.
73
+ - Draft it from the harm the decision prevents, never from the mechanism it uses. A mechanism can keep working long after the reason for choosing it is gone, so re-checking it confirms the wrong fact.
74
+ - Place the sentence after the reasoning and before any verification anchor, so the anchor stays the entry's last sentence.
75
+ - Give a heading holding several principles one sentence, for the premise they share, rather than one per principle.
76
+ - Adopt the requirement by stating the clause `Every decision closes with a revisit sentence.` in the record itself. The clause is the record's own, like the cap, so a checker reads it from the file and gates only a record that states it. A record stating none is reported and never gated.
77
+ - A checker reads presence alone. A vacuous sentence passes it, so the sentence is only as useful as the finding it names.
78
+
68
79
  ## Entry cap
69
80
 
70
81
  Every session pays for this file before any work starts, so a heavy read is a real cost. A file that reads heavy is carrying too many decisions, not decisions written too long, so the bound is a count of decisions rather than of words.
@@ -78,20 +89,20 @@ Every session pays for this file before any work starts, so a heavy read is a re
78
89
 
79
90
  ## Template
80
91
 
81
- The anchor sentence closes a decision whose reasoning cites a measured number and is absent from one that cites none. Each heading below names a slot, so a record starts with one decision per slot and renames each heading to the choice it records.
92
+ The revisit sentence closes the reasoning of every decision. The anchor sentence follows it on a decision whose reasoning cites a measured number and is absent from one that cites none. Each heading below names a slot, so a record starts with one decision per slot and renames each heading to the choice it records.
82
93
 
83
94
  ```markdown
84
95
  # Architecture
85
96
 
86
97
  ## Overview
87
98
 
88
- This record holds at most 12 decisions.
99
+ This record holds at most 12 decisions. Every decision closes with a revisit sentence.
89
100
 
90
101
  ## Key technical decisions
91
102
 
92
103
  ### Stack and runtime
93
104
 
94
- What was chosen, the alternative that lost, and why. Measured at <short-sha> on <YYYY-MM-DD>.
105
+ What was chosen, the alternative that lost, and why. Revisit when <finding>. Measured at <short-sha> on <YYYY-MM-DD>.
95
106
 
96
107
  ### Delivery
97
108
 
@@ -31,6 +31,7 @@ Reference docs for consistent authoring across the toolkit and target projects.
31
31
  - [Requirements reference](requirements.md): Shape and content rules for canon/REQUIREMENTS.md
32
32
  - [Governance rule reference](rule.md): Rule frontmatter, body shape, and voice for .claude/rules files
33
33
  - [Session map reference](session.md): Filename and location, the sections a handoff carries, the write and read procedures, and how a role extends it
34
+ - [Practice skill reference](skill-practice.md): Closing sections and source ledger for a skill carrying engineering practice
34
35
  - [Skill requirement reference](skill-requirement.md): Shape and content rules for the REQUIREMENT.md beside a skill
35
36
  - [Claude skill reference](skill.md): Claude skill structure and authoring rules
36
37
  - [Slug reference](slug.md): Transform from a git branch name to a slug, and the three responses to an empty result
@@ -0,0 +1,51 @@
1
+ ---
2
+ title: Practice skill reference
3
+ description: Closing sections and source ledger for a skill carrying engineering practice
4
+ ---
5
+
6
+ # Practice skill reference
7
+
8
+ Applies to a practice skill, one carrying engineering judgment a session applies while it works, where the failure is skipping the practice or cutting it short rather than not knowing it. Its body takes the reference shape, and this file fixes how it closes and what it cites.
9
+
10
+ ## Scope
11
+
12
+ Governs the closing sections of a practice skill's `skills/<name>/SKILL.md` and the source ledger at `skills/<name>/references/adopted.md` beside it.
13
+
14
+ Does not govern:
15
+
16
+ - The skill folder, its frontmatter, the body rules, and the choice between skill types: `skill.md`
17
+ - The `REQUIREMENT.md` beside the body: `skill-requirement.md`
18
+ - Voice, rhythm, and sentence construction: the `write-human` skill
19
+ - Punctuation, formatting, and word choice: `markdown.md`
20
+
21
+ ## What a working practice skill looks like
22
+
23
+ A practice skill works when a session about to cut the practice short meets the answer before it does:
24
+
25
+ - Does every excuse row name a reason a session has actually given to skip the practice?
26
+ - Can a session spot each red flag in its own work while it is still working?
27
+ - Can a session run every line of the closing checklist against what it produced, and get a yes or a no?
28
+ - Can a reader open each source the ledger names and find the version the body states?
29
+
30
+ A practice skill failing any of these is non-conforming even when every heading below is present.
31
+
32
+ ## Closing sections
33
+
34
+ - Follow every concern group with three H2s in this order: `## Excuses and rebuttals`, `## Red flags`, and `## Before handing over`. A section naming what the skill delegates comes after the three, and nothing else does.
35
+ - Spell each heading exactly. An H3, or a heading carrying more words such as `## Red flags that mean start over`, reads as missing.
36
+ - Write `## Excuses and rebuttals` as a two-column table pairing a reason a session gives to skip or shortcut the practice with the answer to it. Never write a row rebutting an objection nobody raised, since that row is ceremony. Revisit when a practice skill's table is observed failing to stop an excuse it names.
37
+ - Write `## Red flags` as bullets naming signs a session can observe in its own work mid-task, never a restatement of a rule above.
38
+ - Write `## Before handing over` as the exit checklist, each line a check against what the session produced.
39
+
40
+ ## Source ledger
41
+
42
+ - Carry `references/adopted.md` in every practice skill, recording each external source the body draws on.
43
+ - Link each source, or pin it to a commit, so a reader can open the version the body adopted.
44
+ - State what was adopted, what was declined, and why for each, so a later session extends the position rather than re-deriving it.
45
+ - Draw first from the default shelf, [Software Engineering at Google](https://abseil.io/resources/swe-book/html/toc.html) and [Google's engineering practices](https://google.github.io/eng-practices/), and record a source from elsewhere the same way.
46
+
47
+ ## Which skills are practice skills
48
+
49
+ - `canon claude skills audit` keeps the list of practice skills and checks each listed one for the three closing sections and the ledger.
50
+ - Never mark a skill as a practice skill in its own frontmatter, since a self-declared kind lets a skill exempt itself.
51
+ - The list names this toolkit's shipped skills alone, so a project's own practice skill takes this shape with no check behind it.
@@ -16,6 +16,7 @@ Governs a skill folder under `skills/` as one artifact: `SKILL.md`, its siblings
16
16
  Does not govern:
17
17
 
18
18
  - What a `REQUIREMENT.md` must answer, its sections, and its template: `skill-requirement.md`
19
+ - A practice skill's closing sections and ledger: `skill-practice.md`
19
20
  - Path-scoped coding rules, which load on a file match rather than on a request match: `rule.md`
20
21
  - Voice, rhythm, and sentence construction in a skill body: the `write-human` skill
21
22
  - Punctuation, formatting, and word choice in a skill body: `markdown.md`
@@ -45,6 +46,7 @@ Pick the type before writing. It decides the body shape.
45
46
 
46
47
  - Reference: conventions, patterns, and domain knowledge Claude applies inline. The body is rule bullets grouped by concern.
47
48
  - Task: step-by-step workflows Claude executes as actions. The body is numbered steps plus the rules constraining them.
49
+ - Practice: judgment a session may skip, shaped by `skill-practice.md`.
48
50
 
49
51
  A task skill takes the template below. A reference skill takes the same frontmatter and replaces `## Steps` and `## Rules` with one H2 per concern group, each holding constraint bullets.
50
52
 
@@ -105,7 +107,7 @@ allowed-tools: <tools required>
105
107
  - Group bullets under H2 headings by domain concern. Keep dos and don'ts together under the topic they belong to rather than splitting them into flat rules and constraints sections.
106
108
  - One actionable constraint per bullet. Prefer the `X over Y` form for preferences.
107
109
  - Do not include code examples unless a one to three line inline snippet captures a pattern the model cannot infer.
108
- - Do not duplicate general knowledge the model already has. Focus on project-specific conventions and preferences. Revisit when removing a line of general knowledge from a skill is shown to change what the agent does.
110
+ - Keep a statement only when removing it would change what the agent does, naming a principle as a few-word handle rather than explaining it. Revisit when a run shows the model ignoring a principle named only as a handle.
109
111
 
110
112
  ### Progressive disclosure
111
113
 
@@ -1,36 +0,0 @@
1
- ---
2
- name: bash-script
3
- description: Scope boundary for the interactive script house style and the stream discipline underneath it
4
- ---
5
-
6
- # Bash script requirement
7
-
8
- ## Gap
9
-
10
- Without this skill, a session asked for a human-facing shell tool writes one whose frame decoration, log lines, and prompts all land on stdout, so the script cannot be piped and any data it emits arrives mixed with its own presentation.
11
-
12
- Three more failures share a cause, which is that an interactive script is written as though a person is always watching. A prompt blocks forever when stdin is not a terminal, which is the shape every CI job and every agent run has. A cancellation path prints both a cancel line and an error, or an exit trap prints a closing frame the success path already printed, so the same run reports twice. And each generated script picks its own icons, casing, and frame characters, so a folder of them reads as several unrelated tools.
13
-
14
- The visual conventions this skill fixes are otherwise arbitrary. Their value is that they are the same across every script, which is a property no individual script can establish for itself.
15
-
16
- ## Must
17
-
18
- - Keep every frame, log line, and prompt on stderr, and reserve stdout for data. `--help` is the one exception, since its consumer is a person rather than a pipe.
19
- - Guard every prompt on an attached terminal and refuse inside the frame rather than blocking on a read that will never return
20
- - Give the timeline one owner per exit path, so success, cancellation, and error each close it exactly once
21
- - Copy the shared templates rather than restating them, keeping only the colors and functions the script uses
22
-
23
- ## Must not
24
-
25
- - Assert a convention the generated script cannot be checked against by reading it. A rule that cannot be verified from the output is style with no gate behind it.
26
- - Carry a second copy of the timeline, logging, or prompt implementations. The bundled reference is the single copy, and a body that restates it drifts from it.
27
-
28
- ## Guards
29
-
30
- - A request for a script with no human at the terminal stops and routes to `bash-cli-script` rather than generating a timeline nothing will render
31
-
32
- ## Out of scope
33
-
34
- - Non-interactive automation, CI, and agent-run scripts: `bash-cli-script`, which keeps the error handling and the stdout contract and drops the timeline, the icons, and the prompts
35
- - GitHub Actions workflow files: `ci-workflow`
36
- - What the generated script does. This skill fixes the shape of the output and the stream it goes to, and the commands belong to the request.
@@ -1,100 +0,0 @@
1
- ---
2
- name: bash-script
3
- description: Generates production Bash scripts with a visual timeline UI, state-based interactive prompts, and strict error handling. Use when asked for "a human-facing shell tool", an interactive bash script, or a DevOps tool with framed terminal output. Do NOT use for a non-interactive automation, CI, or pipeline script, that is `bash-cli-script`.
4
- ---
5
-
6
- # Bash script
7
-
8
- Generate production-ready Bash scripts for DevOps and CLI workflows. Enforce strict formatting with a visual timeline UI and state-based interactivity.
9
-
10
- Load `${CLAUDE_SKILL_DIR}/references/patterns.md` for the timeline lifecycle, logging, help-screen, and full-script code templates, and `${CLAUDE_SKILL_DIR}/references/prompts.md` for the `ask()` and `select_option()` templates when the script prompts. `${CLAUDE_SKILL_DIR}` expands to this skill's own directory, so the path resolves from any project. Copy those definitions verbatim, keeping only the colors and functions the script actually uses.
11
-
12
- ## Guards
13
-
14
- - A request for a script with no human at the terminal stops and routes to `bash-cli-script`. CI jobs, cron entries, agent-run tasks, and any script whose output is consumed by a pipe render no timeline, so generating one costs the frame and returns nothing.
15
-
16
- ## Script setup
17
-
18
- - Start with `#!/usr/bin/env bash`, `set -e`, and `set -o pipefail`.
19
- - Bash 4+ features (namerefs, associative arrays) are allowed.
20
- - Implement a visual help screen via `show_help` if the script accepts arguments.
21
- - Do not rely on unset variables. Use `${VAR:-default}`.
22
-
23
- ## Visual timeline
24
-
25
- - Maintain a vertical timeline (`│`) from `┌` to `└` throughout all output.
26
- - Write all frame output (`┌`, `│`, `├`, `└`, log lines, prompts) to stderr via `>&2`. Write data (JSON, lists, piped values) to stdout. `--help` is the exception and prints to stdout.
27
- - Open the timeline once at the start of `main()` via `open_timeline "Title"`, before any logic, prompts, or checks.
28
- - Close the timeline via `trap close_timeline EXIT`, registered immediately after `open_timeline`.
29
- - On success, disable with `trap - EXIT`, then print `└\n` and the success message manually.
30
- - On cancellation and error, never print `└` manually. The trap owns those exits.
31
- - Use state transitions for interactive prompts: `◆` active, `◇` inactive.
32
- - Do not add diamonds (`◆` or `◇`) to non-interactive log functions.
33
- - On cancellation, show `◇ ... Cancelled`, exit 1, and make no `log_error` call. Both `ask()` and `select_option()` handle escape cancellation identically.
34
- - Guard interactive prompts against non-TTY stdin with `[ -t 0 ]` and render a framed `log_error` when a TTY is required but absent. Never let `read` block silently on piped input.
35
-
36
- ## Code style
37
-
38
- - Decompose by responsibility: each function does one thing, `main()` orchestrates only.
39
- - Name functions verb-first: `validate_input`, `deploy_service`, `install_dependencies`.
40
- - Do not use global variables except exports from `ask()`.
41
- - Do not define unused color variables.
42
- - Comment only a fact the reader cannot recover from the code, and follow the code-comment rule in `.claude/rules/` when the project installs it.
43
- - Use sentence case for section headers and log messages. Proper nouns and product names retain their casing.
44
- - Quote variables inside parameter expansions: `"${file#"$dir"/}"` not `"${file#$dir/}"`.
45
- - Quote variables in test brackets: `[ "$i" -eq "$cur" ]` not `[ $i -eq $cur ]`.
46
- - Guard commands that return non-zero on valid empty results: `grep ... || true`, `diff ... || true`.
47
-
48
- ## Output hygiene
49
-
50
- - Show external tool output by default (git, npm, gh).
51
- - Include context in error messages: `log_error "npm install failed: check package.json"`.
52
- - Do not echo command names before running them. Output speaks for itself.
53
- - Do not log "Starting..." and "Finished..." around every action.
54
- - Do not log intermediate variable assignments.
55
- - Use `log_add` for item writes (files created, entries added, keys written).
56
- - Use `log_info` for status confirmations only ("up to date", "check passed").
57
- - Use `log_warn` for drift, skipped states, or recoverable issues.
58
- - Never use `log_info` for file or entry writes.
59
-
60
- ## Icon usage
61
-
62
- Interactive prompts only:
63
-
64
- - `◆` (green): active user input required
65
- - `◇` (grey): completed input, transition via `\r\033[K` to rewrite the `◆` line in place
66
- - `❯` (green): selected option in a menu
67
- - plain text (grey): unselected option in a menu
68
-
69
- Non-interactive logs:
70
-
71
- - `├` section branch (`log_step`)
72
- - `✓` (green): success (`log_info`)
73
- - `!` (yellow): warning (`log_warn`)
74
- - `✗` (red): error (`log_error`)
75
- - `+` (green): add item (`log_add`)
76
- - `-` (red): remove item (`log_rem`)
77
-
78
- ## Validation
79
-
80
- Before responding, verify:
81
-
82
- - File starts with `#!/usr/bin/env bash`, `set -e`, `set -o pipefail` and uses exactly 2 spaces for indentation.
83
- - Timeline opens via `open_timeline "Title"`, which writes `┌` and `│ Title` to stderr.
84
- - Timeline closes via `trap close_timeline EXIT` registered immediately after `open_timeline`. Success paths use `trap - EXIT` then manual `└\n` (to stderr) then the success message. Cancellation and error paths never print `└` manually.
85
- - `open_timeline` and `close_timeline` are defined and write to stderr via `>&2`.
86
- - All frame output (`│`, `├`, `└`, log lines, interactive prompts) writes to stderr via `>&2`. Stdout carries data only. `--help` is the exception.
87
- - Interactive prompts (`ask`, `select_option`) guard with `[ -t 0 ]` and call `log_error` if stdin is not a TTY.
88
- - The timeline (`│`) appears in all log functions, and interactive prompts use `◆` → `◇` transitions.
89
- - `ask()` uses `\r\033[K` to rewrite the `◆` line in place, with no `\033[1A` cursor-up sequences.
90
- - `ask()` drains trailing escape bytes with `read -rsn2 -t 0.001 _ || true` before cancelling.
91
- - All log messages use sentence case (proper nouns and product names exempt).
92
- - Only defined color variables are used in the script.
93
- - Cancellation shows a single `◇ ... Cancelled` line without a subsequent `log_error`.
94
- - Escape key in `ask()` triggers `◇ ... Cancelled` and exits, consistent with `select_option()`.
95
- - Functions follow single responsibility: each does one thing, `main()` delegates to helpers.
96
- - Logging is concise: no "Starting.../Finished..." bloat, no intermediate variable logging.
97
- - `log_add` is used for all file, entry, and key writes.
98
- - `log_info` is used for status confirmations only, not writes.
99
- - Every section header uses `log_step`, including the first one after the title block.
100
- - File ends with exactly one empty line.
@@ -1,250 +0,0 @@
1
- # Bash script patterns
2
-
3
- Code templates for the `bash-script` skill. Copy verbatim, keeping only the colors and functions the script uses.
4
-
5
- ## Timeline structure
6
-
7
- ```plaintext
8
- ┌ # Start boundary (alone on its own line)
9
- │ Title # Script or context title (immediately after ┌)
10
- │ # Persistent vertical line (grey)
11
- ├ Section Branch # Section headers (no diamond)
12
- │ ✓ Log message # Info/success logs
13
- │ ! Warning # Warning logs
14
- │ ✗ Error # Error logs
15
- └ # End boundary
16
- ```
17
-
18
- ## Color palette
19
-
20
- Define only the colors the script uses from this set:
21
-
22
- ```bash
23
- GREEN='\033[0;32m' # Success/Active
24
- RED='\033[0;31m' # Error/Delete
25
- YELLOW='\033[0;33m' # Warning
26
- WHITE='\033[1;37m' # Active text
27
- GREY='\033[0;90m' # Timeline/Inactive
28
- CYAN='\033[0;36m' # Optional accent
29
- MAGENTA='\033[0;35m' # Optional highlight
30
- NC='\033[0m' # Reset
31
- ```
32
-
33
- ## Timeline lifecycle
34
-
35
- Define `open_timeline` and `close_timeline`. Call `open_timeline "Title"` at the start of `main()`, then register `close_timeline` as the EXIT trap. The trap guarantees `└` prints on every exit path: normal completion, `exit 1` from cancellation, or unexpected errors. Do not print `└` manually anywhere else.
36
-
37
- ```bash
38
- open_timeline() {
39
- echo -e "${GREY}┌${NC}" >&2
40
- [ -n "${1:-}" ] && echo -e "${GREY}│${NC} ${WHITE}$1${NC}" >&2
41
- }
42
-
43
- close_timeline() {
44
- echo -e "${GREY}└${NC}" >&2
45
- }
46
- ```
47
-
48
- Register the trap inside `main()` right after opening:
49
-
50
- ```bash
51
- open_timeline "Script title"
52
- trap close_timeline EXIT
53
- ```
54
-
55
- ## Logging
56
-
57
- All log functions include the `│` prefix and write to stderr.
58
-
59
- ```bash
60
- log_info() { echo -e "${GREY}│${NC} ${GREEN}✓${NC} $1" >&2; }
61
- log_warn() { echo -e "${GREY}│${NC} ${YELLOW}!${NC} $1" >&2; }
62
- log_error() { echo -e "${GREY}│${NC} ${RED}✗${NC} $1" >&2; exit 1; }
63
- log_step() { echo -e "${GREY}│${NC}\n${GREY}├${NC} ${WHITE}$1${NC}" >&2; }
64
- log_add() { echo -e "${GREY}│${NC} ${GREEN}+${NC} $1" >&2; }
65
- log_rem() { echo -e "${GREY}│${NC} ${RED}-${NC} $1" >&2; }
66
- ```
67
-
68
- ## Section headers
69
-
70
- Use `log_step` for every section header, including the first. It emits a leading blank `│` line to separate the banner from the first section and to give breathing room between subsequent sections:
71
-
72
- ```bash
73
- open_timeline "Script title"
74
- trap close_timeline EXIT
75
-
76
- log_step "Deploy"
77
- log_step "Verify"
78
- ```
79
-
80
- Renders as:
81
-
82
- ```plaintext
83
- ┌
84
- │ Script title
85
- │
86
- ├ Deploy
87
- ...
88
- │
89
- ├ Verify
90
- ...
91
- └
92
- ```
93
-
94
- ## Help system
95
-
96
- ```bash
97
- show_help() {
98
- echo -e "${GREY}┌${NC}"
99
- echo -e "${GREY}├${NC} ${WHITE}Usage:${NC} ./script.sh [options]"
100
- echo -e "${GREY}│${NC}"
101
- echo -e "${GREY}│${NC} ${WHITE}Options:${NC}"
102
- echo -e "${GREY}│${NC} -h, --help ${GREY}# Show this help message${NC}"
103
- echo -e "${GREY}│${NC} [flag] ${GREY}# [Description]${NC}"
104
- echo -e "${GREY}└${NC}"
105
- exit 0
106
- }
107
- ```
108
-
109
- ## Error handling helper
110
-
111
- ```bash
112
- run_check() {
113
- local cmd=$1
114
- local err_msg=$2
115
- if ! eval "$cmd"; then
116
- log_error "$err_msg"
117
- fi
118
- }
119
- ```
120
-
121
- ## Full script structure
122
-
123
- ```bash
124
- #!/usr/bin/env bash
125
- set -e
126
- set -o pipefail
127
-
128
- [Color definitions - only used colors]
129
-
130
- [Function definitions - only needed functions]
131
-
132
- open_timeline() {
133
- echo -e "${GREY}┌${NC}" >&2
134
- [ -n "${1:-}" ] && echo -e "${GREY}│${NC} ${WHITE}$1${NC}" >&2
135
- }
136
-
137
- close_timeline() {
138
- echo -e "${GREY}└${NC}" >&2
139
- }
140
-
141
- check_dependencies() {
142
- [Verify required tools installed]
143
- }
144
-
145
- main() {
146
- check_dependencies
147
-
148
- open_timeline "Script title"
149
- trap close_timeline EXIT
150
-
151
- log_step "First section"
152
- [Script logic with timeline maintained]
153
-
154
- trap - EXIT
155
- echo -e "${GREY}└${NC}\n" >&2
156
- echo -e "${GREEN}✓ Final success message${NC}"
157
- }
158
-
159
- main "$@"
160
- ```
161
-
162
- ## Worked example
163
-
164
- ```bash
165
- #!/usr/bin/env bash
166
- set -e
167
- set -o pipefail
168
-
169
- GREEN='\033[0;32m'
170
- RED='\033[0;31m'
171
- YELLOW='\033[0;33m'
172
- WHITE='\033[1;37m'
173
- GREY='\033[0;90m'
174
- NC='\033[0m'
175
-
176
- log_info() { echo -e "${GREY}│${NC} ${GREEN}✓${NC} $1" >&2; }
177
- log_error() { echo -e "${GREY}│${NC} ${RED}✗${NC} $1" >&2; exit 1; }
178
- log_step() { echo -e "${GREY}│${NC}\n${GREY}├${NC} ${WHITE}$1${NC}" >&2; }
179
- log_add() { echo -e "${GREY}│${NC} ${GREEN}+${NC} $1" >&2; }
180
-
181
- open_timeline() {
182
- echo -e "${GREY}┌${NC}" >&2
183
- [ -n "${1:-}" ] && echo -e "${GREY}│${NC} ${WHITE}$1${NC}" >&2
184
- }
185
-
186
- close_timeline() {
187
- echo -e "${GREY}└${NC}" >&2
188
- }
189
-
190
- ask() {
191
- local prompt_text=$1
192
- local var_name=$2
193
- local default_val=$3
194
- local input=""
195
- local char
196
- local display_default=""
197
- if [ -n "$default_val" ]; then
198
- display_default=" (${default_val})"
199
- fi
200
- if [ ! -t 0 ]; then
201
- log_error "${prompt_text} requires a TTY"
202
- fi
203
- echo -e "${GREY}│${NC}" >&2
204
- echo -ne "${GREEN}◆${NC} ${prompt_text}${display_default} " >&2
205
- while IFS= read -r -s -n1 char; do
206
- if [[ $char == $'\x1b' ]]; then
207
- read -rsn2 -t 0.001 _ || true
208
- echo -ne "\r\033[K" >&2
209
- echo -e "${GREY}◇${NC} ${prompt_text} ${RED}Cancelled${NC}" >&2
210
- exit 1
211
- elif [[ $char == $'\x7f' || $char == $'\x08' ]]; then
212
- if [ -n "$input" ]; then
213
- input="${input%?}"
214
- echo -ne "\b \b" >&2
215
- fi
216
- elif [[ -z "$char" ]]; then
217
- break
218
- else
219
- input+="$char"
220
- echo -n "$char" >&2
221
- fi
222
- done
223
- [ -z "$input" ] && input="$default_val"
224
- echo -ne "\r\033[K" >&2
225
- echo -e "${GREY}◇${NC} ${prompt_text} ${WHITE}${input}${NC}" >&2
226
- export "$var_name"="$input"
227
- }
228
-
229
- check_dependencies() {
230
- command -v npm >/dev/null 2>&1 || log_error "npm not installed"
231
- }
232
-
233
- main() {
234
- check_dependencies
235
-
236
- open_timeline "Project Setup"
237
- trap close_timeline EXIT
238
-
239
- ask "Project name?" "PROJECT_NAME" "my-app"
240
-
241
- log_step "Installing dependencies"
242
- npm install vite
243
- log_add "vite@latest"
244
-
245
- log_info "Setup complete"
246
- echo -e "\n${GREEN}✓ Project created successfully${NC}"
247
- }
248
-
249
- main "$@"
250
- ```
@@ -1,100 +0,0 @@
1
- # Bash script prompts
2
-
3
- Interactive prompt templates for the `bash-script` skill, read only when the script prompts. Copy verbatim, alongside the colors and logging functions from `patterns.md`.
4
-
5
- Both prompts transition `◆` to `◇`, guard against non-TTY stdin with `[ -t 0 ]`, and write UI to stderr.
6
-
7
- ```bash
8
- ask() {
9
- local prompt_text=$1
10
- local var_name=$2
11
- local default_val=$3
12
- local input=""
13
- local char
14
- local display_default=""
15
- if [ -n "$default_val" ]; then
16
- display_default=" (${default_val})"
17
- fi
18
- if [ ! -t 0 ]; then
19
- log_error "${prompt_text} requires a TTY"
20
- fi
21
- echo -e "${GREY}│${NC}" >&2
22
- echo -ne "${GREEN}◆${NC} ${prompt_text}${display_default} " >&2
23
- while IFS= read -r -s -n1 char; do
24
- if [[ $char == $'\x1b' ]]; then
25
- read -rsn2 -t 0.001 _ || true
26
- echo -ne "\r\033[K" >&2
27
- echo -e "${GREY}◇${NC} ${prompt_text} ${RED}Cancelled${NC}" >&2
28
- exit 1
29
- elif [[ $char == $'\x7f' || $char == $'\x08' ]]; then
30
- if [ -n "$input" ]; then
31
- input="${input%?}"
32
- echo -ne "\b \b" >&2
33
- fi
34
- elif [[ -z "$char" ]]; then
35
- break
36
- else
37
- input+="$char"
38
- echo -n "$char" >&2
39
- fi
40
- done
41
- [ -z "$input" ] && input="$default_val"
42
- echo -ne "\r\033[K" >&2
43
- echo -e "${GREY}◇${NC} ${prompt_text} ${WHITE}${input}${NC}" >&2
44
- export "$var_name"="$input"
45
- }
46
- ```
47
-
48
- ```bash
49
- select_option() {
50
- local prompt_text=$1
51
- shift
52
- local options=("$@")
53
- local cur=0
54
- local count=${#options[@]}
55
-
56
- if [ ! -t 0 ]; then
57
- log_error "${prompt_text} requires a TTY"
58
- fi
59
-
60
- echo -ne "${GREY}│${NC}\n${GREEN}◆${NC} ${prompt_text}\n" >&2
61
-
62
- while true; do
63
- for i in "${!options[@]}"; do
64
- if [ "$i" -eq "$cur" ]; then
65
- echo -e "${GREY}│${NC} ${GREEN}❯ ${options[$i]}${NC}" >&2
66
- else
67
- echo -e "${GREY}│${NC} ${GREY}${options[$i]}${NC}" >&2
68
- fi
69
- done
70
-
71
- read -rsn1 key
72
- case "$key" in
73
- $'\x1b')
74
- if read -rsn2 -t 0.001 key_seq; then
75
- if [[ "$key_seq" == "[A" ]]; then cur=$(( (cur - 1 + count) % count )); fi
76
- if [[ "$key_seq" == "[B" ]]; then cur=$(( (cur + 1) % count )); fi
77
- else
78
- echo -en "\033[$((count + 1))A\033[J" >&2
79
- echo -e "\033[1A${GREY}│${NC}\n${GREY}◇${NC} ${prompt_text} ${RED}Cancelled${NC}" >&2
80
- exit 1
81
- fi
82
- ;;
83
- "k") cur=$(( (cur - 1 + count) % count ));;
84
- "j") cur=$(( (cur + 1) % count ));;
85
- "q")
86
- echo -en "\033[$((count + 1))A\033[J" >&2
87
- echo -e "\033[1A${GREY}│${NC}\n${GREY}◇${NC} ${prompt_text} ${RED}Cancelled${NC}" >&2
88
- exit 1
89
- ;;
90
- "") break ;;
91
- esac
92
-
93
- echo -en "\033[${count}A" >&2
94
- done
95
-
96
- echo -en "\033[$((count + 1))A\033[J" >&2
97
- echo -e "\033[1A${GREY}│${NC}\n${GREY}◇${NC} ${prompt_text} ${WHITE}${options[$cur]}${NC}" >&2
98
- SELECTED_OPTION="${options[$cur]}"
99
- }
100
- ```