@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.
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/bash-cli-script/REQUIREMENT.md +10 -9
- package/claude/skills/bash-cli-script/SKILL.md +10 -7
- package/claude/skills/bash-cli-script/references/template.md +4 -28
- package/claude/skills/codebase-layout/REQUIREMENT.md +3 -0
- package/claude/skills/codebase-layout/SKILL.md +28 -0
- package/claude/skills/codebase-layout/references/adopted.md +6 -0
- package/claude/skills/plan-feature/REQUIREMENT.md +4 -0
- package/claude/skills/plan-feature/SKILL.md +4 -0
- package/claude/skills/read-frames/REQUIREMENT.md +1 -0
- package/claude/skills/read-frames/SKILL.md +1 -0
- package/claude/skills/teach-workspace/REQUIREMENT.md +3 -0
- package/claude/skills/teach-workspace/SKILL.md +6 -5
- package/claude/skills/teach-workspace/references/lesson-craft.md +8 -1
- package/claude/skills/test-craft/REQUIREMENT.md +4 -1
- package/claude/skills/test-craft/SKILL.md +20 -2
- package/claude/skills/test-craft/references/adopted.md +3 -1
- package/claude/skills/ui-checklist/REQUIREMENT.md +1 -0
- package/claude/skills/ui-checklist/SKILL.md +11 -1
- package/claude/skills/ux-measure/REQUIREMENT.md +2 -1
- package/claude/skills/ux-measure/SKILL.md +3 -1
- package/claude/skills/ux-walkthrough/REQUIREMENT.md +3 -0
- package/claude/skills/ux-walkthrough/SKILL.md +1 -0
- package/docs/agents/architecture-record.md +5 -3
- package/docs/agents/context-audit-checks.md +1 -1
- package/docs/agents/context-audit.md +3 -3
- package/docs/agents/index.md +3 -3
- package/docs/agents/skills-audit.md +6 -3
- package/docs/agents/teach-authoring.md +3 -3
- package/docs/agents/teach.md +11 -5
- package/docs/workflow/skill-map.md +1 -2
- package/governance/rules/lang/120-bash.md +4 -5
- package/package.json +1 -1
- package/src/audits/catalog.ts +7 -1
- package/src/claude/cases/authoring.ts +0 -5
- package/src/claude/skills-audit.ts +81 -1
- package/src/commands/claude.ts +33 -0
- package/src/commands/context.ts +25 -2
- package/src/commands/serve.ts +2 -92
- package/src/commands/teach.ts +142 -17
- package/src/context/architecture.ts +82 -5
- package/src/context/gate.ts +10 -3
- package/src/gate/measures.ts +52 -5
- package/src/gate/stages.ts +10 -1
- package/src/serve/report.ts +110 -0
- package/src/teach/nav.ts +12 -1
- package/standards/architecture.md +14 -3
- package/standards/index.md +1 -0
- package/standards/skill-practice.md +51 -0
- package/standards/skill.md +3 -1
- package/claude/skills/bash-script/REQUIREMENT.md +0 -36
- package/claude/skills/bash-script/SKILL.md +0 -100
- package/claude/skills/bash-script/references/patterns.md +0 -250
- 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
|
|
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
|
|
package/standards/index.md
CHANGED
|
@@ -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.
|
package/standards/skill.md
CHANGED
|
@@ -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
|
-
-
|
|
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
|
-
```
|