gator-command 1.1.9__tar.gz → 1.2.0__tar.gz

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 (137) hide show
  1. {gator_command-1.1.9/src/gator_command.egg-info → gator_command-1.2.0}/PKG-INFO +1 -1
  2. {gator_command-1.1.9 → gator_command-1.2.0}/pyproject.toml +1 -1
  3. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/dashboard/dashboard.css +168 -0
  4. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/dashboard/dashboard.html +1 -0
  5. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/dashboard/dashboard.js +13 -8
  6. gator_command-1.2.0/src/gator_command/scripts/dashboard/views/audit.js +414 -0
  7. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/dashboard/views/repo.js +154 -2
  8. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gator-audit.py +103 -1
  9. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gator-dashboard.py +201 -2
  10. gator_command-1.2.0/src/gator_command/scripts/gator-session-aggregator.py +429 -0
  11. {gator_command-1.1.9 → gator_command-1.2.0/src/gator_command.egg-info}/PKG-INFO +1 -1
  12. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command.egg-info/SOURCES.txt +3 -0
  13. gator_command-1.2.0/tests/test_audit.py +235 -0
  14. gator_command-1.2.0/tests/test_dashboard_sessions.py +261 -0
  15. gator_command-1.2.0/tests/test_session_aggregator.py +397 -0
  16. gator_command-1.1.9/src/gator_command/scripts/dashboard/views/audit.js +0 -270
  17. gator_command-1.1.9/tests/test_audit.py +0 -116
  18. {gator_command-1.1.9 → gator_command-1.2.0}/LICENSE +0 -0
  19. {gator_command-1.1.9 → gator_command-1.2.0}/PYPI_README.md +0 -0
  20. {gator_command-1.1.9 → gator_command-1.2.0}/README.md +0 -0
  21. {gator_command-1.1.9 → gator_command-1.2.0}/setup.cfg +0 -0
  22. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/__init__.py +0 -0
  23. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/cli.py +0 -0
  24. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/crawler.py +0 -0
  25. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/dashboard/views/fleet.js +0 -0
  26. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/dashboard/views/settings.js +0 -0
  27. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/dashboard/views/updates.js +0 -0
  28. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/enforcer-prompt.md +0 -0
  29. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/extract-claude-sessions.py +0 -0
  30. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/extract-codex-sessions.py +0 -0
  31. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/extract-gemini-sessions.py +0 -0
  32. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gator-charter-draft.py +0 -0
  33. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gator-charter-lint.py +0 -0
  34. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gator-charter-verify.py +0 -0
  35. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gator-deploy.py +0 -0
  36. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gator-drift.py +0 -0
  37. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gator-enforce.py +0 -0
  38. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gator-fleet-intel.py +0 -0
  39. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gator-fleet-report.py +0 -0
  40. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gator-init-command-post.py +0 -0
  41. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gator-init.py +0 -0
  42. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gator-machine-id.py +0 -0
  43. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gator-policy-status.py +0 -0
  44. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gator-pulse.py +0 -0
  45. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gator-repo-status.py +0 -0
  46. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gator-session-common.py +0 -0
  47. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gator-session-sink.py +0 -0
  48. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gator-sessions.py +0 -0
  49. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gator-update.py +0 -0
  50. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gator-version.py +0 -0
  51. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gator_core.py +0 -0
  52. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gator_remote.py +0 -0
  53. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gator_runtime.py +0 -0
  54. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gatorize-actions.sh +0 -0
  55. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gatorize-lib.sh +0 -0
  56. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gatorize-post.sh +0 -0
  57. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gatorize.py +0 -0
  58. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/gatorize.sh +0 -0
  59. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/generate_markdown.py +0 -0
  60. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/generate_wiki.py +0 -0
  61. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/graph_health.py +0 -0
  62. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/memex-lint.py +0 -0
  63. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/memex-lint.sh +0 -0
  64. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/memex.py +0 -0
  65. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/memex_formatters.py +0 -0
  66. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/memex_state.py +0 -0
  67. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/scripts/spawn.py +0 -0
  68. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/blueprints/README.md +0 -0
  69. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/charterignore +0 -0
  70. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/charters/README.md +0 -0
  71. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/charters/_template.md +0 -0
  72. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/commands/commit.md +0 -0
  73. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/commands/init.md +0 -0
  74. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/commands/update.md +0 -0
  75. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/constitution.md +0 -0
  76. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/field-guides/README.md +0 -0
  77. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/gator-start-up.md +0 -0
  78. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/procedures/charter-alignment.md +0 -0
  79. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/procedures/enforcer-review.md +0 -0
  80. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/procedures/field-guide-generation.md +0 -0
  81. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/procedures/knowledge-capture.md +0 -0
  82. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/procedures/significance-check.md +0 -0
  83. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/reference-notes/concierge-responses.md +0 -0
  84. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/reference-notes/dangerous-patterns.md +0 -0
  85. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/reference-notes/dashboard-operations.md +0 -0
  86. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/reference-notes/enforcer-configuration.md +0 -0
  87. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/reference-notes/example-project.md +0 -0
  88. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/reference-notes/failure-modes-and-self-correction.md +0 -0
  89. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/reference-notes/git-workflow.md +0 -0
  90. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/reference-notes/identity-and-ownership.md +0 -0
  91. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/reference-notes/refactor-approach.md +0 -0
  92. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/reference-notes/what-gator-requires-from-a-model.md +0 -0
  93. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/reference-notes/why-navigation-coding-feels-different.md +0 -0
  94. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/reference-notes/workflow-profiles.md +0 -0
  95. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/scripts/__pycache__/enforcer-review.cpython-313.pyc +0 -0
  96. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/scripts/__pycache__/gator-approve.cpython-313.pyc +0 -0
  97. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/scripts/__pycache__/gator-init.cpython-313.pyc +0 -0
  98. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/scripts/__pycache__/gator-pre-commit.cpython-313.pyc +0 -0
  99. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/scripts/__pycache__/gator-update.cpython-313.pyc +0 -0
  100. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/scripts/enforcer-prompt.md +0 -0
  101. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/scripts/enforcer-review.py +0 -0
  102. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/scripts/gator-approve.py +0 -0
  103. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/scripts/gator-enforce.py +0 -0
  104. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/scripts/gator-init.py +0 -0
  105. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/scripts/gator-pre-commit.py +0 -0
  106. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/scripts/gator-pulse.py +0 -0
  107. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/scripts/gator-update.py +0 -0
  108. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/scripts/gator-version.py +0 -0
  109. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/scripts/gator_core.py +0 -0
  110. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/scripts/hooks/__pycache__/commit-msgcpython-313.pyc +0 -0
  111. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/scripts/hooks/__pycache__/post-commitcpython-313.pyc +0 -0
  112. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/scripts/hooks/__pycache__/pre-commitcpython-313.pyc +0 -0
  113. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/scripts/hooks/commit-msg +0 -0
  114. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/scripts/hooks/post-commit +0 -0
  115. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/scripts/hooks/pre-commit +0 -0
  116. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/sessions/.gitignore +0 -0
  117. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/vault/.gitkeep +0 -0
  118. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command/templates/gator-starter/whiteboard.md +0 -0
  119. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command.egg-info/dependency_links.txt +0 -0
  120. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command.egg-info/entry_points.txt +0 -0
  121. {gator_command-1.1.9 → gator_command-1.2.0}/src/gator_command.egg-info/top_level.txt +0 -0
  122. {gator_command-1.1.9 → gator_command-1.2.0}/tests/test_audit_integration.py +0 -0
  123. {gator_command-1.1.9 → gator_command-1.2.0}/tests/test_charter_lint.py +0 -0
  124. {gator_command-1.1.9 → gator_command-1.2.0}/tests/test_deploy.py +0 -0
  125. {gator_command-1.1.9 → gator_command-1.2.0}/tests/test_drift.py +0 -0
  126. {gator_command-1.1.9 → gator_command-1.2.0}/tests/test_fleet_intel.py +0 -0
  127. {gator_command-1.1.9 → gator_command-1.2.0}/tests/test_fleet_report.py +0 -0
  128. {gator_command-1.1.9 → gator_command-1.2.0}/tests/test_gator_core.py +0 -0
  129. {gator_command-1.1.9 → gator_command-1.2.0}/tests/test_gatorize.py +0 -0
  130. {gator_command-1.1.9 → gator_command-1.2.0}/tests/test_hooks.py +0 -0
  131. {gator_command-1.1.9 → gator_command-1.2.0}/tests/test_init.py +0 -0
  132. {gator_command-1.1.9 → gator_command-1.2.0}/tests/test_memex_state.py +0 -0
  133. {gator_command-1.1.9 → gator_command-1.2.0}/tests/test_packaging.py +0 -0
  134. {gator_command-1.1.9 → gator_command-1.2.0}/tests/test_pre_commit.py +0 -0
  135. {gator_command-1.1.9 → gator_command-1.2.0}/tests/test_remote.py +0 -0
  136. {gator_command-1.1.9 → gator_command-1.2.0}/tests/test_session_sink.py +0 -0
  137. {gator_command-1.1.9 → gator_command-1.2.0}/tests/test_sessions.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gator-command
