@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.
Files changed (236) hide show
  1. package/CODE_OF_CONDUCT.md +83 -0
  2. package/CONTRIBUTING.md +54 -0
  3. package/LICENSE +21 -0
  4. package/README.md +378 -2
  5. package/THIRD_PARTY_NOTICES.md +188 -0
  6. package/compose.mjs +125 -0
  7. package/composition.mjs +361 -0
  8. package/config-view.mjs +209 -0
  9. package/config.mjs +381 -0
  10. package/docs/assets/banner-dark.png +0 -0
  11. package/docs/assets/banner-light.png +0 -0
  12. package/docs/assets/overview.png +0 -0
  13. package/docs/configuration.md +269 -0
  14. package/docs/skill-names.md +50 -0
  15. package/examples/person/house-rules.json +15 -0
  16. package/examples/person/rules/10-tooling.md +8 -0
  17. package/examples/person/rules/20-reporting.md +9 -0
  18. package/examples/project/AGENTS.md +18 -0
  19. package/package.json +42 -4
  20. package/rule-references.mjs +91 -0
  21. package/rules/core.md +340 -0
  22. package/rules/modifiers/backlog-outline.md +25 -0
  23. package/rules/modifiers/backlog-visual.md +16 -0
  24. package/rules/modifiers/coded-offers.md +70 -0
  25. package/rules/modifiers/cross-session-relay.md +23 -0
  26. package/rules/modifiers/effort-estimates.md +41 -0
  27. package/rules/modifiers/land-when-done.md +12 -0
  28. package/rules/modifiers/low-quota-handoff.md +20 -0
  29. package/rules/modifiers/model-default-questions.md +33 -0
  30. package/rules/modifiers/multi-agent.md +38 -0
  31. package/rules/modifiers/no-attribution.md +16 -0
  32. package/rules/modifiers/periodic-updates.md +25 -0
  33. package/rules/modifiers/plan-files.md +82 -0
  34. package/rules/modifiers/question-cards.md +48 -0
  35. package/rules/modifiers/release-batching.md +15 -0
  36. package/rules/modifiers/scratch-on-disk.md +15 -0
  37. package/rules/modifiers/shared-host-load.md +19 -0
  38. package/rules/modifiers/solo-operator.md +13 -0
  39. package/rules/modifiers/squash-landing.md +22 -0
  40. package/rules/modifiers/upstream-contributions.md +45 -0
  41. package/rules/modifiers/web-image-formats.md +35 -0
  42. package/rules/references/backlog-view.md +154 -0
  43. package/rules/references/github-actions.md +45 -0
  44. package/rules/references/landing.md +39 -0
  45. package/rules/references/multi-agent.md +51 -0
  46. package/rules/references/release-batching.md +66 -0
  47. package/rules/references/squash-landing.md +50 -0
  48. package/rules/references/thread-visuals.md +45 -0
  49. package/setup.mjs +445 -0
  50. package/skills/hr-agent-guidance-audit/LICENSE +21 -0
  51. package/skills/hr-agent-guidance-audit/SKILL.md +91 -0
  52. package/skills/hr-agent-guidance-audit/agents/openai.yaml +2 -0
  53. package/skills/hr-agent-guidance-audit/references/prune-workflow.md +72 -0
  54. package/skills/hr-agent-guidance-audit/references/records-and-lessons.md +60 -0
  55. package/skills/hr-agent-guidance-audit/references/retirement-verdicts.md +49 -0
  56. package/skills/hr-agent-guidance-refresh/LICENSE +21 -0
  57. package/skills/hr-agent-guidance-refresh/SKILL.md +36 -0
  58. package/skills/hr-agent-guidance-refresh/agents/openai.yaml +2 -0
  59. package/skills/hr-agent-guidance-refresh/references/verification.md +47 -0
  60. package/skills/hr-api-exposure-check/SKILL.md +159 -0
  61. package/skills/hr-api-exposure-check/agents/openai.yaml +3 -0
  62. package/skills/hr-audit-choices/SKILL.md +67 -0
  63. package/skills/hr-audit-choices/agents/openai.yaml +2 -0
  64. package/skills/hr-benchmarking/NOTICE.md +36 -0
  65. package/skills/hr-benchmarking/SKILL.md +279 -0
  66. package/skills/hr-benchmarking/agents/openai.yaml +2 -0
  67. package/skills/hr-benchmarking/references/javascript.md +130 -0
  68. package/skills/hr-benchmarking/references/tools.md +165 -0
  69. package/skills/hr-benchmarking/scripts/process-attribution.ps1 +20 -0
  70. package/skills/hr-benchmarking/scripts/process-attribution.py +107 -0
  71. package/skills/hr-better-accessibility/LICENSE +22 -0
  72. package/skills/hr-better-accessibility/NOTICE.md +46 -0
  73. package/skills/hr-better-accessibility/SKILL.md +119 -0
  74. package/skills/hr-better-accessibility/agents/openai.yaml +3 -0
  75. package/skills/hr-better-accessibility/focus-and-keyboard.md +130 -0
  76. package/skills/hr-better-accessibility/forms.md +82 -0
  77. package/skills/hr-better-accessibility/hit-areas.md +98 -0
  78. package/skills/hr-better-accessibility/motion-and-zoom.md +81 -0
  79. package/skills/hr-better-accessibility/screen-readers.md +102 -0
  80. package/skills/hr-better-accessibility/semantics-and-aria.md +84 -0
  81. package/skills/hr-better-colors/LICENSE +21 -0
  82. package/skills/hr-better-colors/NOTICE.md +37 -0
  83. package/skills/hr-better-colors/SKILL.md +114 -0
  84. package/skills/hr-better-colors/agents/openai.yaml +3 -0
  85. package/skills/hr-better-colors/color-formats.md +90 -0
  86. package/skills/hr-better-colors/color-usage.md +140 -0
  87. package/skills/hr-better-colors/contrast.md +80 -0
  88. package/skills/hr-better-colors/palette-generation.md +104 -0
  89. package/skills/hr-better-colors/palette-structure.md +76 -0
  90. package/skills/hr-better-colors/token-naming.md +97 -0
  91. package/skills/hr-better-layout/LICENSE +22 -0
  92. package/skills/hr-better-layout/NOTICE.md +48 -0
  93. package/skills/hr-better-layout/SKILL.md +92 -0
  94. package/skills/hr-better-layout/agents/openai.yaml +3 -0
  95. package/skills/hr-better-layout/grouping-and-alignment.md +144 -0
  96. package/skills/hr-better-layout/spacing-and-adaptivity.md +215 -0
  97. package/skills/hr-better-typography/LICENSE +22 -0
  98. package/skills/hr-better-typography/NOTICE.md +46 -0
  99. package/skills/hr-better-typography/SKILL.md +172 -0
  100. package/skills/hr-better-typography/agents/openai.yaml +3 -0
  101. package/skills/hr-better-typography/choosing-fonts.md +69 -0
  102. package/skills/hr-better-typography/css-cheat-sheet.md +65 -0
  103. package/skills/hr-better-typography/details-and-accessibility.md +119 -0
  104. package/skills/hr-better-typography/spacing-and-sizing.md +131 -0
  105. package/skills/hr-better-typography/variable-fonts-and-opentype.md +105 -0
  106. package/skills/hr-better-typography/wrapping-and-punctuation.md +57 -0
  107. package/skills/hr-better-writing/LICENSE +22 -0
  108. package/skills/hr-better-writing/NOTICE.md +46 -0
  109. package/skills/hr-better-writing/SKILL.md +122 -0
  110. package/skills/hr-better-writing/agents/openai.yaml +3 -0
  111. package/skills/hr-code-review/LICENSE +21 -0
  112. package/skills/hr-code-review/NOTICE.md +26 -0
  113. package/skills/hr-code-review/SKILL.md +163 -0
  114. package/skills/hr-code-review/agents/openai.yaml +3 -0
  115. package/skills/hr-code-review/references/acp-client.md +43 -0
  116. package/skills/hr-code-review/references/evidence.md +57 -0
  117. package/skills/hr-code-review/references/impact.md +103 -0
  118. package/skills/hr-code-review/references/panel-adjudication.md +58 -0
  119. package/skills/hr-code-review/references/panel-plan.md +61 -0
  120. package/skills/hr-code-review/references/panel-recommendations.json +105 -0
  121. package/skills/hr-code-review/references/panel-recommendations.md +46 -0
  122. package/skills/hr-code-review/references/panel-setup.md +54 -0
  123. package/skills/hr-code-review/references/reviewer-role.md +31 -0
  124. package/skills/hr-code-review/scripts/acp-panel-client.mjs +299 -0
  125. package/skills/hr-code-review/scripts/check-receipts.mjs +263 -0
  126. package/skills/hr-code-review/scripts/panel-plan.mjs +69 -0
  127. package/skills/hr-code-review/scripts/panel-preferences.mjs +220 -0
  128. package/skills/hr-design-exploration/SKILL.md +105 -0
  129. package/skills/hr-design-exploration/agents/openai.yaml +4 -0
  130. package/skills/hr-diagnosing-bugs/NOTICE.md +45 -0
  131. package/skills/hr-diagnosing-bugs/SKILL.md +108 -0
  132. package/skills/hr-diagnosing-bugs/agents/openai.yaml +3 -0
  133. package/skills/hr-diagnosing-bugs/references/reporting.md +97 -0
  134. package/skills/hr-diagnosing-bugs/scripts/check-pr-offer.mjs +47 -0
  135. package/skills/hr-explain-code/NOTICE.md +37 -0
  136. package/skills/hr-explain-code/SKILL.md +110 -0
  137. package/skills/hr-explain-code/agents/openai.yaml +5 -0
  138. package/skills/hr-explain-code/references/helper-brief.md +39 -0
  139. package/skills/hr-extract-shared-steps/SKILL.md +134 -0
  140. package/skills/hr-extract-shared-steps/agents/openai.yaml +3 -0
  141. package/skills/hr-forgotten-work-audit/SKILL.md +130 -0
  142. package/skills/hr-forgotten-work-audit/references/brief-template.md +56 -0
  143. package/skills/hr-forgotten-work-audit/references/result-format.md +46 -0
  144. package/skills/hr-handoff/LICENSE +21 -0
  145. package/skills/hr-handoff/NOTICE.md +29 -0
  146. package/skills/hr-handoff/SKILL.md +114 -0
  147. package/skills/hr-handoff/agents/openai.yaml +5 -0
  148. package/skills/hr-handoff/assets/ledger.example.json +10 -0
  149. package/skills/hr-handoff/assets/receipt.example.json +76 -0
  150. package/skills/hr-handoff/assets/source.example.json +20 -0
  151. package/skills/hr-handoff/references/reconciliation.md +124 -0
  152. package/skills/hr-handoff/references/successor-thread.md +76 -0
  153. package/skills/hr-handoff/scripts/check-reconciliation.mjs +324 -0
  154. package/skills/hr-house-rules-setup/SKILL.md +114 -0
  155. package/skills/hr-house-rules-setup/agents/openai.yaml +5 -0
  156. package/skills/hr-issue-tracker-setup/SKILL.md +49 -0
  157. package/skills/hr-issue-tracker-setup/agents/openai.yaml +5 -0
  158. package/skills/hr-lean-plan/SKILL.md +146 -0
  159. package/skills/hr-lean-plan/agents/openai.yaml +5 -0
  160. package/skills/hr-maintainability-review/SKILL.md +186 -0
  161. package/skills/hr-maintainability-review/agents/openai.yaml +5 -0
  162. package/skills/hr-manual-qa/NOTICE.md +36 -0
  163. package/skills/hr-manual-qa/SKILL.md +37 -0
  164. package/skills/hr-manual-qa/agents/openai.yaml +3 -0
  165. package/skills/hr-navigation-retro/LICENSE +21 -0
  166. package/skills/hr-navigation-retro/NOTICE.md +5 -0
  167. package/skills/hr-navigation-retro/SKILL.md +86 -0
  168. package/skills/hr-navigation-retro/agents/openai.yaml +4 -0
  169. package/skills/hr-navigation-retro/references/inputs.md +67 -0
  170. package/skills/hr-navigation-retro/scripts/retro.py +552 -0
  171. package/skills/hr-navigation-retro/scripts/transcripts.py +205 -0
  172. package/skills/hr-ordering-tests/SKILL.md +51 -0
  173. package/skills/hr-ordering-tests/agents/openai.yaml +2 -0
  174. package/skills/hr-plain-prose/SKILL.md +264 -0
  175. package/skills/hr-plain-prose/agents/openai.yaml +3 -0
  176. package/skills/hr-plain-prose/scripts/check.mjs +331 -0
  177. package/skills/hr-pr-followup/LICENSE +21 -0
  178. package/skills/hr-pr-followup/NOTICE.md +9 -0
  179. package/skills/hr-pr-followup/SKILL.md +74 -0
  180. package/skills/hr-pr-followup/agents/openai.yaml +2 -0
  181. package/skills/hr-pr-followup/references/review-helpers.md +111 -0
  182. package/skills/hr-pr-followup/scripts/check-landing.py +204 -0
  183. package/skills/hr-pr-followup/scripts/check-pr-review.ts +510 -0
  184. package/skills/hr-pr-followup/scripts/merge-pr.sh +100 -0
  185. package/skills/hr-pr-followup/scripts/resolve-pr-thread.ts +219 -0
  186. package/skills/hr-pr-followup/scripts/verify-landed.sh +184 -0
  187. package/skills/hr-progress-report/SKILL.md +49 -0
  188. package/skills/hr-progress-report/agents/openai.yaml +5 -0
  189. package/skills/hr-read-reddit/SKILL.md +44 -0
  190. package/skills/hr-read-reddit/agents/openai.yaml +2 -0
  191. package/skills/hr-read-reddit/scripts/reddit.ts +252 -0
  192. package/skills/hr-read-x-links/SKILL.md +42 -0
  193. package/skills/hr-read-x-links/agents/openai.yaml +2 -0
  194. package/skills/hr-read-x-links/scripts/x-post.ts +264 -0
  195. package/skills/hr-research-shared-links/SKILL.md +103 -0
  196. package/skills/hr-split-to-prs/LICENSE +21 -0
  197. package/skills/hr-split-to-prs/NOTICE.md +9 -0
  198. package/skills/hr-split-to-prs/SKILL.md +61 -0
  199. package/skills/hr-split-to-prs/agents/openai.yaml +2 -0
  200. package/skills/hr-sqlite/SKILL.md +174 -0
  201. package/skills/hr-sqlite/agents/openai.yaml +2 -0
  202. package/skills/hr-sqlite/references/evidence.md +94 -0
  203. package/skills/hr-sqlite/references/tool-decisions.md +39 -0
  204. package/skills/hr-sqlite/scripts/inspect-sqlite.py +245 -0
  205. package/skills/hr-stock-ui-audit/SKILL.md +132 -0
  206. package/skills/hr-stock-ui-audit/agents/openai.yaml +3 -0
  207. package/skills/hr-stock-ui-audit/scripts/scan.mjs +619 -0
  208. package/skills/hr-tdd/LICENSE +21 -0
  209. package/skills/hr-tdd/NOTICE.md +10 -0
  210. package/skills/hr-tdd/SKILL.md +40 -0
  211. package/skills/hr-tdd/agents/openai.yaml +3 -0
  212. package/skills/hr-tdd/mocking.md +39 -0
  213. package/skills/hr-tdd/tests.md +77 -0
  214. package/skills/hr-test-audit/LICENSE +21 -0
  215. package/skills/hr-test-audit/NOTICE.md +67 -0
  216. package/skills/hr-test-audit/SKILL.md +146 -0
  217. package/skills/hr-test-audit/agents/openai.yaml +3 -0
  218. package/skills/hr-test-audit/scripts/floor-guard.ts +137 -0
  219. package/skills/hr-usage-report/SKILL.md +90 -0
  220. package/skills/hr-usage-report/agents/openai.yaml +2 -0
  221. package/skills/hr-usage-report/assets/example-report.json +125 -0
  222. package/skills/hr-usage-report/assets/report-template.html +377 -0
  223. package/skills/hr-usage-report/scripts/render-report.ts +86 -0
  224. package/skills/hr-usage-report/scripts/usage-by-model.ts +499 -0
  225. package/skills/hr-what-next/SKILL.md +151 -0
  226. package/skills/hr-what-next/agents/openai.yaml +4 -0
  227. package/skills/hr-what-next/references/reporting.md +59 -0
  228. package/skills/hr-what-next/scripts/check-open-work.mjs +711 -0
  229. package/skills/hr-writing-for-agents/NOTICE.md +46 -0
  230. package/skills/hr-writing-for-agents/SKILL-MECHANICS.md +83 -0
  231. package/skills/hr-writing-for-agents/SKILL.md +108 -0
  232. package/skills/hr-writing-for-agents/agents/openai.yaml +3 -0
  233. package/skills/hr-writing-pr/NOTICE.md +48 -0
  234. package/skills/hr-writing-pr/SKILL.md +119 -0
  235. package/skills/hr-writing-pr/agents/openai.yaml +2 -0
  236. package/skills/hr-writing-pr/scripts/benefit-chart.py +200 -0
