@bompus/house-rules 0.11.0 → 0.14.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 (112) hide show
  1. package/CODE_OF_CONDUCT.md +83 -0
  2. package/README.md +102 -63
  3. package/compose.mjs +8 -2
  4. package/composition.mjs +11 -3
  5. package/config-view.mjs +18 -3
  6. package/config.mjs +74 -13
  7. package/docs/assets/overview.png +0 -0
  8. package/docs/configuration.md +85 -9
  9. package/package.json +4 -2
  10. package/rules/core.md +118 -209
  11. package/rules/modifiers/backlog-outline.md +25 -0
  12. package/rules/modifiers/backlog-visual.md +16 -0
  13. package/rules/modifiers/coded-offers.md +47 -17
  14. package/rules/modifiers/cross-session-relay.md +23 -0
  15. package/rules/modifiers/low-quota-handoff.md +4 -3
  16. package/rules/modifiers/model-default-questions.md +33 -0
  17. package/rules/modifiers/multi-agent.md +6 -2
  18. package/rules/modifiers/no-attribution.md +7 -1
  19. package/rules/modifiers/periodic-updates.md +25 -0
  20. package/rules/modifiers/plan-files.md +35 -3
  21. package/rules/modifiers/release-batching.md +4 -2
  22. package/rules/modifiers/shared-host-load.md +13 -42
  23. package/rules/modifiers/squash-landing.md +1 -1
  24. package/rules/modifiers/upstream-contributions.md +45 -0
  25. package/rules/modifiers/web-image-formats.md +35 -0
  26. package/rules/references/backlog-view.md +154 -0
  27. package/rules/references/landing.md +14 -14
  28. package/rules/references/multi-agent.md +6 -2
  29. package/rules/references/release-batching.md +37 -4
  30. package/rules/references/squash-landing.md +10 -4
  31. package/rules/references/thread-visuals.md +45 -0
  32. package/setup.mjs +87 -17
  33. package/skills/hr-agent-guidance-audit/SKILL.md +10 -5
  34. package/skills/hr-agent-guidance-audit/references/prune-workflow.md +72 -0
  35. package/skills/hr-agent-guidance-audit/references/records-and-lessons.md +1 -1
  36. package/skills/hr-agent-guidance-audit/references/retirement-verdicts.md +49 -0
  37. package/skills/hr-agent-guidance-refresh/SKILL.md +25 -57
  38. package/skills/hr-agent-guidance-refresh/references/verification.md +1 -1
  39. package/skills/hr-audit-choices/SKILL.md +1 -1
  40. package/skills/hr-benchmarking/SKILL.md +122 -159
  41. package/skills/hr-benchmarking/references/javascript.md +113 -0
  42. package/skills/hr-benchmarking/references/tools.md +62 -57
  43. package/skills/hr-better-accessibility/SKILL.md +1 -1
  44. package/skills/hr-better-accessibility/screen-readers.md +1 -0
  45. package/skills/hr-code-review/SKILL.md +19 -3
  46. package/skills/hr-code-review/agents/openai.yaml +1 -1
  47. package/skills/hr-code-review/references/acp-client.md +43 -0
  48. package/skills/{hr-change-impact/SKILL.md → hr-code-review/references/impact.md} +2 -12
  49. package/skills/hr-code-review/references/panel-adjudication.md +58 -0
  50. package/skills/hr-code-review/references/panel-plan.md +61 -0
  51. package/skills/hr-code-review/references/panel-recommendations.json +105 -0
  52. package/skills/hr-code-review/references/panel-recommendations.md +46 -0
  53. package/skills/hr-code-review/references/panel-setup.md +54 -0
  54. package/skills/hr-code-review/references/reviewer-role.md +31 -0
  55. package/skills/hr-code-review/scripts/acp-panel-client.mjs +299 -0
  56. package/skills/hr-code-review/scripts/check-receipts.mjs +263 -0
  57. package/skills/hr-code-review/scripts/panel-plan.mjs +69 -0
  58. package/skills/hr-code-review/scripts/panel-preferences.mjs +220 -0
  59. package/skills/hr-design-exploration/SKILL.md +24 -1
  60. package/skills/hr-design-exploration/agents/openai.yaml +2 -2
  61. package/skills/hr-diagnosing-bugs/SKILL.md +2 -2
  62. package/skills/hr-diagnosing-bugs/references/reporting.md +29 -4
  63. package/skills/hr-diagnosing-bugs/scripts/check-pr-offer.mjs +47 -0
  64. package/skills/hr-explain-code/SKILL.md +1 -1
  65. package/skills/hr-extract-shared-steps/SKILL.md +1 -1
  66. package/skills/hr-forgotten-work-audit/SKILL.md +130 -0
  67. package/skills/hr-forgotten-work-audit/references/brief-template.md +56 -0
  68. package/skills/hr-forgotten-work-audit/references/result-format.md +46 -0
  69. package/skills/hr-handoff/LICENSE +21 -0
  70. package/skills/hr-handoff/NOTICE.md +29 -0
  71. package/skills/hr-handoff/SKILL.md +24 -1
  72. package/skills/hr-handoff/references/reconciliation.md +25 -6
  73. package/skills/hr-handoff/references/successor-thread.md +76 -0
  74. package/skills/hr-handoff/scripts/check-reconciliation.mjs +48 -6
  75. package/skills/hr-house-rules-setup/SKILL.md +22 -8
  76. package/skills/hr-lean-plan/SKILL.md +4 -0
  77. package/skills/hr-maintainability-review/SKILL.md +14 -4
  78. package/skills/hr-maintainability-review/agents/openai.yaml +1 -1
  79. package/skills/hr-manual-qa/SKILL.md +1 -1
  80. package/skills/hr-ordering-tests/SKILL.md +1 -1
  81. package/skills/hr-plain-prose/SKILL.md +7 -4
  82. package/skills/hr-pr-followup/SKILL.md +6 -3
  83. package/skills/hr-pr-followup/references/review-helpers.md +40 -7
  84. package/skills/hr-pr-followup/scripts/check-landing.py +204 -0
  85. package/skills/hr-pr-followup/scripts/check-pr-review.ts +19 -8
  86. package/skills/hr-pr-followup/scripts/merge-pr.sh +100 -0
  87. package/skills/hr-pr-followup/scripts/verify-landed.sh +184 -0
  88. package/skills/hr-read-reddit/SKILL.md +1 -1
  89. package/skills/hr-research-shared-links/SKILL.md +103 -0
  90. package/skills/hr-sqlite/SKILL.md +174 -0
  91. package/skills/hr-sqlite/agents/openai.yaml +2 -0
  92. package/skills/hr-sqlite/references/evidence.md +94 -0
  93. package/skills/hr-sqlite/references/tool-decisions.md +39 -0
  94. package/skills/hr-sqlite/scripts/inspect-sqlite.py +245 -0
  95. package/skills/hr-stock-ui-audit/SKILL.md +1 -1
  96. package/skills/hr-test-audit/LICENSE +21 -0
  97. package/skills/hr-test-audit/NOTICE.md +67 -0
  98. package/skills/hr-usage-report/SKILL.md +90 -0
  99. package/skills/hr-usage-report/agents/openai.yaml +2 -0
  100. package/skills/hr-usage-report/assets/example-report.json +125 -0
  101. package/skills/hr-usage-report/assets/report-template.html +377 -0
  102. package/skills/hr-usage-report/scripts/render-report.ts +86 -0
  103. package/skills/hr-usage-report/scripts/usage-by-model.ts +499 -0
  104. package/skills/hr-what-next/SKILL.md +100 -10
  105. package/skills/hr-what-next/references/reporting.md +13 -22
  106. package/skills/hr-what-next/scripts/check-open-work.mjs +711 -0
  107. package/skills/hr-writing-pr/SKILL.md +29 -14
  108. package/skills/hr-writing-pr/scripts/benefit-chart.py +200 -0
  109. package/docs/npm-release.md +0 -45
  110. package/rules/modifiers/swarmail.md +0 -12
  111. package/skills/hr-benchmarking/references/priority-findings-20261006.md +0 -115
  112. package/skills/hr-change-impact/agents/openai.yaml +0 -5