3
- Version: 1.1.9
3
+ Version: 1.2.0
4
4
  Summary: Git-native governance for AI-assisted engineering
5
5
  Author-email: Cumberland Laboratories <support@cumberlandlaboratories.com>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "gator-command"
7
- version = "1.1.9"
7
+ version = "1.2.0"
8
8
  description = "Git-native governance for AI-assisted engineering"
9
9
  readme = "PYPI_README.md"
10
10
  license = {text = "MIT"}
@@ -360,6 +360,91 @@ html, body {
360
360
  background: #f8f9fb;
361
361
  }
362
362
 
363
+ /* ── session table ─────────────────────────────────────────────────────── */
364
+
365
+ .session-row:hover td {
366
+ background: #eef3fa;
367
+ }
368
+
369
+ .session-row-expanded td {
370
+ background: #f0f4fa;
371
+ border-bottom-color: transparent;
372
+ }
373
+
374
+ .session-detail td {
375
+ padding: 0;
376
+ background: #fafbfd;
377
+ }
378
+
379
+ .session-detail-content {
380
+ padding: 10px 16px 14px;
381
+ border-left: 3px solid #d0d8e4;
382
+ margin: 0 8px 8px;
383
+ }
384
+
385
+ .detail-section {
386
+ margin-bottom: 8px;
387
+ }
388
+
389
+ .commit-entry {
390
+ font-size: 12px;
391
+ margin: 2px 0;
392
+ color: #333;
393
+ }
394
+
395
+ .commit-type {
396
+ display: inline-block;
397
+ font-size: 10px;
398
+ padding: 1px 5px;
399
+ border-radius: 3px;
400
+ background: #eee;
401
+ color: #555;
402
+ margin: 0 4px;
403
+ }
404
+
405
+ .tag-badge {
406
+ display: inline-block;
407
+ font-size: 10px;
408
+ padding: 1px 6px;
409
+ border-radius: 8px;
410
+ background: #e8eef5;
411
+ color: #3a5a8a;
412
+ margin: 1px 2px;
413
+ }
414
+
415
+ .status-complete {
416
+ color: #4a7a4a;
417
+ font-size: 11px;
418
+ }
419
+
420
+ .status-inflight {
421
+ color: #b8860b;
422
+ font-size: 11px;
423
+ font-weight: 600;
424
+ }
425
+
426
+ .fleet-toggle-btn {
427
+ border: 1px solid #ccc;
428
+ background: #f5f5f5;
429
+ color: #555;
430
+ padding: 2px 10px;
431
+ border-radius: 4px;
432
+ font-size: 11px;
433
+ cursor: pointer;
434
+ margin-left: 10px;
435
+ vertical-align: middle;
436
+ }
437
+
438
+ .fleet-toggle-btn:hover {
439
+ background: #e8e8e8;
440
+ }
441
+
442
+ .fleet-toggle-btn.active {
443
+ background: #3a5a8a;
444
+ color: #fff;
445
+ border-color: #3a5a8a;
446
+ }
447
+
363
448
  /* ── health badges ─────────────────────────────────────────────────────── */
