@arbiterforge/ca-pi 0.6.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/LICENSE +661 -0
- package/README.md +558 -0
- package/package.json +35 -0
- package/plugins/ca-pi/CHANGELOG.md +1030 -0
- package/plugins/ca-pi/COMMANDS.md +90 -0
- package/plugins/ca-pi/ORCHESTRATOR.md +159 -0
- package/plugins/ca-pi/SKILLS.md +47 -0
- package/plugins/ca-pi/SPRINT.md +142 -0
- package/plugins/ca-pi/agents/INDEX.md +31 -0
- package/plugins/ca-pi/agents/architecture-drift-reviewer.md +86 -0
- package/plugins/ca-pi/agents/auth-crypto-reviewer.md +60 -0
- package/plugins/ca-pi/agents/backend-author.md +60 -0
- package/plugins/ca-pi/agents/checkpoint-aggregator.md +111 -0
- package/plugins/ca-pi/agents/coverage-auditor.md +71 -0
- package/plugins/ca-pi/agents/decision-challenger.md +116 -0
- package/plugins/ca-pi/agents/dependency-reviewer.md +79 -0
- package/plugins/ca-pi/agents/design-quality-reviewer.md +80 -0
- package/plugins/ca-pi/agents/finding-triage.md +86 -0
- package/plugins/ca-pi/agents/frontend-author.md +64 -0
- package/plugins/ca-pi/agents/grader.md +173 -0
- package/plugins/ca-pi/agents/infra-author.md +64 -0
- package/plugins/ca-pi/agents/map-deps.md +35 -0
- package/plugins/ca-pi/agents/map-structure.md +37 -0
- package/plugins/ca-pi/agents/migration-reviewer.md +65 -0
- package/plugins/ca-pi/agents/scout.md +127 -0
- package/plugins/ca-pi/agents/security-reviewer.md +72 -0
- package/plugins/ca-pi/agents/tribunal-lens-reviewer.md +65 -0
- package/plugins/ca-pi/extensions/codearbiter-child.js +1885 -0
- package/plugins/ca-pi/extensions/codearbiter.js +9802 -0
- package/plugins/ca-pi/generated/command-catalog.json +197 -0
- package/plugins/ca-pi/generated/roles.json +213 -0
- package/plugins/ca-pi/helpers/windows-supervisor.js +205 -0
- package/plugins/ca-pi/hooks/_activationlib.py +196 -0
- package/plugins/ca-pi/hooks/_arbiterstatelib.py +208 -0
- package/plugins/ca-pi/hooks/_babysitlib.py +76 -0
- package/plugins/ca-pi/hooks/_bashguardlib.py +1667 -0
- package/plugins/ca-pi/hooks/_boxlib.py +131 -0
- package/plugins/ca-pi/hooks/_colorlib.py +304 -0
- package/plugins/ca-pi/hooks/_durabilitylib.py +186 -0
- package/plugins/ca-pi/hooks/_entrylib.py +41 -0
- package/plugins/ca-pi/hooks/_fmtlib.py +161 -0
- package/plugins/ca-pi/hooks/_gitexec.py +45 -0
- package/plugins/ca-pi/hooks/_githooks.py +920 -0
- package/plugins/ca-pi/hooks/_gitlib.py +110 -0
- package/plugins/ca-pi/hooks/_hooklib.py +595 -0
- package/plugins/ca-pi/hooks/_host.py +115 -0
- package/plugins/ca-pi/hooks/_intentlib.py +242 -0
- package/plugins/ca-pi/hooks/_ledgerlib.py +1035 -0
- package/plugins/ca-pi/hooks/_metricslib.py +709 -0
- package/plugins/ca-pi/hooks/_pathnorm.py +74 -0
- package/plugins/ca-pi/hooks/_planfilelib.py +664 -0
- package/plugins/ca-pi/hooks/_previewlib.py +193 -0
- package/plugins/ca-pi/hooks/_protectedlib.py +312 -0
- package/plugins/ca-pi/hooks/_protectedstatelib.py +411 -0
- package/plugins/ca-pi/hooks/_provenancelib.py +971 -0
- package/plugins/ca-pi/hooks/_prunelib.py +1398 -0
- package/plugins/ca-pi/hooks/_prunepolicy.py +235 -0
- package/plugins/ca-pi/hooks/_readinjectlib.py +1080 -0
- package/plugins/ca-pi/hooks/_releaselib.py +2657 -0
- package/plugins/ca-pi/hooks/_scopelib.py +262 -0
- package/plugins/ca-pi/hooks/_segmentslib.py +278 -0
- package/plugins/ca-pi/hooks/_sensitivelib.py +270 -0
- package/plugins/ca-pi/hooks/_sessionlib.py +78 -0
- package/plugins/ca-pi/hooks/_sloplib.py +244 -0
- package/plugins/ca-pi/hooks/_standuplib.py +214 -0
- package/plugins/ca-pi/hooks/_subagentslib.py +219 -0
- package/plugins/ca-pi/hooks/_taskboardlib.py +1088 -0
- package/plugins/ca-pi/hooks/_updatelib.py +278 -0
- package/plugins/ca-pi/hooks/babysit.py +47 -0
- package/plugins/ca-pi/hooks/boardsync.py +129 -0
- package/plugins/ca-pi/hooks/doctor.py +420 -0
- package/plugins/ca-pi/hooks/git-enforce.py +325 -0
- package/plugins/ca-pi/hooks/hostapi.py +460 -0
- package/plugins/ca-pi/hooks/init-codearbiter.py +225 -0
- package/plugins/ca-pi/hooks/metrics.py +62 -0
- package/plugins/ca-pi/hooks/migration-pass.py +129 -0
- package/plugins/ca-pi/hooks/pi-bridge.py +543 -0
- package/plugins/ca-pi/hooks/post-write-edit.py +231 -0
- package/plugins/ca-pi/hooks/pre-bash.py +90 -0
- package/plugins/ca-pi/hooks/pre-edit.py +284 -0
- package/plugins/ca-pi/hooks/pre-read.py +81 -0
- package/plugins/ca-pi/hooks/pre-write.py +217 -0
- package/plugins/ca-pi/hooks/preview.py +69 -0
- package/plugins/ca-pi/hooks/prune-transcript.py +232 -0
- package/plugins/ca-pi/hooks/releasehash.py +216 -0
- package/plugins/ca-pi/hooks/security-pass.py +139 -0
- package/plugins/ca-pi/hooks/session-start.py +1218 -0
- package/plugins/ca-pi/hooks/statusline.py +736 -0
- package/plugins/ca-pi/hooks/taskwrite.py +351 -0
- package/plugins/ca-pi/hooks/update-refresh.py +51 -0
- package/plugins/ca-pi/hooks/wire-statusline.py +435 -0
- package/plugins/ca-pi/includes/anti-slop-design/INDEX.md +55 -0
- package/plugins/ca-pi/includes/anti-slop-design/color.md +43 -0
- package/plugins/ca-pi/includes/anti-slop-design/core.md +244 -0
- package/plugins/ca-pi/includes/anti-slop-design/images.md +32 -0
- package/plugins/ca-pi/includes/anti-slop-design/layout.md +45 -0
- package/plugins/ca-pi/includes/anti-slop-design/medium-cli.md +39 -0
- package/plugins/ca-pi/includes/anti-slop-design/medium-dataviz.md +43 -0
- package/plugins/ca-pi/includes/anti-slop-design/medium-diagram.md +35 -0
- package/plugins/ca-pi/includes/anti-slop-design/medium-documents.md +70 -0
- package/plugins/ca-pi/includes/anti-slop-design/medium-slides.md +30 -0
- package/plugins/ca-pi/includes/anti-slop-design/medium-web.md +39 -0
- package/plugins/ca-pi/includes/anti-slop-design/typography.md +51 -0
- package/plugins/ca-pi/includes/author-tdd-workflow.md +14 -0
- package/plugins/ca-pi/includes/compaction-charter.md +16 -0
- package/plugins/ca-pi/includes/cut-docs.md +16 -0
- package/plugins/ca-pi/includes/dev-mode.md +30 -0
- package/plugins/ca-pi/includes/farm.md +237 -0
- package/plugins/ca-pi/includes/fresh-verification.md +14 -0
- package/plugins/ca-pi/includes/harvest.md +69 -0
- package/plugins/ca-pi/includes/maturity-coverage.md +102 -0
- package/plugins/ca-pi/includes/pi-host-notes.md +69 -0
- package/plugins/ca-pi/includes/redirect.md +69 -0
- package/plugins/ca-pi/includes/reference-map.md +22 -0
- package/plugins/ca-pi/includes/review-matrix.md +14 -0
- package/plugins/ca-pi/includes/reviewer-contract.md +53 -0
- package/plugins/ca-pi/includes/routing-table.md +47 -0
- package/plugins/ca-pi/includes/security-gate-record.md +22 -0
- package/plugins/ca-pi/includes/smarts/core.md +90 -0
- package/plugins/ca-pi/includes/smarts/decision-log-format.md +56 -0
- package/plugins/ca-pi/routines/INDEX.md +32 -0
- package/plugins/ca-pi/routines/brainstorming/SKILL.md +122 -0
- package/plugins/ca-pi/routines/commit-gate/SKILL.md +151 -0
- package/plugins/ca-pi/routines/context-check/SKILL.md +85 -0
- package/plugins/ca-pi/routines/context-creation/SKILL.md +171 -0
- package/plugins/ca-pi/routines/crypto-compliance/SKILL.md +41 -0
- package/plugins/ca-pi/routines/debug/SKILL.md +99 -0
- package/plugins/ca-pi/routines/decision-lifecycle/SKILL.md +104 -0
- package/plugins/ca-pi/routines/decision-lifecycle/references/adr-template.md +74 -0
- package/plugins/ca-pi/routines/decision-variance/SKILL.md +147 -0
- package/plugins/ca-pi/routines/decompose/SKILL.md +168 -0
- package/plugins/ca-pi/routines/dispatching-parallel-agents/SKILL.md +76 -0
- package/plugins/ca-pi/routines/executing-plans/SKILL.md +83 -0
- package/plugins/ca-pi/routines/finishing-a-development-branch/SKILL.md +91 -0
- package/plugins/ca-pi/routines/post-merge-cleanup/SKILL.md +233 -0
- package/plugins/ca-pi/routines/refactor/SKILL.md +91 -0
- package/plugins/ca-pi/routines/release/SKILL.md +315 -0
- package/plugins/ca-pi/routines/secret-handling/SKILL.md +67 -0
- package/plugins/ca-pi/routines/security-architecture/SKILL.md +63 -0
- package/plugins/ca-pi/routines/skill-author/SKILL.md +108 -0
- package/plugins/ca-pi/routines/skill-author/references/skill-template.md +58 -0
- package/plugins/ca-pi/routines/subagent-driven-development/SKILL.md +149 -0
- package/plugins/ca-pi/routines/subagent-driven-development/references/farm-dispatch.md +145 -0
- package/plugins/ca-pi/routines/tdd/SKILL.md +139 -0
- package/plugins/ca-pi/routines/tribunal/SKILL.md +109 -0
- package/plugins/ca-pi/routines/tribunal/references/ai-markers.md +29 -0
- package/plugins/ca-pi/routines/tribunal/references/cost-and-models.md +64 -0
- package/plugins/ca-pi/routines/tribunal/references/finding-record.md +27 -0
- package/plugins/ca-pi/routines/tribunal/references/issue-filing.md +47 -0
- package/plugins/ca-pi/routines/tribunal/references/lenses/appsec.md +22 -0
- package/plugins/ca-pi/routines/tribunal/references/lenses/architecture.md +23 -0
- package/plugins/ca-pi/routines/tribunal/references/lenses/coverage.md +20 -0
- package/plugins/ca-pi/routines/tribunal/references/lenses/infra.md +24 -0
- package/plugins/ca-pi/routines/tribunal/references/lenses/migration.md +22 -0
- package/plugins/ca-pi/routines/tribunal/references/lenses/observability.md +21 -0
- package/plugins/ca-pi/routines/tribunal/references/lenses/performance.md +22 -0
- package/plugins/ca-pi/routines/tribunal/references/lenses/reliability.md +23 -0
- package/plugins/ca-pi/routines/tribunal/references/lenses/secrets-supply.md +22 -0
- package/plugins/ca-pi/routines/tribunal/references/lenses/test-fidelity.md +24 -0
- package/plugins/ca-pi/routines/tribunal/references/lenses/typesafety.md +21 -0
- package/plugins/ca-pi/routines/tribunal/references/report.md +19 -0
- package/plugins/ca-pi/routines/tribunal/references/schemas.md +58 -0
- package/plugins/ca-pi/routines/tribunal/references/telemetry.md +28 -0
- package/plugins/ca-pi/routines/tribunal/references/triage.md +53 -0
- package/plugins/ca-pi/routines/using-git-worktrees/SKILL.md +85 -0
- package/plugins/ca-pi/routines/writing-plans/SKILL.md +129 -0
- package/plugins/ca-pi/routines/writing-plans/references/farm-plan.md +50 -0
- package/plugins/ca-pi/skills/ca-add-dep/SKILL.md +88 -0
- package/plugins/ca-pi/skills/ca-adr/SKILL.md +30 -0
- package/plugins/ca-pi/skills/ca-adr-status/SKILL.md +30 -0
- package/plugins/ca-pi/skills/ca-arbiter/SKILL.md +36 -0
- package/plugins/ca-pi/skills/ca-audit/SKILL.md +51 -0
- package/plugins/ca-pi/skills/ca-btw/SKILL.md +23 -0
- package/plugins/ca-pi/skills/ca-checkpoint/SKILL.md +50 -0
- package/plugins/ca-pi/skills/ca-chore/SKILL.md +58 -0
- package/plugins/ca-pi/skills/ca-cleanup/SKILL.md +55 -0
- package/plugins/ca-pi/skills/ca-commands/SKILL.md +21 -0
- package/plugins/ca-pi/skills/ca-commit/SKILL.md +27 -0
- package/plugins/ca-pi/skills/ca-conflict/SKILL.md +61 -0
- package/plugins/ca-pi/skills/ca-context-check/SKILL.md +32 -0
- package/plugins/ca-pi/skills/ca-create-context/SKILL.md +32 -0
- package/plugins/ca-pi/skills/ca-debug/SKILL.md +42 -0
- package/plugins/ca-pi/skills/ca-decompose/SKILL.md +30 -0
- package/plugins/ca-pi/skills/ca-dev/SKILL.md +42 -0
- package/plugins/ca-pi/skills/ca-doctor/SKILL.md +44 -0
- package/plugins/ca-pi/skills/ca-feature/SKILL.md +105 -0
- package/plugins/ca-pi/skills/ca-fix/SKILL.md +42 -0
- package/plugins/ca-pi/skills/ca-init/SKILL.md +56 -0
- package/plugins/ca-pi/skills/ca-metrics/SKILL.md +80 -0
- package/plugins/ca-pi/skills/ca-new-skill/SKILL.md +34 -0
- package/plugins/ca-pi/skills/ca-override/SKILL.md +72 -0
- package/plugins/ca-pi/skills/ca-pr/SKILL.md +61 -0
- package/plugins/ca-pi/skills/ca-preview/SKILL.md +86 -0
- package/plugins/ca-pi/skills/ca-prune/SKILL.md +100 -0
- package/plugins/ca-pi/skills/ca-reconcile/SKILL.md +43 -0
- package/plugins/ca-pi/skills/ca-refactor/SKILL.md +43 -0
- package/plugins/ca-pi/skills/ca-release/SKILL.md +57 -0
- package/plugins/ca-pi/skills/ca-review/SKILL.md +63 -0
- package/plugins/ca-pi/skills/ca-spike/SKILL.md +41 -0
- package/plugins/ca-pi/skills/ca-sprint/SKILL.md +44 -0
- package/plugins/ca-pi/skills/ca-standup/SKILL.md +112 -0
- package/plugins/ca-pi/skills/ca-status/SKILL.md +56 -0
- package/plugins/ca-pi/skills/ca-task/SKILL.md +61 -0
- package/plugins/ca-pi/skills/ca-threat-model/SKILL.md +45 -0
- package/plugins/ca-pi/skills/ca-tribunal/SKILL.md +44 -0
- package/plugins/ca-pi/skills/ca-watch/SKILL.md +78 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# codeArbiter — statusline display-formatting helpers (extracted from
|
|
3
|
+
# statusline.py, architecture-004).
|
|
4
|
+
#
|
|
5
|
+
# Owns the small pure formatters the box uses to turn raw numbers/paths/durations
|
|
6
|
+
# into the compact strings it renders: token/USD formatting, a home-relative path
|
|
7
|
+
# abbreviator, human-friendly durations, ISO-timestamp parsing, and the per-call
|
|
8
|
+
# burn sparkline. Carries no box-layout or segment-assembly logic.
|
|
9
|
+
#
|
|
10
|
+
# Design principles (mirroring _ledgerlib.py):
|
|
11
|
+
# - Stdlib only; no third-party imports ever.
|
|
12
|
+
# - Zero side effects at import time.
|
|
13
|
+
# - Pure functions, fully testable with synthetic input.
|
|
14
|
+
# - Never raise on malformed input — degrade to a safe default.
|
|
15
|
+
#
|
|
16
|
+
# Public API:
|
|
17
|
+
# fmt_tok(n) -> str compact token count (999 / 1.5K / 1.0M)
|
|
18
|
+
# usd(c) -> str whole-dollar-above-$100 USD format
|
|
19
|
+
# usd_fine(c) -> str finer-grained USD format (statusline cost cells)
|
|
20
|
+
# short_path(p) -> str home-relative, middle-truncated path
|
|
21
|
+
# human_dur(secs) -> str compact duration (3h12m, 4d6h, 45m, 30s)
|
|
22
|
+
# parse_iso(s) -> float|None ISO-8601 (tolerating trailing Z) -> epoch seconds
|
|
23
|
+
# to_epoch(v) -> float|None ISO string OR numeric (s/ms) epoch -> epoch seconds
|
|
24
|
+
# sparkline(vals, grad=True) -> str colored sparkline of recent values
|
|
25
|
+
|
|
26
|
+
import os
|
|
27
|
+
from datetime import datetime, timezone
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
import _colorlib
|
|
31
|
+
_RESET, _V2, _SPARK, _gradient_h = (_colorlib.RESET, _colorlib.V2,
|
|
32
|
+
_colorlib.SPARK, _colorlib.gradient_h)
|
|
33
|
+
except Exception: # pragma: no cover — never let an import break the statusline
|
|
34
|
+
_colorlib = None
|
|
35
|
+
_RESET, _V2, _SPARK = "", "", "▁▂▃▄▅▆▇█"
|
|
36
|
+
|
|
37
|
+
def _gradient_h(text, width, c_from=(120, 80, 200), c_to=(205, 140, 255)):
|
|
38
|
+
return text
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def sync_palette():
|
|
42
|
+
"""Refresh captured color exports after _colorlib runtime activation."""
|
|
43
|
+
global _RESET, _V2, _gradient_h
|
|
44
|
+
if _colorlib is not None:
|
|
45
|
+
_RESET, _V2, _gradient_h = _colorlib.RESET, _colorlib.V2, _colorlib.gradient_h
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def num(x, default=0.0):
|
|
49
|
+
"""Coerce any host value to float; tolerate strings, None, and containers."""
|
|
50
|
+
try:
|
|
51
|
+
return float(x)
|
|
52
|
+
except (TypeError, ValueError):
|
|
53
|
+
return default
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def fmt_tok(n):
|
|
57
|
+
n = num(n)
|
|
58
|
+
if n >= 999_500: # round into M before the K branch can print 1000.0K
|
|
59
|
+
return f"{n/1_000_000:.1f}M"
|
|
60
|
+
if n >= 1000:
|
|
61
|
+
return f"{n/1000:.1f}K"
|
|
62
|
+
return str(int(n))
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def usd(c):
|
|
66
|
+
c = num(c)
|
|
67
|
+
return f"${c:.0f}" if c >= 100 else f"${c:.2f}"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def usd_fine(c):
|
|
71
|
+
c = num(c)
|
|
72
|
+
if c >= 100:
|
|
73
|
+
return f"${c:.0f}"
|
|
74
|
+
if c >= 10:
|
|
75
|
+
return f"${c:.1f}"
|
|
76
|
+
if 0 < c < 0.01: # don't render a measured nonzero cost as $0.00
|
|
77
|
+
return "<$.01"
|
|
78
|
+
return f"${c:.2f}"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def short_path(p):
|
|
82
|
+
if not p:
|
|
83
|
+
return "?"
|
|
84
|
+
p = str(p).replace("\\", "/").rstrip("/")
|
|
85
|
+
unc = p.startswith("//") # UNC share: \\server\share -> //server/share
|
|
86
|
+
home = os.path.expanduser("~").replace("\\", "/").rstrip("/")
|
|
87
|
+
if p == home:
|
|
88
|
+
return "~"
|
|
89
|
+
if home and p.startswith(home + "/"):
|
|
90
|
+
p = "~" + p[len(home):]
|
|
91
|
+
parts = [x for x in p.split("/") if x]
|
|
92
|
+
if not parts:
|
|
93
|
+
return "//" if unc else "/"
|
|
94
|
+
if unc:
|
|
95
|
+
# keep //server/share intact, abbreviate any deep middle
|
|
96
|
+
if len(parts) <= 3:
|
|
97
|
+
return "//" + "/".join(parts)
|
|
98
|
+
return "//" + "/".join(parts[:2] + [s[0] for s in parts[2:-2]] + parts[-2:])
|
|
99
|
+
drive = parts[0].endswith(":") or parts[0] == "~"
|
|
100
|
+
prefix = "" if drive else "/"
|
|
101
|
+
if len(parts) <= 3:
|
|
102
|
+
return prefix + "/".join(parts)
|
|
103
|
+
return prefix + "/".join([parts[0]] + [s[0] for s in parts[1:-2]] + parts[-2:])
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def human_dur(secs):
|
|
107
|
+
"""Compact human duration: 3h12m, 4d6h, 45m, 30s."""
|
|
108
|
+
secs = int(max(0, secs))
|
|
109
|
+
if secs < 60:
|
|
110
|
+
return f"{secs}s"
|
|
111
|
+
m = secs // 60
|
|
112
|
+
if m < 60:
|
|
113
|
+
return f"{m}m"
|
|
114
|
+
h = m // 60
|
|
115
|
+
m = m % 60
|
|
116
|
+
if h < 24:
|
|
117
|
+
return f"{h}h{m:02d}m" if m else f"{h}h"
|
|
118
|
+
d = h // 24
|
|
119
|
+
h = h % 24
|
|
120
|
+
return f"{d}d{h}h" if h else f"{d}d"
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def parse_iso(s):
|
|
124
|
+
"""Parse an ISO-8601 timestamp (tolerating a trailing Z) to epoch seconds."""
|
|
125
|
+
if not s or not isinstance(s, str):
|
|
126
|
+
return None
|
|
127
|
+
try:
|
|
128
|
+
dt = datetime.fromisoformat(s.strip().replace("Z", "+00:00"))
|
|
129
|
+
if dt.tzinfo is None:
|
|
130
|
+
dt = dt.replace(tzinfo=timezone.utc) # naive timestamps are UTC, not local
|
|
131
|
+
return dt.timestamp()
|
|
132
|
+
except Exception:
|
|
133
|
+
return None
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def to_epoch(v):
|
|
137
|
+
"""A reset timestamp may arrive as an ISO-8601 string OR a numeric epoch
|
|
138
|
+
(seconds or milliseconds). Normalize to epoch seconds; None if unusable."""
|
|
139
|
+
if v is None or isinstance(v, bool):
|
|
140
|
+
return None
|
|
141
|
+
if isinstance(v, (int, float)):
|
|
142
|
+
v = float(v)
|
|
143
|
+
if v > 1e12: # milliseconds
|
|
144
|
+
v /= 1000.0
|
|
145
|
+
return v if v > 0 else None
|
|
146
|
+
return parse_iso(v)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def sparkline(vals, grad=True):
|
|
150
|
+
vals = [num(v, None) for v in vals]
|
|
151
|
+
vals = [v for v in vals if v is not None]
|
|
152
|
+
if len(vals) < 2:
|
|
153
|
+
return ""
|
|
154
|
+
lo, hi = min(vals), max(vals)
|
|
155
|
+
span = hi - lo
|
|
156
|
+
chars = []
|
|
157
|
+
for v in vals:
|
|
158
|
+
idx = 3 if span <= 0 else int((v - lo) / span * (len(_SPARK) - 1) + 0.5)
|
|
159
|
+
chars.append(_SPARK[max(0, min(len(_SPARK) - 1, idx))])
|
|
160
|
+
s = "".join(chars)
|
|
161
|
+
return _gradient_h(s, len(s)) if grad else f"{_V2}{s}{_RESET}"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# codeArbiter — optional absolute executable identities for shared Git helpers.
|
|
3
|
+
#
|
|
4
|
+
# Claude and Codex retain their historical bare-executable behavior when the
|
|
5
|
+
# variables are absent. The Pi bridge supplies validated absolute identities;
|
|
6
|
+
# every shared Git subprocess then consumes the same identity, and the managed
|
|
7
|
+
# Git hook carries it into the later hook process.
|
|
8
|
+
#
|
|
9
|
+
# Public API:
|
|
10
|
+
# trusted_git_executable() -> str|None validated CODEARBITER_GIT_EXECUTABLE, or
|
|
11
|
+
# None if unset
|
|
12
|
+
# trusted_python_executable() -> str|None validated CODEARBITER_PYTHON_EXECUTABLE, or
|
|
13
|
+
# None if unset
|
|
14
|
+
# git_executable() -> str trusted_git_executable() or the bare "git"
|
|
15
|
+
# fallback
|
|
16
|
+
|
|
17
|
+
import os
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
GIT_ENV = "CODEARBITER_GIT_EXECUTABLE"
|
|
21
|
+
PYTHON_ENV = "CODEARBITER_PYTHON_EXECUTABLE"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _trusted_environment_path(name):
|
|
25
|
+
value = os.environ.get(name)
|
|
26
|
+
if not value:
|
|
27
|
+
return None
|
|
28
|
+
if not os.path.isabs(value):
|
|
29
|
+
raise RuntimeError(f"{name} must be absolute")
|
|
30
|
+
canonical = os.path.realpath(value)
|
|
31
|
+
if not os.path.isfile(canonical):
|
|
32
|
+
raise RuntimeError(f"{name} is unavailable")
|
|
33
|
+
return canonical
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def trusted_git_executable():
|
|
37
|
+
return _trusted_environment_path(GIT_ENV)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def trusted_python_executable():
|
|
41
|
+
return _trusted_environment_path(PYTHON_ENV)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def git_executable():
|
|
45
|
+
return trusted_git_executable() or "git"
|