@coldtea/abide 0.0.1
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 +21 -0
- package/README.md +10 -0
- package/dist/abide-hook.d.ts +2 -0
- package/dist/abide-hook.d.ts.map +1 -0
- package/dist/abide-hook.js +23 -0
- package/dist/abide-hook.js.map +1 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +64 -0
- package/dist/bin.js.map +1 -0
- package/dist/commands/audit.d.ts +3 -0
- package/dist/commands/audit.d.ts.map +1 -0
- package/dist/commands/audit.js +79 -0
- package/dist/commands/audit.js.map +1 -0
- package/dist/commands/bench.d.ts +3 -0
- package/dist/commands/bench.d.ts.map +1 -0
- package/dist/commands/bench.js +206 -0
- package/dist/commands/bench.js.map +1 -0
- package/dist/commands/calibrate.d.ts +3 -0
- package/dist/commands/calibrate.d.ts.map +1 -0
- package/dist/commands/calibrate.js +159 -0
- package/dist/commands/calibrate.js.map +1 -0
- package/dist/commands/check.d.ts +3 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +106 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/compile.d.ts +3 -0
- package/dist/commands/compile.d.ts.map +1 -0
- package/dist/commands/compile.js +116 -0
- package/dist/commands/compile.js.map +1 -0
- package/dist/commands/init.d.ts +4 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +93 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/login.d.ts +3 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +58 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/replay.d.ts +4 -0
- package/dist/commands/replay.d.ts.map +1 -0
- package/dist/commands/replay.js +118 -0
- package/dist/commands/replay.js.map +1 -0
- package/dist/commands/report.d.ts +5 -0
- package/dist/commands/report.d.ts.map +1 -0
- package/dist/commands/report.js +78 -0
- package/dist/commands/report.js.map +1 -0
- package/dist/commands/rubric.d.ts +3 -0
- package/dist/commands/rubric.d.ts.map +1 -0
- package/dist/commands/rubric.js +40 -0
- package/dist/commands/rubric.js.map +1 -0
- package/dist/commands/uninstall.d.ts +2 -0
- package/dist/commands/uninstall.d.ts.map +1 -0
- package/dist/commands/uninstall.js +31 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/hooks/postToolUse.d.ts +3 -0
- package/dist/hooks/postToolUse.d.ts.map +1 -0
- package/dist/hooks/postToolUse.js +145 -0
- package/dist/hooks/postToolUse.js.map +1 -0
- package/dist/hooks/sessionStart.d.ts +11 -0
- package/dist/hooks/sessionStart.d.ts.map +1 -0
- package/dist/hooks/sessionStart.js +83 -0
- package/dist/hooks/sessionStart.js.map +1 -0
- package/dist/hooks/stop.d.ts +26 -0
- package/dist/hooks/stop.d.ts.map +1 -0
- package/dist/hooks/stop.js +227 -0
- package/dist/hooks/stop.js.map +1 -0
- package/dist/hooks/turnStart.d.ts +8 -0
- package/dist/hooks/turnStart.d.ts.map +1 -0
- package/dist/hooks/turnStart.js +40 -0
- package/dist/hooks/turnStart.js.map +1 -0
- package/dist/lib/applyPatch.d.ts +22 -0
- package/dist/lib/applyPatch.d.ts.map +1 -0
- package/dist/lib/applyPatch.js +63 -0
- package/dist/lib/applyPatch.js.map +1 -0
- package/dist/lib/audit.d.ts +40 -0
- package/dist/lib/audit.d.ts.map +1 -0
- package/dist/lib/audit.js +215 -0
- package/dist/lib/audit.js.map +1 -0
- package/dist/lib/band.d.ts +20 -0
- package/dist/lib/band.d.ts.map +1 -0
- package/dist/lib/band.js +40 -0
- package/dist/lib/band.js.map +1 -0
- package/dist/lib/calibration.d.ts +13 -0
- package/dist/lib/calibration.d.ts.map +1 -0
- package/dist/lib/calibration.js +25 -0
- package/dist/lib/calibration.js.map +1 -0
- package/dist/lib/checkRunner.d.ts +34 -0
- package/dist/lib/checkRunner.d.ts.map +1 -0
- package/dist/lib/checkRunner.js +76 -0
- package/dist/lib/checkRunner.js.map +1 -0
- package/dist/lib/claude.d.ts +4 -0
- package/dist/lib/claude.d.ts.map +1 -0
- package/dist/lib/claude.js +28 -0
- package/dist/lib/claude.js.map +1 -0
- package/dist/lib/compilePrompt.d.ts +22 -0
- package/dist/lib/compilePrompt.d.ts.map +1 -0
- package/dist/lib/compilePrompt.js +33 -0
- package/dist/lib/compilePrompt.js.map +1 -0
- package/dist/lib/constants.d.ts +33 -0
- package/dist/lib/constants.d.ts.map +1 -0
- package/dist/lib/constants.js +33 -0
- package/dist/lib/constants.js.map +1 -0
- package/dist/lib/coverage.d.ts +7 -0
- package/dist/lib/coverage.d.ts.map +1 -0
- package/dist/lib/coverage.js +17 -0
- package/dist/lib/coverage.js.map +1 -0
- package/dist/lib/credentials.d.ts +27 -0
- package/dist/lib/credentials.d.ts.map +1 -0
- package/dist/lib/credentials.js +89 -0
- package/dist/lib/credentials.js.map +1 -0
- package/dist/lib/diff.d.ts +30 -0
- package/dist/lib/diff.d.ts.map +1 -0
- package/dist/lib/diff.js +139 -0
- package/dist/lib/diff.js.map +1 -0
- package/dist/lib/events.d.ts +5 -0
- package/dist/lib/events.d.ts.map +1 -0
- package/dist/lib/events.js +38 -0
- package/dist/lib/events.js.map +1 -0
- package/dist/lib/git.d.ts +37 -0
- package/dist/lib/git.d.ts.map +1 -0
- package/dist/lib/git.js +163 -0
- package/dist/lib/git.js.map +1 -0
- package/dist/lib/hookRunner.d.ts +10 -0
- package/dist/lib/hookRunner.d.ts.map +1 -0
- package/dist/lib/hookRunner.js +52 -0
- package/dist/lib/hookRunner.js.map +1 -0
- package/dist/lib/hosts.d.ts +20 -0
- package/dist/lib/hosts.d.ts.map +1 -0
- package/dist/lib/hosts.js +101 -0
- package/dist/lib/hosts.js.map +1 -0
- package/dist/lib/jev.d.ts +24 -0
- package/dist/lib/jev.d.ts.map +1 -0
- package/dist/lib/jev.js +169 -0
- package/dist/lib/jev.js.map +1 -0
- package/dist/lib/lintConfig.d.ts +3 -0
- package/dist/lib/lintConfig.d.ts.map +1 -0
- package/dist/lib/lintConfig.js +55 -0
- package/dist/lib/lintConfig.js.map +1 -0
- package/dist/lib/loadRules.d.ts +12 -0
- package/dist/lib/loadRules.d.ts.map +1 -0
- package/dist/lib/loadRules.js +31 -0
- package/dist/lib/loadRules.js.map +1 -0
- package/dist/lib/opencodePlugin.d.ts +10 -0
- package/dist/lib/opencodePlugin.d.ts.map +1 -0
- package/dist/lib/opencodePlugin.js +36 -0
- package/dist/lib/opencodePlugin.js.map +1 -0
- package/dist/lib/output.d.ts +6 -0
- package/dist/lib/output.d.ts.map +1 -0
- package/dist/lib/output.js +35 -0
- package/dist/lib/output.js.map +1 -0
- package/dist/lib/packageRoot.d.ts +12 -0
- package/dist/lib/packageRoot.d.ts.map +1 -0
- package/dist/lib/packageRoot.js +50 -0
- package/dist/lib/packageRoot.js.map +1 -0
- package/dist/lib/paths.d.ts +24 -0
- package/dist/lib/paths.d.ts.map +1 -0
- package/dist/lib/paths.js +75 -0
- package/dist/lib/paths.js.map +1 -0
- package/dist/lib/reason.d.ts +11 -0
- package/dist/lib/reason.d.ts.map +1 -0
- package/dist/lib/reason.js +31 -0
- package/dist/lib/reason.js.map +1 -0
- package/dist/lib/regularFile.d.ts +26 -0
- package/dist/lib/regularFile.d.ts.map +1 -0
- package/dist/lib/regularFile.js +77 -0
- package/dist/lib/regularFile.js.map +1 -0
- package/dist/lib/replay.d.ts +74 -0
- package/dist/lib/replay.d.ts.map +1 -0
- package/dist/lib/replay.js +250 -0
- package/dist/lib/replay.js.map +1 -0
- package/dist/lib/replayCodex.d.ts +6 -0
- package/dist/lib/replayCodex.d.ts.map +1 -0
- package/dist/lib/replayCodex.js +132 -0
- package/dist/lib/replayCodex.js.map +1 -0
- package/dist/lib/replayOpencode.d.ts +15 -0
- package/dist/lib/replayOpencode.d.ts.map +1 -0
- package/dist/lib/replayOpencode.js +154 -0
- package/dist/lib/replayOpencode.js.map +1 -0
- package/dist/lib/rubricFile.d.ts +31 -0
- package/dist/lib/rubricFile.d.ts.map +1 -0
- package/dist/lib/rubricFile.js +75 -0
- package/dist/lib/rubricFile.js.map +1 -0
- package/dist/lib/scope.d.ts +4 -0
- package/dist/lib/scope.d.ts.map +1 -0
- package/dist/lib/scope.js +14 -0
- package/dist/lib/scope.js.map +1 -0
- package/dist/lib/session.d.ts +48 -0
- package/dist/lib/session.d.ts.map +1 -0
- package/dist/lib/session.js +182 -0
- package/dist/lib/session.js.map +1 -0
- package/dist/lib/settings.d.ts +26 -0
- package/dist/lib/settings.d.ts.map +1 -0
- package/dist/lib/settings.js +104 -0
- package/dist/lib/settings.js.map +1 -0
- package/dist/lib/sources.d.ts +28 -0
- package/dist/lib/sources.d.ts.map +1 -0
- package/dist/lib/sources.js +137 -0
- package/dist/lib/sources.js.map +1 -0
- package/dist/lib/stdin.d.ts +3 -0
- package/dist/lib/stdin.d.ts.map +1 -0
- package/dist/lib/stdin.js +24 -0
- package/dist/lib/stdin.js.map +1 -0
- package/dist/lib/transcript.d.ts +3 -0
- package/dist/lib/transcript.d.ts.map +1 -0
- package/dist/lib/transcript.js +57 -0
- package/dist/lib/transcript.js.map +1 -0
- package/dist/lib/ui.d.ts +8 -0
- package/dist/lib/ui.d.ts.map +1 -0
- package/dist/lib/ui.js +34 -0
- package/dist/lib/ui.js.map +1 -0
- package/dist/ui/components/Buckets.d.ts +6 -0
- package/dist/ui/components/Buckets.d.ts.map +1 -0
- package/dist/ui/components/Buckets.js +11 -0
- package/dist/ui/components/Buckets.js.map +1 -0
- package/dist/ui/components/Callout.d.ts +11 -0
- package/dist/ui/components/Callout.d.ts.map +1 -0
- package/dist/ui/components/Callout.js +10 -0
- package/dist/ui/components/Callout.js.map +1 -0
- package/dist/ui/components/Checklist.d.ts +10 -0
- package/dist/ui/components/Checklist.d.ts.map +1 -0
- package/dist/ui/components/Checklist.js +8 -0
- package/dist/ui/components/Checklist.js.map +1 -0
- package/dist/ui/components/Columns.d.ts +28 -0
- package/dist/ui/components/Columns.d.ts.map +1 -0
- package/dist/ui/components/Columns.js +20 -0
- package/dist/ui/components/Columns.js.map +1 -0
- package/dist/ui/components/Header.d.ts +9 -0
- package/dist/ui/components/Header.d.ts.map +1 -0
- package/dist/ui/components/Header.js +7 -0
- package/dist/ui/components/Header.js.map +1 -0
- package/dist/ui/components/Live.d.ts +16 -0
- package/dist/ui/components/Live.d.ts.map +1 -0
- package/dist/ui/components/Live.js +40 -0
- package/dist/ui/components/Live.js.map +1 -0
- package/dist/ui/components/RuleTable.d.ts +15 -0
- package/dist/ui/components/RuleTable.d.ts.map +1 -0
- package/dist/ui/components/RuleTable.js +81 -0
- package/dist/ui/components/RuleTable.js.map +1 -0
- package/dist/ui/components/Section.d.ts +10 -0
- package/dist/ui/components/Section.d.ts.map +1 -0
- package/dist/ui/components/Section.js +8 -0
- package/dist/ui/components/Section.js.map +1 -0
- package/dist/ui/components/Verdicts.d.ts +9 -0
- package/dist/ui/components/Verdicts.d.ts.map +1 -0
- package/dist/ui/components/Verdicts.js +12 -0
- package/dist/ui/components/Verdicts.js.map +1 -0
- package/dist/ui/render.d.ts +20 -0
- package/dist/ui/render.d.ts.map +1 -0
- package/dist/ui/render.js +32 -0
- package/dist/ui/render.js.map +1 -0
- package/dist/ui/theme.d.ts +37 -0
- package/dist/ui/theme.d.ts.map +1 -0
- package/dist/ui/theme.js +63 -0
- package/dist/ui/theme.js.map +1 -0
- package/dist/ui/views/AuditView.d.ts +18 -0
- package/dist/ui/views/AuditView.d.ts.map +1 -0
- package/dist/ui/views/AuditView.js +48 -0
- package/dist/ui/views/AuditView.js.map +1 -0
- package/dist/ui/views/BenchView.d.ts +33 -0
- package/dist/ui/views/BenchView.d.ts.map +1 -0
- package/dist/ui/views/BenchView.js +19 -0
- package/dist/ui/views/BenchView.js.map +1 -0
- package/dist/ui/views/CalibrateView.d.ts +26 -0
- package/dist/ui/views/CalibrateView.d.ts.map +1 -0
- package/dist/ui/views/CalibrateView.js +42 -0
- package/dist/ui/views/CalibrateView.js.map +1 -0
- package/dist/ui/views/CheckView.d.ts +19 -0
- package/dist/ui/views/CheckView.d.ts.map +1 -0
- package/dist/ui/views/CheckView.js +15 -0
- package/dist/ui/views/CheckView.js.map +1 -0
- package/dist/ui/views/ErrorView.d.ts +4 -0
- package/dist/ui/views/ErrorView.d.ts.map +1 -0
- package/dist/ui/views/ErrorView.js +26 -0
- package/dist/ui/views/ErrorView.js.map +1 -0
- package/dist/ui/views/HelpView.d.ts +2 -0
- package/dist/ui/views/HelpView.d.ts.map +1 -0
- package/dist/ui/views/HelpView.js +25 -0
- package/dist/ui/views/HelpView.js.map +1 -0
- package/dist/ui/views/InitView.d.ts +27 -0
- package/dist/ui/views/InitView.d.ts.map +1 -0
- package/dist/ui/views/InitView.js +24 -0
- package/dist/ui/views/InitView.js.map +1 -0
- package/dist/ui/views/ReplayView.d.ts +16 -0
- package/dist/ui/views/ReplayView.d.ts.map +1 -0
- package/dist/ui/views/ReplayView.js +44 -0
- package/dist/ui/views/ReplayView.js.map +1 -0
- package/dist/ui/views/ReportView.d.ts +14 -0
- package/dist/ui/views/ReportView.d.ts.map +1 -0
- package/dist/ui/views/ReportView.js +86 -0
- package/dist/ui/views/ReportView.js.map +1 -0
- package/dist/ui/views/RubricView.d.ts +18 -0
- package/dist/ui/views/RubricView.d.ts.map +1 -0
- package/dist/ui/views/RubricView.js +18 -0
- package/dist/ui/views/RubricView.js.map +1 -0
- package/opencode/abide.mjs +246 -0
- package/package.json +50 -0
- package/skills/abide-compile/SKILL.md +173 -0
- package/src/abide-hook.ts +23 -0
- package/src/bin.ts +64 -0
- package/src/commands/audit.ts +97 -0
- package/src/commands/bench.ts +226 -0
- package/src/commands/calibrate.ts +165 -0
- package/src/commands/check.ts +114 -0
- package/src/commands/compile.ts +127 -0
- package/src/commands/init.ts +109 -0
- package/src/commands/login.ts +61 -0
- package/src/commands/replay.ts +148 -0
- package/src/commands/report.ts +83 -0
- package/src/commands/rubric.ts +45 -0
- package/src/commands/uninstall.ts +33 -0
- package/src/hooks/postToolUse.ts +171 -0
- package/src/hooks/sessionStart.ts +100 -0
- package/src/hooks/stop.ts +275 -0
- package/src/hooks/turnStart.ts +36 -0
- package/src/lib/applyPatch.ts +75 -0
- package/src/lib/audit.ts +265 -0
- package/src/lib/band.ts +49 -0
- package/src/lib/calibration.ts +35 -0
- package/src/lib/checkRunner.ts +124 -0
- package/src/lib/claude.ts +35 -0
- package/src/lib/compilePrompt.ts +67 -0
- package/src/lib/constants.ts +39 -0
- package/src/lib/coverage.ts +21 -0
- package/src/lib/credentials.ts +104 -0
- package/src/lib/diff.ts +209 -0
- package/src/lib/events.ts +34 -0
- package/src/lib/git.ts +206 -0
- package/src/lib/hookRunner.ts +55 -0
- package/src/lib/hosts.ts +112 -0
- package/src/lib/jev.ts +223 -0
- package/src/lib/lintConfig.ts +53 -0
- package/src/lib/loadRules.ts +39 -0
- package/src/lib/opencodePlugin.ts +38 -0
- package/src/lib/output.ts +44 -0
- package/src/lib/packageRoot.ts +48 -0
- package/src/lib/paths.ts +96 -0
- package/src/lib/reason.ts +46 -0
- package/src/lib/regularFile.ts +96 -0
- package/src/lib/replay.ts +314 -0
- package/src/lib/replayCodex.ts +130 -0
- package/src/lib/replayOpencode.ts +186 -0
- package/src/lib/rubricFile.ts +99 -0
- package/src/lib/scope.ts +18 -0
- package/src/lib/session.ts +223 -0
- package/src/lib/settings.ts +112 -0
- package/src/lib/sources.ts +154 -0
- package/src/lib/stdin.ts +23 -0
- package/src/lib/transcript.ts +49 -0
- package/src/lib/ui.ts +38 -0
- package/src/ui/components/Buckets.tsx +30 -0
- package/src/ui/components/Callout.tsx +26 -0
- package/src/ui/components/Checklist.tsx +29 -0
- package/src/ui/components/Columns.tsx +62 -0
- package/src/ui/components/Header.tsx +35 -0
- package/src/ui/components/Live.tsx +73 -0
- package/src/ui/components/RuleTable.tsx +94 -0
- package/src/ui/components/Section.tsx +24 -0
- package/src/ui/components/Verdicts.tsx +40 -0
- package/src/ui/render.tsx +51 -0
- package/src/ui/theme.ts +65 -0
- package/src/ui/views/AuditView.tsx +140 -0
- package/src/ui/views/BenchView.tsx +87 -0
- package/src/ui/views/CalibrateView.tsx +108 -0
- package/src/ui/views/CheckView.tsx +56 -0
- package/src/ui/views/ErrorView.tsx +32 -0
- package/src/ui/views/HelpView.tsx +51 -0
- package/src/ui/views/InitView.tsx +103 -0
- package/src/ui/views/ReplayView.tsx +100 -0
- package/src/ui/views/ReportView.tsx +225 -0
- package/src/ui/views/RubricView.tsx +82 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Text } from "ink";
|
|
2
|
+
import { isAbideError, type AbideErrorCode } from "@coldtea/abide-schema";
|
|
3
|
+
import { Callout } from "../components/Callout.js";
|
|
4
|
+
import { palette } from "../theme.js";
|
|
5
|
+
|
|
6
|
+
/** Our own sentence for each failure; the machine's detail sits under it, dimmed. */
|
|
7
|
+
const TITLES: Record<AbideErrorCode, string> = {
|
|
8
|
+
NO_API_KEY: "No API key, so nothing can be checked",
|
|
9
|
+
NO_INSTRUCTION_FILES: "No instruction files here, so there is nothing to compile",
|
|
10
|
+
RUBRIC_INVALID: "The rubric could not be read",
|
|
11
|
+
RUBRIC_MISSING: "No rubric yet",
|
|
12
|
+
SETTINGS_INVALID: "The settings file could not be changed",
|
|
13
|
+
GIT_UNAVAILABLE: "Git history is not readable here",
|
|
14
|
+
CLAUDE_UNAVAILABLE: "Claude Code did not finish the turn",
|
|
15
|
+
HOST_UNKNOWN: "That is not an agent abide knows",
|
|
16
|
+
HOST_NOT_FOUND: "No supported agent was found on this machine",
|
|
17
|
+
CHECK_TIMEOUT: "Jev did not answer in time",
|
|
18
|
+
CHECK_FAILED: "Jev refused the check",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function ErrorView({ error }: { error: unknown }) {
|
|
22
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
23
|
+
if (!isAbideError(error)) return <Callout tone="bad" title={detail} />;
|
|
24
|
+
return (
|
|
25
|
+
<Callout tone="bad" title={TITLES[error.code]}>
|
|
26
|
+
<Text color={palette.mist} wrap="wrap">
|
|
27
|
+
{detail}
|
|
28
|
+
</Text>
|
|
29
|
+
<Text color={palette.ash}>{error.code}</Text>
|
|
30
|
+
</Callout>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Box, Text } from "ink";
|
|
2
|
+
import { Header } from "../components/Header.js";
|
|
3
|
+
import { palette } from "../theme.js";
|
|
4
|
+
|
|
5
|
+
const COMMANDS: [string, string][] = [
|
|
6
|
+
["login [--gateway]", "store your TypeSafe key in ~/.abide/.env, owner-only"],
|
|
7
|
+
["init [agent] [--project]", "hook into claude, codex, opencode, or every one found here"],
|
|
8
|
+
["compile", "compile the rubric now, in a headless Claude Code turn"],
|
|
9
|
+
["tune [--global]", "rewrite rules that never fire, with their statistics attached"],
|
|
10
|
+
["rubric validate [--global]", "check .abide/rubric.json and fill in source hashes"],
|
|
11
|
+
["calibrate [--global]", "test every rule against this repo's recent history"],
|
|
12
|
+
["check [paths] [--all]", "check uncommitted changes the way the hooks would"],
|
|
13
|
+
["audit [paths] [--all]", "judge every file in scope as if just written; what breaks which rule"],
|
|
14
|
+
["report", "what is compiled, what fired, what never fires"],
|
|
15
|
+
[
|
|
16
|
+
"replay <agent>",
|
|
17
|
+
"judge past claude, codex or opencode sessions in this repo as if abide had been installed",
|
|
18
|
+
],
|
|
19
|
+
["bench [--runs N]", "latency and spend, measured on this machine"],
|
|
20
|
+
["uninstall [agent] [--project]", "remove the hooks from one agent, or all"],
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
export function HelpView() {
|
|
24
|
+
return (
|
|
25
|
+
<Box flexDirection="column">
|
|
26
|
+
<Header
|
|
27
|
+
command="help"
|
|
28
|
+
note="Enforce your own AGENTS.md rules on every edit a coding agent makes."
|
|
29
|
+
/>
|
|
30
|
+
{COMMANDS.map(([name, what]) => (
|
|
31
|
+
<Box key={name}>
|
|
32
|
+
<Box width={30}>
|
|
33
|
+
<Text color={palette.cloud} bold>
|
|
34
|
+
{name}
|
|
35
|
+
</Text>
|
|
36
|
+
</Box>
|
|
37
|
+
<Text color={palette.mist}>{what}</Text>
|
|
38
|
+
</Box>
|
|
39
|
+
))}
|
|
40
|
+
<Box marginTop={1} flexDirection="column">
|
|
41
|
+
<Text color={palette.ash}>
|
|
42
|
+
--json prints machine-readable output on report, check, audit, bench and calibrate.
|
|
43
|
+
</Text>
|
|
44
|
+
<Text color={palette.ash}>
|
|
45
|
+
Key: abide login, or TYPESAFE_AI_API_KEY (or AI_GATEWAY_API_KEY) in the environment or a
|
|
46
|
+
.env at the repo root.
|
|
47
|
+
</Text>
|
|
48
|
+
</Box>
|
|
49
|
+
</Box>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Box, Text } from "ink";
|
|
2
|
+
import { assertNever } from "@coldtea/abide-schema";
|
|
3
|
+
import { Callout } from "../components/Callout.js";
|
|
4
|
+
import { Checklist, type Step } from "../components/Checklist.js";
|
|
5
|
+
import { Header } from "../components/Header.js";
|
|
6
|
+
import { palette, scopeLabel } from "../theme.js";
|
|
7
|
+
|
|
8
|
+
export type InitData =
|
|
9
|
+
| { kind: "no-key"; root: string }
|
|
10
|
+
| { kind: "no-sources"; root: string }
|
|
11
|
+
| {
|
|
12
|
+
kind: "installed";
|
|
13
|
+
root: string;
|
|
14
|
+
steps: Step[];
|
|
15
|
+
hosts: string[];
|
|
16
|
+
afterwards: string[];
|
|
17
|
+
sources: { path: string; scope: string; global: boolean }[];
|
|
18
|
+
rubric: { path: string; rules: number } | null;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function InitView({ data }: { data: InitData }) {
|
|
22
|
+
switch (data.kind) {
|
|
23
|
+
case "no-key":
|
|
24
|
+
return (
|
|
25
|
+
<Box flexDirection="column">
|
|
26
|
+
<Header command="init" where={data.root} />
|
|
27
|
+
<Callout tone="warn" title="No API key found, so abide has nothing to check with">
|
|
28
|
+
<Text color={palette.cloud}>
|
|
29
|
+
Get a TypeSafe key at typesafe.ai, then run{" "}
|
|
30
|
+
<Text color={palette.ceramic}>abide login</Text> and paste it.
|
|
31
|
+
</Text>
|
|
32
|
+
<Text color={palette.mist}>
|
|
33
|
+
Or put TYPESAFE_AI_API_KEY (or a Vercel AI_GATEWAY_API_KEY) in the environment or a
|
|
34
|
+
.env at the repo root.
|
|
35
|
+
</Text>
|
|
36
|
+
<Text color={palette.ash}>Never a flag. It is never logged.</Text>
|
|
37
|
+
</Callout>
|
|
38
|
+
</Box>
|
|
39
|
+
);
|
|
40
|
+
case "no-sources":
|
|
41
|
+
return (
|
|
42
|
+
<Box flexDirection="column">
|
|
43
|
+
<Header command="init" where={data.root} />
|
|
44
|
+
<Callout tone="warn" title="Found 0 instruction files, so there are 0 rules">
|
|
45
|
+
<Text color={palette.cloud}>
|
|
46
|
+
Add an AGENTS.md and run abide init again. Abide ships no rules of its own.
|
|
47
|
+
</Text>
|
|
48
|
+
</Callout>
|
|
49
|
+
</Box>
|
|
50
|
+
);
|
|
51
|
+
case "installed":
|
|
52
|
+
return (
|
|
53
|
+
<Box flexDirection="column">
|
|
54
|
+
<Header command="init" where={data.root} />
|
|
55
|
+
<Checklist steps={data.steps} />
|
|
56
|
+
<Box flexDirection="column" marginTop={1}>
|
|
57
|
+
<Text color={palette.mist}>Instruction files</Text>
|
|
58
|
+
{data.sources.map((s) => (
|
|
59
|
+
<Box key={s.path}>
|
|
60
|
+
<Text color={palette.cloud}> {s.path}</Text>
|
|
61
|
+
<Text color={palette.ash}>
|
|
62
|
+
{" "}
|
|
63
|
+
{s.global
|
|
64
|
+
? "global, every repo"
|
|
65
|
+
: s.scope === "**/*"
|
|
66
|
+
? "everywhere in this repo"
|
|
67
|
+
: `applies to ${scopeLabel([s.scope])}`}
|
|
68
|
+
</Text>
|
|
69
|
+
</Box>
|
|
70
|
+
))}
|
|
71
|
+
</Box>
|
|
72
|
+
<Box marginTop={1}>
|
|
73
|
+
{data.rubric ? (
|
|
74
|
+
<Callout tone="ok" title={`Rubric present: ${data.rubric.rules} rules`}>
|
|
75
|
+
<Text color={palette.mist}>
|
|
76
|
+
{data.rubric.path}. It is rehashed at the start of every session.
|
|
77
|
+
</Text>
|
|
78
|
+
</Callout>
|
|
79
|
+
) : (
|
|
80
|
+
<Callout tone="accent" title={`Next: start ${data.hosts.join(" or ")} in this repo`}>
|
|
81
|
+
<Text color={palette.cloud}>
|
|
82
|
+
The first turn compiles the rubric, on your own subscription.
|
|
83
|
+
</Text>
|
|
84
|
+
<Text color={palette.mist}>
|
|
85
|
+
To compile right now instead: <Text color={palette.ceramic}>abide compile</Text>
|
|
86
|
+
</Text>
|
|
87
|
+
</Callout>
|
|
88
|
+
)}
|
|
89
|
+
</Box>
|
|
90
|
+
{data.afterwards.map((line) => (
|
|
91
|
+
<Text key={line} color={palette.amber}>
|
|
92
|
+
{line}
|
|
93
|
+
</Text>
|
|
94
|
+
))}
|
|
95
|
+
<Text color={palette.ash}>
|
|
96
|
+
Later: abide report shows what fired, abide bench measures latency and spend here.
|
|
97
|
+
</Text>
|
|
98
|
+
</Box>
|
|
99
|
+
);
|
|
100
|
+
default:
|
|
101
|
+
return assertNever(data);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { Box, Text } from "ink";
|
|
2
|
+
import type { DriftRow, ReplayResult, RuleTally } from "../../lib/replay.js";
|
|
3
|
+
import { ms, usd } from "../../lib/ui.js";
|
|
4
|
+
import { Columns } from "../components/Columns.js";
|
|
5
|
+
import { Header } from "../components/Header.js";
|
|
6
|
+
import { Section } from "../components/Section.js";
|
|
7
|
+
import { glyph, palette } from "../theme.js";
|
|
8
|
+
|
|
9
|
+
export type ReplayData = {
|
|
10
|
+
root: string;
|
|
11
|
+
host: string;
|
|
12
|
+
sessions: number;
|
|
13
|
+
edits: number;
|
|
14
|
+
result: ReplayResult;
|
|
15
|
+
drift: DriftRow[];
|
|
16
|
+
tallies: RuleTally[];
|
|
17
|
+
spendUsd: number;
|
|
18
|
+
elapsedMs: number;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const percent = (rate: number): string => `${Math.round(rate * 100)}%`;
|
|
22
|
+
|
|
23
|
+
export function ReplayView({ data }: { data: ReplayData }) {
|
|
24
|
+
const judged = data.result.edits.filter((e) => e.error === undefined);
|
|
25
|
+
const broken = judged.filter((e) => e.verdicts.some((v) => v.band === "act"));
|
|
26
|
+
const failed = data.result.edits.filter((e) => e.error !== undefined);
|
|
27
|
+
const brokenTurns = data.result.turns.filter((t) => t.verdicts.some((v) => v.band === "act"));
|
|
28
|
+
const rows = data.tallies.filter((t) => t.broken + t.flagged > 0);
|
|
29
|
+
return (
|
|
30
|
+
<Box flexDirection="column">
|
|
31
|
+
<Header
|
|
32
|
+
command="replay"
|
|
33
|
+
where={data.root}
|
|
34
|
+
note={`${data.host} ${glyph.dotSep} ${data.sessions} sessions ${glyph.dotSep} ${judged.length} edits judged ${glyph.dotSep} ${ms(data.elapsedMs)} ${glyph.dotSep} about ${usd(data.spendUsd)}`}
|
|
35
|
+
/>
|
|
36
|
+
<Section
|
|
37
|
+
title="What abide would have caught"
|
|
38
|
+
aside={`${broken.length} of ${judged.length} edits, ${brokenTurns.length} of ${data.result.turns.length} turns`}
|
|
39
|
+
>
|
|
40
|
+
<Columns
|
|
41
|
+
indent={2}
|
|
42
|
+
columns={[
|
|
43
|
+
{ key: "label", label: "when", width: 20 },
|
|
44
|
+
{ key: "edits", label: "edits", width: 6, align: "right" },
|
|
45
|
+
{ key: "broken", label: "broken", width: 7, align: "right" },
|
|
46
|
+
{ key: "rate", label: "rate", width: 6, align: "right" },
|
|
47
|
+
]}
|
|
48
|
+
rows={data.drift.map((d) => ({
|
|
49
|
+
label: { text: d.label, color: palette.cloud },
|
|
50
|
+
edits: { text: String(d.edits), color: palette.mist },
|
|
51
|
+
broken: { text: String(d.broken), color: d.broken > 0 ? palette.rose : palette.mist },
|
|
52
|
+
rate: { text: percent(d.rate), color: palette.cloud, bold: true },
|
|
53
|
+
}))}
|
|
54
|
+
/>
|
|
55
|
+
</Section>
|
|
56
|
+
<Section
|
|
57
|
+
title="By rule"
|
|
58
|
+
aside={rows.length === 0 ? "nothing fired" : `${rows.length} rules fired`}
|
|
59
|
+
>
|
|
60
|
+
{rows.length === 0 ? (
|
|
61
|
+
<Text color={palette.sage}>
|
|
62
|
+
{glyph.check} No edit or turn broke a rule above the act line.
|
|
63
|
+
</Text>
|
|
64
|
+
) : (
|
|
65
|
+
<Columns
|
|
66
|
+
indent={2}
|
|
67
|
+
columns={[
|
|
68
|
+
{ key: "rule", label: "rule", width: 34 },
|
|
69
|
+
{ key: "phase", label: "phase", width: 5 },
|
|
70
|
+
{ key: "broken", label: "broken", width: 7, align: "right" },
|
|
71
|
+
{ key: "flagged", label: "uncertain", width: 9, align: "right" },
|
|
72
|
+
{ key: "of", label: "of", width: 6, align: "right" },
|
|
73
|
+
{ key: "example", label: "for example", keep: "tail" },
|
|
74
|
+
]}
|
|
75
|
+
rows={rows.map((t) => ({
|
|
76
|
+
rule: { text: t.rule, color: palette.cloud, bold: true },
|
|
77
|
+
phase: { text: t.phase, color: palette.mist },
|
|
78
|
+
broken: { text: String(t.broken), color: t.broken > 0 ? palette.rose : palette.mist },
|
|
79
|
+
flagged: {
|
|
80
|
+
text: String(t.flagged),
|
|
81
|
+
color: t.flagged > 0 ? palette.amber : palette.mist,
|
|
82
|
+
},
|
|
83
|
+
of: { text: String(t.of), color: palette.ash },
|
|
84
|
+
example: { text: t.example, color: palette.ash },
|
|
85
|
+
}))}
|
|
86
|
+
/>
|
|
87
|
+
)}
|
|
88
|
+
</Section>
|
|
89
|
+
{failed.length > 0 ? (
|
|
90
|
+
<Text color={palette.amber}>
|
|
91
|
+
{failed.length} edits could not be judged: {failed[0]?.error}
|
|
92
|
+
</Text>
|
|
93
|
+
) : null}
|
|
94
|
+
<Text color={palette.ash}>
|
|
95
|
+
Jev is the judge here, so this is what abide would have flagged, not ground truth.
|
|
96
|
+
Shell-made changes are not in a transcript and were not judged.
|
|
97
|
+
</Text>
|
|
98
|
+
</Box>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { Box, Text } from "ink";
|
|
2
|
+
import type { AbideEvent, Rule } from "@coldtea/abide-schema";
|
|
3
|
+
import { Buckets } from "../components/Buckets.js";
|
|
4
|
+
import { Callout } from "../components/Callout.js";
|
|
5
|
+
import { Columns } from "../components/Columns.js";
|
|
6
|
+
import { Header } from "../components/Header.js";
|
|
7
|
+
import { RuleTable, type RuleStats } from "../components/RuleTable.js";
|
|
8
|
+
import { Section } from "../components/Section.js";
|
|
9
|
+
import { median, ms, usd } from "../../lib/ui.js";
|
|
10
|
+
import { glyph, palette } from "../theme.js";
|
|
11
|
+
|
|
12
|
+
export type ReportData = {
|
|
13
|
+
root: string;
|
|
14
|
+
rules: Rule[];
|
|
15
|
+
events: AbideEvent[];
|
|
16
|
+
stats: Map<string, RuleStats>;
|
|
17
|
+
dead: Rule[];
|
|
18
|
+
problems: string[];
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type Check = Extract<AbideEvent, { kind: "check" }>;
|
|
22
|
+
|
|
23
|
+
const reasonOf = (rule: Rule): string =>
|
|
24
|
+
rule.check.type === "deferred" || rule.check.type === "unenforceable" ? rule.check.reason : "";
|
|
25
|
+
|
|
26
|
+
export function ReportView({ data }: { data: ReportData }) {
|
|
27
|
+
const checks = data.events.filter((e): e is Check => e.kind === "check");
|
|
28
|
+
const errors = data.events.filter((e) => e.kind === "error").length;
|
|
29
|
+
const edit = checks.filter((c) => c.phase === "edit");
|
|
30
|
+
const turn = checks.filter((c) => c.phase === "turn");
|
|
31
|
+
const spend = (list: readonly Check[]) => list.reduce((s, c) => s + (c.usage?.costUsd ?? 0), 0);
|
|
32
|
+
|
|
33
|
+
const stuck = data.rules.filter((r) => r.status === "weak" || r.status === "noisy");
|
|
34
|
+
const notRunning = [...stuck, ...data.dead];
|
|
35
|
+
const lintable = data.rules.flatMap((r) =>
|
|
36
|
+
r.check.type === "lint"
|
|
37
|
+
? [
|
|
38
|
+
{
|
|
39
|
+
id: r.id,
|
|
40
|
+
how: r.check.how ?? `pattern: ${r.check.pattern ?? ""}`,
|
|
41
|
+
overlaps: r.check.overlaps,
|
|
42
|
+
},
|
|
43
|
+
]
|
|
44
|
+
: [],
|
|
45
|
+
);
|
|
46
|
+
const deferred = data.rules.filter((r) => r.check.type === "deferred");
|
|
47
|
+
const unenforceable = data.rules.filter((r) => r.check.type === "unenforceable");
|
|
48
|
+
const quiet = data.rules.filter((r) => {
|
|
49
|
+
const s = data.stats.get(r.id);
|
|
50
|
+
return (
|
|
51
|
+
r.status === "active" &&
|
|
52
|
+
r.check.type === "model" &&
|
|
53
|
+
s !== undefined &&
|
|
54
|
+
s.checks > 0 &&
|
|
55
|
+
s.fired === 0 &&
|
|
56
|
+
s.flagged === 0 &&
|
|
57
|
+
!data.dead.includes(r)
|
|
58
|
+
);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<Box flexDirection="column">
|
|
63
|
+
<Header command="report" where={data.root} />
|
|
64
|
+
{data.problems.map((p) => (
|
|
65
|
+
<Callout key={p} tone="warn" title="A rubric could not be read and is being ignored">
|
|
66
|
+
<Text color={palette.mist}>{p}</Text>
|
|
67
|
+
</Callout>
|
|
68
|
+
))}
|
|
69
|
+
|
|
70
|
+
<Section title="Rules">
|
|
71
|
+
<Buckets rules={data.rules} />
|
|
72
|
+
<Box marginTop={1}>
|
|
73
|
+
<RuleTable
|
|
74
|
+
rules={data.rules}
|
|
75
|
+
stats={checks.length > 0 ? data.stats : undefined}
|
|
76
|
+
runnableOnly
|
|
77
|
+
/>
|
|
78
|
+
</Box>
|
|
79
|
+
{quiet.length > 0 ? (
|
|
80
|
+
<Box marginTop={1}>
|
|
81
|
+
<Text color={palette.ash}>
|
|
82
|
+
{quiet.length} {quiet.length === 1 ? "rule has" : "rules have"} answered no on every
|
|
83
|
+
check so far, which is what a clear answer looks like.
|
|
84
|
+
</Text>
|
|
85
|
+
</Box>
|
|
86
|
+
) : null}
|
|
87
|
+
</Section>
|
|
88
|
+
|
|
89
|
+
{notRunning.length > 0 ? (
|
|
90
|
+
<Callout
|
|
91
|
+
tone="warn"
|
|
92
|
+
title={`${notRunning.length} ${notRunning.length === 1 ? "rule is switched off or never answers" : "rules are switched off or never answer"}. Likely badly worded.`}
|
|
93
|
+
>
|
|
94
|
+
{notRunning.map((r) => (
|
|
95
|
+
<Text key={r.id} color={palette.cloud}>
|
|
96
|
+
{r.id}
|
|
97
|
+
<Text color={palette.ash}>
|
|
98
|
+
{" "}
|
|
99
|
+
{r.status === "weak"
|
|
100
|
+
? "weak, off"
|
|
101
|
+
: r.status === "noisy"
|
|
102
|
+
? "noisy, off"
|
|
103
|
+
: "never fired or flagged"}
|
|
104
|
+
{r.calibration ? ` ${glyph.dotSep} median ${r.calibration.median.toFixed(2)}` : ""}
|
|
105
|
+
</Text>
|
|
106
|
+
</Text>
|
|
107
|
+
))}
|
|
108
|
+
<Text color={palette.mist}>Run abide tune to rewrite them.</Text>
|
|
109
|
+
</Callout>
|
|
110
|
+
) : null}
|
|
111
|
+
|
|
112
|
+
{lintable.length > 0 ? (
|
|
113
|
+
<Section
|
|
114
|
+
title="For your linter"
|
|
115
|
+
aside={`${lintable.length} ${lintable.length === 1 ? "rule" : "rules"} a linter enforces exactly. Abide does not run them.`}
|
|
116
|
+
>
|
|
117
|
+
<Columns
|
|
118
|
+
indent={2}
|
|
119
|
+
columns={[
|
|
120
|
+
{ key: "id", label: "", width: 30 },
|
|
121
|
+
{ key: "have", label: "", width: 16 },
|
|
122
|
+
{ key: "how", label: "" },
|
|
123
|
+
]}
|
|
124
|
+
rows={lintable.map((r) => ({
|
|
125
|
+
id: { text: r.id, color: palette.mist },
|
|
126
|
+
how: { text: r.how, color: palette.cloud },
|
|
127
|
+
have: r.overlaps
|
|
128
|
+
? { text: `${glyph.check} configured`, color: palette.sage }
|
|
129
|
+
: { text: "not configured", color: palette.amber },
|
|
130
|
+
}))}
|
|
131
|
+
/>
|
|
132
|
+
</Section>
|
|
133
|
+
) : null}
|
|
134
|
+
|
|
135
|
+
{deferred.length + unenforceable.length > 0 ? (
|
|
136
|
+
<Section
|
|
137
|
+
title="Not checked"
|
|
138
|
+
aside={`${deferred.length} deferred ${glyph.dotSep} ${unenforceable.length} unenforceable`}
|
|
139
|
+
>
|
|
140
|
+
{deferred.length > 0 ? (
|
|
141
|
+
<Box flexDirection="column">
|
|
142
|
+
<Text color={palette.ceramicWarm}>
|
|
143
|
+
Need the rest of the repository, which a diff does not carry
|
|
144
|
+
</Text>
|
|
145
|
+
<Columns
|
|
146
|
+
indent={2}
|
|
147
|
+
columns={[
|
|
148
|
+
{ key: "id", label: "", width: 30 },
|
|
149
|
+
{ key: "why", label: "" },
|
|
150
|
+
]}
|
|
151
|
+
rows={deferred.map((r) => ({
|
|
152
|
+
id: { text: r.id, color: palette.mist },
|
|
153
|
+
why: { text: reasonOf(r), color: palette.ash },
|
|
154
|
+
}))}
|
|
155
|
+
/>
|
|
156
|
+
</Box>
|
|
157
|
+
) : null}
|
|
158
|
+
{unenforceable.length > 0 ? (
|
|
159
|
+
<Box flexDirection="column" marginTop={deferred.length > 0 ? 1 : 0}>
|
|
160
|
+
<Text color={palette.ash}>
|
|
161
|
+
About the process or the conversation, not a change to a file
|
|
162
|
+
</Text>
|
|
163
|
+
<Text color={palette.ash} wrap="wrap">
|
|
164
|
+
{" "}
|
|
165
|
+
{unenforceable.map((r) => r.id).join(` ${glyph.dotSep} `)}
|
|
166
|
+
</Text>
|
|
167
|
+
</Box>
|
|
168
|
+
) : null}
|
|
169
|
+
</Section>
|
|
170
|
+
) : null}
|
|
171
|
+
|
|
172
|
+
<Section
|
|
173
|
+
title="Checks"
|
|
174
|
+
aside={
|
|
175
|
+
checks.length === 0 ? "none recorded yet" : `${checks.length} in .abide/events.jsonl`
|
|
176
|
+
}
|
|
177
|
+
>
|
|
178
|
+
{checks.length === 0 ? (
|
|
179
|
+
<Text color={palette.ash}>
|
|
180
|
+
Every check the hooks run lands here: what fired, how long it took, what it cost.
|
|
181
|
+
</Text>
|
|
182
|
+
) : (
|
|
183
|
+
<Box flexDirection="column">
|
|
184
|
+
<Columns
|
|
185
|
+
indent={2}
|
|
186
|
+
columns={[
|
|
187
|
+
{ key: "phase", label: "", width: 6 },
|
|
188
|
+
{ key: "checks", label: "checks", width: 7, align: "right" },
|
|
189
|
+
{ key: "blocked", label: "blocked", width: 8, align: "right" },
|
|
190
|
+
{ key: "latency", label: "median", width: 8, align: "right" },
|
|
191
|
+
{ key: "spend", label: "spend", width: 10, align: "right" },
|
|
192
|
+
{ key: "pad", label: "" },
|
|
193
|
+
]}
|
|
194
|
+
rows={[edit, turn].map((list, i) => ({
|
|
195
|
+
phase: {
|
|
196
|
+
text: i === 0 ? "edit" : "turn",
|
|
197
|
+
color: i === 0 ? palette.mist : palette.plum,
|
|
198
|
+
},
|
|
199
|
+
checks: { text: String(list.length), color: palette.cloud },
|
|
200
|
+
blocked: {
|
|
201
|
+
text: String(list.filter((c) => c.blocked).length),
|
|
202
|
+
color: list.some((c) => c.blocked) ? palette.rose : palette.mist,
|
|
203
|
+
},
|
|
204
|
+
latency: {
|
|
205
|
+
text: list.length ? ms(median(list.map((c) => c.latencyMs)) ?? 0) : "",
|
|
206
|
+
color: palette.cloud,
|
|
207
|
+
},
|
|
208
|
+
spend: { text: usd(spend(list)), color: palette.ceramic },
|
|
209
|
+
pad: { text: "" },
|
|
210
|
+
}))}
|
|
211
|
+
/>
|
|
212
|
+
{errors > 0 ? (
|
|
213
|
+
<Box marginTop={1}>
|
|
214
|
+
<Text color={palette.amber}>
|
|
215
|
+
{errors} {errors === 1 ? "check" : "checks"} failed or timed out. Those edits went
|
|
216
|
+
unchecked.
|
|
217
|
+
</Text>
|
|
218
|
+
</Box>
|
|
219
|
+
) : null}
|
|
220
|
+
</Box>
|
|
221
|
+
)}
|
|
222
|
+
</Section>
|
|
223
|
+
</Box>
|
|
224
|
+
);
|
|
225
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Box, Text } from "ink";
|
|
2
|
+
import type { Rule } from "@coldtea/abide-schema";
|
|
3
|
+
import { Buckets } from "../components/Buckets.js";
|
|
4
|
+
import { Callout } from "../components/Callout.js";
|
|
5
|
+
import { Header } from "../components/Header.js";
|
|
6
|
+
import { RuleTable } from "../components/RuleTable.js";
|
|
7
|
+
import { Section } from "../components/Section.js";
|
|
8
|
+
import { glyph, palette } from "../theme.js";
|
|
9
|
+
|
|
10
|
+
export type RubricData =
|
|
11
|
+
| { kind: "invalid"; root: string; file: string; issues: string[] }
|
|
12
|
+
| {
|
|
13
|
+
kind: "ok";
|
|
14
|
+
root: string;
|
|
15
|
+
file: string;
|
|
16
|
+
rules: Rule[];
|
|
17
|
+
missing: string[];
|
|
18
|
+
orphaned: string[];
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function RubricView({ data }: { data: RubricData }) {
|
|
22
|
+
if (data.kind === "invalid") {
|
|
23
|
+
return (
|
|
24
|
+
<Box flexDirection="column">
|
|
25
|
+
<Header command="rubric validate" where={data.root} />
|
|
26
|
+
<Callout tone="bad" title={`${data.file} is not a valid rubric`}>
|
|
27
|
+
{data.issues.map((issue) => (
|
|
28
|
+
<Text key={issue} color={palette.cloud}>
|
|
29
|
+
{glyph.dot} {issue}
|
|
30
|
+
</Text>
|
|
31
|
+
))}
|
|
32
|
+
</Callout>
|
|
33
|
+
</Box>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
const stuck = data.rules.filter((r) => r.status === "weak" || r.status === "noisy");
|
|
37
|
+
return (
|
|
38
|
+
<Box flexDirection="column">
|
|
39
|
+
<Header command="rubric validate" where={data.root} />
|
|
40
|
+
<Text color={palette.sage}>
|
|
41
|
+
{glyph.check} wrote {data.file} with source hashes
|
|
42
|
+
</Text>
|
|
43
|
+
{data.missing.length > 0 ? (
|
|
44
|
+
<Callout tone="bad" title="Sources that could not be hashed. Fix the path.">
|
|
45
|
+
<Text color={palette.cloud}>{data.missing.join(", ")}</Text>
|
|
46
|
+
</Callout>
|
|
47
|
+
) : null}
|
|
48
|
+
{data.orphaned.length > 0 ? (
|
|
49
|
+
<Callout
|
|
50
|
+
tone="bad"
|
|
51
|
+
title='Rules whose source is not listed under "sources". Add it or fix the path.'
|
|
52
|
+
>
|
|
53
|
+
<Text color={palette.cloud}>{data.orphaned.join(", ")}</Text>
|
|
54
|
+
</Callout>
|
|
55
|
+
) : null}
|
|
56
|
+
<Box marginTop={1}>
|
|
57
|
+
<Section title="Rules">
|
|
58
|
+
<Buckets rules={data.rules} />
|
|
59
|
+
<Box marginTop={1}>
|
|
60
|
+
<RuleTable rules={data.rules} />
|
|
61
|
+
</Box>
|
|
62
|
+
</Section>
|
|
63
|
+
</Box>
|
|
64
|
+
{stuck.length > 0 ? (
|
|
65
|
+
<Callout
|
|
66
|
+
tone="warn"
|
|
67
|
+
title={`${stuck.length} ${stuck.length === 1 ? "rule" : "rules"} will not run until rewritten`}
|
|
68
|
+
>
|
|
69
|
+
<Text color={palette.cloud}>
|
|
70
|
+
{stuck
|
|
71
|
+
.map(
|
|
72
|
+
(r) =>
|
|
73
|
+
`${r.id} (${r.status}${r.calibration ? `, median ${r.calibration.median.toFixed(2)}` : ""})`,
|
|
74
|
+
)
|
|
75
|
+
.join(", ")}
|
|
76
|
+
</Text>
|
|
77
|
+
<Text color={palette.mist}>Run abide tune.</Text>
|
|
78
|
+
</Callout>
|
|
79
|
+
) : null}
|
|
80
|
+
</Box>
|
|
81
|
+
);
|
|
82
|
+
}
|