364
449
 
365
450
  .badge {
@@ -930,6 +1015,89 @@ a:hover, .link-btn:hover {
930
1015
  color: #0366d6;
931
1016
  }
932
1017
 
1018
+ /* ── search ────────────────────────────────────────────────────────────── */
1019
+
1020
+ .topbar-search {
1021
+ border: 1px solid #ccc;
1022
+ border-radius: 4px;
1023
+ padding: 3px 8px;
1024
+ font-size: 12px;
1025
+ width: 180px;
1026
+ margin-right: 10px;
1027
+ outline: none;
1028
+ }
1029
+
1030
+ .topbar-search:focus {
1031
+ border-color: #0366d6;
1032
+ box-shadow: 0 0 0 2px rgba(3,102,214,0.15);
1033
+ }
1034
+
1035
+ .in-doc-search {
1036
+ border: 1px solid #ccc;
1037
+ border-radius: 4px;
1038
+ padding: 2px 6px;
1039
+ font-size: 11px;
1040
+ width: 130px;
1041
+ margin-right: 6px;
1042
+ outline: none;
1043
+ }
1044
+
1045
+ .in-doc-search:focus {
1046
+ border-color: #0366d6;
1047
+ }
1048
+
1049
+ .file-header-right {
1050
+ display: flex;
1051
+ align-items: center;
1052
+ gap: 4px;
1053
+ }
1054
+
1055
+ .copy-content-btn {
1056
+ border: none;
1057
+ background: none;
1058
+ cursor: pointer;
1059
+ font-size: 13px;
1060
+ color: #aaa;
1061
+ padding: 0 4px;
1062
+ vertical-align: middle;
1063
+ }
1064
+
1065
+ .copy-content-btn:hover {
1066
+ color: #0366d6;
1067
+ }
1068
+
1069
+ mark.search-hit {
1070
+ background: #fff3b0;
1071
+ padding: 0 1px;
1072
+ border-radius: 2px;
1073
+ }
1074
+
1075
+ .search-result {
1076
+ padding: 8px 12px;
1077
+ margin-bottom: 6px;
1078
+ border: 1px solid #e8e8e8;
1079
+ border-radius: 4px;
1080
+ background: #fafbfd;
1081
+ }
1082
+
1083
+ .search-result:hover {
1084
+ background: #f0f4fa;
1085
+ border-color: #d0d8e4;
1086
+ }
1087
+
1088
+ .search-result-title {
1089
+ font-size: 13px;
1090
+ font-weight: 600;
1091
+ color: #0366d6;
1092
+ margin-bottom: 3px;
1093
+ }
1094
+
1095
+ .search-result-snippet {
1096
+ font-size: 12px;
1097
+ color: #555;
1098
+ line-height: 1.4;
1099
+ }
1100
+
933
1101
  /* ── Markdown rendering ────────────────────────────────────────────────── */
934
1102
 
935
1103
  .repo-markdown h1 { font-size: 22px; font-weight: 600; margin: 20px 0 12px; color: #111; }
@@ -55,6 +55,7 @@
55
55
  <span id="topbar-subtitle"></span>
56
56
  </div>
57
57
  <div id="topbar-right">
58
+ <input type="text" id="repo-search-input" class="topbar-search" placeholder="Search... (AND / OR)" style="display:none" />
58
59
  <span id="generated-at"></span>
59
60
  <button id="refresh-btn" title="Re-run fleet scan">↺ Refresh</button>
60
61
  </div>
@@ -20,6 +20,7 @@
20
20
  data: null, // fast_data from server
21
21
  activeView: "fleet",
22
22
  activeRepo: null, // name of currently loaded repo (Repo view)
23
+ activeRepoKey: null, // path-hash key for session audit identity
23
24
  settings: null, // dashboard settings (mode, etc.)
24
25
  standalone: false, // true when no command post detected
25
26
  };
@@ -93,6 +94,13 @@
93
94
  btn.classList.toggle("active", btn.dataset.view === name);
94
95
  });