@@ -0,0 +1,245 @@
1
+ #!/usr/bin/env python3
2
+ """Read-only inspection of a SQLite database file and, optionally, the source
3
+ that opens it.
4
+
5
+ inspect-sqlite.py DATABASE [--source PATH ...] [--quick-check] [--json]
6
+
7
+ Reports what the file itself records (journal mode, page and freelist counts,
8
+ planner statistics, FTS tables, WAL size) plus the filesystem it sits on. It
9
+ never writes to the database. SQLite may create an empty `-shm` file beside a
10
+ WAL database that no process has open.
11
+
12
+ Connection settings such as busy_timeout, synchronous, foreign_keys and
13
+ cache_size are not stored in the file, so no database inspection can show what
14
+ an application sets. `--source` is a text scan: for each mention of
15
+ journal_mode it reports whether a busy_timeout mention comes earlier in the
16
+ same file. It does not establish run order or which connection each belongs
17
+ to. Treat those lines as places to read, not verdicts. A source path that cannot
18
+ be read is listed as not scanned and the exit status is 2.
19
+ """
20
+ import argparse
21
+ import json
22
+ import os
23
+ import pathlib
24
+ import re
25
+ import sqlite3
26
+ import sys
27
+
28
+ NETWORK_FILESYSTEMS = {"9p", "drvfs", "cifs", "smb3", "smbfs", "nfs", "nfs4", "fuse.sshfs", "virtiofs"}
29
+ SHADOW_SUFFIXES = ("_data", "_idx", "_content", "_docsize", "_config", "_segments", "_segdir", "_stat", "_node", "_parent", "_rowid")
30
+ SOURCE_SUFFIXES = {".py", ".js", ".mjs", ".cjs", ".ts", ".tsx", ".rs", ".go", ".rb", ".java", ".kt", ".cs", ".c", ".cc", ".cpp", ".swift", ".php", ".sql"}
31
+
32
+
33
+ def mount_for(path):
34
+ """Return (mount point, filesystem type, options) for the longest mount prefix."""
35
+ try:
36
+ with open("/proc/self/mountinfo", encoding="utf-8", errors="replace") as handle:
37
+ lines = handle.read().splitlines()
38
+ except OSError:
39
+ return None
40
+ real = os.path.realpath(path)
41
+ best = None
42
+ for line in lines:
43
+ left, _, right = line.partition(" - ")
44
+ fields = left.split()
45
+ if len(fields) < 6 or len(right.split()) < 3:
46
+ continue
47
+ point = fields[4].replace("\\040", " ")
48
+ if real == point or real.startswith(point.rstrip("/") + "/") or point == "/":
49
+ if best is None or len(point) > len(best[0]):
50
+ kind, _, super_options = right.split()[:3]
51
+ best = (point, kind, ",".join([fields[5], super_options]))
52
+ return best
53
+
54
+
55
+ def scan_source(paths):
56
+ """For each file that mentions journal_mode, note where busy_timeout appears.
57
+
58
+ Returns (findings, errors). A path that cannot be read is an error, so a
59
+ failed scan never looks like a scan that found nothing.
60
+ """
61
+ findings = []
62
+ errors = []
63
+ files = []
64
+
65
+ def walk_error(error):
66
+ errors.append({"file": str(error.filename), "error": error.strerror or str(error)})
67
+
68
+ for entry in paths:
69
+ if os.path.isdir(entry):
70
+ for root, dirs, names in os.walk(entry, onerror=walk_error):
71
+ dirs[:] = [d for d in dirs if d not in {".git", "node_modules", ".venv", "target", "dist"}]
72
+ files.extend(
73
+ os.path.join(root, n) for n in names if os.path.splitext(n)[1] in SOURCE_SUFFIXES
74
+ )
75
+ else:
76
+ files.append(entry)
77
+ for file in sorted(files):
78
+ try:
79
+ with open(file, encoding="utf-8", errors="replace") as handle:
80
+ lines = handle.read().splitlines()
81
+ except OSError as error:
82
+ errors.append({"file": file, "error": error.strerror or str(error)})
83
+ continue
84
+ journals = [i + 1 for i, l in enumerate(lines) if re.search(r"journal_mode", l, re.I)]
85
+ timeouts = [i + 1 for i, l in enumerate(lines) if re.search(r"busy_timeout|busyTimeout|set_busy_timeout", l, re.I)]
86
+ for journal in journals:
87
+ earlier = [t for t in timeouts if t < journal]
88
+ if earlier:
89
+ status = "busy_timeout at line %d comes before journal_mode at line %d" % (earlier[-1], journal)
90
+ elif timeouts:
91
+ status = "journal_mode at line %d comes before busy_timeout at line %d" % (journal, timeouts[0])
92
+ else:
93
+ status = "journal_mode at line %d; no busy_timeout in this file" % journal
94
+ findings.append({"file": file, "line": journal, "status": status, "ok": bool(earlier)})
95
+ return findings, errors
96
+
97
+
98
+ def inspect(path, quick_check=False):
99
+ result = {"path": os.path.abspath(path)}
100
+ size = os.path.getsize(path)
101
+ result["fileBytes"] = size
102
+ uri = pathlib.Path(result["path"]).as_uri() + "?mode=ro"
103
+ connection = sqlite3.connect(uri, uri=True, timeout=2)
104
+ try:
105
+ connection.execute("PRAGMA busy_timeout = 2000")
106
+
107
+ def pragma(name):
108
+ row = connection.execute("PRAGMA " + name).fetchone()
109
+ return row[0] if row else None
110
+
111
+ result["library"] = {"version": sqlite3.sqlite_version}
112
+ options = [r[0] for r in connection.execute("PRAGMA compile_options")]
113
+ result["library"]["memstatusDisabled"] = "DEFAULT_MEMSTATUS=0" in options
114
+ result["library"]["threadsafe"] = next((o for o in options if o.startswith("THREADSAFE")), None)
115
+ result["journalMode"] = pragma("journal_mode")
116
+ result["pageSize"] = pragma("page_size")
117
+ result["pageCount"] = pragma("page_count")
118
+ result["freelistCount"] = pragma("freelist_count")
119
+ result["autoVacuum"] = pragma("auto_vacuum")
120
+ result["userVersion"] = pragma("user_version")
121
+ result["encoding"] = pragma("encoding")
122
+ names = connection.execute(
123
+ "SELECT name, sql FROM sqlite_master WHERE type = 'table' ORDER BY name"
124
+ ).fetchall()
125
+ tables = [n for n, _ in names if not n.startswith("sqlite_")]
126
+ virtual = {n: s for n, s in names if s and s.upper().startswith("CREATE VIRTUAL TABLE")}
127
+ shadow = {n for table in virtual for n in tables if n in {table + s for s in SHADOW_SUFFIXES}}
128
+ plain = [n for n in tables if n not in virtual and n not in shadow]
129
+ result["tables"] = len(plain)
130
+ result["indexes"] = connection.execute(
131
+ "SELECT count(*) FROM sqlite_master WHERE type = 'index' AND name NOT LIKE 'sqlite_%'"
132
+ ).fetchone()[0]
133
+ result["virtualTables"] = {
134
+ n: (re.search(r"USING\s+(\w+)", s, re.I) or [None, "unknown"])[1].lower()
135
+ for n, s in virtual.items()
136
+ }
137
+ has_stat = any(n == "sqlite_stat1" for n, _ in names)
138
+ analyzed = set()
139
+ if has_stat:
140
+ analyzed = {r[0] for r in connection.execute("SELECT DISTINCT tbl FROM sqlite_stat1")}
141
+ result["statistics"] = {
142
+ "sqliteStat1": has_stat,
143
+ "rows": connection.execute("SELECT count(*) FROM sqlite_stat1").fetchone()[0] if has_stat else 0,
144
+ "tablesWithoutStatistics": [n for n in plain if n not in analyzed],
145
+ }
146
+ if quick_check:
147
+ result["quickCheck"] = [r[0] for r in connection.execute("PRAGMA quick_check(10)")]
148
+ finally:
149
+ connection.close()
150
+ for suffix, key in (("-wal", "walBytes"), ("-shm", "shmBytes")):
151
+ side = result["path"] + suffix
152
+ result[key] = os.path.getsize(side) if os.path.exists(side) else None
153
+ mount = mount_for(result["path"])
154
+ if mount:
155
+ point, kind, options = mount
156
+ result["filesystem"] = {"mount": point, "type": kind, "options": options}
157
+ else:
158
+ result["filesystem"] = None
159
+ return result
160
+
161
+
162
+ def notes(result):
163
+ out = []
164
+ if result["journalMode"] != "wal":
165
+ out.append("journal_mode is %s, not wal; in a rollback journal a writer's commit waits for readers and blocks new ones while it commits" % result["journalMode"])
166
+ stats = result["statistics"]
167
+ if not stats["sqliteStat1"]:
168
+ out.append("no sqlite_stat1: the planner has no statistics; run PRAGMA optimize at a time you choose (the first run on a large file can take over a minute and hold the write lock)")
169
+ elif stats["tablesWithoutStatistics"]:
170
+ out.append("tables with no statistics rows: " + ", ".join(stats["tablesWithoutStatistics"][:8]))
171
+ pages = result["pageCount"] or 0
172
+ if pages and result["freelistCount"] and result["freelistCount"] / pages > 0.2:
173
+ out.append("more than 20%% of pages are free (%d of %d); the file is larger than its content" % (result["freelistCount"], pages))
174
+ if result["walBytes"] and result["walBytes"] > result["fileBytes"]:
175
+ out.append("the WAL file is larger than the database; look for a long-running reader or a blocked checkpoint")
176
+ fs = result["filesystem"]
177
+ if fs:
178
+ if fs["type"] in NETWORK_FILESYSTEMS:
179
+ out.append("filesystem %s: WAL needs shared memory on one host and does not work reliably here" % fs["type"])
180
+ if re.search(r"(^|,)discard(,|$)", fs["options"]):
181
+ out.append("mounted with online discard; this stalled fsync on one host (see the evidence), so check fsync latency")
182
+ if "quickCheck" in result and result["quickCheck"] != ["ok"]:
183
+ out.append("quick_check reported: " + "; ".join(result["quickCheck"][:3]))
184
+ return out
185
+
186
+
187
+ def render(result, source, source_errors=()):
188
+ lines = ["database: %s" % result["path"]]
189
+ lines.append("size: %d bytes; %s pages of %s bytes; %s free" % (
190
+ result["fileBytes"], result["pageCount"], result["pageSize"], result["freelistCount"]))
191
+ lines.append("journal_mode: %s; auto_vacuum: %s; user_version: %s; encoding: %s" % (
192
+ result["journalMode"], result["autoVacuum"], result["userVersion"], result["encoding"]))
193
+ lines.append("tables: %d; indexes: %d; virtual tables: %s" % (
194
+ result["tables"], result["indexes"],
195
+ ", ".join("%s (%s)" % kv for kv in result["virtualTables"].items()) or "none"))
196
+ stats = result["statistics"]
197
+ lines.append("statistics: sqlite_stat1 %s, %d rows" % ("present" if stats["sqliteStat1"] else "absent", stats["rows"]))
198
+ lines.append("wal file: %s; shm file: %s" % (result["walBytes"], result["shmBytes"]))
199
+ fs = result["filesystem"]
200
+ lines.append("filesystem: %s" % ("%s at %s (%s)" % (fs["type"], fs["mount"], fs["options"]) if fs else "not available on this platform"))
201
+ lines.append("inspector library: SQLite %s, %s" % (result["library"]["version"], result["library"]["threadsafe"]))
202
+ lines.append("not in the file: busy_timeout, synchronous, foreign_keys, cache_size, mmap_size, temp_store; read the code that opens the connection")
203
+ for note in notes(result):
204
+ lines.append("note: " + note)
205
+ if source is not None:
206
+ lines.append("source scan (journal_mode mentions): %d" % len(source))
207
+ for item in source:
208
+ lines.append(" %s: %s%s" % (item["file"], item["status"], "" if item["ok"] else " <- read this"))
209
+ for item in source_errors:
210
+ lines.append(" not scanned: %s (%s)" % (item["file"], item["error"]))
211
+ return "\n".join(lines)
212
+
213
+
214
+ def main(argv=None):
215
+ parser = argparse.ArgumentParser(description=__doc__.split("\n\n")[0])
216
+ parser.add_argument("database")
217
+ parser.add_argument("--source", nargs="+", metavar="PATH", help="files or directories to scan for busy_timeout order")
218
+ parser.add_argument("--quick-check", action="store_true", help="also run PRAGMA quick_check (reads the whole file)")
219
+ parser.add_argument("--json", action="store_true")
220
+ args = parser.parse_args(argv)
221
+ if not os.path.isfile(args.database):
222
+ print("error: not a file: %s" % args.database, file=sys.stderr)
223
+ return 2
224
+ try:
225
+ result = inspect(args.database, args.quick_check)
226
+ except (sqlite3.Error, OSError) as error:
227
+ print("error: %s" % error, file=sys.stderr)
228
+ return 2
229
+ source, source_errors = scan_source(args.source) if args.source else (None, [])
230
+ if args.json:
231
+ payload = dict(result)
232
+ payload["notes"] = notes(result)
233
+ if source is not None:
234
+ payload["source"] = source
235
+ payload["sourceErrors"] = source_errors
236
+ print(json.dumps(payload, indent=2, sort_keys=True))
237
+ else:
238
+ print(render(result, source, source_errors))
239
+ for item in source_errors:
240
+ print("error: could not scan %s: %s" % (item["file"], item["error"]), file=sys.stderr)
241
+ return 2 if source_errors else 0
242
+
243
+
244
+ if __name__ == "__main__":
245
+ sys.exit(main())
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: hr-stock-ui-audit
3
- description: Find template-default styling in frontend code that nobody chose for this product (stock violet accents, purple-to-blue gradients, one stock font, emoji icons, glow and blur effects), triage each hit in context, and report or fix the real ones as the request asks. Use when asked to audit or remove generic or AI-looking styling in existing UI. It does not set a visual direction.
3
+ description: Find template-default styling nobody chose for this product (stock violet accents, purple-to-blue gradients, one stock font, emoji icons, glow and blur effects), triage each hit in context, and report or fix the real ones. Use when asked to audit or remove generic or AI-looking styling in existing UI. It does not set a visual direction.
4
4
  ---
