gator-command 1.0.0__py3-none-any.whl

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 (110) hide show
  1. gator_command/__init__.py +2 -0
  2. gator_command/cli.py +137 -0
  3. gator_command/scripts/crawler.py +633 -0
  4. gator_command/scripts/dashboard/dashboard.css +982 -0
  5. gator_command/scripts/dashboard/dashboard.html +84 -0
  6. gator_command/scripts/dashboard/dashboard.js +419 -0
  7. gator_command/scripts/dashboard/views/audit.js +270 -0
  8. gator_command/scripts/dashboard/views/fleet.js +307 -0
  9. gator_command/scripts/dashboard/views/repo.js +599 -0
  10. gator_command/scripts/dashboard/views/settings.js +173 -0
  11. gator_command/scripts/dashboard/views/updates.js +308 -0
  12. gator_command/scripts/enforcer-prompt.md +22 -0
  13. gator_command/scripts/extract-claude-sessions.py +489 -0
  14. gator_command/scripts/extract-codex-sessions.py +477 -0
  15. gator_command/scripts/extract-gemini-sessions.py +410 -0
  16. gator_command/scripts/gator-audit.py +956 -0
  17. gator_command/scripts/gator-charter-draft.py +919 -0
  18. gator_command/scripts/gator-charter-lint.py +427 -0
  19. gator_command/scripts/gator-charter-verify.py +606 -0
  20. gator_command/scripts/gator-dashboard.py +1271 -0
  21. gator_command/scripts/gator-deploy.py +916 -0
  22. gator_command/scripts/gator-drift.py +569 -0
  23. gator_command/scripts/gator-enforce.py +82 -0
  24. gator_command/scripts/gator-fleet-intel.py +460 -0
  25. gator_command/scripts/gator-fleet-report.py +615 -0
  26. gator_command/scripts/gator-init-command-post.py +315 -0
  27. gator_command/scripts/gator-init.py +434 -0
  28. gator_command/scripts/gator-machine-id.py +153 -0
  29. gator_command/scripts/gator-policy-status.py +631 -0
  30. gator_command/scripts/gator-pulse.py +459 -0
  31. gator_command/scripts/gator-repo-status.py +649 -0
  32. gator_command/scripts/gator-session-common.py +372 -0
  33. gator_command/scripts/gator-session-sink.py +831 -0
  34. gator_command/scripts/gator-sessions.py +1244 -0
  35. gator_command/scripts/gator-update.py +615 -0
  36. gator_command/scripts/gator-version.py +38 -0
  37. gator_command/scripts/gator_core.py +489 -0
  38. gator_command/scripts/gator_remote.py +381 -0
  39. gator_command/scripts/gator_runtime.py +142 -0
  40. gator_command/scripts/gatorize-actions.sh +989 -0
  41. gator_command/scripts/gatorize-lib.sh +166 -0
  42. gator_command/scripts/gatorize-post.sh +394 -0
  43. gator_command/scripts/gatorize.py +1163 -0
  44. gator_command/scripts/gatorize.sh +185 -0
  45. gator_command/scripts/generate_markdown.py +212 -0
  46. gator_command/scripts/generate_wiki.py +424 -0
  47. gator_command/scripts/graph_health.py +780 -0
  48. gator_command/scripts/memex-lint.py +286 -0
  49. gator_command/scripts/memex-lint.sh +205 -0
  50. gator_command/scripts/memex.py +1472 -0
  51. gator_command/scripts/memex_formatters.py +191 -0
  52. gator_command/scripts/memex_state.py +236 -0
  53. gator_command/scripts/spawn.py +650 -0
  54. gator_command/templates/gator-starter/blueprints/README.md +32 -0
  55. gator_command/templates/gator-starter/charterignore +53 -0
  56. gator_command/templates/gator-starter/charters/README.md +178 -0
  57. gator_command/templates/gator-starter/charters/_template.md +31 -0
  58. gator_command/templates/gator-starter/commands/commit.md +33 -0
  59. gator_command/templates/gator-starter/commands/init.md +11 -0
  60. gator_command/templates/gator-starter/commands/update.md +5 -0
  61. gator_command/templates/gator-starter/constitution.md +165 -0
  62. gator_command/templates/gator-starter/field-guides/README.md +25 -0
  63. gator_command/templates/gator-starter/gator-start-up.md +119 -0
  64. gator_command/templates/gator-starter/procedures/charter-alignment.md +83 -0
  65. gator_command/templates/gator-starter/procedures/enforcer-review.md +317 -0
  66. gator_command/templates/gator-starter/procedures/field-guide-generation.md +176 -0
  67. gator_command/templates/gator-starter/procedures/knowledge-capture.md +57 -0
  68. gator_command/templates/gator-starter/procedures/significance-check.md +69 -0
  69. gator_command/templates/gator-starter/reference-notes/concierge-responses.md +535 -0
  70. gator_command/templates/gator-starter/reference-notes/dangerous-patterns.md +91 -0
  71. gator_command/templates/gator-starter/reference-notes/dashboard-operations.md +22 -0
  72. gator_command/templates/gator-starter/reference-notes/enforcer-configuration.md +232 -0
  73. gator_command/templates/gator-starter/reference-notes/example-project.md +289 -0
  74. gator_command/templates/gator-starter/reference-notes/failure-modes-and-self-correction.md +72 -0
  75. gator_command/templates/gator-starter/reference-notes/git-workflow.md +60 -0
  76. gator_command/templates/gator-starter/reference-notes/identity-and-ownership.md +37 -0
  77. gator_command/templates/gator-starter/reference-notes/refactor-approach.md +155 -0
  78. gator_command/templates/gator-starter/reference-notes/what-gator-requires-from-a-model.md +108 -0
  79. gator_command/templates/gator-starter/reference-notes/why-navigation-coding-feels-different.md +99 -0
  80. gator_command/templates/gator-starter/reference-notes/workflow-profiles.md +155 -0
  81. gator_command/templates/gator-starter/scripts/__pycache__/enforcer-review.cpython-313.pyc +0 -0
  82. gator_command/templates/gator-starter/scripts/__pycache__/gator-approve.cpython-313.pyc +0 -0
  83. gator_command/templates/gator-starter/scripts/__pycache__/gator-init.cpython-313.pyc +0 -0
  84. gator_command/templates/gator-starter/scripts/__pycache__/gator-pre-commit.cpython-313.pyc +0 -0
  85. gator_command/templates/gator-starter/scripts/__pycache__/gator-update.cpython-313.pyc +0 -0
  86. gator_command/templates/gator-starter/scripts/enforcer-prompt.md +55 -0
  87. gator_command/templates/gator-starter/scripts/enforcer-review.py +1551 -0
  88. gator_command/templates/gator-starter/scripts/gator-approve.py +139 -0
  89. gator_command/templates/gator-starter/scripts/gator-enforce.py +82 -0
  90. gator_command/templates/gator-starter/scripts/gator-init.py +434 -0
  91. gator_command/templates/gator-starter/scripts/gator-pre-commit.py +2670 -0
  92. gator_command/templates/gator-starter/scripts/gator-pulse.py +459 -0
  93. gator_command/templates/gator-starter/scripts/gator-update.py +615 -0
  94. gator_command/templates/gator-starter/scripts/gator-version.py +38 -0
  95. gator_command/templates/gator-starter/scripts/gator_core.py +487 -0
  96. gator_command/templates/gator-starter/scripts/hooks/__pycache__/commit-msgcpython-313.pyc +0 -0
  97. gator_command/templates/gator-starter/scripts/hooks/__pycache__/post-commitcpython-313.pyc +0 -0
  98. gator_command/templates/gator-starter/scripts/hooks/__pycache__/pre-commitcpython-313.pyc +0 -0
  99. gator_command/templates/gator-starter/scripts/hooks/commit-msg +5 -0
  100. gator_command/templates/gator-starter/scripts/hooks/post-commit +7 -0
  101. gator_command/templates/gator-starter/scripts/hooks/pre-commit +5 -0
  102. gator_command/templates/gator-starter/sessions/.gitignore +7 -0
  103. gator_command/templates/gator-starter/vault/.gitkeep +0 -0
  104. gator_command/templates/gator-starter/whiteboard.md +5 -0
  105. gator_command-1.0.0.dist-info/METADATA +122 -0
  106. gator_command-1.0.0.dist-info/RECORD +110 -0
  107. gator_command-1.0.0.dist-info/WHEEL +5 -0
  108. gator_command-1.0.0.dist-info/entry_points.txt +2 -0
  109. gator_command-1.0.0.dist-info/licenses/LICENSE +21 -0
  110. gator_command-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,487 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ gator_core.py — Shared infrastructure for Gator Command scripts.