95
96
 
97
+ // Show/hide repo search input
98
+ const repoSearchInput = document.getElementById("repo-search-input");
99
+ if (repoSearchInput) {
100
+ repoSearchInput.style.display = (name === "repo") ? "" : "none";
101
+ if (name !== "repo") repoSearchInput.value = "";
102
+ }
103
+
96
104
  // Clear slot
97
105
  viewSlot.innerHTML = "";
98
106
 
@@ -115,7 +123,7 @@
115
123
  if (name === "audit") {
116
124
  updateTopbar("audit", "Governance posture");
117
125
  if (views.audit) {
118
- views.audit(state.data, viewSlot);
126
+ views.audit(state.data, viewSlot, state.activeRepoKey);
119
127
  } else {
120
128
  viewSlot.innerHTML = "<p class='muted'>Audit view not available.</p>";
121
129
  }
@@ -130,6 +138,10 @@
130
138
  return;
131
139
  }
132
140
  state.activeRepo = repoName;
141
+ // Resolve repo_key from fleet data
142
+ const allRepos = (state.data && state.data.fleet && state.data.fleet.repos) || (state.data && state.data.repos) || [];
143
+ const matchedRepo = allRepos.find(r => r.name === repoName);
144
+ state.activeRepoKey = (matchedRepo && matchedRepo.repo_key) || null;
133
145
  // Update repo sidebar label
134
146
  repoTab.textContent = "";
135
147
  const icon = document.createElement("span");
@@ -405,13 +417,6 @@
405
417
 
406
418
  state.standalone = !!(state.data && state.data.standalone);
407
419
 
