@bompus/house-rules 0.0.0-stage → 0.13.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/CODE_OF_CONDUCT.md +83 -0
- package/CONTRIBUTING.md +54 -0
- package/LICENSE +21 -0
- package/README.md +378 -2
- package/THIRD_PARTY_NOTICES.md +188 -0
- package/compose.mjs +125 -0
- package/composition.mjs +361 -0
- package/config-view.mjs +209 -0
- package/config.mjs +381 -0
- package/docs/assets/banner-dark.png +0 -0
- package/docs/assets/banner-light.png +0 -0
- package/docs/assets/overview.png +0 -0
- package/docs/configuration.md +269 -0
- package/docs/skill-names.md +50 -0
- package/examples/person/house-rules.json +15 -0
- package/examples/person/rules/10-tooling.md +8 -0
- package/examples/person/rules/20-reporting.md +9 -0
- package/examples/project/AGENTS.md +18 -0
- package/package.json +42 -4
- package/rule-references.mjs +91 -0
- package/rules/core.md +340 -0
- package/rules/modifiers/backlog-outline.md +25 -0
- package/rules/modifiers/backlog-visual.md +16 -0
- package/rules/modifiers/coded-offers.md +70 -0
- package/rules/modifiers/cross-session-relay.md +23 -0
- package/rules/modifiers/effort-estimates.md +41 -0
- package/rules/modifiers/land-when-done.md +12 -0
- package/rules/modifiers/low-quota-handoff.md +20 -0
- package/rules/modifiers/model-default-questions.md +33 -0
- package/rules/modifiers/multi-agent.md +38 -0
- package/rules/modifiers/no-attribution.md +16 -0
- package/rules/modifiers/periodic-updates.md +25 -0
- package/rules/modifiers/plan-files.md +82 -0
- package/rules/modifiers/question-cards.md +48 -0
- package/rules/modifiers/release-batching.md +15 -0
- package/rules/modifiers/scratch-on-disk.md +15 -0
- package/rules/modifiers/shared-host-load.md +19 -0
- package/rules/modifiers/solo-operator.md +13 -0
- package/rules/modifiers/squash-landing.md +22 -0
- package/rules/modifiers/upstream-contributions.md +45 -0
- package/rules/modifiers/web-image-formats.md +35 -0
- package/rules/references/backlog-view.md +154 -0
- package/rules/references/github-actions.md +45 -0
- package/rules/references/landing.md +39 -0
- package/rules/references/multi-agent.md +51 -0
- package/rules/references/release-batching.md +66 -0
- package/rules/references/squash-landing.md +50 -0
- package/rules/references/thread-visuals.md +45 -0
- package/setup.mjs +445 -0
- package/skills/hr-agent-guidance-audit/LICENSE +21 -0
- package/skills/hr-agent-guidance-audit/SKILL.md +91 -0
- package/skills/hr-agent-guidance-audit/agents/openai.yaml +2 -0
- package/skills/hr-agent-guidance-audit/references/prune-workflow.md +72 -0
- package/skills/hr-agent-guidance-audit/references/records-and-lessons.md +60 -0
- package/skills/hr-agent-guidance-audit/references/retirement-verdicts.md +49 -0
- package/skills/hr-agent-guidance-refresh/LICENSE +21 -0
- package/skills/hr-agent-guidance-refresh/SKILL.md +36 -0
- package/skills/hr-agent-guidance-refresh/agents/openai.yaml +2 -0
- package/skills/hr-agent-guidance-refresh/references/verification.md +47 -0
- package/skills/hr-api-exposure-check/SKILL.md +159 -0
- package/skills/hr-api-exposure-check/agents/openai.yaml +3 -0
- package/skills/hr-audit-choices/SKILL.md +67 -0
- package/skills/hr-audit-choices/agents/openai.yaml +2 -0
- package/skills/hr-benchmarking/NOTICE.md +36 -0
- package/skills/hr-benchmarking/SKILL.md +279 -0
- package/skills/hr-benchmarking/agents/openai.yaml +2 -0
- package/skills/hr-benchmarking/references/javascript.md +130 -0
- package/skills/hr-benchmarking/references/tools.md +165 -0
- package/skills/hr-benchmarking/scripts/process-attribution.ps1 +20 -0
- package/skills/hr-benchmarking/scripts/process-attribution.py +107 -0
- package/skills/hr-better-accessibility/LICENSE +22 -0
- package/skills/hr-better-accessibility/NOTICE.md +46 -0
- package/skills/hr-better-accessibility/SKILL.md +119 -0
- package/skills/hr-better-accessibility/agents/openai.yaml +3 -0
- package/skills/hr-better-accessibility/focus-and-keyboard.md +130 -0
- package/skills/hr-better-accessibility/forms.md +82 -0
- package/skills/hr-better-accessibility/hit-areas.md +98 -0
- package/skills/hr-better-accessibility/motion-and-zoom.md +81 -0
- package/skills/hr-better-accessibility/screen-readers.md +102 -0
- package/skills/hr-better-accessibility/semantics-and-aria.md +84 -0
- package/skills/hr-better-colors/LICENSE +21 -0
- package/skills/hr-better-colors/NOTICE.md +37 -0
- package/skills/hr-better-colors/SKILL.md +114 -0
- package/skills/hr-better-colors/agents/openai.yaml +3 -0
- package/skills/hr-better-colors/color-formats.md +90 -0
- package/skills/hr-better-colors/color-usage.md +140 -0
- package/skills/hr-better-colors/contrast.md +80 -0
- package/skills/hr-better-colors/palette-generation.md +104 -0
- package/skills/hr-better-colors/palette-structure.md +76 -0
- package/skills/hr-better-colors/token-naming.md +97 -0
- package/skills/hr-better-layout/LICENSE +22 -0
- package/skills/hr-better-layout/NOTICE.md +48 -0
- package/skills/hr-better-layout/SKILL.md +92 -0
- package/skills/hr-better-layout/agents/openai.yaml +3 -0
- package/skills/hr-better-layout/grouping-and-alignment.md +144 -0
- package/skills/hr-better-layout/spacing-and-adaptivity.md +215 -0
- package/skills/hr-better-typography/LICENSE +22 -0
- package/skills/hr-better-typography/NOTICE.md +46 -0
- package/skills/hr-better-typography/SKILL.md +172 -0
- package/skills/hr-better-typography/agents/openai.yaml +3 -0
- package/skills/hr-better-typography/choosing-fonts.md +69 -0
- package/skills/hr-better-typography/css-cheat-sheet.md +65 -0
- package/skills/hr-better-typography/details-and-accessibility.md +119 -0
- package/skills/hr-better-typography/spacing-and-sizing.md +131 -0
- package/skills/hr-better-typography/variable-fonts-and-opentype.md +105 -0
- package/skills/hr-better-typography/wrapping-and-punctuation.md +57 -0
- package/skills/hr-better-writing/LICENSE +22 -0
- package/skills/hr-better-writing/NOTICE.md +46 -0
- package/skills/hr-better-writing/SKILL.md +122 -0
- package/skills/hr-better-writing/agents/openai.yaml +3 -0
- package/skills/hr-code-review/LICENSE +21 -0
- package/skills/hr-code-review/NOTICE.md +26 -0
- package/skills/hr-code-review/SKILL.md +163 -0
- package/skills/hr-code-review/agents/openai.yaml +3 -0
- package/skills/hr-code-review/references/acp-client.md +43 -0
- package/skills/hr-code-review/references/evidence.md +57 -0
- package/skills/hr-code-review/references/impact.md +103 -0
- package/skills/hr-code-review/references/panel-adjudication.md +58 -0
- package/skills/hr-code-review/references/panel-plan.md +61 -0
- package/skills/hr-code-review/references/panel-recommendations.json +105 -0
- package/skills/hr-code-review/references/panel-recommendations.md +46 -0
- package/skills/hr-code-review/references/panel-setup.md +54 -0
- package/skills/hr-code-review/references/reviewer-role.md +31 -0
- package/skills/hr-code-review/scripts/acp-panel-client.mjs +299 -0
- package/skills/hr-code-review/scripts/check-receipts.mjs +263 -0
- package/skills/hr-code-review/scripts/panel-plan.mjs +69 -0
- package/skills/hr-code-review/scripts/panel-preferences.mjs +220 -0
- package/skills/hr-design-exploration/SKILL.md +105 -0
- package/skills/hr-design-exploration/agents/openai.yaml +4 -0
- package/skills/hr-diagnosing-bugs/NOTICE.md +45 -0
- package/skills/hr-diagnosing-bugs/SKILL.md +108 -0
- package/skills/hr-diagnosing-bugs/agents/openai.yaml +3 -0
- package/skills/hr-diagnosing-bugs/references/reporting.md +97 -0
- package/skills/hr-diagnosing-bugs/scripts/check-pr-offer.mjs +47 -0
- package/skills/hr-explain-code/NOTICE.md +37 -0
- package/skills/hr-explain-code/SKILL.md +110 -0
- package/skills/hr-explain-code/agents/openai.yaml +5 -0
- package/skills/hr-explain-code/references/helper-brief.md +39 -0
- package/skills/hr-extract-shared-steps/SKILL.md +134 -0
- package/skills/hr-extract-shared-steps/agents/openai.yaml +3 -0
- package/skills/hr-forgotten-work-audit/SKILL.md +130 -0
- package/skills/hr-forgotten-work-audit/references/brief-template.md +56 -0
- package/skills/hr-forgotten-work-audit/references/result-format.md +46 -0
- package/skills/hr-handoff/LICENSE +21 -0
- package/skills/hr-handoff/NOTICE.md +29 -0
- package/skills/hr-handoff/SKILL.md +114 -0
- package/skills/hr-handoff/agents/openai.yaml +5 -0
- package/skills/hr-handoff/assets/ledger.example.json +10 -0
- package/skills/hr-handoff/assets/receipt.example.json +76 -0
- package/skills/hr-handoff/assets/source.example.json +20 -0
- package/skills/hr-handoff/references/reconciliation.md +124 -0
- package/skills/hr-handoff/references/successor-thread.md +76 -0
- package/skills/hr-handoff/scripts/check-reconciliation.mjs +324 -0
- package/skills/hr-house-rules-setup/SKILL.md +114 -0
- package/skills/hr-house-rules-setup/agents/openai.yaml +5 -0
- package/skills/hr-issue-tracker-setup/SKILL.md +49 -0
- package/skills/hr-issue-tracker-setup/agents/openai.yaml +5 -0
- package/skills/hr-lean-plan/SKILL.md +146 -0
- package/skills/hr-lean-plan/agents/openai.yaml +5 -0
- package/skills/hr-maintainability-review/SKILL.md +186 -0
- package/skills/hr-maintainability-review/agents/openai.yaml +5 -0
- package/skills/hr-manual-qa/NOTICE.md +36 -0
- package/skills/hr-manual-qa/SKILL.md +37 -0
- package/skills/hr-manual-qa/agents/openai.yaml +3 -0
- package/skills/hr-navigation-retro/LICENSE +21 -0
- package/skills/hr-navigation-retro/NOTICE.md +5 -0
- package/skills/hr-navigation-retro/SKILL.md +86 -0
- package/skills/hr-navigation-retro/agents/openai.yaml +4 -0
- package/skills/hr-navigation-retro/references/inputs.md +67 -0
- package/skills/hr-navigation-retro/scripts/retro.py +552 -0
- package/skills/hr-navigation-retro/scripts/transcripts.py +205 -0
- package/skills/hr-ordering-tests/SKILL.md +51 -0
- package/skills/hr-ordering-tests/agents/openai.yaml +2 -0
- package/skills/hr-plain-prose/SKILL.md +264 -0
- package/skills/hr-plain-prose/agents/openai.yaml +3 -0
- package/skills/hr-plain-prose/scripts/check.mjs +331 -0
- package/skills/hr-pr-followup/LICENSE +21 -0
- package/skills/hr-pr-followup/NOTICE.md +9 -0
- package/skills/hr-pr-followup/SKILL.md +74 -0
- package/skills/hr-pr-followup/agents/openai.yaml +2 -0
- package/skills/hr-pr-followup/references/review-helpers.md +111 -0
- package/skills/hr-pr-followup/scripts/check-landing.py +204 -0
- package/skills/hr-pr-followup/scripts/check-pr-review.ts +510 -0
- package/skills/hr-pr-followup/scripts/merge-pr.sh +100 -0
- package/skills/hr-pr-followup/scripts/resolve-pr-thread.ts +219 -0
- package/skills/hr-pr-followup/scripts/verify-landed.sh +184 -0
- package/skills/hr-progress-report/SKILL.md +49 -0
- package/skills/hr-progress-report/agents/openai.yaml +5 -0
- package/skills/hr-read-reddit/SKILL.md +44 -0
- package/skills/hr-read-reddit/agents/openai.yaml +2 -0
- package/skills/hr-read-reddit/scripts/reddit.ts +252 -0
- package/skills/hr-read-x-links/SKILL.md +42 -0
- package/skills/hr-read-x-links/agents/openai.yaml +2 -0
- package/skills/hr-read-x-links/scripts/x-post.ts +264 -0
- package/skills/hr-research-shared-links/SKILL.md +103 -0
- package/skills/hr-split-to-prs/LICENSE +21 -0
- package/skills/hr-split-to-prs/NOTICE.md +9 -0
- package/skills/hr-split-to-prs/SKILL.md +61 -0
- package/skills/hr-split-to-prs/agents/openai.yaml +2 -0
- package/skills/hr-sqlite/SKILL.md +174 -0
- package/skills/hr-sqlite/agents/openai.yaml +2 -0
- package/skills/hr-sqlite/references/evidence.md +94 -0
- package/skills/hr-sqlite/references/tool-decisions.md +39 -0
- package/skills/hr-sqlite/scripts/inspect-sqlite.py +245 -0
- package/skills/hr-stock-ui-audit/SKILL.md +132 -0
- package/skills/hr-stock-ui-audit/agents/openai.yaml +3 -0
- package/skills/hr-stock-ui-audit/scripts/scan.mjs +619 -0
- package/skills/hr-tdd/LICENSE +21 -0
- package/skills/hr-tdd/NOTICE.md +10 -0
- package/skills/hr-tdd/SKILL.md +40 -0
- package/skills/hr-tdd/agents/openai.yaml +3 -0
- package/skills/hr-tdd/mocking.md +39 -0
- package/skills/hr-tdd/tests.md +77 -0
- package/skills/hr-test-audit/LICENSE +21 -0
- package/skills/hr-test-audit/NOTICE.md +67 -0
- package/skills/hr-test-audit/SKILL.md +146 -0
- package/skills/hr-test-audit/agents/openai.yaml +3 -0
- package/skills/hr-test-audit/scripts/floor-guard.ts +137 -0
- package/skills/hr-usage-report/SKILL.md +90 -0
- package/skills/hr-usage-report/agents/openai.yaml +2 -0
- package/skills/hr-usage-report/assets/example-report.json +125 -0
- package/skills/hr-usage-report/assets/report-template.html +377 -0
- package/skills/hr-usage-report/scripts/render-report.ts +86 -0
- package/skills/hr-usage-report/scripts/usage-by-model.ts +499 -0
- package/skills/hr-what-next/SKILL.md +151 -0
- package/skills/hr-what-next/agents/openai.yaml +4 -0
- package/skills/hr-what-next/references/reporting.md +59 -0
- package/skills/hr-what-next/scripts/check-open-work.mjs +711 -0
- package/skills/hr-writing-for-agents/NOTICE.md +46 -0
- package/skills/hr-writing-for-agents/SKILL-MECHANICS.md +83 -0
- package/skills/hr-writing-for-agents/SKILL.md +108 -0
- package/skills/hr-writing-for-agents/agents/openai.yaml +3 -0
- package/skills/hr-writing-pr/NOTICE.md +48 -0
- package/skills/hr-writing-pr/SKILL.md +119 -0
- package/skills/hr-writing-pr/agents/openai.yaml +2 -0
- package/skills/hr-writing-pr/scripts/benefit-chart.py +200 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""Selected-file adapters for OpenCode exports, Cursor streams and ACP v1 logs.
|
|
2
|
+
|
|
3
|
+
No store discovery or recorded-command execution. Adapters emit the same events
|
|
4
|
+
as retro.py. ACP state is folded in a first pass so later input/name updates do
|
|
5
|
+
not change tool numbering or multiply a single call's results.
|
|
6
|
+
"""
|
|
7
|
+
import json
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def records(path):
|
|
11
|
+
with open(path, errors="replace") as source:
|
|
12
|
+
first = source.readline()
|
|
13
|
+
try:
|
|
14
|
+
record = json.loads(first)
|
|
15
|
+
except ValueError:
|
|
16
|
+
# Pretty-printed session exports are one JSON object, not JSONL.
|
|
17
|
+
if first.strip() == '{':
|
|
18
|
+
source.seek(0)
|
|
19
|
+
record = json.load(source)
|
|
20
|
+
if isinstance(record, dict):
|
|
21
|
+
yield record
|
|
22
|
+
return
|
|
23
|
+
record = None
|
|
24
|
+
if isinstance(record, dict):
|
|
25
|
+
yield record
|
|
26
|
+
for line in source:
|
|
27
|
+
try:
|
|
28
|
+
record = json.loads(line)
|
|
29
|
+
except ValueError:
|
|
30
|
+
continue
|
|
31
|
+
if isinstance(record, dict):
|
|
32
|
+
yield record
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def text(value):
|
|
36
|
+
if isinstance(value, str):
|
|
37
|
+
return value
|
|
38
|
+
if isinstance(value, list):
|
|
39
|
+
return '\n'.join(text(v) for v in value)
|
|
40
|
+
if isinstance(value, dict):
|
|
41
|
+
for key in ('text', 'content', 'output'):
|
|
42
|
+
if key in value:
|
|
43
|
+
return text(value[key])
|
|
44
|
+
return json.dumps(value)
|
|
45
|
+
return '' if value is None else json.dumps(value)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def tool(name, value, kind=None, locations=None):
|
|
49
|
+
"""Normalize documented names/inputs; unknown names remain ordinary tools."""
|
|
50
|
+
names = {'read': 'Read', 'read_file': 'Read', 'grep': 'Grep', 'glob': 'Glob',
|
|
51
|
+
'list': 'LS', 'bash': 'exec_command', 'Bash': 'exec_command',
|
|
52
|
+
'write': 'Write', 'edit': 'Edit'}
|
|
53
|
+
if not isinstance(name, str):
|
|
54
|
+
name = None
|
|
55
|
+
if not isinstance(kind, str):
|
|
56
|
+
kind = None
|
|
57
|
+
name = names.get(name, {'read': 'Read', 'search': 'Grep',
|
|
58
|
+
'edit': 'Edit', 'execute': 'exec_command'}.get(kind, name or 'unknown'))
|
|
59
|
+
if name == 'Read' and not isinstance(value, (dict, str)):
|
|
60
|
+
value = {}
|
|
61
|
+
if not isinstance(value, dict):
|
|
62
|
+
value = value if isinstance(value, str) else json.dumps(value or {})
|
|
63
|
+
return name, value
|
|
64
|
+
if name == 'Read':
|
|
65
|
+
path = value.get('file_path') or value.get('filePath') or value.get('path')
|
|
66
|
+
if not path and isinstance(locations, list) and len(locations) == 1:
|
|
67
|
+
path = locations[0].get('path') if isinstance(locations[0], dict) else None
|
|
68
|
+
return name, path if isinstance(path, str) else ''
|
|
69
|
+
if name in ('Grep', 'Glob'):
|
|
70
|
+
return name, str(value.get('pattern') or json.dumps(value))
|
|
71
|
+
return name, json.dumps(value)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def opencode(record, timestamp):
|
|
75
|
+
info = record.get('info')
|
|
76
|
+
if not isinstance(info, dict) or not isinstance(record.get('messages'), list):
|
|
77
|
+
return
|
|
78
|
+
time = info.get('time') if isinstance(info.get('time'), dict) else {}
|
|
79
|
+
title = info.get('title') if isinstance(info.get('title'), str) else ''
|
|
80
|
+
directory = info.get('directory') if isinstance(info.get('directory'), str) else None
|
|
81
|
+
yield ('meta', directory, title, bool(info.get('parentID')), timestamp(time.get('created')))
|
|
82
|
+
for message in record['messages']:
|
|
83
|
+
if not isinstance(message, dict) or not isinstance(message.get('info'), dict):
|
|
84
|
+
continue
|
|
85
|
+
meta = message['info']
|
|
86
|
+
time = meta.get('time') if isinstance(meta.get('time'), dict) else {}
|
|
87
|
+
stamp = timestamp(time.get('created'))
|
|
88
|
+
path = meta.get('path')
|
|
89
|
+
if isinstance(path, dict) and isinstance(path.get('cwd'), str):
|
|
90
|
+
yield ('context', path['cwd'], stamp)
|
|
91
|
+
role = meta.get('role')
|
|
92
|
+
for part in message.get('parts') if isinstance(message.get('parts'), list) else []:
|
|
93
|
+
if not isinstance(part, dict):
|
|
94
|
+
continue
|
|
95
|
+
if part.get('type') == 'text' and role in ('user', 'assistant'):
|
|
96
|
+
yield (role, text(part.get('text')), stamp)
|
|
97
|
+
elif part.get('type') == 'tool' and role == 'assistant':
|
|
98
|
+
state = part.get('state')
|
|
99
|
+
if not isinstance(state, dict):
|
|
100
|
+
continue
|
|
101
|
+
times = state.get('time') if isinstance(state.get('time'), dict) else {}
|
|
102
|
+
name, arg = tool(part.get('tool'), state.get('input'))
|
|
103
|
+
call_id = part.get('callID') or part.get('id')
|
|
104
|
+
if not isinstance(call_id, str):
|
|
105
|
+
continue
|
|
106
|
+
yield ('call', name, arg, call_id, timestamp(times.get('start')) or stamp)
|
|
107
|
+
if state.get('status') in ('completed', 'error'):
|
|
108
|
+
yield ('result', call_id, text(state.get('output') or state.get('error')),
|
|
109
|
+
state['status'] == 'error', timestamp(times.get('end')) or stamp)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def acp_update(record):
|
|
113
|
+
params = record.get('params')
|
|
114
|
+
if isinstance(params, dict) and record.get('method') == 'session/update':
|
|
115
|
+
return params.get('sessionId', ''), params.get('update')
|
|
116
|
+
update = record.get('update')
|
|
117
|
+
if isinstance(update, dict):
|
|
118
|
+
return record.get('sessionId', ''), update
|
|
119
|
+
if 'sessionUpdate' in record:
|
|
120
|
+
return record.get('sessionId', ''), record
|
|
121
|
+
return None, None
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def acp_calls(path):
|
|
125
|
+
calls = {}
|
|
126
|
+
for record in records(path):
|
|
127
|
+
session, update = acp_update(record)
|
|
128
|
+
if not isinstance(update, dict) or update.get('sessionUpdate') not in ('tool_call', 'tool_call_update'):
|
|
129
|
+
continue
|
|
130
|
+
call_id = update.get('toolCallId')
|
|
131
|
+
if not isinstance(call_id, str):
|
|
132
|
+
continue
|
|
133
|
+
if not isinstance(session, str):
|
|
134
|
+
continue
|
|
135
|
+
state = calls.setdefault((session, call_id), {})
|
|
136
|
+
for key, value in update.items():
|
|
137
|
+
if value is not None: # ACP v1 null/omission leaves the prior value.
|
|
138
|
+
state[key] = value
|
|
139
|
+
return calls
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def acp(record, calls, seen, finished, timestamp):
|
|
143
|
+
session, update = acp_update(record)
|
|
144
|
+
if not isinstance(update, dict) or not isinstance(session, str):
|
|
145
|
+
return
|
|
146
|
+
stamp = timestamp(record.get('timestamp'))
|
|
147
|
+
kind = update.get('sessionUpdate')
|
|
148
|
+
if kind in ('user_message_chunk', 'agent_message_chunk'):
|
|
149
|
+
yield ('user_chunk' if kind.startswith('user') else 'assistant_chunk', text(update.get('content')), stamp)
|
|
150
|
+
elif kind in ('tool_call', 'tool_call_update'):
|
|
151
|
+
call_id = update.get('toolCallId')
|
|
152
|
+
if not isinstance(call_id, str):
|
|
153
|
+
return
|
|
154
|
+
key = (session, call_id)
|
|
155
|
+
state = calls[key]
|
|
156
|
+
if key not in seen:
|
|
157
|
+
seen.add(key)
|
|
158
|
+
name, arg = tool(state.get('name'), state.get('rawInput'), state.get('kind'), state.get('locations'))
|
|
159
|
+
yield ('call', name, arg, call_id, stamp)
|
|
160
|
+
if update.get('status') in ('completed', 'failed') and key not in finished:
|
|
161
|
+
finished.add(key)
|
|
162
|
+
output = state.get('rawOutput')
|
|
163
|
+
content = state.get('content')
|
|
164
|
+
if output is None and isinstance(content, list):
|
|
165
|
+
output = [item.get('content') for item in content if isinstance(item, dict)
|
|
166
|
+
and item.get('type') == 'content' and isinstance(item.get('content'), dict)
|
|
167
|
+
and item['content'].get('type') == 'text']
|
|
168
|
+
yield ('result', call_id, text(output),
|
|
169
|
+
update['status'] == 'failed', stamp)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def cursor(record, seen, timestamp):
|
|
173
|
+
stamp = timestamp(record.get('timestamp_ms')) or timestamp(record.get('timestamp'))
|
|
174
|
+
outer = record.get('tool_call')
|
|
175
|
+
if not isinstance(outer, dict):
|
|
176
|
+
return
|
|
177
|
+
name, args, result = 'unknown', {}, None
|
|
178
|
+
for key, canonical in (('readToolCall', 'Read'), ('writeToolCall', 'Write')):
|
|
179
|
+
value = outer.get(key)
|
|
180
|
+
if isinstance(value, dict):
|
|
181
|
+
name, args, result = canonical, value.get('args'), value.get('result')
|
|
182
|
+
break
|
|
183
|
+
else:
|
|
184
|
+
function = outer.get('function')
|
|
185
|
+
if isinstance(function, dict):
|
|
186
|
+
name, args = function.get('name'), function.get('arguments')
|
|
187
|
+
if isinstance(args, str):
|
|
188
|
+
try:
|
|
189
|
+
args = json.loads(args)
|
|
190
|
+
except ValueError:
|
|
191
|
+
pass
|
|
192
|
+
result = outer.get('result')
|
|
193
|
+
name, arg = tool(name, args)
|
|
194
|
+
call_id = record.get('call_id')
|
|
195
|
+
if not isinstance(call_id, str):
|
|
196
|
+
return
|
|
197
|
+
if ('call', call_id) not in seen:
|
|
198
|
+
seen.add(('call', call_id))
|
|
199
|
+
yield ('call', name, arg, call_id, stamp)
|
|
200
|
+
if record.get('subtype') == 'completed' and ('result', call_id) not in seen:
|
|
201
|
+
seen.add(('result', call_id))
|
|
202
|
+
error = isinstance(result, dict) and 'error' in result
|
|
203
|
+
if isinstance(result, dict):
|
|
204
|
+
result = result.get('error') if error else result.get('success', result)
|
|
205
|
+
yield ('result', call_id, text(result), error, stamp)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: hr-ordering-tests
|
|
3
|
+
description: "Enumerate event orderings in ordinary unit tests. Use when a bug or change depends on the order of events between actors (tabs, workers, polling loops, storage writers, retries), or when code merges writes from more than one source. Not for pure functions of a single input."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Ordering tests
|
|
7
|
+
|
|
8
|
+
A hand-written test covers an ordering someone thought of. This technique
|
|
9
|
+
generates every ordering of a small set of events up to a fixed length and runs
|
|
10
|
+
each one through the real code, so it also finds the orderings nobody listed.
|
|
11
|
+
That is a model checker's job, done in the project's own test runner against
|
|
12
|
+
the implementation instead of a separate model that can drift from it.
|
|
13
|
+
|
|
14
|
+
## Steps
|
|
15
|
+
|
|
16
|
+
1. **Name the invariants.** State what must hold after any sequence, such as
|
|
17
|
+
"a completed record never reopens" or "the stored timestamp never goes
|
|
18
|
+
back". Done when each one is a yes/no check on observable state.
|
|
19
|
+
2. **List the events.** Include each distinct write, message, restart or reset
|
|
20
|
+
an actor can produce, with the variants that take different code paths:
|
|
21
|
+
an older, equal and newer timestamp; a field left out; another identity
|
|
22
|
+
(session, tenant, account); two events delivered in one batch. Aim for 6 to
|
|
23
|
+
10 events, and merge variants that exercise the same path.
|
|
24
|
+
3. **Enumerate.** Walk every sequence up to length N depth-first. At each node,
|
|
25
|
+
restore the state its prefix left, apply one event through the real entry
|
|
26
|
+
point (listener, handler, reducer), then check every invariant. The cost is
|
|
27
|
+
events^N applications. N of 3 or 4 is usually enough, since ordering bugs
|
|
28
|
+
rarely need more than three events, and the test should finish within a
|
|
29
|
+
few seconds under full-suite load.
|
|
30
|
+
4. **Report sequences.** Record each violation as the invariant plus the
|
|
31
|
+
sequence, such as `a completed record reopened after A → B → C`, and
|
|
32
|
+
assert the list is empty, so one run shows several failures.
|
|
33
|
+
5. **Prove it discriminates.** Break one guard an invariant depends on,
|
|
34
|
+
confirm the test fails and names a sequence, then revert.
|
|
35
|
+
6. **Act on what it finds.** Pin each sequence the search finds as one named
|
|
36
|
+
regression test next to the enumeration, its owning boundary, and fix it. When the bug is out
|
|
37
|
+
of the change's scope, record it as a follow-up and exclude that invariant
|
|
38
|
+
with the reason until the fix lands.
|
|
39
|
+
|
|
40
|
+
## Keeping it deterministic
|
|
41
|
+
|
|
42
|
+
Drive asynchronous boundaries with fake timers, barriers or a synchronous
|
|
43
|
+
flush; never sleep or use real time. Clone event payloads per application so
|
|
44
|
+
one sequence cannot leak state into the next. For sequences too long to
|
|
45
|
+
enumerate, a seeded random walk over the same events is the fallback; print
|
|
46
|
+
the seed with the failing sequence.
|
|
47
|
+
|
|
48
|
+
## Maintenance
|
|
49
|
+
|
|
50
|
+
When the code gains a new kind of event, add it to the list in the same
|
|
51
|
+
change. Reviewers check for this.
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: hr-plain-prose
|
|
3
|
+
description: Revise text a person will read (commit messages, PR descriptions, docs, code comments, replies, the final report on a task) so it is plain, specific and free of AI tells. Use when drafting or revising human-facing text, even a short message or reply, or when asked to "unslop" text or make it sound less generated.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Plain prose
|
|
7
|
+
|
|
8
|
+
Readers spot generated text by its habits, not by any one word: vague claims,
|
|
9
|
+
inflated vocabulary, the same sentence shapes over and over, decoration in
|
|
10
|
+
place of content. This skill removes those habits while keeping every fact the
|
|
11
|
+
author meant. Apply it to your own drafts before they ship, and to text the
|
|
12
|
+
user hands you. For pull request structure, use `hr-writing-pr`; for text an agent
|
|
13
|
+
reads, use `hr-writing-for-agents`.
|
|
14
|
+
|
|
15
|
+
## Before editing
|
|
16
|
+
|
|
17
|
+
Mark what must survive unchanged: code, identifiers, commands, paths, version
|
|
18
|
+
numbers, error messages, quoted material, links and any technical claim. You
|
|
19
|
+
may rephrase around these, never inside them. Note the author's deliberate
|
|
20
|
+
choices too, such as a project's house terms, a required template or a
|
|
21
|
+
consistent "we". Those stay.
|
|
22
|
+
|
|
23
|
+
A passage the author has flagged to keep, by telling you so or with a
|
|
24
|
+
comment such as `plain-prose: keep`, stays exactly as written, whatever the
|
|
25
|
+
checks below say about it.
|
|
26
|
+
|
|
27
|
+
## Say the specific thing
|
|
28
|
+
|
|
29
|
+
A sentence that could appear unchanged in any other project carries no
|
|
30
|
+
information. Replace it with the mechanism, the number, the file or the source,
|
|
31
|
+
or cut it.
|
|
32
|
+
|
|
33
|
+
- "Improves performance significantly" becomes "Cuts cold start from 2.1 s to
|
|
34
|
+
0.8 s by caching the parsed config."
|
|
35
|
+
- "Various edge cases are handled" becomes "Empty input and a missing trailing
|
|
36
|
+
newline no longer crash the parser."
|
|
37
|
+
|
|
38
|
+
If the specific is not known, say what is unknown ("not measured", "untested on
|
|
39
|
+
Windows"). Never invent a figure, an example or a source to fill the gap. The
|
|
40
|
+
same goes for thresholds and norms. A judgment word such as "normal", "on
|
|
41
|
+
target", "reasonable" or "on track" needs the baseline it is measured against
|
|
42
|
+
("RSS of 1.4 GB, under the 2 GB container limit"). If nothing defines the
|
|
43
|
+
expected value, write that it is undefined rather than passing judgment.
|
|
44
|
+
|
|
45
|
+
- **Unnamed authorities.** "Research suggests", "it is widely accepted" and
|
|
46
|
+
"most developers prefer" with no source behind them present an opinion as
|
|
47
|
+
consensus. Name the study, the survey or the person, or delete the
|
|
48
|
+
sentence.
|
|
49
|
+
- **Fake ranges.** "Handles everything from SQLite files to billing disputes"
|
|
50
|
+
joins two ends that sit on no shared scale, so nothing lies between them.
|
|
51
|
+
List the things it handles.
|
|
52
|
+
|
|
53
|
+
## Choose plain words
|
|
54
|
+
|
|
55
|
+
- Prefer the short common word: "use" over "leverage", "help" over
|
|
56
|
+
"facilitate", "start" over "commence".
|
|
57
|
+
- Let "is" and "has" do their job. "The cache serves as a buffer" is "The cache
|
|
58
|
+
is a buffer."
|
|
59
|
+
- Drop intensifiers and padding: "very", "truly", "it is worth noting that",
|
|
60
|
+
"in order to", "at the end of the day".
|
|
61
|
+
- Keep one hedge where doubt is real and drop the rest. "This might possibly
|
|
62
|
+
help in some cases" becomes "This may help when the queue is full."
|
|
63
|
+
- An adverb attached to a weak verb signals that a stronger verb or a
|
|
64
|
+
measurement belongs there. "Loads really fast" becomes "Loads in 180 ms";
|
|
65
|
+
"sharply reduces errors" becomes "Cuts failed uploads from 4% to 0.3%".
|
|
66
|
+
|
|
67
|
+
The words below are cues to reread a sentence, not proof of anything. When
|
|
68
|
+
one turns up, check whether it says more than its plain replacement would, and
|
|
69
|
+
look harder where several cluster in one paragraph. The checker script under
|
|
70
|
+
"Check the result" reads this table and the one under "Name the actual
|
|
71
|
+
thing", so keep each row as `| term | replacement |`.
|
|
72
|
+
|
|
73
|
+
| Word | Write instead |
|
|
74
|
+
| --------------------- | ------------------------------------------ |
|
|
75
|
+
| delve into | look at, read, test |
|
|
76
|
+
| pivotal, crucial | say what breaks without it |
|
|
77
|
+
| robust | name the failure it survives |
|
|
78
|
+
| seamless | name the step that no longer fails, or cut |
|
|
79
|
+
| comprehensive | name what is covered |
|
|
80
|
+
| streamline | name the step removed |
|
|
81
|
+
| showcase | show |
|
|
82
|
+
| underscore | show, or cut |
|
|
83
|
+
| foster | help, build |
|
|
84
|
+
| elevate | improve, raise |
|
|
85
|
+
| tapestry | cut |
|
|
86
|
+
| realm | area, or cut |
|
|
87
|
+
| testament to | evidence of, or cut |
|
|
88
|
+
| moreover, furthermore | also, or cut |
|
|
89
|
+
| notably | cut |
|
|
90
|
+
|
|
91
|
+
## Name the actual thing
|
|
92
|
+
|
|
93
|
+
Some nouns sound technical but describe a problem as terrain or a physical
|
|
94
|
+
force when an ordinary word for it is available. Name the actual thing.
|
|
95
|
+
|
|
96
|
+
| Stand-in | Plain word |
|
|
97
|
+
| ----------------- | -------------------------------------------- |
|
|
98
|
+
| landscape | the tools or vendors, listed |
|
|
99
|
+
| ecosystem | the packages, plugins or companies meant |
|
|
100
|
+
| friction | the extra step, prompt or wait |
|
|
101
|
+
| traction | users, downloads or revenue, with the number |
|
|
102
|
+
| momentum | what shipped, and when |
|
|
103
|
+
| headwinds | the specific problems |
|
|
104
|
+
| fault line | the boundary where the two designs disagree |
|
|
105
|
+
| center of gravity | the module or team that decides |
|
|
106
|
+
| bandwidth | time |
|
|
107
|
+
| lens | the criterion |
|
|
108
|
+
| pillars | parts, goals |
|
|
109
|
+
| erosion | the drop, with before and after figures |
|
|
110
|
+
|
|
111
|
+
Write figurative language literally too. That covers metaphors, figurative
|
|
112
|
+
verbs, code or documents described as if they had wishes, and slogan-like
|
|
113
|
+
one-liners.
|
|
114
|
+
|
|
115
|
+
- "The parser chokes on tabs" becomes "The parser rejects tab characters."
|
|
116
|
+
- "The README wants you to start with the setup section" becomes "Read the
|
|
117
|
+
setup section first."
|
|
118
|
+
|
|
119
|
+
## Break the sentence habits
|
|
120
|
+
|
|
121
|
+
- **Reflexive threes.** Lists of three adjectives or three parallel clauses
|
|
122
|
+
appear because the rhythm feels finished, not because there are three things.
|
|
123
|
+
Keep the items that are true and distinct.
|
|
124
|
+
- **Contrast framing.** "Not only X, but also Y" and "This isn't about X, it's
|
|
125
|
+
about Y" set up a contrast nobody raised. State Y.
|
|
126
|
+
- **Tacked-on significance.** A trailing participle that announces importance
|
|
127
|
+
("..., highlighting the need for better tests") adds a claim without support.
|
|
128
|
+
Cut it, or make it its own sentence with evidence.
|
|
129
|
+
- **Closing summaries.** A last paragraph that restates the ones above it, or an
|
|
130
|
+
"Overall, ..." line, adds length only. End on the last new fact.
|
|
131
|
+
- **Rotating synonyms.** Calling the same thing "the worker", "the process" and
|
|
132
|
+
"the job" in three sentences makes the reader wonder whether there are three.
|
|
133
|
+
Pick one name and keep it.
|
|
134
|
+
|
|
135
|
+
## Fix the punctuation
|
|
136
|
+
|
|
137
|
+
- **Em dashes.** Leave them out of any prose a person reads. At the spot
|
|
138
|
+
where one would sit, end the sentence and start another, or use a comma. No
|
|
139
|
+
other mark may take its role, which rules out en dashes, spaced hyphens,
|
|
140
|
+
ellipses, colons inside a sentence and parenthetical asides. Do not contort
|
|
141
|
+
the sentence to avoid the missing dash either. Hyphenated words keep their
|
|
142
|
+
hyphens, and code, quotations and numeric ranges such as 10-20 keep their
|
|
143
|
+
dashes.
|
|
144
|
+
- **Colons.** Colons introduce a list, a sample or a block quotation. A
|
|
145
|
+
colon between two clauses in running prose usually sets up a reveal ("The
|
|
146
|
+
cause was simple: a stale cache."). Write the point directly ("A stale cache
|
|
147
|
+
caused the failure.").
|
|
148
|
+
- **Quotes.** Anything headed for source files, commit messages, a shell or a
|
|
149
|
+
Markdown file gets straight quotes and apostrophes. Typographic (curly)
|
|
150
|
+
quotes break shell commands and exact-match searches.
|
|
151
|
+
|
|
152
|
+
## Strip the formatting habits
|
|
153
|
+
|
|
154
|
+
- Bold only what a skimming reader must not miss, at most a few times per page.
|
|
155
|
+
- Avoid bullets that open with a bold label and a colon and then repeat the
|
|
156
|
+
label in the sentence. Write the sentence, or use a heading. Opening a bullet
|
|
157
|
+
with a bolded name and a period, then giving information the name does not
|
|
158
|
+
already say, is fine.
|
|
159
|
+
- No emoji as decoration or as bullet markers.
|
|
160
|
+
- Use sentence case for headings unless the project's style says otherwise.
|
|
161
|
+
- Prefer a paragraph to a bullet list when the points depend on each other.
|
|
162
|
+
|
|
163
|
+
## Remove chat leftovers
|
|
164
|
+
|
|
165
|
+
Text that leaves the conversation should not carry its manners. Delete
|
|
166
|
+
praise for the question ("Great question!"), offers of further help ("Let me
|
|
167
|
+
know if you'd like..."), announcements of what comes next ("Here's a breakdown
|
|
168
|
+
of..."), self-congratulation on a discovery ("Bingo!", "That did it!") and
|
|
169
|
+
self-reference ("As an AI..."). Start with the content.
|
|
170
|
+
|
|
171
|
+
## Make it easy to read
|
|
172
|
+
|
|
173
|
+
- Keep spaces between prose words, counts and units, including agent messages.
|
|
174
|
+
Write "has 12 unfinished cases" and "64 MiB process peak", never
|
|
175
|
+
"has12unfinished" or "64MiBprocesspeak". Preserve literal identifiers and
|
|
176
|
+
filenames byte-for-byte in code spans. Run the checker on outgoing drafts;
|
|
177
|
+
correct its missing-space cues before sending. It cannot detect every joined word.
|
|
178
|
+
- One idea per sentence. Split a sentence that joins two claims with "and".
|
|
179
|
+
- Use active voice with a named actor: "The scheduler drops the job", not "The
|
|
180
|
+
job is dropped".
|
|
181
|
+
- Write replies as complete sentences about the actual work. Translate plan
|
|
182
|
+
labels and internal shorthand into ordinary names before giving identifiers.
|
|
183
|
+
Keep technical terms and exact identifiers when the reader needs them to
|
|
184
|
+
understand, find or verify the work; explain an unfamiliar term when first used.
|
|
185
|
+
- Before each decision question, explain what would change and why it matters
|
|
186
|
+
to the user. Make clear what each answer authorizes, including the limits of
|
|
187
|
+
the work. Already-clear facts need no repeated explanation.
|
|
188
|
+
- Cap sentence length. Keep a step the reader carries out near 20 words and a
|
|
189
|
+
descriptive sentence near 25. A longer one usually holds two ideas. Vary the
|
|
190
|
+
length below the cap.
|
|
191
|
+
- Write each step as a command with one action, and put its condition first:
|
|
192
|
+
"If the build fails, rerun it with `--verbose`." Two actions share a step
|
|
193
|
+
only when they happen at the same time.
|
|
194
|
+
- Before sending a reply, read it without the task notes. Then read only its
|
|
195
|
+
first and last lines. The opening should state the answer or result. The
|
|
196
|
+
ending should make any required next action or decision clear. When none
|
|
197
|
+
remains, end without inventing another task or repeating the answer.
|
|
198
|
+
Rewrite sentences that depend on unexplained labels, abbreviations or
|
|
199
|
+
knowledge of the agent's workflow. State permission limits plainly when
|
|
200
|
+
they affect the proposed action; routine updates do not need the unchanged list.
|
|
201
|
+
- In a report or a note to the user, make each heading or bold lead state the
|
|
202
|
+
takeaway ("Multi-turn ask is switched off"), not only the topic ("Prompt
|
|
203
|
+
caching and multi-turn ask"). Reference pages keep topic headings.
|
|
204
|
+
|
|
205
|
+
## Do not overcorrect
|
|
206
|
+
|
|
207
|
+
- Judge by density. One "robust" or one list of three is fine; a paragraph built
|
|
208
|
+
from them is not. No single word proves the text was generated.
|
|
209
|
+
- Do not swap stiffness for forced casualness: no slang, jokes or "Honestly,"
|
|
210
|
+
openers the author did not write.
|
|
211
|
+
- Vary sentence length. Uniform short sentences are as mechanical as uniform
|
|
212
|
+
long ones.
|
|
213
|
+
- Let emphasis be uneven. A piece can hold one or two quiet, ordinary
|
|
214
|
+
sentences that make no point of their own. Do not fake this with misspellings,
|
|
215
|
+
padding or put-on informality.
|
|
216
|
+
- Editing out old habits can breed new, equally regular ones. Look for a short
|
|
217
|
+
moral at the close of each paragraph ("Small fixes add up."), a denial
|
|
218
|
+
followed at once by its correction ("The bug was not in the parser. It was
|
|
219
|
+
in the cache."), and openers that tease an answer ("So why did it fail?").
|
|
220
|
+
Once in a whole piece reads as voice; once per paragraph reads as a
|
|
221
|
+
template.
|
|
222
|
+
- When a stock phrase goes, delete it rather than swapping in another. A
|
|
223
|
+
replacement that keeps appearing across edits becomes a habit of its own.
|
|
224
|
+
- Leave text alone when it is already plain. A light edit beats a rewrite.
|
|
225
|
+
|
|
226
|
+
## Check the result
|
|
227
|
+
|
|
228
|
+
Run `bun <this skill's directory>/scripts/check.mjs <file>` (Node 22+ also
|
|
229
|
+
works; with no file it reads standard input). It lists each of these with its
|
|
230
|
+
line and column, and exits 1 when it finds any:
|
|
231
|
+
|
|
232
|
+
- an em dash or other dash stand-in
|
|
233
|
+
- a colon between two clauses
|
|
234
|
+
- a curly quote or a filler phrase
|
|
235
|
+
- counts or measurement units joined to prose without spaces
|
|
236
|
+
- a word from the two tables above
|
|
237
|
+
- a sentence over 30 words
|
|
238
|
+
|
|
239
|
+
Each is a cue to reread, not a verdict, because the script cannot tell a
|
|
240
|
+
deliberate word from a habit. It skips code, URLs, block quotations and text
|
|
241
|
+
in straight double quotes, also when the quote wraps a line. It also skips
|
|
242
|
+
word-table rows like the ones above, a table cell holding only a dash and the
|
|
243
|
+
paragraph after a `plain-prose: keep` comment. A colon that ends the label
|
|
244
|
+
opening a list item is not counted. It does not compare the revision with the
|
|
245
|
+
original; the steps below do.
|
|
246
|
+
|
|
247
|
+
Compare the revision against the original, sentence by sentence:
|
|
248
|
+
|
|
249
|
+
1. Every fact, number, condition and caveat from the original is still there,
|
|
250
|
+
with the same meaning and strength.
|
|
251
|
+
2. Every item you marked in "Before editing" is byte-for-byte unchanged.
|
|
252
|
+
3. Nothing new was claimed that the original or the evidence does not support.
|
|
253
|
+
4. Read it once start to finish. If a sentence still sounds like it could
|
|
254
|
+
belong to any project, fix it or cut it.
|
|
255
|
+
5. Read the opening sentence of every paragraph and nothing else, top to
|
|
256
|
+
bottom. When that sequence reads like a neat abstract of the whole text,
|
|
257
|
+
the paragraphs follow a template. Combine some, change their order or begin
|
|
258
|
+
a section with something other than its summary, without changing what it
|
|
259
|
+
says. Specs, runbooks and reference pages are meant to be skimmed this way,
|
|
260
|
+
so skip the step for them.
|
|
261
|
+
|
|
262
|
+
When revising someone else's text, return the edited version and, if any
|
|
263
|
+
meaning was unclear, list the spots where you had to guess rather than guess
|
|
264
|
+
silently.
|