@@ -0,0 +1,204 @@
1
+ #!/usr/bin/env python3
2
+ """Report whether a checkout's work has landed in the base branch.
3
+
4
+ Squash-aware: a session branch whose commits were squash-merged is never an
5
+ ancestor of the base, so ancestry alone would cry unlanded forever. The check
6
+ therefore compares content: files unique to the checkout must be identical in
7
+ the base. Read-only; never fetches — the caller fetches first.
8
+
9
+ It also lists live processes whose working directory is inside the checkout
10
+ (Linux /proc only), excluding the checker's ancestors and its pipeline. Each listed process
11
+ blocks worktree removal unless its PID is one you recorded when you started it. `own_session`
12
+ is true only for a process that descends from the nearest agent CLI above the checker (a
13
+ process whose comm is one of AGENT_COMMS); a process under another session of the same agent
14
+ host's server, or any process when the checker has no agent ancestor, is false. A process
15
+ reparented to init after its parent exited is false too.
16
+
17
+ Usage:
18
+ python3 scripts/check-landing.py [--path <checkout>] [--base <ref>]
19
+ python3 scripts/check-landing.py --path <checkout> --fail-on-unlanded
20
+
21
+ Without --base the base is origin/HEAD (the remote's default branch, which
22
+ need not be main), falling back to origin/main when origin/HEAD is unset.
23
+ """
24
+
25
+ import argparse
26
+ import json
27
+ import os
28
+ import subprocess
29
+ import sys
30
+
31
+
32
+ def git(path, *args):
33
+ return subprocess.run(
34
+ ["git", *args],
35
+ cwd=path,
36
+ capture_output=True,
37
+ text=True,
38
+ timeout=60,
39
+ )
40
+
41
+
42
+ def default_base(path):
43
+ """origin/HEAD's target (e.g. origin/fork/consolidated), else origin/main."""
44
+ r = git(path, "symbolic-ref", "--quiet", "--short",
45
+ "refs/remotes/origin/HEAD")
46
+ return r.stdout.strip() if r.returncode == 0 and r.stdout.strip() \
47
+ else "origin/main"
48
+
49
+
50
+ def _ppid(pid):
51
+ try:
52
+ with open(f"/proc/{pid}/stat", encoding="utf-8", errors="replace") as f:
53
+ return int(f.read().rsplit(")", 1)[1].split()[1])
54
+ except (OSError, ValueError, IndexError):
55
+ return 0
56
+
57
+
58
+ # comm names of the agent CLIs. "opencode.exe" is matched without its suffix.
59
+ AGENT_COMMS = {"claude", "codex", "cursor-agent", "opencode", "grok", "devin", "agy"}
60
+
61
+
62
+ def _comm(pid):
63
+ try:
64
+ with open(f"/proc/{pid}/comm", encoding="utf-8", errors="replace") as f:
65
+ return f.read().strip()
66
+ except OSError:
67
+ return ""
68
+
69
+
70
+ def _chain(pid):
71
+ """pid and its parents up to, but excluding, PID 1."""
72
+ chain = []
73
+ while pid > 1 and pid not in chain:
74
+ chain.append(pid)
75
+ pid = _ppid(pid)
76
+ return chain
77
+
78
+
79
+ def nearest_agent(chain, read_comm=_comm):
80
+ """First PID in chain whose comm is an agent CLI, else None."""
81
+ for pid in chain:
82
+ name = read_comm(pid)
83
+ if name.endswith(".exe"):
84
+ name = name[:-4]
85
+ if name in AGENT_COMMS:
86
+ return pid
87
+ return None
88
+
89
+
90
+ def checkout_processes(path):
91
+ """Processes whose cwd is inside path, or None without /proc."""
92
+ if not os.path.isdir("/proc/self"):
93
+ return None
94
+ root = os.path.realpath(path)
95
+ mine = _chain(os.getpid())
96
+ ancestors = set(mine)
97
+ agent = nearest_agent(mine)
98
+ own_group = os.getpgid(0)
99
+ found = []
100
+ for entry in os.listdir("/proc"):
101
+ if not entry.isdigit() or int(entry) in ancestors:
102
+ continue
103
+ try:
104
+ if os.getpgid(int(entry)) == own_group:
105
+ continue
106
+ cwd = os.readlink(f"/proc/{entry}/cwd")
107
+ with open(f"/proc/{entry}/comm", encoding="utf-8", errors="replace") as f:
108
+ comm = f.read().strip()
109
+ except (OSError, ProcessLookupError):
110
+ continue
111
+ if cwd != root and not cwd.startswith(root + os.sep):
112
+ continue
113
+ own = agent is not None and agent in _chain(int(entry))
114
+ found.append({"pid": int(entry), "own_session": own,
115
+ "comm": comm})
116
+ return found
117
+
118
+
119
+ def landing_state(path, base):
120
+ """Return a JSON-serializable report dict for the checkout at path."""
121
+ report = {"path": str(path), "base": base, "ok": False, "reason": ""}
122
+ r = git(path, "rev-parse", "--verify", "--quiet", base)
123
+ if r.returncode != 0:
124
+ report["reason"] = f"base ref missing: {base}"
125
+ return report
126
+ report["base_head"] = r.stdout.strip()
127
+ r = git(path, "rev-parse", "HEAD")
128
+ if r.returncode != 0:
129
+ report["reason"] = "not a git checkout (no HEAD)"
130
+ return report
131
+ report["head"] = r.stdout.strip()
132
+ r = git(path, "symbolic-ref", "--quiet", "--short", "HEAD")
133
+ report["branch"] = r.stdout.strip() if r.returncode == 0 else None
134
+ r = git(path, "status", "--porcelain")
135
+ if r.returncode != 0:
136
+ report["reason"] = "git status failed"
137
+ return report
138
+ report["clean"] = r.stdout.strip() == ""
139
+ report["processes"] = checkout_processes(path)
140
+ r = git(path, "merge-base", "--is-ancestor", "HEAD", base)
141
+ report["ancestor"] = r.returncode == 0
142
+ if report["ancestor"]:
143
+ report["content_landed"] = True
144
+ else:
145
+ r = git(path, "merge-base", base, "HEAD")
146
+ if r.returncode != 0:
147
+ report["reason"] = "no merge base with base ref"
148
+ return report
149
+ mb = r.stdout.strip()
150
+ r = git(path, "diff", "--name-only", "--no-renames", "-z", mb, "HEAD")
151
+ if r.returncode != 0:
152
+ report["reason"] = "git diff failed"
153
+ return report
154
+ files = [f for f in r.stdout.split("\0") if f]
155
+ report["unique_files"] = len(files)
156
+ if not files:
157
+ report["content_landed"] = True
158
+ else:
159
+ r = git(path, "diff", "--quiet", "HEAD", base, "--", *files)
160
+ report["content_landed"] = r.returncode == 0
161
+ report["indeterminate"] = False
162
+ if not report["content_landed"]:
163
+ # Base may have advanced on the same files after the merge
164
+ # base: then a diff is expected even for landed content.
165
+ r = git(path, "diff", "--name-only", "--no-renames", "-z", mb, base,
166
+ "--", *files)
167
+ if r.returncode == 0 and any(
168
+ f for f in r.stdout.split("\0") if f):
169
+ report["indeterminate"] = True
170
+ landed = bool(report["content_landed"])
171
+ if landed and report["clean"]:
172
+ report["ok"] = True
173
+ report["reason"] = "landed and clean"
174
+ elif landed:
175
+ report["reason"] = "content landed but tree is dirty"
176
+ elif report.get("indeterminate"):
177
+ report["reason"] = ("indeterminate - base advanced on touched "
178
+ "files, confirm PR")
179
+ else:
180
+ report["reason"] = "unlanded content vs base"
181
+ return report
182
+
183
+
184
+ def main(argv=None):
185
+ ap = argparse.ArgumentParser(description=__doc__)
186
+ ap.add_argument("--path", default=".", help="checkout to inspect")
187
+ ap.add_argument(
188
+ "--base", help="base ref (default: origin/HEAD, else origin/main)"
189
+ )
190
+ ap.add_argument(
191
+ "--fail-on-unlanded",
192
+ action="store_true",
193
+ help="exit 1 unless the checkout is clean and landed",
194
+ )
195
+ args = ap.parse_args(argv)
196
+ report = landing_state(args.path, args.base or default_base(args.path))
197
+ print(json.dumps(report, indent=2))
198
+ if args.fail_on_unlanded and not report["ok"]:
199
+ return 1
200
+ return 0
201
+
202
+
203
+ if __name__ == "__main__":
204
+ sys.exit(main())