4
+
5
+ Centralizes utilities that were previously duplicated across 6+ scripts:
6
+ version resolution, command-post discovery, registry parsing, path
7
+ normalization, git helpers, and stdout setup.
8
+
9
+ Usage (automatic — scripts/ is on sys.path when any sibling runs):
10
+ from gator_core import get_version, find_command_post, parse_registry, git
11
+
12
+ Not a standalone script — imported by other gator-* scripts.
13
+
14
+ @reads: filesystem, git tags, registry.md, .gator/command-post.md
15
+ @writes: nothing (pure library)
16
+ """
17
+
18
+ import io
19
+ import json
20
+ import re
21
+ import subprocess
22
+ import sys
23
+ from pathlib import Path
24
+
25
+
26
+ # ---------------------------------------------------------------------------
27
+ # Version
28
+ # ---------------------------------------------------------------------------
29
+
30
+ def _read_version_file(start=None):
31
+ """Read VERSION file by walking up from start (or this script's dir)."""
32
+ d = start or Path(__file__).resolve().parent
33
+ for _ in range(5):
34
+ vf = d / "VERSION"
35
+ if vf.is_file():
36
+ text = vf.read_text(encoding="utf-8", errors="replace").strip()
37
+ if text:
38
+ return f"v{text}" if not text.startswith("v") else text
39
+ d = d.parent
40
+ return None
41
+
42
+
43
+ def get_version(cwd=None):
44
+ """Get the current version from git tags, falling back to VERSION file.
45
+
46
+ Resolution order:
47
+ 1. git describe --tags --always (exact tag or tag+offset)
48
+ 2. VERSION file (for deployed repos without full git history)
49
+ 3. git rev-parse --short HEAD (bare hash)
50
+ 4. "dev"
51
+ """
52
+ if cwd is None:
53
+ cwd = Path(__file__).resolve().parent
54
+ for _ in range(5):
55
+ if (cwd / ".git").is_dir():
56
+ break
57
+ cwd = cwd.parent
58
+
59
+ try:
60
+ result = subprocess.run(
61
+ ["git", "describe", "--tags", "--always"],
62
+ capture_output=True, text=True, cwd=cwd, timeout=5,
63
+ )
64
+ if result.returncode == 0 and result.stdout.strip():
65
+ version = result.stdout.strip()
66
+ if version.startswith("v"):
67
+ return version
68
+ except (OSError, subprocess.TimeoutExpired):
69
+ pass
70
+
71
+ # No tagged version from git — try VERSION file
72
+ from_file = _read_version_file(cwd)
73
+ if from_file:
74
+ return from_file
75
+
76
+ try:
77
+ result = subprocess.run(
78
+ ["git", "rev-parse", "--short", "HEAD"],
79
+ capture_output=True, text=True, cwd=cwd, timeout=5,
80
+ )
81
+ if result.returncode == 0 and result.stdout.strip():
82
+ return f"dev-g{result.stdout.strip()}"
83
+ except (OSError, subprocess.TimeoutExpired):
84
+ pass
85
+
86
+ return "dev"
87
+
88
+
89
+ def get_version_short(cwd=None):
90
+ """Get just the tag version, without commit count or hash.
91
+
92
+ Returns "v0.1.0" whether on the tag or after it.
93
+ Falls back to "dev" if no tags.
94
+ """
95
+ version = get_version(cwd)
96
+ if version.startswith("v"):
97
+ return version.split("-")[0]
98
+ return version
99
+
100
+
101
+ # ---------------------------------------------------------------------------
102
+ # Path helpers
103
+ # ---------------------------------------------------------------------------
104
+
105
+ def normalize_path(raw_path):
106
+ """Normalize MSYS2/Git Bash paths (/c/Users/...) to native Windows.
107
+
108
+ Converts /c/Users/... to C:/Users/... for cross-platform compatibility.
109
+ No-op on paths that don't match the MSYS2 pattern.
110
+ """
111
+ if re.match(r'^/[a-zA-Z]/', raw_path):
112
+ raw_path = raw_path[1].upper() + ":" + raw_path[2:]
113
+ return raw_path
114
+
115
+
116
+ def find_command_post(start_path=None):
117
+ """Find the gator-command repo root.
118
+
119
+ Walks up from start_path looking for a directory containing
120
+ gator-command/mission.md. Used by command-post scripts (fleet-report,
121
+ drift, audit, init-command-post).
122
+ """
123
+ path = Path(start_path) if start_path else Path.cwd()
124
+ path = path.resolve()
125
+
126
+ if (path / "gator-command" / "mission.md").exists():
127
+ return path
128
+ if (path / "mission.md").exists() and (path / "active-threads").is_dir():
129
+ return path.parent
130
+ for parent in path.parents:
131
+ if (parent / "gator-command" / "mission.md").exists():
132
+ return parent
133
+ return None
134
+
135
+
136
+ def find_gator_root(start_path=None):
137
+ """Walk up from start_path looking for .gator/ directory.
138
+
139
+ Used by per-repo scripts (init, update) to find the governed repo root.
140
+ """
141
+ path = Path(start_path) if start_path else Path.cwd()
142
+ path = path.resolve()
143
+ if (path / ".gator").is_dir():
144
+ return path
145
+ for parent in path.parents:
146
+ if (parent / ".gator").is_dir():
147
+ return parent
148
+ return None
149
+
150
+
151
+ def resolve_thin_link(gator_dir):
152
+ """Read the command-post thin link and resolve the path.
153
+
154
+ Used by per-repo scripts (update) to find the command post from
155
+ a governed repo's .gator/command-post.md.
156
+ """
157
+ cp_file = gator_dir / "command-post.md"
158
+ if not cp_file.exists():
159
+ return None
160
+
161
+ text = cp_file.read_text(encoding="utf-8", errors="replace")
162
+
163
+ # Try command-post-absolute first (most reliable on same machine)
164
+ for line in text.splitlines():
165
+ if line.startswith("command-post-absolute:"):
166
+ raw = line.split(":", 1)[1].strip().split("#")[0].strip()
167
+ if raw:
168
+ resolved = Path(normalize_path(raw))
169
+ if resolved.is_dir():
170
+ return resolved
171
+
172
+ # Fall back to command-post (relative or absolute)
173
+ for line in text.splitlines():
174
+ if line.startswith("command-post:") and not line.startswith("command-post-absolute:"):
175
+ raw = line.split(":", 1)[1].strip()
176
+ if raw:
177
+ # Try as relative from repo root
178
+ resolved = (gator_dir.parent / raw).resolve()
179
+ if resolved.is_dir():
180
+ return resolved
181
+ # Try as absolute
182
+ resolved = Path(normalize_path(raw))
183
+ if resolved.is_dir():
184
+ return resolved
185
+
186
+ return None
187
+
188
+
189
+ # ---------------------------------------------------------------------------
190
+ # Product source and repo topology
191
+ # ---------------------------------------------------------------------------
192
+
193
+ def read_product_source(gator_dir):
194
+ """Read .gator/product-source.json. Returns dict or None if missing/corrupt."""
195
+ ps_file = Path(gator_dir) / "product-source.json"
196
+ if not ps_file.exists():
197
+ return None
198
+ try:
199
+ data = json.loads(ps_file.read_text(encoding="utf-8"))
200
+ return data if isinstance(data, dict) else None
201
+ except (json.JSONDecodeError, OSError):
202
+ return None
203
+
204
+
205
+ def resolve_template_source(gator_dir, source_override=None):
206
+ """Resolve the template source directory for product updates.
207
+
208
+ Resolution order:
209
+ 1. Explicit --source override
210
+ 2. .gator/product-source.json (gator_root + template_dir)
211
+ 3. Thin link -> command post -> templates (legacy fallback)
212
+ 4. None (caller should fail with clear message)
213
+
214
+ Returns (template_path, gator_root) or (None, None).
215
+ """
216
+ # 1. Explicit override
217
+ if source_override:
218
+ root = Path(source_override).resolve()
219
+ for tpl_rel in ("gator-engine/templates/gator-starter",
220
+ "gator-command/templates/gator-starter",
221
+ "templates/gator-starter"):
222
+ tpl = root / tpl_rel
223
+ if tpl.is_dir():
224
+ return tpl, root
225
+ return None, None
226
+
227
+ gator_dir = Path(gator_dir)
228
+
229
+ # 2. product-source.json
230
+ ps = read_product_source(gator_dir)
231
+ if ps:
232
+ root = Path(ps.get("gator_root", ""))
233
+ tpl_rel = ps.get("template_dir", "")
234
+ if root.is_dir() and tpl_rel:
235
+ tpl = root / tpl_rel
236
+ if tpl.is_dir():
237
+ return tpl, root
238
+
239
+ # 3. Thin link fallback
240
+ cp = resolve_thin_link(gator_dir)
241
+ if cp:
242
+ for tpl_rel in ("gator-command/templates/gator-starter",
243
+ "gator-engine/templates/gator-starter",
244
+ "templates/gator-starter"):
245
+ tpl = Path(cp) / tpl_rel
246
+ if tpl.is_dir():
247
+ return tpl, Path(cp)
248
+
249
+ return None, None
250
+
251
+
252
+ def get_repo_topology(gator_dir):
253
+ """Derive the governance topology of a repo.
254
+
255
+ Returns one of:
256
+ "policy-synced" — active thin link, repo receives org policy
257
+ "standalone" — no thin link, no policy artifacts, template updates only
258
+ "inconsistent" — partial artifacts remain, needs repair
259
+ """
260
+ gator_dir = Path(gator_dir)
261
+ cp_file = gator_dir / "command-post.md"
262
+ gov_source = gator_dir / "governance-source.json"
263
+ policy_link = gator_dir / "policy-link.json"
264
+ policy_cache = gator_dir / "policy-cache"
265
+
266
+ has_thin_link = cp_file.exists()
267
+ has_policy_content = policy_link.exists() or (
268
+ policy_cache.is_dir() and any(policy_cache.iterdir())
269
+ )
270
+ has_gov_source = gov_source.exists()
271
+
272
+ # Thin link present and resolvable → policy-synced
273
+ if has_thin_link:
274
+ cp = resolve_thin_link(gator_dir)
275
+ if cp:
276
+ return "policy-synced"
277
+ # Thin link exists but points nowhere
278
+ return "inconsistent"
279
+
280
+ # No thin link — check for orphaned policy artifacts
281
+ if has_policy_content or has_gov_source:
282
+ return "inconsistent"
283
+
284
+ return "standalone"
285
+
286
+
287
+ def clear_policy_artifacts(gator_dir):
288
+ """Remove all policy-source artifacts to make a repo standalone.
289
+
290
+ Clears: command-post.md, governance-source.json, policy-link.json, policy-cache/.
291
+ """
292
+ import shutil
293
+ gator_dir = Path(gator_dir)
294
+
295
+ for name in ("command-post.md", "governance-source.json", "policy-link.json"):
296
+ f = gator_dir / name
297
+ if f.exists():
298
+ f.unlink()
299
+
300
+ cache = gator_dir / "policy-cache"
301
+ if cache.is_dir():
302
+ shutil.rmtree(cache)
303
+
304
+
305
+ # ---------------------------------------------------------------------------
306
+ # Registry
307
+ # ---------------------------------------------------------------------------
308
+
309
+ def parse_registry(command_post):
310
+ """Parse registry.md into a list of repo entries.
311
+
312
+ Each entry is a dict with: name, path, remote, registered, status.
313
+ """
314
+ registry_file = command_post / "gator-command" / "registry.md"
315
+ if not registry_file.exists():
316
+ return []
317
+
318
+ repos = []
319
+ text = registry_file.read_text(encoding="utf-8", errors="replace")
320
+ in_table = False
321
+
322
+ for line in text.splitlines():
323
+ stripped = line.strip()
324
+ if stripped.startswith("| Repo"):
325
+ in_table = True
326
+ continue
327
+ if stripped.startswith("|---"):
328
+ continue
329
+ if in_table and stripped.startswith("|"):
330
+ cells = [c.strip() for c in stripped.split("|")[1:-1]]
331
+ if len(cells) >= 5:
332
+ repos.append({
333
+ "name": cells[0],
334
+ "path": cells[1],
335
+ "remote": cells[2],
336
+ "registered": cells[3],
337
+ "status": cells[4],
338
+ })
339
+
340
+ return repos
341
+
342
+
343
+ # ---------------------------------------------------------------------------
344
+ # Git helpers
345
+ # ---------------------------------------------------------------------------
346
+
347
+ def git(*args, cwd=None):
348
+ """Run a git command, return (stdout, success).
349
+
350
+ Returns a tuple so callers can distinguish 'git returned nothing'
351
+ from 'git failed'. Silent emptiness is the wrong failure mode for
352
+ a product whose claim is trustworthy governance telemetry.
353
+ """
354
+ try:
355
+ result = subprocess.run(
356
+ ["git"] + list(args),
357
+ capture_output=True, text=True, cwd=cwd, timeout=10,
358
+ )
359
+ return result.stdout.strip(), result.returncode == 0
360
+ except (OSError, subprocess.TimeoutExpired):
361
+ return "", False
362
+
363
+
364
+ # ---------------------------------------------------------------------------
365
+ # Stdout setup
366
+ # ---------------------------------------------------------------------------
367
+
368
+ def ensure_utf8_stdout():
369
+ """Ensure stdout uses UTF-8 encoding (needed on Windows)."""
370
+ if sys.stdout.encoding != "utf-8":
371
+ sys.stdout = io.TextIOWrapper(
372
+ sys.stdout.buffer, encoding="utf-8", errors="replace"
373
+ )
374
+
375
+
376
+ # ---------------------------------------------------------------------------
377
+ # Script import helper
378
+ # ---------------------------------------------------------------------------
379
+
380
+ def import_sibling(name):
381
+ """Import a sibling script by filename (handles hyphens).
382
+
383
+ Example: import_sibling("gator-fleet-report") imports gator-fleet-report.py
384
+ from the same directory as the calling script.
385
+
386
+ Returns the module on success, None if the file doesn't exist.
387
+ Raises ImportError with diagnostic context if the file exists but
388
+ fails to load — callers should catch this if they want resilience.
389
+ """
390
+ import importlib.util
391
+
392
+ scripts_dir = Path(__file__).resolve().parent
393
+ path = scripts_dir / f"{name}.py"
394
+ if not path.exists():
395
+ return None
396
+
397
+ spec = importlib.util.spec_from_file_location(
398
+ name.replace("-", "_"), path
399
+ )
400
+ mod = importlib.util.module_from_spec(spec)
401
+ try:
402
+ spec.loader.exec_module(mod)
403
+ except Exception as e:
404
+ raise ImportError(
405
+ f"Failed to load {path.name}: {type(e).__name__}: {e}"
406
+ ) from e
407
+ return mod
408
+
409
+
410
+ # ---------------------------------------------------------------------------
411
+ # Charter surface resolution
412
+ # ---------------------------------------------------------------------------
413
+
414
+ def resolve_charter_surface(repo_root=None):
415
+ """Return the authoritative charter surface for this repo.
416
+
417
+ Two modes:
418
+ - source-command-post: the gator-command development repo.
419
+ Charters at gator-command/charters/, cross-cutting is
420
+ scripts-cross-cutting.md.
421
+ - governed-repo: all other repos (fleet repos, deployed command posts).
422
+ Charters at .gator/charters/, cross-cutting is cross-cutting.md.
423
+
424
+ Returns dict with:
425
+ mode: "source-command-post" | "governed-repo"
426
+ charter_dir: Path to authoritative charter directory
427
+ cross_cutting: filename of the cross-cutting charter (or None)
428
+ index_file: Path to INDEX.md (or None if absent)
429
+ """
430
+ if repo_root is None:
431
+ repo_root = Path.cwd()
432
+ else:
433
+ repo_root = Path(repo_root)
434
+
435
+ # Detect source-command-post mode: gator-command/charters/ AND
436
+ # gator-command/scripts/ both exist. This is the development repo.
437
+ cp_charters = repo_root / "gator-command" / "charters"
438
+ cp_scripts = repo_root / "gator-command" / "scripts"
439
+ if cp_charters.is_dir() and cp_scripts.is_dir():
440
+ cross_cutting = None
441
+ for f in cp_charters.iterdir():
442
+ if "cross-cutting" in f.name and f.suffix == ".md":
443
+ cross_cutting = f.name
444
+ break
445
+ index_file = cp_charters / "INDEX.md"
446
+ return {
447
+ "mode": "source-command-post",
448
+ "charter_dir": cp_charters,
449
+ "cross_cutting": cross_cutting,
450
+ "index_file": index_file if index_file.is_file() else None,
451
+ }
452
+
453
+ # Default: governed-repo mode
454
+ charter_dir = repo_root / ".gator" / "charters"
455
+ cross_cutting = None
456
+ if charter_dir.is_dir():
457
+ for f in charter_dir.iterdir():
458
+ if "cross-cutting" in f.name and f.suffix == ".md":
459
+ cross_cutting = f.name
460
+ break
461
+ index_file = charter_dir / "INDEX.md"
462
+ return {
463
+ "mode": "governed-repo",
464
+ "charter_dir": charter_dir,
465
+ "cross_cutting": cross_cutting,
466
+ "index_file": index_file if index_file.is_file() else None,
467
+ }
468
+
469
+
470
+ # ---------------------------------------------------------------------------
471
+ # Branding
472
+ # ---------------------------------------------------------------------------
473
+
474
+ # Template generation — single source of truth for Python scripts.
475
+ # gatorize.sh reads this value at runtime via grep.
476
+ # Gen 0: pre-gatorize.sh installs (no command-post.md, no .gator-version)
477
+ # Gen 1: first gatorize.sh installs (has command-post.md, no .gator-version)
478
+ # Gen 2: current (has .gator-version, overlay-not-replace upgrade logic)
479
+ CURRENT_GENERATION = 2
480
+
481
+ GATOR_MARK_LINES = [
482
+ r" .... . ..... ... .... ",
483
+ r" / ___| / \|_ _|/ _ \| _ \ .... ",
484
+ r"| | _ / _ \ | | | | | | |_) | ./( o )\_______",
485
+ r"| |_/ \/ /_\ \| | | |_| | _ < _/vVvVvVvV",
486
+ r" \____/m/ \m\_| \___/|m| \m\ \__.---------",
487
+ ]
@@ -0,0 +1,5 @@
1
+ #!/bin/bash
2
+ # Gator commit-msg hook — appends Gator-* trailers to the commit message.
3
+ # Installed by gatorize.sh. Do not edit manually.
4
+
5
+ python .gator/scripts/gator-pre-commit.py --phase trailers "$1"
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ # Gator post-commit hook — resets commit_draft.md after successful commit.
3
+ # Installed by gatorize.sh. Do not edit manually.
4
+ #
5
+ # Cleanup now runs in Python so the shell wrapper stays minimal.
6
+
7
+ python .gator/scripts/gator-pre-commit.py --phase cleanup
@@ -0,0 +1,5 @@
1
+ #!/bin/bash
2
+ # Gator pre-commit hook — deterministic governance gate.
3
+ # Installed by gatorize.sh. Do not edit manually.
4
+
5
+ python .gator/scripts/gator-pre-commit.py --phase validate
@@ -0,0 +1,7 @@
1
+ # Rolling active session ledgers are local working artifacts.
2
+ # They are NOT committed — only session snippets and summaries are git-tracked.
3
+ _active/
4
+
5
+ # NOTE: .gator/session-snippets/ is NOT gitignored. Snippets are durable
6
+ # commit-linked artifacts that MUST be committed. They provide the traceability
7
+ # bridge between session history and git history.
File without changes
@@ -0,0 +1,5 @@
1
+
2
+
3
+ ## Review — 2026-06-04 15:31 UTC — enforcer-review.py (Layer 1)
4
+
5
+ Enforcer review: clean. No findings.
@@ -0,0 +1,122 @@
1
+ Metadata-Version: 2.4
2
+ Name: gator-command
3
+ Version: 1.0.0
4
+ Summary: Git-native governance for AI-assisted engineering
5
+ Author-email: Cumberland Laboratories <support@cumberlandlaboratories.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/cumberland-laboratories/gator
8
+ Project-URL: Repository, https://github.com/cumberland-laboratories/gator
9
+ Keywords: governance,ai,git,charters,audit,compliance
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Software Development :: Quality Assurance
19
+ Classifier: Topic :: Software Development :: Version Control :: Git
20
+ Requires-Python: >=3.9
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Dynamic: license-file
24
+
25
+ # Gator Command
26
+
27
+ The command post for [Gator](https://github.com/cumberland-laboratories/gator) — AI-assisted engineering governance.
28
+
29
+ ## How to Use This Repo
30
+
31
+ **Open it in an AI coding assistant.** Claude Code, Codex, Gemini CLI, Cursor — whichever you use. The agent reads the constitution and acts as your concierge: it knows the architecture, the configuration options, the procedures, and the fleet state. Ask it what you need.
32
+
33
+ Gator is designed to be operated *with* an AI agent, not studied as documentation. The governance layer is markdown that agents read natively. The concierge layer routes your questions to the right procedures and reference material. The agent is the interface.
34
+
35
+ ```
36
+ You: "gator init"
37
+ You: "Gatorize my project at ~/code/my-app"
38
+ You: "Set up an enforcer using GPT as the reviewer"
39
+ You: "Check if the charters still match the code"
40
+ You: "What's the status of the fleet?"
41
+ ```
42
+
43
+ ## What Gator Does
44
+
45
+ Gator governs AI-assisted coding. It keeps human comprehension synchronized with AI-generated code through constitutions, module charters, deterministic commit gates, and cross-model enforcement.
46
+
47
+ ### Use Cases
48
+
49
+ **Solo developer with one repo** — You want the AI agent to maintain a map of your codebase (charters), enforce that the map stays current (commit gate), and preserve session context across conversations (knowledge layer). Install Gator, bootstrap charters, and the loop runs from there.
50
+
51
+ **Solo developer with multiple repos** — Same as above, plus: this command post becomes your cross-repo control plane. One org policy propagated to every repo. Fleet status, drift detection, and audit dashboards from one place.
52
+
53
+ **Team with shared repos** — Gator's knowledge layer travels with the repo via git. When a teammate (human or AI) opens the repo, they get the full architectural context — mission, charters, threads, decisions. The enforcer provides independent review from a different model than the one writing code.
54
+
55
+ **Compliance and audit** — Every commit carries `Gator-*` trailers (change type, significance, charter status, agent identity). Session summaries provide durable evidence of what was decided and who was supervising. Fleet-wide audit reports aggregate governance health across repos.
56
+
57
+ ### Configurations
58
+
59
+ Gator is model-neutral and modular. The PI chooses the configuration that fits their stack:
60
+
61
+ | Decision | Options |
62
+ |----------|---------|
63
+ | **Primary agent** | Claude Code, Codex CLI, Gemini CLI, Cursor, any markdown-aware AI tool |
64
+ | **Enforcer model** | Anthropic Sonnet, OpenAI GPT, Google Gemini, local via Ollama, or none |
65
+ | **Review cadence** | Light (lint only), Standard (lint + model review on significant changes), Rigorous (pre-code + post-code review) |
66
+ | **Fleet scope** | Single repo (no command post needed), multi-repo (this command post), remote repos (bare cache scanning) |
67
+ | **Session archaeology** | Off, local spool, committed summaries (git-tracked audit trail) |
68
+
69
+ The concierge agent walks you through these choices. You don't need to read configuration docs — tell the agent what you want and it configures the system.
70
+
71
+ ## Quick Start
72
+
73
+ ```bash
74
+ # Gatorize an existing project repo
75
+ bash gator-command/scripts/gatorize.sh /path/to/your/project
76
+
77
+ # Then open that project in your AI assistant — the concierge bootstrap begins
78
+ ```
79
+
80
+ For fleet operations (from within this command post):
81
+ ```bash
82
+ # Fleet status
83
+ python gator-command/scripts/gator-fleet-report.py
84
+
85
+ # Policy drift across all governed repos
86
+ python gator-command/scripts/gator-drift.py
87
+
88
+ # Audit dashboard
89
+ python gator-command/scripts/gator-audit.py
90
+ ```
91
+
92
+ ## Architecture at a Glance
93
+
94
+ ```
95
+ This repo (command post):
96
+ constitution.md Governance rules for this command post
97
+ gator-command/ Knowledge graph + machinery + fleet tooling
98
+ scripts/ CLI tooling (gatorize, fleet-report, drift, audit)
99
+ templates/ Starter kit deployed to project repos
100
+ procedures/ Standards and workflows
101
+ reference-notes/ Concierge knowledge (the agent reads these)
102
+
103
+ Each governed repo:
104
+ .gator/
105
+ constitution.md Per-repo governance rules
106
+ charters/ Module maps (the intelligent codebase layer)
107
+ scripts/ Hooks + enforcer
108
+ field-guides/ Language pattern references (optional)
109
+ command-post.md Thin link back to this command post
110
+ ```
111
+
112
+ ## Multi-Model Architecture
113
+
114
+ The constitutions are the interface contract. Any model that reads markdown and follows instructions can operate the system.
115
+
116
+ - **Primary agent**: Reads charters, writes code, updates the knowledge layer
117
+ - **Enforcer**: Different model, read-only, audits the agent's work against charters
118
+ - **The architecture outlives any individual model** — switch tools mid-project without losing context
119
+
120
+ ## License
121
+
122
+ MIT — [Cumberland Laboratories](https://github.com/cumberland-laboratories)