408
- // Hide Audit tab in standalone mode (no data model without command post)
409
- if (state.standalone) {
410
- document.querySelectorAll('.sidebar-item[data-view="audit"]').forEach(el => {
411
- el.style.display = "none";
412
- });
413
- }
414
-
415
420
  // Check for ?repo= query param
416
421
  const params = new URLSearchParams(window.location.search);
417
422
  const preloadRepo = params.get("repo");
@@ -0,0 +1,414 @@
1
+ /**
2
+ * audit.js — Audit view for Gator Dashboard.
3
+ *
4
+ * Data source: data.audit (gator-audit --json) for governance metrics,
5
+ * GET /api/audit/sessions for session summaries (lazy).
6
+ *
7
+ * Primary question: can I show human oversight? where are the gaps?
8
+ */
9
+
10
+ (function () {
11
+ "use strict";
12
+
13
+ window.GatorViews = window.GatorViews || {};
14
+
15
+ function escHtml(str) {
16
+ return String(str)
17
+ .replace(/&/g, "&amp;")
18
+ .replace(/</g, "&lt;")
19
+ .replace(/>/g, "&gt;")
20
+ .replace(/"/g, "&quot;");
21
+ }
22
+
23
+ function fmtTs(ts) {
24
+ if (!ts) return "—";
25
+ try {
26
+ return new Date(ts).toLocaleDateString(undefined, {
27
+ year: "numeric", month: "short", day: "numeric",
28
+ });
29
+ } catch (_) { return ts; }
30
+ }
31
+
32
+ function fmtDateRange(startedAt, endedAt) {
33
+ if (!startedAt) return "—";
34
+ try {
35
+ const s = new Date(startedAt);
36
+ const e = endedAt ? new Date(endedAt) : null;
37
+ const datePart = s.toLocaleDateString(undefined, { month: "short", day: "numeric" });
38
+ const startTime = s.toLocaleTimeString(undefined, { hour: "2-digit", minute: "2-digit", hour12: false });
39
+ const endTime = e ? e.toLocaleTimeString(undefined, { hour: "2-digit", minute: "2-digit", hour12: false }) : "";
40
+ return endTime ? `${datePart} ${startTime}\u2013${endTime}` : `${datePart} ${startTime}`;
41
+ } catch (_) { return startedAt.slice(0, 16); }
42
+ }
43
+
44
+ function sessionStatus(endedAt) {
45
+ if (!endedAt) return "in-flight";
46
+ try {
47
+ const ago = Date.now() - new Date(endedAt).getTime();
48
+ return ago > 2 * 60 * 60 * 1000 ? "complete" : "in-flight";
49
+ } catch (_) { return "?"; }
50
+ }
51
+
52
+ // ── significance bar chart ─────────────────────────────────────────────────
53
+
54
+ function renderSigBars(dist) {
55
+ const order = ["critical", "high", "medium", "low", "routine"];
56
+ const colors = {
57
+ critical: "#a02020",
58
+ high: "#c44020",
59
+ medium: "#b8860b",
60
+ low: "#4a7a4a",
61
+ routine: "#888",
62
+ };
63
+
64
+ const total = Object.values(dist).reduce((s, v) => s + v, 0);
65
+ if (total === 0) return "<p class='muted'>No governed commits found in this period.</p>";
66
+
67
+ let html = "";
68
+ for (const level of order) {
69
+ const count = dist[level] || 0;
70
+ if (count === 0) continue;
71
+ const pct = Math.round((count / total) * 100);
72
+ html += `
73
+ <div class="sig-bar">
74
+ <span class="sig-bar-label">${escHtml(level)}</span>
75
+ <div class="sig-bar-track">
76
+ <div class="sig-bar-fill" style="width:${pct}%;background:${colors[level] || '#888'}"></div>
77
+ </div>
78
+ <span class="sig-bar-count">${count}</span>
79
+ </div>
80
+ `;
81
+ }
82
+ return html;
83
+ }
84
+
85
+ // ── governed commits table ─────────────────────────────────────────────────
86
+
87
+ function renderGoverned(govCommits) {
88
+ const entries = Object.entries(govCommits).filter(([k]) => k !== "total");
89
+ if (entries.length === 0) return "<p class='muted'>No data.</p>";
90
+
91
+ entries.sort((a, b) => b[1] - a[1]);
92
+
93
+ let html = `
94
+ <table class="data-table">
95
+ <thead><tr><th>Repo</th><th>Governed commits (7d)</th></tr></thead>
96
+ <tbody>
97
+ `;
98
+ for (const [repo, count] of entries) {
99
+ html += `<tr><td>${escHtml(repo)}</td><td class="mono">${count}</td></tr>`;
100
+ }
101
+ html += "</tbody></table>";
102
+ return html;
103
+ }
104
+
105
+ // ── override events table ──────────────────────────────────────────────────
106
+
107
+ function renderOverrides(events) {
108
+ if (!events || events.length === 0) {
109
+ return "<p class='muted'>No override events found.</p>";
110
+ }
111
+
112
+ let html = `
113
+ <table class="data-table">
114
+ <thead>
115
+ <tr>
116
+ <th>Repo</th>
117
+ <th>Commit</th>
118
+ <th>Date</th>
119
+ <th>Type</th>
120
+ <th>Approver</th>
121
+ </tr>
122
+ </thead>
123
+ <tbody>
124
+ `;
125
+ for (const ev of events) {
126
+ html += `
127
+ <tr>
128
+ <td>${escHtml(ev.repo || "—")}</td>
129
+ <td class="mono">${escHtml((ev.hash || "").slice(0, 7))}</td>
130
+ <td class="nowrap">${fmtTs(ev.timestamp)}</td>
131
+ <td>${escHtml(ev.override_type || "—")}</td>
132
+ <td>${escHtml(ev.approver || "—")}</td>
133
+ </tr>
134
+ `;
135
+ }
136
+ html += "</tbody></table>";
137
+ return html;
138
+ }
139
+
140
+ // ── session summary table (from /api/audit/sessions) ───────────────────────
141
+
142
+ function renderSessionTable(summaries, fleet) {
143
+ if (!summaries || summaries.length === 0) {
144
+ return "<p class='muted'>No session summaries available. Sessions are generated from committed snippets.</p>";
145
+ }
146
+
147
+ let html = `
148
+ <table class="data-table session-table">
149
+ <thead>
150
+ <tr>
151
+ <th>Session</th>
152
+ ${fleet ? '<th>Repo</th>' : ''}
153
+ <th>Model</th>
154
+ <th>Commits</th>
155
+ <th>Goal</th>
156
+ <th>Tags</th>
157
+ <th>Status</th>
158
+ </tr>
159
+ </thead>
160
+ <tbody>
161
+ `;
162
+
163
+ for (const s of summaries) {
164
+ const dateRange = fmtDateRange(s.started_at, s.ended_at);
165
+ const goal = s.goal || "";
166
+ const goalDisplay = goal.length > 50 ? goal.slice(0, 50) + "…" : goal;
167
+ const tags = (s.decision_tags || []).slice(0, 4);
168
+ const tagBadges = tags.map(t => `<span class="tag-badge">${escHtml(t)}</span>`).join(" ");
169
+ const extraTags = (s.decision_tags || []).length > 4 ? ` <span class="muted">+${s.decision_tags.length - 4}</span>` : "";
170
+ const status = sessionStatus(s.ended_at);
171
+ const statusClass = status === "complete" ? "status-complete" : "status-inflight";
172
+ const rowKey = escHtml((s.repo_key || "") + "-" + (s.session_id || ""));
173
+
174
+ html += `
175
+ <tr class="session-row" data-row-key="${rowKey}">
176
+ <td class="nowrap">${escHtml(dateRange)}</td>
177
+ ${fleet ? `<td>${escHtml(s.repo || "—")}</td>` : ''}
178
+ <td>${escHtml(s.model || "—")}</td>
179
+ <td class="mono">${s.commit_count || 0}</td>
180
+ <td title="${escHtml(goal)}">${escHtml(goalDisplay || "—")}</td>
181
+ <td>${tagBadges}${extraTags}</td>
182
+ <td><span class="${statusClass}">${status}</span></td>
183
+ </tr>
184
+ <tr class="session-detail" id="detail-${rowKey}" style="display:none">
185
+ <td colspan="${fleet ? 7 : 6}">
186
+ <div class="session-detail-content">
187
+ ${renderSessionDetail(s)}
188
+ </div>
189
+ </td>
190
+ </tr>
191
+ `;
192
+ }
193
+
194
+ html += "</tbody></table>";
195
+ return html;
196
+ }
197
+
198
+ function renderSessionDetail(s) {
199
+ let html = "";
200
+
201
+ // Commits
202
+ const commits = s.commits || [];
203
+ if (commits.length > 0) {
204
+ html += '<div class="detail-section"><strong>Commits</strong><div class="commit-list">';
205
+ for (const c of commits) {
206
+ html += `<div class="commit-entry"><span class="mono">${escHtml(c.short_commit || "?")}</span> <span class="commit-type">${escHtml(c.change_type || "")}</span> ${escHtml(c.intent || "")}</div>`;
207
+ }
208
+ html += "</div></div>";
209
+ }
210
+
211
+ // Files touched
212
+ const files = s.files_touched || [];
213
+ if (files.length > 0) {
214
+ html += '<div class="detail-section"><strong>Files touched</strong> <span class="muted">(' + files.length + ')</span><div class="file-list">';
215
+ const displayFiles = files.slice(0, 20);
216
+ for (const f of displayFiles) {
217
+ html += `<div class="mono" style="font-size:11px;color:#555">${escHtml(f)}</div>`;
218
+ }
219
+ if (files.length > 20) {
220
+ html += `<div class="muted">… and ${files.length - 20} more</div>`;
221
+ }
222
+ html += "</div></div>";
223
+ }
224
+
225
+ // Notes
226
+ const notes = s.notes || [];
227
+ if (notes.length > 0) {
228
+ html += '<div class="detail-section"><strong>Notes</strong><div class="notes-list">';
229
+ for (const n of notes) {
230
+ html += `<div style="font-size:12px;color:#444;margin:2px 0">• ${escHtml(n)}</div>`;
231
+ }
232
+ html += "</div></div>";
233
+ }
234
+
235
+ // Transcript link
236
+ if (s.transcript_ref) {
237
+ html += `<div class="detail-section"><strong>Transcript</strong> <span class="mono muted">${escHtml(s.transcript_ref)}</span></div>`;
238
+ }
239
+
240
+ if (!html) {
241
+ html = '<span class="muted">No additional details.</span>';
242
+ }
243
+
244
+ return html;
245
+ }
246
+
247
+ // ── main render ─────────────────────────────────────────────────────────────
248
+
249
+ window.GatorViews.audit = function (data, container, activeRepoKey) {
250
+ const audit = data.audit || {};
251
+
252
+ if (audit.error) {
253
+ container.innerHTML = `<div class="error-block">Audit data unavailable: ${escHtml(audit.error)}</div>`;
254
+ return;
255
+ }
256
+
257
+ const governance = audit.governance || {};
258
+ const sessions = audit.sessions || {};
259
+ const overrides = audit.override_events || [];
260
+ const sigDist = audit.significance_distribution || {};
261
+ const govCommits = audit.governed_commits || {};
262
+ const sinceDays = audit.since_days || 7;
263
+
264
+ // Compute summary metrics
265
+ const totalRepos = governance.repos || 0;
266
+ const hookCoverage = totalRepos > 0
267
+ ? Math.round((governance.hooks_installed / totalRepos) * 100)
268
+ : 0;
269
+ const overrideRate = (govCommits.total || 0) > 0
270
+ ? Math.round((overrides.length / govCommits.total) * 100)
271
+ : 0;
272
+
273
+ // Session summary coverage: repos with recent sessions / total repos
274
+ const reposWithSessions = Object.keys(sessions.by_repo || {}).length;
275
+ const sessionCoverage = totalRepos > 0
276
+ ? Math.round((reposWithSessions / totalRepos) * 100)
277
+ : 0;
278
+
279
+ let html = `
280
+ <div class="view-header">
281
+ <span class="view-title">Audit</span>
282
+ <span class="view-subtitle">Last ${sinceDays} days &nbsp;·&nbsp; ${totalRepos} repos</span>
283
+ </div>
284
+
285
+ <div class="card-row">
286
+ <div class="card">
287
+ <div class="card-label">Hook coverage</div>
288
+ <div class="card-value">${hookCoverage}%</div>
289
+ <div class="card-note">${governance.hooks_installed || 0} of ${totalRepos} repos</div>
290
+ </div>
291
+ <div class="card">
292
+ <div class="card-label">Governed commits</div>
293
+ <div class="card-value">${govCommits.total || 0}</div>
294
+ <div class="card-note">fleet total, ${sinceDays}d window</div>
295
+ </div>
296
+ <div class="card">
297
+ <div class="card-label">Override events</div>
298
+ <div class="card-value" style="color:${overrides.length > 0 ? 'var(--color-drifted)' : 'inherit'}">${overrides.length}</div>
299
+ <div class="card-note">${overrideRate}% of governed commits</div>
300
+ </div>
301
+ <div class="card">
302
+ <div class="card-label">Session coverage</div>
303
+ <div class="card-value">${sessionCoverage}%</div>
304
+ <div class="card-note">${reposWithSessions} repos with sessions (${sinceDays}d)</div>
305
+ </div>
306
+ </div>
307
+
308
+ <div class="section">
309
+ <div class="section-title">Significance distribution (fleet)</div>
310
+ ${renderSigBars(sigDist)}
311
+ </div>
312
+
313
+ <div class="section">
314
+ <div class="section-title">Governed commits by repo (${sinceDays}d)</div>
315
+ ${renderGoverned(govCommits)}
316
+ </div>
317
+
318
+ <div class="section">
319
+ <div class="section-title">Override events</div>
320
+ ${renderOverrides(overrides)}
321
+ </div>
322
+
323
+ <div class="section">
324
+ <div class="section-title">
325
+ Session summaries
326
+ <button class="fleet-toggle-btn" id="session-fleet-toggle" title="Toggle fleet view">Fleet</button>
327
+ </div>
328
+ <div id="session-table-container">
329
+ <p class="muted" style="padding:12px">Loading sessions...</p>
330
+ </div>
331
+ </div>
332
+ `;
333
+
334
+ // Sessions by vendor
335
+ if (sessions.by_vendor && Object.keys(sessions.by_vendor).length > 0) {
336
+ html += `
337
+ <div class="section">
338
+ <div class="section-title">Sessions by agent (${sinceDays}d)</div>
339
+ <table class="data-table">
340
+ <thead><tr><th>Agent</th><th>Sessions</th></tr></thead>
341
+ <tbody>
342
+ `;
343
+ for (const [vendor, count] of Object.entries(sessions.by_vendor)) {
344
+ html += `<tr><td>${escHtml(vendor)}</td><td class="mono">${count}</td></tr>`;
345
+ }
346
+ html += "</tbody></table></div>";
347
+ }
348
+
349
+ container.innerHTML = html;
350
+
351
+ // Load session summaries lazily from /api/audit/sessions
352
+ // If no repo selected, start in fleet mode and hide the toggle
353
+ let fleetMode = !activeRepoKey;
354
+ const tableContainer = container.querySelector("#session-table-container");
355
+ const fleetBtn = container.querySelector("#session-fleet-toggle");
356
+
357
+ if (!activeRepoKey && fleetBtn) {
358
+ fleetBtn.style.display = "none"; // no repo context — fleet-only, hide toggle
359
+ } else if (fleetBtn) {
360
+ fleetBtn.classList.toggle("active", fleetMode);
361
+ fleetBtn.textContent = fleetMode ? "Single repo" : "Fleet";
362
+ }
363
+
364
+ function loadSessions() {
365
+ let url;
366
+ if (fleetMode) {
367
+ url = "/api/audit/sessions?fleet=true";
368
+ } else {
369
+ url = "/api/audit/sessions?repo=" + encodeURIComponent(activeRepoKey);
370
+ }
371
+ tableContainer.innerHTML = '<p class="muted" style="padding:12px">Loading sessions...</p>';
372
+ fetch(url)
373
+ .then(r => r.json())
374
+ .then(summaries => {
375
+ if (summaries.error) {
376
+ tableContainer.innerHTML = `<p class="muted" style="padding:12px">${escHtml(summaries.error)}</p>`;
377
+ return;
378
+ }
379
+ tableContainer.innerHTML = renderSessionTable(summaries, fleetMode);
380
+
381
+ // Expandable row click handlers
382
+ tableContainer.querySelectorAll(".session-row").forEach(row => {
383
+ row.style.cursor = "pointer";
384
+ row.addEventListener("click", function () {
385
+ const key = this.dataset.rowKey;
386
+ const detail = document.getElementById("detail-" + key);
387
+ if (detail) {
388
+ const visible = detail.style.display !== "none";
389
+ detail.style.display = visible ? "none" : "";
390
+ this.classList.toggle("session-row-expanded", !visible);
391
+ }
392
+ });
393
+ });
394
+ })
395
+ .catch(err => {
396
+ tableContainer.innerHTML = `<p class="muted" style="padding:12px">Failed to load sessions: ${escHtml(err.message)}</p>`;
397
+ });
398
+ }
399
+
400
+ if (fleetBtn) {
401
+ fleetBtn.addEventListener("click", function () {
402
+ fleetMode = !fleetMode;
403
+ this.classList.toggle("active", fleetMode);
404
+ this.textContent = fleetMode ? "Single repo" : "Fleet";
405
+ loadSessions();
406
+ });
407
+ }
408
+
409
+ // Initial load
410
+ if (!window.GATOR_SNAPSHOT) {
411
+ loadSessions();
412
+ }
413
+ };
414
+ })();