5
5
 
6
6
  # Stock UI audit
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Aaron Queen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,67 @@
1
+ # Source notices
2
+
3
+ This notice accompanies the hr-test-audit bundle when copied on its own.
4
+ Each source below is published under the MIT License. The licence text at the
5
+ end applies to each of them, with the copyright line shown for that source.
6
+
7
+ ## obra/superpowers
8
+
9
+ Source: https://github.com/obra/superpowers (compared at revision `8ca22db`)
10
+
11
+ - `SKILL.md`: cites `writing-good-tests.md`.
12
+
13
+ Copyright (c) 2025 Jesse Vincent
14
+
15
+ ## addyosmani/agent-skills
16
+
17
+ Source: https://github.com/addyosmani/agent-skills (compared at revision `9d0c60d`)
18
+
19
+ - `scripts/floor-guard.ts`: adapted from constraint-driven-development.
20
+
21
+ Copyright (c) 2025 Addy Osmani
22
+
23
+ ## openclaw/openclaw
24
+
25
+ Source: https://github.com/openclaw/openclaw (linked at revision `65f1e4d`, compared at `1fda703`)
26
+
27
+ - `SKILL.md`: the authoring gate, junk patterns, retention bar and evidence
28
+ fields are adapted from `.agents/skills/test-audit/`.
29
+
30
+ Copyright (c) 2026 OpenClaw Foundation
31
+
32
+ ## no-human-ai/no_human
33
+
34
+ Source: https://github.com/no-human-ai/no_human (compared at revision `9b50bf6`)
35
+
36
+ - `SKILL.md`: the rule that weakening a test counts as removing it is adapted
37
+ from its tamper guard.
38
+
39
+ Copyright (c) 2026 Eyal Golan
40
+
41
+ ## omkamal/pypict-claude-skill
42
+
43
+ Source: https://github.com/omkamal/pypict-claude-skill (compared at revision `fbda212`)
44
+
45
+ - `SKILL.md`: the pairwise-coverage guidance is adapted from this skill.
46
+
47
+ Copyright (c) 2025 pypict-claude-skill contributors
48
+
49
+ ## MIT License text
50
+
51
+ Permission is hereby granted, free of charge, to any person obtaining a copy
52
+ of this software and associated documentation files (the "Software"), to deal
53
+ in the Software without restriction, including without limitation the rights
54
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
55
+ copies of the Software, and to permit persons to whom the Software is
56
+ furnished to do so, subject to the following conditions:
57
+
58
+ The above copyright notice and this permission notice shall be included in all
59
+ copies or substantial portions of the Software.
60
+
61
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
62
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
63
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
64
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
65
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
66
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
67
+ SOFTWARE.
@@ -0,0 +1,90 @@
1
+ ---
2
+ name: hr-usage-report
3
+ description: Measure where Claude Code, Codex and OpenCode usage went by provider, model and role, and chart model comparisons as HTML. Use for quota or cost questions and to show a model or role recommendation visually.
4
+ ---
5
+
6
+ # Usage report
7
+
8
+ Answer quota and model-role questions from measured usage and published
9
+ evidence, then show the result as charts and tables. Run the scripts from this
10
+ skill's directory with Bun.
11
+
12
+ ## 1. Measure where usage went
13
+
14
+ ```bash
15
+ bun scripts/usage-by-model.ts --days 7 [--providers claude,codex,opencode] [--claude-projects <dir,dir>] [--prices <api.json>] [--json]
16
+ ```
17
+
18
+ It reads Claude Code transcripts (`projects` under `~/.claude` and under every
19
+ other `~/.claude_*` or `~/.claude-*` configuration directory, so sessions run
20
+ with a separate `CLAUDE_CONFIG_DIR` count; `--claude-projects` replaces that
21
+ list), Codex rollouts
22
+ (`~/.codex/sessions`) and the OpenCode database, all read-only and local. It
23
+ prices each request at [models.dev](https://models.dev) list rates, including
24
+ long-context tiers, and splits usage by provider, model and role: main session,
25
+ subagent, advisor. Without `--prices` it downloads `https://models.dev/api.json`;
26
+ that request carries no local data. Use `--since <ISO>` for a fixed start.
27
+
28
+ Read the output beside the provider's live quota readings:
29
+
30
+ - **Shares, not dollars.** List price stands in for subscription quota; no
31
+ provider publishes the conversion. Say so in the report.
32
+ - **Cache reads.** When cache reads dominate a row's cost, context length, not
33
+ the model's output, drives usage. Compare the main-request context median
34
+ with the session-start median: the gap is conversation growth; the start is
35
+ fixed overhead (system prompt, rules, tool schemas).
36
+ - **Advisor rows.** Each advisor call re-reads the whole transcript uncached at
37
+ the advisor's rates; a few calls can outweigh many main turns.
38
+ - **Cross-provider work.** The same table shows what the author's sessions and
39
+ another vendor's reviewer each cost.
40
+ - **Unpriced models.** The summary lists models models.dev has no price for;
41
+ report them rather than dropping them.
42
+
43
+ ## 2. Pick evidence by question
44
+
45
+ | Question | Evidence | Read |
46
+ | ------------------------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------- |
47
+ | Quality per dollar for implementation | Agentic coding boards that publish effort, harness and cost per task | Score against cost at every effort; cost per solved task |
48
+ | Review and bug finding | Bug-finding boards with run counts per row | The effort curve; treat single runs as noisy |
49
+ | Raw speed | Output tokens per second | Generation speed only |
50
+ | Task speed | Boards that time whole tasks | Time to finish; a model that generates faster can still take longer |
51
+ | Fit for your own work | A known-answer trial on your own tasks (`hr-benchmarking`) | Correctness and model time per task shape |
52
+
53
+ Compare models at the same effort and harness, or say they differ. A vendor
54
+ table that sets one model's top effort against another's lower effort is not a
55
+ matched comparison. Keep token speed and task time in separate charts; never
56
+ infer one from the other.
57
+
58
+ ## 3. Render the report
59
+
60
+ Write a data file, then:
61
+
62
+ ```bash
63
+ bun scripts/render-report.ts <data.json> <out.html>
64
+ ```
65
+
66
+ The page is self-contained, follows the host's theme variables when it is
67
+ embedded, and falls back to a dark surface. `assets/example-report.json` uses
68
+ every section type.
69
+
70
+ The data file declares `series` once, so each model keeps one color across
71
+ every chart in the report: up to three colored series with slots 0 to 2, and at
72
+ most one `"reference": true` series drawn dashed and neutral. The renderer
73
+ rejects a fourth colored series because no fourth hue passes the color-vision
74
+ check on the dark surface against the other three. Compare more models in a
75
+ second report file.
76
+
77
+ Section types:
78
+
79
+ - `curve`: points with `x`, `y`, `label`, `tip` per series; `xLog`, domains,
80
+ ticks and axis affixes. Use for score against cost across efforts.
81
+ - `ladder`: `categories` (usually efforts) and `values` per series, `null` for a
82
+ missing row, optional `notes` such as run counts.
83
+ - `bars`: `rows` of `label`, `key`, `value`, optional `display` and `tip`.
84
+ - `pair`: two sections side by side, such as token speed beside task time.
85
+ - `table`: `columns` and `rows`; a cell may be `{ "text", "verdict": "yes" | "maybe" | "no" }`;
86
+ `numeric` lists right-aligned column indexes.
87
+
88
+ End with one verdict table per model or role: role, key evidence with numbers,
89
+ verdict. Name the evidence date in the title, and apply any model or effort
90
+ limits the user has set before recommending a configuration.
@@ -0,0 +1,2 @@
1
+ interface:
2
+ display_name: "HR: Usage report"
@@ -0,0 +1,125 @@
1
+ {
2
+ "title": "Example: three models across effort levels (illustrative data)",
3
+ "series": {
4
+ "small": { "name": "Model S", "slot": 0 },
5
+ "mid": { "name": "Model M", "slot": 1 },
6
+ "large": { "name": "Model L", "slot": 2 },
7
+ "previous": { "name": "Model L (previous)", "reference": true }
8
+ },
9
+ "sections": [
10
+ {
11
+ "type": "curve",
12
+ "title": "Score against cost per task, every effort level",
13
+ "sub": "Illustrative values. Each line runs low to max on a log cost axis.",
14
+ "xLog": true,
15
+ "xDomain": [0.05, 20],
16
+ "yDomain": [20, 60],
17
+ "xTicks": [0.1, 0.3, 1, 3, 10],
18
+ "xPrefix": "$",
19
+ "yTicks": [20, 30, 40, 50, 60],
20
+ "ySuffix": "%",
21
+ "xLabel": "cost per task (log)",
22
+ "series": [
23
+ {
24
+ "key": "small",
25
+ "points": [
26
+ { "label": "low", "x": 0.08, "y": 28, "tip": "28% · $0.08/task" },
27
+ { "label": "medium", "x": 0.2, "y": 34, "tip": "34% · $0.20/task" },
28
+ { "label": "max", "x": 1, "y": 45, "tip": "45% · $1.00/task" }
29
+ ]
30
+ },
31
+ {
32
+ "key": "mid",
33
+ "points": [
34
+ { "label": "low", "x": 0.5, "y": 35, "tip": "35% · $0.50/task" },
35
+ { "label": "medium", "x": 0.8, "y": 40, "tip": "40% · $0.80/task" },
36
+ { "label": "max", "x": 8, "y": 54, "tip": "54% · $8.00/task" }
37
+ ]
38
+ },
39
+ {
40
+ "key": "large",
41
+ "points": [
42
+ { "label": "low", "x": 1.2, "y": 44, "tip": "44% · $1.20/task" },
43
+ { "label": "medium", "x": 3, "y": 52, "tip": "52% · $3.00/task" },
44
+ { "label": "max", "x": 12, "y": 57, "tip": "57% · $12.00/task" }
45
+ ]
46
+ },
47
+ {
48
+ "key": "previous",
49
+ "points": [
50
+ { "label": "low", "x": 5, "y": 43, "tip": "43% · $5.00/task" },
51
+ { "label": "max", "x": 16, "y": 50, "tip": "50% · $16.00/task" }
52
+ ]
53
+ }
54
+ ]
55
+ },
56
+ {
57
+ "type": "ladder",
58
+ "title": "Review: bugs found per effort level",
59
+ "sub": "Illustrative values; notes carry run counts.",
60
+ "categories": ["low", "medium", "high", "max"],
61
+ "yDomain": [0, 50],
62
+ "yTicks": [0, 25, 50],
63
+ "series": [
64
+ {
65
+ "key": "small",
66
+ "values": [10, 12, 16, null],
67
+ "notes": ["1 run", "1 run", "2 runs", "no row"]
68
+ },
69
+ {
70
+ "key": "mid",
71
+ "values": [18, 23, 34, 45],
72
+ "notes": ["3 runs", "3 runs", "3 runs", "3 runs"]
73
+ },
74
+ {
75
+ "key": "large",
76
+ "values": [22, 30, 32, 41],
77
+ "notes": ["3 runs", "3 runs", "3 runs", "3 runs"]
78
+ }
79
+ ]
80
+ },
81
+ {
82
+ "type": "pair",
83
+ "items": [
84
+ {
85
+ "type": "bars",
86
+ "title": "Generation speed: output tokens/s",
87
+ "max": 400,
88
+ "rows": [
89
+ { "label": "Model S", "key": "small", "value": 380 },
90
+ { "label": "Model M", "key": "mid", "value": 220 },
91
+ { "label": "Model L", "key": "large", "value": 125 }
92
+ ]
93
+ },
94
+ {
95
+ "type": "bars",
96
+ "title": "Task time: minutes per task",
97
+ "max": 3,
98
+ "rows": [
99
+ { "label": "Model S", "key": "small", "value": 2.6, "display": "2:36" },
100
+ { "label": "Model M", "key": "mid", "value": 1, "display": "1:00" },
101
+ { "label": "Model L", "key": "large", "value": 2.1, "display": "2:06" }
102
+ ]
103
+ }
104
+ ]
105
+ },
106
+ {
107
+ "type": "table",
108
+ "title": "Model S: where it fits",
109
+ "columns": ["Role", "Key evidence", "Verdict"],
110
+ "rows": [
111
+ [
112
+ "Narrow read-only lookups",
113
+ "Equal accuracy on known-answer lookups at a fraction of the cost",
114
+ { "text": "Yes", "verdict": "yes" }
115
+ ],
116
+ [
117
+ "Mechanical edits",
118
+ "Matches Model M medium at lower cost; untested on your work",
119
+ { "text": "Try on a named task", "verdict": "maybe" }
120
+ ],
121
+ ["Review", "Finds about half of what Model L finds", { "text": "No", "verdict": "no" }]
122
+ ]
123
+ }
124
+ ]
125
+ }