tribunal-kit 6.0.0 → 7.0.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/.agent/.manifest.json +78 -0
- package/.agent/ARCHITECTURE.md +19 -241
- package/.agent/agents/accessibility-reviewer.md +23 -16
- package/.agent/agents/ai-code-reviewer.md +25 -21
- package/.agent/agents/anti-pattern-reviewer.md +12 -7
- package/.agent/agents/api-architect.md +1 -0
- package/.agent/agents/backend-specialist.md +23 -22
- package/.agent/agents/cloud-engineer.md +1 -0
- package/.agent/agents/code-archaeologist.md +1 -0
- package/.agent/agents/complexity-reviewer.md +10 -7
- package/.agent/agents/database-architect.md +3 -2
- package/.agent/agents/db-latency-auditor.md +6 -2
- package/.agent/agents/debugger.md +3 -2
- package/.agent/agents/dependency-reviewer.md +1 -0
- package/.agent/agents/devops-engineer.md +9 -8
- package/.agent/agents/documentation-writer.md +6 -5
- package/.agent/agents/explorer-agent.md +1 -0
- package/.agent/agents/frontend-reviewer.md +21 -17
- package/.agent/agents/frontend-specialist.md +23 -20
- package/.agent/agents/game-developer.md +1 -0
- package/.agent/agents/interaction-reviewer.md +10 -5
- package/.agent/agents/logic-reviewer.md +3 -0
- package/.agent/agents/minimalist-reviewer.md +6 -0
- package/.agent/agents/mobile-developer.md +7 -6
- package/.agent/agents/mobile-reviewer.md +14 -13
- package/.agent/agents/orchestrator.md +3 -0
- package/.agent/agents/penetration-tester.md +1 -0
- package/.agent/agents/performance-optimizer.md +6 -5
- package/.agent/agents/performance-reviewer.md +6 -6
- package/.agent/agents/pipeline-reviewer.md +144 -0
- package/.agent/agents/precedence-reviewer.md +1 -0
- package/.agent/agents/product-manager.md +1 -0
- package/.agent/agents/product-owner.md +1 -0
- package/.agent/agents/product-reviewer.md +12 -7
- package/.agent/agents/project-planner.md +3 -0
- package/.agent/agents/qa-automation-engineer.md +50 -43
- package/.agent/agents/resilience-reviewer.md +1 -0
- package/.agent/agents/schema-reviewer.md +1 -0
- package/.agent/agents/security-auditor.md +24 -24
- package/.agent/agents/seo-specialist.md +33 -23
- package/.agent/agents/sql-reviewer.md +5 -4
- package/.agent/agents/supervisor-agent.md +12 -2
- package/.agent/agents/swarm-worker-contracts.md +13 -13
- package/.agent/agents/swarm-worker-registry.md +51 -51
- package/.agent/agents/system-architect.md +1 -0
- package/.agent/agents/test-coverage-reviewer.md +25 -22
- package/.agent/agents/test-engineer.md +12 -11
- package/.agent/agents/throughput-optimizer.md +45 -40
- package/.agent/agents/type-safety-reviewer.md +5 -4
- package/.agent/agents/ui-ux-auditor.md +6 -4
- package/.agent/agents/ui-visual-auditor.md +3 -0
- package/.agent/agents/ux-reviewer.md +11 -6
- package/.agent/agents/visual-reviewer.md +11 -6
- package/.agent/agents/vitals-reviewer.md +5 -4
- package/.agent/history/memory/.memory.idx +3716 -1
- package/.agent/history/memory/MEMORY.md +246 -1
- package/.agent/mcp_config.json +1 -6
- package/.agent/routing_index.json +2034 -263
- package/.agent/rules/GEMINI.md +12 -10
- package/.agent/rules/GEMINI_PLANNER.md +19 -21
- package/.agent/scripts/_colors.js +52 -54
- package/.agent/scripts/_utils.js +52 -53
- package/.agent/scripts/auto_preview.js +39 -43
- package/.agent/scripts/bundle_analyzer.js +47 -60
- package/.agent/scripts/case_law_manager.js +200 -294
- package/.agent/scripts/checklist.js +73 -94
- package/.agent/scripts/cicd_validator.js +305 -0
- package/.agent/scripts/compile_router.py +4 -111
- package/.agent/scripts/context_broker.js +317 -412
- package/.agent/scripts/contract_engine.js +402 -0
- package/.agent/scripts/dependency_analyzer.js +112 -124
- package/.agent/scripts/graph_builder.js +75 -103
- package/.agent/scripts/graph_visualizer.js +10 -12
- package/.agent/scripts/graph_zoom.js +38 -55
- package/.agent/scripts/guardrail_engine.js +195 -145
- package/.agent/scripts/impact_classifier.js +55 -30
- package/.agent/scripts/inner_loop_validator.js +108 -159
- package/.agent/scripts/integrity_manifest.js +59 -67
- package/.agent/scripts/lint_runner.js +65 -75
- package/.agent/scripts/marathon_harness.js +131 -208
- package/.agent/scripts/migrate_skills_frontmatter.py +4 -63
- package/.agent/scripts/minify_context.js +29 -33
- package/.agent/scripts/minimal_change_engine.js +95 -87
- package/.agent/scripts/mutation_runner.js +46 -62
- package/.agent/scripts/pipeline_engine.js +320 -209
- package/.agent/scripts/prompt_compiler.js +118 -93
- package/.agent/scripts/schema_validator.js +73 -121
- package/.agent/scripts/security_scan.js +88 -117
- package/.agent/scripts/session_manager.js +52 -69
- package/.agent/scripts/signal_detector.js +24 -22
- package/.agent/scripts/skill_evolution.js +184 -245
- package/.agent/scripts/skill_integrator.js +35 -48
- package/.agent/scripts/socratic_gate_policy.js +15 -12
- package/.agent/scripts/strengthen_skills.js +29 -33
- package/.agent/scripts/swarm_dispatcher.js +120 -151
- package/.agent/scripts/test_runner.js +54 -56
- package/.agent/scripts/token_budget_broker.js +9 -7
- package/.agent/scripts/trace_engine.js +126 -0
- package/.agent/scripts/verify_all.js +76 -93
- package/.agent/scripts/visual_audit.js +16 -21
- package/.agent/skill_topic_map.json +237 -0
- package/.agent/skills/12-principles-of-animation/SKILL.md +7 -0
- package/.agent/skills/60fps-animation/SKILL.md +7 -0
- package/.agent/skills/accessible-animation/SKILL.md +5 -1
- package/.agent/skills/adapt/SKILL.md +7 -1
- package/.agent/skills/advanced-rag-pipelines/SKILL.md +1 -0
- package/.agent/skills/agent-organizer/SKILL.md +1 -0
- package/.agent/skills/agentic-patterns/SKILL.md +33 -25
- package/.agent/skills/agentic-workflows-2026/SKILL.md +80 -0
- package/.agent/skills/ai-app-hardening/SKILL.md +59 -0
- package/.agent/skills/ai-prompt-injection-defense/SKILL.md +7 -6
- package/.agent/skills/animation-on-scroll/SKILL.md +24 -15
- package/.agent/skills/animation-systems/SKILL.md +11 -10
- package/.agent/skills/antfu-conventions/SKILL.md +7 -2
- package/.agent/skills/api-patterns/SKILL.md +14 -10
- package/.agent/skills/api-security-auditor/SKILL.md +15 -14
- package/.agent/skills/app-builder/SKILL.md +1 -0
- package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +2 -2
- package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +12 -12
- package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +3 -3
- package/.agent/skills/apple-design/SKILL.md +8 -7
- package/.agent/skills/architecture/SKILL.md +3 -2
- package/.agent/skills/audit-and-fix/SKILL.md +8 -5
- package/.agent/skills/authentication-best-practices/SKILL.md +9 -8
- package/.agent/skills/backend-security-expert/SKILL.md +1 -0
- package/.agent/skills/baseline-ui/SKILL.md +8 -7
- package/.agent/skills/bash-linux/SKILL.md +1 -0
- package/.agent/skills/behavioral-modes/SKILL.md +1 -0
- package/.agent/skills/better-colors/SKILL.md +11 -7
- package/.agent/skills/better-typography/SKILL.md +30 -16
- package/.agent/skills/better-ui/SKILL.md +17 -10
- package/.agent/skills/bolder/SKILL.md +6 -0
- package/.agent/skills/brainstorming/SKILL.md +6 -4
- package/.agent/skills/browser-native-ai/SKILL.md +5 -4
- package/.agent/skills/build-primitive/SKILL.md +5 -0
- package/.agent/skills/building-native-ui/SKILL.md +1 -0
- package/.agent/skills/cicd-pro/SKILL.md +2 -1
- package/.agent/skills/clarify/SKILL.md +19 -14
- package/.agent/skills/clean-code/SKILL.md +16 -15
- package/.agent/skills/cloud-architect/SKILL.md +1 -0
- package/.agent/skills/cobejs/SKILL.md +7 -9
- package/.agent/skills/code-review-checklist/SKILL.md +1 -0
- package/.agent/skills/codebase-design/SKILL.md +7 -2
- package/.agent/skills/colorize/SKILL.md +8 -3
- package/.agent/skills/compact-landing/SKILL.md +8 -3
- package/.agent/skills/company-logos/SKILL.md +6 -0
- package/.agent/skills/config-validator/SKILL.md +10 -9
- package/.agent/skills/containerization-pro/SKILL.md +6 -5
- package/.agent/skills/context-engineering-pro/SKILL.md +70 -0
- package/.agent/skills/create-design-md/SKILL.md +13 -5
- package/.agent/skills/critique/SKILL.md +10 -8
- package/.agent/skills/csharp-developer/SKILL.md +1 -0
- package/.agent/skills/data-validation-schemas/SKILL.md +28 -23
- package/.agent/skills/database-design/SKILL.md +2 -1
- package/.agent/skills/delight/SKILL.md +6 -0
- package/.agent/skills/deployment-procedures/SKILL.md +2 -1
- package/.agent/skills/design-lab/SKILL.md +5 -0
- package/.agent/skills/devops-engineer/SKILL.md +3 -2
- package/.agent/skills/devops-incident-responder/SKILL.md +2 -1
- package/.agent/skills/diagnosing-bugs/SKILL.md +12 -2
- package/.agent/skills/distill/SKILL.md +7 -2
- package/.agent/skills/documentation-templates/SKILL.md +3 -2
- package/.agent/skills/domain-modeling/SKILL.md +12 -4
- package/.agent/skills/duckdb-analytical-sql/SKILL.md +59 -0
- package/.agent/skills/edge-ai-mobile/SKILL.md +50 -0
- package/.agent/skills/edge-computing/SKILL.md +9 -8
- package/.agent/skills/emil-design-eng/SKILL.md +43 -27
- package/.agent/skills/error-resilience/SKILL.md +34 -16
- package/.agent/skills/expo-router-v4/SKILL.md +81 -0
- package/.agent/skills/extract-design-system/SKILL.md +12 -11
- package/.agent/skills/fabel-protocol/SKILL.md +24 -17
- package/.agent/skills/fixing-accessibility/SKILL.md +23 -16
- package/.agent/skills/fixing-metadata/SKILL.md +24 -13
- package/.agent/skills/fixing-motion-performance/SKILL.md +7 -5
- package/.agent/skills/framer-motion-expert/SKILL.md +29 -15
- package/.agent/skills/frontend-design/SKILL.md +21 -13
- package/.agent/skills/frontend-security-expert/SKILL.md +1 -0
- package/.agent/skills/game-design-expert/SKILL.md +1 -0
- package/.agent/skills/game-engineering-expert/SKILL.md +1 -0
- package/.agent/skills/generative-ui-expert/SKILL.md +3 -2
- package/.agent/skills/geo-fundamentals/SKILL.md +2 -1
- package/.agent/skills/git-pro/SKILL.md +10 -9
- package/.agent/skills/github-operations/SKILL.md +2 -1
- package/.agent/skills/gpt-taste/SKILL.md +7 -0
- package/.agent/skills/gsap-core/SKILL.md +26 -22
- package/.agent/skills/gsap-frameworks/SKILL.md +6 -5
- package/.agent/skills/gsap-performance/SKILL.md +4 -3
- package/.agent/skills/gsap-plugins/SKILL.md +34 -33
- package/.agent/skills/gsap-react/SKILL.md +9 -8
- package/.agent/skills/gsap-scrolltrigger/SKILL.md +34 -33
- package/.agent/skills/gsap-timeline/SKILL.md +18 -15
- package/.agent/skills/gsap-utils/SKILL.md +25 -24
- package/.agent/skills/harden/SKILL.md +9 -3
- package/.agent/skills/harness-protocol/SKILL.md +1 -0
- package/.agent/skills/i18n-localization/SKILL.md +10 -9
- package/.agent/skills/impeccable/SKILL.md +5 -0
- package/.agent/skills/improve-codebase-architecture/SKILL.md +10 -5
- package/.agent/skills/improve-ui/SKILL.md +12 -5
- package/.agent/skills/intelligent-routing/SKILL.md +7 -6
- package/.agent/skills/knowledge-graph/SKILL.md +1 -0
- package/.agent/skills/landing-page/SKILL.md +1 -0
- package/.agent/skills/lint-and-validate/SKILL.md +9 -4
- package/.agent/skills/llm-engineering/SKILL.md +55 -54
- package/.agent/skills/local-first/SKILL.md +20 -19
- package/.agent/skills/local-first-architecture/SKILL.md +6 -1
- package/.agent/skills/lottie-animation/SKILL.md +7 -3
- package/.agent/skills/marquee-loop/SKILL.md +1 -0
- package/.agent/skills/masked-reveal/SKILL.md +17 -4
- package/.agent/skills/mcp-builder/SKILL.md +13 -10
- package/.agent/skills/micro-interaction/SKILL.md +20 -5
- package/.agent/skills/mobile-design/SKILL.md +26 -15
- package/.agent/skills/monorepo-management/SKILL.md +3 -2
- package/.agent/skills/morphing-icons/SKILL.md +16 -6
- package/.agent/skills/motion-engineering/SKILL.md +19 -15
- package/.agent/skills/nextjs-react-expert/SKILL.md +26 -21
- package/.agent/skills/nodejs-best-practices/SKILL.md +89 -76
- package/.agent/skills/observability/SKILL.md +45 -44
- package/.agent/skills/opentelemetry-observability/SKILL.md +59 -0
- package/.agent/skills/page-transition-animation/SKILL.md +6 -3
- package/.agent/skills/parallel-agents/SKILL.md +7 -2
- package/.agent/skills/performance-profiling/SKILL.md +24 -13
- package/.agent/skills/plan-writing/SKILL.md +1 -0
- package/.agent/skills/platform-engineer/SKILL.md +3 -2
- package/.agent/skills/platform-engineering-opentofu/SKILL.md +68 -0
- package/.agent/skills/playwright-ai-e2e/SKILL.md +60 -0
- package/.agent/skills/playwright-best-practices/SKILL.md +29 -28
- package/.agent/skills/polish/SKILL.md +13 -12
- package/.agent/skills/powershell-windows/SKILL.md +1 -0
- package/.agent/skills/pricing-page/SKILL.md +8 -3
- package/.agent/skills/product-aware-heuristics/SKILL.md +122 -72
- package/.agent/skills/progressive-blur/SKILL.md +9 -4
- package/.agent/skills/project-idioms/SKILL.md +1 -0
- package/.agent/skills/property-based-testing/SKILL.md +58 -0
- package/.agent/skills/python-patterns/SKILL.md +1 -0
- package/.agent/skills/python-pro/SKILL.md +1 -0
- package/.agent/skills/quieter/SKILL.md +6 -0
- package/.agent/skills/react-doctor/SKILL.md +6 -0
- package/.agent/skills/react-specialist/SKILL.md +32 -28
- package/.agent/skills/readme-builder/SKILL.md +1 -0
- package/.agent/skills/realtime-patterns/SKILL.md +34 -33
- package/.agent/skills/red-team-tactics/SKILL.md +1 -0
- package/.agent/skills/redesign-skill/SKILL.md +6 -0
- package/.agent/skills/review-animations/SKILL.md +5 -2
- package/.agent/skills/review-animations/STANDARDS.md +37 -23
- package/.agent/skills/rust-pro/SKILL.md +1 -0
- package/.agent/skills/seo-fundamentals/SKILL.md +4 -3
- package/.agent/skills/server-management/SKILL.md +1 -0
- package/.agent/skills/shadcn-ui-expert/SKILL.md +28 -22
- package/.agent/skills/shape/SKILL.md +9 -2
- package/.agent/skills/skill-creator/SKILL.md +1 -0
- package/.agent/skills/soft-skill/SKILL.md +6 -0
- package/.agent/skills/sounds-on-the-web/SKILL.md +1 -0
- package/.agent/skills/sql-pro/SKILL.md +1 -0
- package/.agent/skills/supabase-postgres-best-practices/SKILL.md +1 -0
- package/.agent/skills/svg-animation/SKILL.md +16 -7
- package/.agent/skills/swiftui-expert/SKILL.md +1 -0
- package/.agent/skills/swiss-design/SKILL.md +5 -0
- package/.agent/skills/system-design-pro/SKILL.md +1 -0
- package/.agent/skills/systematic-debugging/SKILL.md +1 -0
- package/.agent/skills/tailwind-patterns/SKILL.md +25 -11
- package/.agent/skills/taste-skill/SKILL.md +6 -0
- package/.agent/skills/tdd-workflow/SKILL.md +5 -0
- package/.agent/skills/test-result-analyzer/SKILL.md +1 -0
- package/.agent/skills/testing-patterns/SKILL.md +123 -115
- package/.agent/skills/thermo-nuclear-code-quality-review/SKILL.md +5 -0
- package/.agent/skills/thinking-protocol/SKILL.md +3 -0
- package/.agent/skills/to-spring-or-not-to-spring/SKILL.md +9 -8
- package/.agent/skills/transitions-dev/SKILL.md +19 -6
- package/.agent/skills/trend-researcher/SKILL.md +1 -0
- package/.agent/skills/typescript-advanced/SKILL.md +23 -18
- package/.agent/skills/typeset/SKILL.md +10 -1
- package/.agent/skills/ui-reasoning-engine/SKILL.md +67 -48
- package/.agent/skills/ui-skill-packs/SKILL.md +6 -1
- package/.agent/skills/ui-skills-root/SKILL.md +39 -38
- package/.agent/skills/ui-ux-pro-max/SKILL.md +34 -26
- package/.agent/skills/ui-ux-researcher/SKILL.md +1 -0
- package/.agent/skills/vector-search-pgvector/SKILL.md +78 -0
- package/.agent/skills/vue-expert/SKILL.md +22 -19
- package/.agent/skills/vulnerability-scanner/SKILL.md +22 -21
- package/.agent/skills/web-accessibility-auditor/SKILL.md +2 -1
- package/.agent/skills/web-design-guidelines/SKILL.md +16 -10
- package/.agent/skills/web-quality-audit/SKILL.md +5 -0
- package/.agent/skills/webapp-testing/SKILL.md +14 -13
- package/.agent/skills/webgpu-performance/SKILL.md +1 -0
- package/.agent/skills/whimsy-injector/SKILL.md +9 -5
- package/.agent/skills/workflow-optimizer/SKILL.md +1 -0
- package/.agent/skills/zero-trust-passkeys/SKILL.md +90 -0
- package/.agent/templates/DESIGN.md +119 -110
- package/.agent/templates/design-tokens.json +9 -6
- package/.agent/workflows/acf.md +1 -0
- package/.agent/workflows/api-tester.md +1 -0
- package/.agent/workflows/audit.md +1 -0
- package/.agent/workflows/brainstorm.md +1 -0
- package/.agent/workflows/changelog.md +1 -0
- package/.agent/workflows/contract.md +60 -0
- package/.agent/workflows/create.md +1 -0
- package/.agent/workflows/debug.md +1 -0
- package/.agent/workflows/deploy.md +1 -0
- package/.agent/workflows/enhance.md +1 -0
- package/.agent/workflows/fix-ci.md +89 -0
- package/.agent/workflows/fix.md +1 -0
- package/.agent/workflows/generate.md +1 -0
- package/.agent/workflows/marathon.md +1 -0
- package/.agent/workflows/migrate.md +1 -0
- package/.agent/workflows/minimal.md +9 -7
- package/.agent/workflows/orchestrate.md +1 -0
- package/.agent/workflows/performance-benchmarker.md +1 -0
- package/.agent/workflows/pipeline.md +26 -21
- package/.agent/workflows/plan.md +1 -0
- package/.agent/workflows/preview.md +1 -0
- package/.agent/workflows/refactor.md +3 -2
- package/.agent/workflows/review-ai.md +4 -3
- package/.agent/workflows/review.md +1 -0
- package/.agent/workflows/session.md +1 -0
- package/.agent/workflows/status.md +1 -0
- package/.agent/workflows/strengthen-skills.md +1 -0
- package/.agent/workflows/super-prompt.md +1 -0
- package/.agent/workflows/swarm.md +15 -3
- package/.agent/workflows/test.md +8 -7
- package/.agent/workflows/tribunal-backend.md +3 -2
- package/.agent/workflows/tribunal-cicd.md +104 -0
- package/.agent/workflows/tribunal-database.md +1 -0
- package/.agent/workflows/tribunal-frontend.md +8 -6
- package/.agent/workflows/tribunal-full.md +15 -14
- package/.agent/workflows/tribunal-mobile.md +6 -5
- package/.agent/workflows/tribunal-performance.md +1 -0
- package/.agent/workflows/tribunal-speed.md +1 -0
- package/.agent/workflows/tribunal-ui.md +29 -21
- package/.agent/workflows/ui-ux-pro-max.md +19 -15
- package/CONTRIBUTING.md +3 -3
- package/README.md +160 -88
- package/SECURITY.md +11 -11
- package/bin/mcp-server.js +512 -233
- package/bin/tribunal-kit.js +29 -30
- package/bin/wrapper.js +45 -48
- package/dist/cli.js +26 -0
- package/dist/commands/case.js +2 -0
- package/dist/commands/contract.js +440 -0
- package/dist/commands/memory.js +3 -2
- package/dist/commands/native.js +36 -0
- package/dist/commands/status.js +6 -0
- package/dist/esm/index.mjs +32 -0
- package/dist/index.d.ts +58 -3
- package/package.json +20 -16
- package/scripts/benchmark.js +62 -86
- package/scripts/changelog.js +67 -73
- package/scripts/fix-vbc.js +13 -15
- package/scripts/fix-vbc.ps1 +2 -118
- package/scripts/stress_benchmark.js +358 -0
- package/scripts/sync-version.js +71 -41
- package/scripts/validate-payload.js +23 -30
- package/scripts/visual_audit.js +23 -13
|
@@ -18,28 +18,27 @@
|
|
|
18
18
|
* .agent/history/case-law/cases/ ← one JSON file per case
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
'use strict';
|
|
22
22
|
|
|
23
|
-
const fs = require(
|
|
24
|
-
const path = require(
|
|
25
|
-
const crypto = require(
|
|
26
|
-
const readline = require(
|
|
23
|
+
const fs = require('fs');
|
|
24
|
+
const path = require('path');
|
|
25
|
+
const crypto = require('crypto');
|
|
26
|
+
const readline = require('readline');
|
|
27
27
|
|
|
28
28
|
// ── Colours ──────────────────────────────────────────────────────────────────
|
|
29
|
-
const { GREEN, YELLOW, CYAN, RED, BOLD, DIM, RESET } = require(
|
|
29
|
+
const { GREEN, YELLOW, CYAN, RED, BOLD, DIM, RESET } = require('./_colors');
|
|
30
30
|
|
|
31
31
|
// ── Shared Utilities ──────────────────────────────────────────────────────────
|
|
32
|
-
const { findAgentDir } = require(
|
|
33
|
-
|
|
32
|
+
const { findAgentDir } = require('./_utils');
|
|
34
33
|
|
|
35
34
|
// ── Lazy path resolution (avoids side effects at require-time) ───────────────
|
|
36
35
|
let _paths = null;
|
|
37
36
|
function getPaths() {
|
|
38
37
|
if (_paths) return _paths;
|
|
39
38
|
const agentDir = findAgentDir();
|
|
40
|
-
const historyDir = path.join(agentDir,
|
|
41
|
-
const casesDir = path.join(historyDir,
|
|
42
|
-
const indexFile = path.join(historyDir,
|
|
39
|
+
const historyDir = path.join(agentDir, 'history', 'case-law');
|
|
40
|
+
const casesDir = path.join(historyDir, 'cases');
|
|
41
|
+
const indexFile = path.join(historyDir, 'index.json');
|
|
43
42
|
_paths = {
|
|
44
43
|
AGENT_DIR: agentDir,
|
|
45
44
|
HISTORY_DIR: historyDir,
|
|
@@ -50,23 +49,23 @@ function getPaths() {
|
|
|
50
49
|
}
|
|
51
50
|
|
|
52
51
|
const VALID_DOMAINS = new Set([
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
52
|
+
'backend',
|
|
53
|
+
'frontend',
|
|
54
|
+
'database',
|
|
55
|
+
'security',
|
|
56
|
+
'performance',
|
|
57
|
+
'mobile',
|
|
58
|
+
'testing',
|
|
59
|
+
'devops',
|
|
60
|
+
'minimal_change',
|
|
61
|
+
'general',
|
|
63
62
|
]);
|
|
64
63
|
const VALID_VERDICTS = new Set([
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
'REJECTED',
|
|
65
|
+
'APPROVED_WITH_CONDITIONS',
|
|
66
|
+
'PRECEDENT_SET',
|
|
67
|
+
'OVERRULED',
|
|
68
|
+
'MINIMALITY_REJECTED',
|
|
70
69
|
]);
|
|
71
70
|
|
|
72
71
|
// ── Noise filter ────────────────────────────────────────────────────────────
|
|
@@ -84,7 +83,7 @@ const NOISE_PATTERNS = [
|
|
|
84
83
|
|
|
85
84
|
function isNoiseRejection(reason) {
|
|
86
85
|
const lower = reason.toLowerCase();
|
|
87
|
-
return NOISE_PATTERNS.some(
|
|
86
|
+
return NOISE_PATTERNS.some(p => p.test(lower));
|
|
88
87
|
}
|
|
89
88
|
|
|
90
89
|
// ── Trivial-change filter (Semantic Delta) ────────────────────────────────────
|
|
@@ -97,36 +96,32 @@ const TRIVIAL_PATTERNS = [
|
|
|
97
96
|
];
|
|
98
97
|
|
|
99
98
|
function isTrivialLine(line) {
|
|
100
|
-
return TRIVIAL_PATTERNS.some(
|
|
99
|
+
return TRIVIAL_PATTERNS.some(p => p.test(line));
|
|
101
100
|
}
|
|
102
101
|
|
|
103
102
|
function semanticDelta(diffText) {
|
|
104
|
-
const lines = diffText.split(
|
|
103
|
+
const lines = diffText.split('\n');
|
|
105
104
|
const meaningful = [];
|
|
106
105
|
for (const line of lines) {
|
|
107
|
-
if (
|
|
108
|
-
line.startsWith("+++") ||
|
|
109
|
-
line.startsWith("---") ||
|
|
110
|
-
line.startsWith("@@")
|
|
111
|
-
) {
|
|
106
|
+
if (line.startsWith('+++') || line.startsWith('---') || line.startsWith('@@')) {
|
|
112
107
|
meaningful.push(line);
|
|
113
108
|
continue;
|
|
114
109
|
}
|
|
115
|
-
if (line.startsWith(
|
|
110
|
+
if (line.startsWith('+') || line.startsWith('-')) {
|
|
116
111
|
const codePart = line.slice(1);
|
|
117
112
|
if (!isTrivialLine(codePart)) meaningful.push(line);
|
|
118
113
|
} else {
|
|
119
114
|
meaningful.push(line);
|
|
120
115
|
}
|
|
121
116
|
}
|
|
122
|
-
let filtered = meaningful.join(
|
|
123
|
-
filtered = filtered.replace(/(\n[ ]?\n){3,}/g,
|
|
117
|
+
let filtered = meaningful.join('\n');
|
|
118
|
+
filtered = filtered.replace(/(\n[ ]?\n){3,}/g, '\n\n');
|
|
124
119
|
return filtered.trim();
|
|
125
120
|
}
|
|
126
121
|
|
|
127
122
|
function contentHash(text) {
|
|
128
123
|
const cleaned = semanticDelta(text);
|
|
129
|
-
return crypto.createHash(
|
|
124
|
+
return crypto.createHash('sha256').update(cleaned).digest('hex').slice(0, 8);
|
|
130
125
|
}
|
|
131
126
|
|
|
132
127
|
// ── Index helpers ─────────────────────────────────────────────────────────────
|
|
@@ -141,31 +136,28 @@ function loadIndex() {
|
|
|
141
136
|
const { INDEX_FILE } = getPaths();
|
|
142
137
|
if (fs.existsSync(INDEX_FILE)) {
|
|
143
138
|
try {
|
|
144
|
-
return JSON.parse(fs.readFileSync(INDEX_FILE,
|
|
139
|
+
return JSON.parse(fs.readFileSync(INDEX_FILE, 'utf8'));
|
|
145
140
|
} catch {
|
|
146
141
|
/* fallthrough */
|
|
147
142
|
}
|
|
148
143
|
}
|
|
149
|
-
return { version:
|
|
144
|
+
return { version: '1.0', cases: [], next_id: 1 };
|
|
150
145
|
}
|
|
151
146
|
|
|
152
147
|
function saveIndex(index) {
|
|
153
148
|
ensureDirs();
|
|
154
149
|
const { INDEX_FILE } = getPaths();
|
|
155
|
-
const tmp = INDEX_FILE +
|
|
156
|
-
fs.writeFileSync(tmp, JSON.stringify(index, null, 2),
|
|
150
|
+
const tmp = INDEX_FILE + '.tmp';
|
|
151
|
+
fs.writeFileSync(tmp, JSON.stringify(index, null, 2), 'utf8');
|
|
157
152
|
fs.renameSync(tmp, INDEX_FILE);
|
|
158
153
|
}
|
|
159
154
|
|
|
160
155
|
function loadCase(caseId) {
|
|
161
156
|
const { CASES_DIR } = getPaths();
|
|
162
|
-
const p = path.join(
|
|
163
|
-
CASES_DIR,
|
|
164
|
-
`case-${String(caseId).padStart(4, "0")}.json`,
|
|
165
|
-
);
|
|
157
|
+
const p = path.join(CASES_DIR, `case-${String(caseId).padStart(4, '0')}.json`);
|
|
166
158
|
if (!fs.existsSync(p)) return null;
|
|
167
159
|
try {
|
|
168
|
-
return JSON.parse(fs.readFileSync(p,
|
|
160
|
+
return JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
169
161
|
} catch {
|
|
170
162
|
return null;
|
|
171
163
|
}
|
|
@@ -173,61 +165,58 @@ function loadCase(caseId) {
|
|
|
173
165
|
|
|
174
166
|
function saveCase(caseRecord) {
|
|
175
167
|
const { CASES_DIR } = getPaths();
|
|
176
|
-
const p = path.join(
|
|
177
|
-
|
|
178
|
-
`case-${String(caseRecord.id).padStart(4, "0")}.json`,
|
|
179
|
-
);
|
|
180
|
-
fs.writeFileSync(p, JSON.stringify(caseRecord, null, 2), "utf8");
|
|
168
|
+
const p = path.join(CASES_DIR, `case-${String(caseRecord.id).padStart(4, '0')}.json`);
|
|
169
|
+
fs.writeFileSync(p, JSON.stringify(caseRecord, null, 2), 'utf8');
|
|
181
170
|
}
|
|
182
171
|
|
|
183
172
|
// ── Keyword/tag extraction ─────────────────────────────────────────────────────
|
|
184
173
|
function extractTags(text) {
|
|
185
174
|
const tokens = text.match(/\b[a-zA-Z_][a-zA-Z0-9_]{2,}\b/g) || [];
|
|
186
175
|
const stopWords = new Set([
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
176
|
+
'the',
|
|
177
|
+
'and',
|
|
178
|
+
'for',
|
|
179
|
+
'was',
|
|
180
|
+
'this',
|
|
181
|
+
'with',
|
|
182
|
+
'that',
|
|
183
|
+
'has',
|
|
184
|
+
'a',
|
|
185
|
+
'an',
|
|
186
|
+
'from',
|
|
187
|
+
'are',
|
|
188
|
+
'not',
|
|
189
|
+
'use',
|
|
190
|
+
'but',
|
|
191
|
+
'also',
|
|
192
|
+
'code',
|
|
193
|
+
'have',
|
|
194
|
+
'will',
|
|
195
|
+
'should',
|
|
196
|
+
'must',
|
|
197
|
+
'can',
|
|
198
|
+
'may',
|
|
199
|
+
'any',
|
|
200
|
+
'all',
|
|
201
|
+
'new',
|
|
202
|
+
'old',
|
|
203
|
+
'add',
|
|
204
|
+
'get',
|
|
205
|
+
'set',
|
|
206
|
+
'var',
|
|
207
|
+
'let',
|
|
208
|
+
'const',
|
|
209
|
+
'function',
|
|
210
|
+
'return',
|
|
211
|
+
'import',
|
|
212
|
+
'export',
|
|
213
|
+
'class',
|
|
214
|
+
'async',
|
|
215
|
+
'await',
|
|
216
|
+
'true',
|
|
217
|
+
'false',
|
|
218
|
+
'null',
|
|
219
|
+
'undefined',
|
|
231
220
|
]);
|
|
232
221
|
const seen = new Set();
|
|
233
222
|
const tags = [];
|
|
@@ -259,10 +248,8 @@ function extractTags(text) {
|
|
|
259
248
|
function parseVersionFilter(filterStr) {
|
|
260
249
|
const result = {};
|
|
261
250
|
if (!filterStr) return result;
|
|
262
|
-
for (const segment of filterStr.split(
|
|
263
|
-
const m = segment
|
|
264
|
-
.trim()
|
|
265
|
-
.match(/^([a-zA-Z0-9_.-]+)\s*(?:>=|>|<=|<|=)?\s*(\d+(?:\.\d+)?)/);
|
|
251
|
+
for (const segment of filterStr.split(',')) {
|
|
252
|
+
const m = segment.trim().match(/^([a-zA-Z0-9_.-]+)\s*(?:>=|>|<=|<|=)?\s*(\d+(?:\.\d+)?)/);
|
|
266
253
|
if (m) result[m[1].toLowerCase()] = parseFloat(m[2]);
|
|
267
254
|
}
|
|
268
255
|
return result;
|
|
@@ -288,10 +275,8 @@ function caseMatchesVersionFilter(caseEntry, versionFilter) {
|
|
|
288
275
|
|
|
289
276
|
// Parse the case's own version constraint
|
|
290
277
|
const caseConstraints = [];
|
|
291
|
-
for (const segment of caseEntry.stack_version.split(
|
|
292
|
-
const m = segment
|
|
293
|
-
.trim()
|
|
294
|
-
.match(/^([a-zA-Z0-9_.-]+)\s*(>=|>|<=|<|=)\s*(\d+(?:\.\d+)?)/);
|
|
278
|
+
for (const segment of caseEntry.stack_version.split(',')) {
|
|
279
|
+
const m = segment.trim().match(/^([a-zA-Z0-9_.-]+)\s*(>=|>|<=|<|=)\s*(\d+(?:\.\d+)?)/);
|
|
295
280
|
if (m)
|
|
296
281
|
caseConstraints.push({
|
|
297
282
|
lib: m[1].toLowerCase(),
|
|
@@ -306,13 +291,13 @@ function caseMatchesVersionFilter(caseEntry, versionFilter) {
|
|
|
306
291
|
if (projectVer === undefined) continue; // Unknown lib → don't filter on it
|
|
307
292
|
|
|
308
293
|
const satisfied =
|
|
309
|
-
op ===
|
|
294
|
+
op === '>='
|
|
310
295
|
? projectVer >= ver
|
|
311
|
-
: op ===
|
|
296
|
+
: op === '>'
|
|
312
297
|
? projectVer > ver
|
|
313
|
-
: op ===
|
|
298
|
+
: op === '<='
|
|
314
299
|
? projectVer <= ver
|
|
315
|
-
: op ===
|
|
300
|
+
: op === '<'
|
|
316
301
|
? projectVer < ver
|
|
317
302
|
: /* = */ projectVer === ver;
|
|
318
303
|
if (!satisfied) return false; // This case's version context doesn't match
|
|
@@ -369,104 +354,74 @@ function createRl() {
|
|
|
369
354
|
}
|
|
370
355
|
|
|
371
356
|
function ask(rl, prompt) {
|
|
372
|
-
return new Promise((resolve)
|
|
373
|
-
rl.question(` ${BOLD}${prompt}${RESET} `, resolve),
|
|
374
|
-
);
|
|
357
|
+
return new Promise(resolve => rl.question(` ${BOLD}${prompt}${RESET} `, resolve));
|
|
375
358
|
}
|
|
376
359
|
|
|
377
360
|
function askMultiline(rl, prompt, sentinel) {
|
|
378
|
-
return new Promise(
|
|
361
|
+
return new Promise(resolve => {
|
|
379
362
|
console.log(` ${BOLD}${prompt}${RESET}`);
|
|
380
363
|
console.log(
|
|
381
364
|
` ${DIM}(Type or paste content. Type '${sentinel}' on its own line when done.)${RESET}`,
|
|
382
365
|
);
|
|
383
366
|
const lines = [];
|
|
384
|
-
const listener =
|
|
367
|
+
const listener = line => {
|
|
385
368
|
if (line.trim() === sentinel) {
|
|
386
|
-
rl.removeListener(
|
|
387
|
-
resolve(lines.join(
|
|
369
|
+
rl.removeListener('line', listener);
|
|
370
|
+
resolve(lines.join('\n'));
|
|
388
371
|
} else {
|
|
389
372
|
lines.push(line);
|
|
390
373
|
}
|
|
391
374
|
};
|
|
392
|
-
rl.on(
|
|
375
|
+
rl.on('line', listener);
|
|
393
376
|
});
|
|
394
377
|
}
|
|
395
378
|
|
|
396
379
|
function askChoice(rl, label, choices, defaultVal) {
|
|
397
|
-
return new Promise(
|
|
398
|
-
const opts = choices
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
(answer) => {
|
|
404
|
-
const val = (answer || "").trim().toLowerCase();
|
|
405
|
-
resolve(val && choices.includes(val) ? val : defaultVal);
|
|
406
|
-
},
|
|
407
|
-
);
|
|
380
|
+
return new Promise(resolve => {
|
|
381
|
+
const opts = choices.map(c => (c === defaultVal ? `${BOLD}${c}${RESET}` : c)).join(' / ');
|
|
382
|
+
rl.question(` ${BOLD}${label}${RESET} [${opts}] (default: ${defaultVal}): `, answer => {
|
|
383
|
+
const val = (answer || '').trim().toLowerCase();
|
|
384
|
+
resolve(val && choices.includes(val) ? val : defaultVal);
|
|
385
|
+
});
|
|
408
386
|
});
|
|
409
387
|
}
|
|
410
388
|
|
|
411
389
|
// ── Commands ──────────────────────────────────────────────────────────────────
|
|
412
390
|
async function cmdAddCase() {
|
|
413
|
-
console.log(
|
|
414
|
-
`\n${BOLD}${CYAN}━━━ Recording New Case ━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`,
|
|
415
|
-
);
|
|
391
|
+
console.log(`\n${BOLD}${CYAN}━━━ Recording New Case ━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`);
|
|
416
392
|
const rl = createRl();
|
|
417
393
|
|
|
418
|
-
const diffText = await askMultiline(
|
|
419
|
-
rl,
|
|
420
|
-
"Paste the REJECTED diff (code snippet):",
|
|
421
|
-
"END_DIFF",
|
|
422
|
-
);
|
|
394
|
+
const diffText = await askMultiline(rl, 'Paste the REJECTED diff (code snippet):', 'END_DIFF');
|
|
423
395
|
if (!diffText.trim()) {
|
|
424
396
|
console.log(`${RED}✖ Diff cannot be empty. Aborting.${RESET}`);
|
|
425
397
|
rl.close();
|
|
426
398
|
process.exit(1);
|
|
427
399
|
}
|
|
428
400
|
|
|
429
|
-
const reason = await ask(rl,
|
|
401
|
+
const reason = await ask(rl, 'Rejection reason (1-2 sentences):');
|
|
430
402
|
if (!reason.trim()) {
|
|
431
403
|
console.log(`${RED}✖ Reason cannot be empty. Aborting.${RESET}`);
|
|
432
404
|
rl.close();
|
|
433
405
|
process.exit(1);
|
|
434
406
|
}
|
|
435
407
|
|
|
436
|
-
const domain = await askChoice(
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
[...VALID_DOMAINS].sort(),
|
|
440
|
-
"general",
|
|
441
|
-
);
|
|
442
|
-
const verdict = await askChoice(
|
|
443
|
-
rl,
|
|
444
|
-
"Verdict",
|
|
445
|
-
[...VALID_VERDICTS].sort(),
|
|
446
|
-
"REJECTED",
|
|
447
|
-
);
|
|
448
|
-
const prRef =
|
|
449
|
-
(await ask(rl, "PR / commit reference (optional, e.g. PR-404):")).trim() ||
|
|
450
|
-
null;
|
|
408
|
+
const domain = await askChoice(rl, 'Domain', [...VALID_DOMAINS].sort(), 'general');
|
|
409
|
+
const verdict = await askChoice(rl, 'Verdict', [...VALID_VERDICTS].sort(), 'REJECTED');
|
|
410
|
+
const prRef = (await ask(rl, 'PR / commit reference (optional, e.g. PR-404):')).trim() || null;
|
|
451
411
|
const reviewer =
|
|
452
|
-
(
|
|
453
|
-
await ask(rl, "Reviewer agent (optional, e.g. security-auditor):")
|
|
454
|
-
).trim() || null;
|
|
412
|
+
(await ask(rl, 'Reviewer agent (optional, e.g. security-auditor):')).trim() || null;
|
|
455
413
|
|
|
456
414
|
// FIX: stack_version — prevents stale cases from blocking valid code after
|
|
457
415
|
// framework upgrades. Format: "react>=19, node>=20" or leave blank for all.
|
|
458
416
|
const stackVersionRaw = (
|
|
459
|
-
await ask(
|
|
460
|
-
rl,
|
|
461
|
-
"Stack version constraint (optional, e.g. react>=18, node>=20):",
|
|
462
|
-
)
|
|
417
|
+
await ask(rl, 'Stack version constraint (optional, e.g. react>=18, node>=20):')
|
|
463
418
|
).trim();
|
|
464
419
|
const stackVersion = stackVersionRaw || null;
|
|
465
420
|
rl.close();
|
|
466
421
|
|
|
467
422
|
const delta = semanticDelta(diffText);
|
|
468
423
|
const fingerprint = contentHash(diffText);
|
|
469
|
-
const tags = extractTags(diffText +
|
|
424
|
+
const tags = extractTags(diffText + ' ' + reason);
|
|
470
425
|
|
|
471
426
|
const index = loadIndex();
|
|
472
427
|
const caseId = index.next_id;
|
|
@@ -501,26 +456,23 @@ async function cmdAddCase() {
|
|
|
501
456
|
index.next_id = caseId + 1;
|
|
502
457
|
saveIndex(index);
|
|
503
458
|
|
|
504
|
-
console.log(
|
|
505
|
-
`\n${GREEN}✔ Case #${String(caseId).padStart(4, "0")} recorded${RESET}`,
|
|
506
|
-
);
|
|
459
|
+
console.log(`\n${GREEN}✔ Case #${String(caseId).padStart(4, '0')} recorded${RESET}`);
|
|
507
460
|
console.log(` ${DIM}Fingerprint : ${fingerprint}${RESET}`);
|
|
508
461
|
console.log(` ${DIM}Domain : ${domain}${RESET}`);
|
|
509
|
-
console.log(` ${DIM}Tags : ${tags.slice(0, 8).join(
|
|
510
|
-
if (stackVersion)
|
|
511
|
-
console.log(` ${DIM}Stack version : ${stackVersion}${RESET}`);
|
|
462
|
+
console.log(` ${DIM}Tags : ${tags.slice(0, 8).join(', ')}${RESET}`);
|
|
463
|
+
if (stackVersion) console.log(` ${DIM}Stack version : ${stackVersion}${RESET}`);
|
|
512
464
|
console.log();
|
|
513
465
|
}
|
|
514
466
|
|
|
515
467
|
function cmdSearchCases(args) {
|
|
516
|
-
let query = args.filter(
|
|
468
|
+
let query = args.filter(a => !a.startsWith('--')).join(' ');
|
|
517
469
|
if (!query) {
|
|
518
|
-
const qi = process.argv.indexOf(
|
|
470
|
+
const qi = process.argv.indexOf('--query');
|
|
519
471
|
if (qi !== -1)
|
|
520
472
|
query = process.argv
|
|
521
473
|
.slice(qi + 1)
|
|
522
|
-
.filter(
|
|
523
|
-
.join(
|
|
474
|
+
.filter(a => !a.startsWith('--'))
|
|
475
|
+
.join(' ');
|
|
524
476
|
}
|
|
525
477
|
if (!query) {
|
|
526
478
|
console.log(
|
|
@@ -532,11 +484,9 @@ function cmdSearchCases(args) {
|
|
|
532
484
|
// FIX: --version-filter skips cases whose stack_version constraint doesn't
|
|
533
485
|
// match the specified versions. Prevents stale React 17/18 cases blocking React 19 code.
|
|
534
486
|
// Usage: search-cases "useEffect" --version-filter "react=19,node=22"
|
|
535
|
-
const vfIdx = args.indexOf(
|
|
487
|
+
const vfIdx = args.indexOf('--version-filter');
|
|
536
488
|
const versionFilter =
|
|
537
|
-
vfIdx !== -1 && args[vfIdx + 1]
|
|
538
|
-
? parseVersionFilter(args[vfIdx + 1])
|
|
539
|
-
: null;
|
|
489
|
+
vfIdx !== -1 && args[vfIdx + 1] ? parseVersionFilter(args[vfIdx + 1]) : null;
|
|
540
490
|
|
|
541
491
|
const queryTags = extractTags(query);
|
|
542
492
|
const index = loadIndex();
|
|
@@ -549,12 +499,12 @@ function cmdSearchCases(args) {
|
|
|
549
499
|
|
|
550
500
|
// Apply version filter before scoring
|
|
551
501
|
const eligibleCases = versionFilter
|
|
552
|
-
? index.cases.filter(
|
|
502
|
+
? index.cases.filter(e => caseMatchesVersionFilter(e, versionFilter))
|
|
553
503
|
: index.cases;
|
|
554
504
|
|
|
555
505
|
const skipped = index.cases.length - eligibleCases.length;
|
|
556
506
|
|
|
557
|
-
const corpus = eligibleCases.map(
|
|
507
|
+
const corpus = eligibleCases.map(e => e.tags || []);
|
|
558
508
|
const idf = buildIdf(corpus);
|
|
559
509
|
|
|
560
510
|
const scored = [];
|
|
@@ -571,41 +521,29 @@ function cmdSearchCases(args) {
|
|
|
571
521
|
console.log(
|
|
572
522
|
` ${DIM}${skipped} case(s) skipped by version filter: ${args[vfIdx + 1]}${RESET}`,
|
|
573
523
|
);
|
|
574
|
-
console.log(
|
|
575
|
-
` ${DIM}Try broader terms or check 'list' for available cases.${RESET}`,
|
|
576
|
-
);
|
|
524
|
+
console.log(` ${DIM}Try broader terms or check 'list' for available cases.${RESET}`);
|
|
577
525
|
return;
|
|
578
526
|
}
|
|
579
527
|
|
|
580
|
-
console.log(
|
|
581
|
-
`\n${BOLD}${CYAN}━━━ Case Law Search Results ━━━━━━━━━━━━━━━━━━━━━━━${RESET}`,
|
|
582
|
-
);
|
|
528
|
+
console.log(`\n${BOLD}${CYAN}━━━ Case Law Search Results ━━━━━━━━━━━━━━━━━━━━━━━${RESET}`);
|
|
583
529
|
console.log(` Query : ${BOLD}${query}${RESET}`);
|
|
584
530
|
console.log(
|
|
585
531
|
` Matches: ${top.length} of ${index.cases.length} cases` +
|
|
586
|
-
(skipped > 0
|
|
587
|
-
|
|
588
|
-
: "") +
|
|
589
|
-
"\n",
|
|
532
|
+
(skipped > 0 ? ` ${DIM}(${skipped} skipped by version filter)${RESET}` : '') +
|
|
533
|
+
'\n',
|
|
590
534
|
);
|
|
591
535
|
|
|
592
536
|
for (const { score, entry } of top) {
|
|
593
|
-
const vc = entry.verdict ===
|
|
537
|
+
const vc = entry.verdict === 'REJECTED' ? RED : YELLOW;
|
|
594
538
|
console.log(
|
|
595
|
-
` ${BOLD}Case #${String(entry.id).padStart(4,
|
|
539
|
+
` ${BOLD}Case #${String(entry.id).padStart(4, '0')}${RESET} ${vc}[${entry.verdict}]${RESET} ${DIM}${(entry.timestamp || '').slice(0, 10)}${RESET} score=${score.toFixed(2)}`,
|
|
596
540
|
);
|
|
597
541
|
console.log(` ${DIM}Domain: ${entry.domain}${RESET}`);
|
|
598
542
|
console.log(` ${entry.reason_summary}`);
|
|
599
|
-
console.log(
|
|
600
|
-
` ${DIM}Tags: ${(entry.tags || []).slice(0, 8).join(", ")}${RESET}\n`,
|
|
601
|
-
);
|
|
543
|
+
console.log(` ${DIM}Tags: ${(entry.tags || []).slice(0, 8).join(', ')}${RESET}\n`);
|
|
602
544
|
}
|
|
603
|
-
console.log(
|
|
604
|
-
|
|
605
|
-
);
|
|
606
|
-
console.log(
|
|
607
|
-
`${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}\n`,
|
|
608
|
-
);
|
|
545
|
+
console.log(` ${DIM}Run 'show --id <N>' to see the full diff for any case.${RESET}`);
|
|
546
|
+
console.log(`${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}\n`);
|
|
609
547
|
}
|
|
610
548
|
|
|
611
549
|
function cmdList(args) {
|
|
@@ -617,40 +555,33 @@ function cmdList(args) {
|
|
|
617
555
|
}
|
|
618
556
|
|
|
619
557
|
let domainFilter = null;
|
|
620
|
-
const di = args.indexOf(
|
|
558
|
+
const di = args.indexOf('--domain');
|
|
621
559
|
if (di !== -1 && args[di + 1]) domainFilter = args[di + 1].toLowerCase();
|
|
622
560
|
|
|
623
|
-
const filtered = domainFilter
|
|
624
|
-
? cases.filter((c) => c.domain === domainFilter)
|
|
625
|
-
: cases;
|
|
561
|
+
const filtered = domainFilter ? cases.filter(c => c.domain === domainFilter) : cases;
|
|
626
562
|
const total = filtered.length;
|
|
627
563
|
|
|
628
|
-
console.log(
|
|
629
|
-
|
|
630
|
-
);
|
|
631
|
-
if (domainFilter)
|
|
632
|
-
console.log(` ${DIM}Filtered by domain: ${domainFilter}${RESET}\n`);
|
|
564
|
+
console.log(`\n${BOLD}${CYAN}━━━ Case Law Index (${total} cases) ━━━━━━━━━━━━━━━━━━━━${RESET}`);
|
|
565
|
+
if (domainFilter) console.log(` ${DIM}Filtered by domain: ${domainFilter}${RESET}\n`);
|
|
633
566
|
|
|
634
567
|
const last20 = filtered.slice(-20).reverse();
|
|
635
568
|
for (const entry of last20) {
|
|
636
|
-
const vc = entry.verdict ===
|
|
569
|
+
const vc = entry.verdict === 'REJECTED' ? RED : YELLOW;
|
|
637
570
|
console.log(
|
|
638
|
-
` ${BOLD}#${String(entry.id).padStart(4,
|
|
571
|
+
` ${BOLD}#${String(entry.id).padStart(4, '0')}${RESET} ${vc}[${entry.verdict}]${RESET} ${DIM}${(entry.domain || '').toUpperCase()}${RESET} ${(entry.timestamp || '').slice(0, 10)}`,
|
|
639
572
|
);
|
|
640
|
-
console.log(` ${(entry.reason_summary ||
|
|
573
|
+
console.log(` ${(entry.reason_summary || '').slice(0, 80)}`);
|
|
641
574
|
}
|
|
642
575
|
if (total > 20)
|
|
643
576
|
console.log(
|
|
644
577
|
`\n ${YELLOW}... showing last 20 of ${total}. Use 'export' for full history.${RESET}`,
|
|
645
578
|
);
|
|
646
|
-
console.log(
|
|
647
|
-
`${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}\n`,
|
|
648
|
-
);
|
|
579
|
+
console.log(`${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}\n`);
|
|
649
580
|
}
|
|
650
581
|
|
|
651
582
|
function cmdShow(args) {
|
|
652
583
|
let caseId = null;
|
|
653
|
-
const ii = args.indexOf(
|
|
584
|
+
const ii = args.indexOf('--id');
|
|
654
585
|
if (ii !== -1 && args[ii + 1]) caseId = parseInt(args[ii + 1], 10);
|
|
655
586
|
if (caseId == null || isNaN(caseId)) {
|
|
656
587
|
console.log(`${RED}✖ Provide a case ID: show --id 7${RESET}`);
|
|
@@ -659,15 +590,13 @@ function cmdShow(args) {
|
|
|
659
590
|
|
|
660
591
|
const caseRecord = loadCase(caseId);
|
|
661
592
|
if (!caseRecord) {
|
|
662
|
-
console.log(
|
|
663
|
-
`${RED}✖ Case #${String(caseId).padStart(4, "0")} not found.${RESET}`,
|
|
664
|
-
);
|
|
593
|
+
console.log(`${RED}✖ Case #${String(caseId).padStart(4, '0')} not found.${RESET}`);
|
|
665
594
|
process.exit(1);
|
|
666
595
|
}
|
|
667
596
|
|
|
668
|
-
const vc = caseRecord.verdict ===
|
|
597
|
+
const vc = caseRecord.verdict === 'REJECTED' ? RED : YELLOW;
|
|
669
598
|
console.log(
|
|
670
|
-
`\n${BOLD}${CYAN}━━━ Case #${String(caseRecord.id).padStart(4,
|
|
599
|
+
`\n${BOLD}${CYAN}━━━ Case #${String(caseRecord.id).padStart(4, '0')} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`,
|
|
671
600
|
);
|
|
672
601
|
console.log(` Verdict : ${vc}${BOLD}${caseRecord.verdict}${RESET}`);
|
|
673
602
|
console.log(` Domain : ${caseRecord.domain}`);
|
|
@@ -678,22 +607,18 @@ function cmdShow(args) {
|
|
|
678
607
|
console.log(` ${caseRecord.reason}`);
|
|
679
608
|
console.log(`\n ${BOLD}Semantic Delta (meaningful changes only):${RESET}`);
|
|
680
609
|
console.log(` ${DIM}─────────────────────────────────────────${RESET}`);
|
|
681
|
-
const deltaLines = (caseRecord.diff_delta || caseRecord.diff_raw)
|
|
682
|
-
.split("\n")
|
|
683
|
-
.slice(0, 40);
|
|
610
|
+
const deltaLines = (caseRecord.diff_delta || caseRecord.diff_raw).split('\n').slice(0, 40);
|
|
684
611
|
for (const line of deltaLines) {
|
|
685
|
-
if (line.startsWith(
|
|
686
|
-
else if (line.startsWith(
|
|
612
|
+
if (line.startsWith('+')) console.log(` ${GREEN}${line}${RESET}`);
|
|
613
|
+
else if (line.startsWith('-')) console.log(` ${RED}${line}${RESET}`);
|
|
687
614
|
else console.log(` ${DIM}${line}${RESET}`);
|
|
688
615
|
}
|
|
689
|
-
console.log(`\n ${BOLD}Tags:${RESET} ${(caseRecord.tags || []).join(
|
|
690
|
-
console.log(
|
|
691
|
-
`${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}\n`,
|
|
692
|
-
);
|
|
616
|
+
console.log(`\n ${BOLD}Tags:${RESET} ${(caseRecord.tags || []).join(', ')}`);
|
|
617
|
+
console.log(`${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}\n`);
|
|
693
618
|
}
|
|
694
619
|
|
|
695
620
|
function cmdExport(args) {
|
|
696
|
-
const toStdout = args.includes(
|
|
621
|
+
const toStdout = args.includes('--stdout');
|
|
697
622
|
const index = loadIndex();
|
|
698
623
|
const cases = index.cases || [];
|
|
699
624
|
if (!cases.length) {
|
|
@@ -703,30 +628,30 @@ function cmdExport(args) {
|
|
|
703
628
|
|
|
704
629
|
const now = new Date().toISOString().slice(0, 19);
|
|
705
630
|
const lines = [
|
|
706
|
-
|
|
631
|
+
'# Tribunal Case Law — Full Export\n',
|
|
707
632
|
`Generated: ${now}`,
|
|
708
633
|
`Total Cases: ${cases.length}\n`,
|
|
709
|
-
|
|
634
|
+
'---\n',
|
|
710
635
|
];
|
|
711
636
|
for (const entry of cases) {
|
|
712
637
|
const caseRecord = loadCase(entry.id) || entry;
|
|
713
|
-
const badge = `[${caseRecord.verdict ||
|
|
714
|
-
lines.push(`## Case #${String(entry.id).padStart(4,
|
|
638
|
+
const badge = `[${caseRecord.verdict || 'REJECTED'}]`;
|
|
639
|
+
lines.push(`## Case #${String(entry.id).padStart(4, '0')} ${badge}`);
|
|
715
640
|
lines.push(`**Domain:** ${entry.domain} `);
|
|
716
|
-
lines.push(`**Recorded:** ${(entry.timestamp ||
|
|
641
|
+
lines.push(`**Recorded:** ${(entry.timestamp || '').slice(0, 10)} `);
|
|
717
642
|
if (caseRecord.pr_ref) lines.push(`**PR/Ref:** ${caseRecord.pr_ref} `);
|
|
718
643
|
lines.push(`\n**Reason:** ${entry.reason_summary}\n`);
|
|
719
|
-
lines.push(`**Tags:** \`${(entry.tags || []).slice(0, 8).join(
|
|
720
|
-
lines.push(
|
|
644
|
+
lines.push(`**Tags:** \`${(entry.tags || []).slice(0, 8).join(', ')}\`\n`);
|
|
645
|
+
lines.push('---\n');
|
|
721
646
|
}
|
|
722
|
-
const content = lines.join(
|
|
647
|
+
const content = lines.join('\n');
|
|
723
648
|
if (toStdout) {
|
|
724
649
|
console.log(content);
|
|
725
650
|
return;
|
|
726
651
|
}
|
|
727
652
|
|
|
728
|
-
const outPath = path.join(getPaths().HISTORY_DIR,
|
|
729
|
-
fs.writeFileSync(outPath, content,
|
|
653
|
+
const outPath = path.join(getPaths().HISTORY_DIR, 'case-law-export.md');
|
|
654
|
+
fs.writeFileSync(outPath, content, 'utf8');
|
|
730
655
|
console.log(`${GREEN}✔ Exported ${cases.length} cases to ${outPath}${RESET}`);
|
|
731
656
|
}
|
|
732
657
|
|
|
@@ -740,48 +665,40 @@ function cmdStats() {
|
|
|
740
665
|
verdictCounts[c.verdict] = (verdictCounts[c.verdict] || 0) + 1;
|
|
741
666
|
}
|
|
742
667
|
|
|
743
|
-
console.log(
|
|
744
|
-
`\n${BOLD}${CYAN}━━━ Case Law Statistics ━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`,
|
|
745
|
-
);
|
|
668
|
+
console.log(`\n${BOLD}${CYAN}━━━ Case Law Statistics ━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`);
|
|
746
669
|
console.log(` Total cases: ${BOLD}${cases.length}${RESET}`);
|
|
747
670
|
console.log(`\n ${BOLD}By Verdict:${RESET}`);
|
|
748
671
|
for (const v of Object.keys(verdictCounts).sort()) {
|
|
749
|
-
const color = v ===
|
|
672
|
+
const color = v === 'REJECTED' ? RED : YELLOW;
|
|
750
673
|
console.log(` ${color}${v.padEnd(30)}${RESET} ${verdictCounts[v]}`);
|
|
751
674
|
}
|
|
752
675
|
console.log(`\n ${BOLD}By Domain:${RESET}`);
|
|
753
|
-
for (const [d, c] of Object.entries(domainCounts).sort(
|
|
754
|
-
(a, b) => b[1] - a[1],
|
|
755
|
-
)) {
|
|
676
|
+
for (const [d, c] of Object.entries(domainCounts).sort((a, b) => b[1] - a[1])) {
|
|
756
677
|
console.log(` ${CYAN}${d.padEnd(20)}${RESET} ${c}`);
|
|
757
678
|
}
|
|
758
|
-
console.log(
|
|
759
|
-
`${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}\n`,
|
|
760
|
-
);
|
|
679
|
+
console.log(`${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}\n`);
|
|
761
680
|
}
|
|
762
681
|
|
|
763
682
|
function cmdAutoRecord() {
|
|
764
683
|
function getFlag(name) {
|
|
765
684
|
const flag = `--${name}`;
|
|
766
685
|
const idx = process.argv.indexOf(flag);
|
|
767
|
-
return idx !== -1 && process.argv[idx + 1] ? process.argv[idx + 1] :
|
|
686
|
+
return idx !== -1 && process.argv[idx + 1] ? process.argv[idx + 1] : '';
|
|
768
687
|
}
|
|
769
688
|
|
|
770
|
-
const diffText = getFlag(
|
|
771
|
-
const reason = getFlag(
|
|
772
|
-
let domain = getFlag(
|
|
773
|
-
let verdict = getFlag(
|
|
774
|
-
const reviewer = getFlag(
|
|
775
|
-
const prRef = getFlag(
|
|
689
|
+
const diffText = getFlag('diff');
|
|
690
|
+
const reason = getFlag('reason');
|
|
691
|
+
let domain = getFlag('domain') || 'general';
|
|
692
|
+
let verdict = getFlag('verdict') || 'REJECTED';
|
|
693
|
+
const reviewer = getFlag('reviewer') || null;
|
|
694
|
+
const prRef = getFlag('pr-ref') || null;
|
|
776
695
|
// FIX: --stack-version persists version context with the case record so
|
|
777
696
|
// future searches can skip it when the project has moved past that version.
|
|
778
697
|
// Example: --stack-version "react=18,next=14"
|
|
779
|
-
const stackVersion = getFlag(
|
|
698
|
+
const stackVersion = getFlag('stack-version') || null;
|
|
780
699
|
|
|
781
700
|
if (!diffText || !reason) {
|
|
782
|
-
console.log(
|
|
783
|
-
`${RED}✖ auto-record requires --diff and --reason flags.${RESET}`,
|
|
784
|
-
);
|
|
701
|
+
console.log(`${RED}✖ auto-record requires --diff and --reason flags.${RESET}`);
|
|
785
702
|
console.log(
|
|
786
703
|
` Usage: auto-record --diff "code" --reason "why" --domain security --reviewer agent-name --stack-version "react=18"`,
|
|
787
704
|
);
|
|
@@ -789,27 +706,25 @@ function cmdAutoRecord() {
|
|
|
789
706
|
}
|
|
790
707
|
|
|
791
708
|
if (isNoiseRejection(reason)) {
|
|
792
|
-
console.log(
|
|
793
|
-
`${DIM}⊘ Skipped: trivial rejection (noise filter matched).${RESET}`,
|
|
794
|
-
);
|
|
709
|
+
console.log(`${DIM}⊘ Skipped: trivial rejection (noise filter matched).${RESET}`);
|
|
795
710
|
return;
|
|
796
711
|
}
|
|
797
|
-
if (!VALID_DOMAINS.has(domain)) domain =
|
|
798
|
-
if (!VALID_VERDICTS.has(verdict)) verdict =
|
|
712
|
+
if (!VALID_DOMAINS.has(domain)) domain = 'general';
|
|
713
|
+
if (!VALID_VERDICTS.has(verdict)) verdict = 'REJECTED';
|
|
799
714
|
|
|
800
715
|
const fingerprint = contentHash(diffText);
|
|
801
716
|
const index = loadIndex();
|
|
802
717
|
for (const existing of index.cases) {
|
|
803
718
|
if (existing.fingerprint === fingerprint) {
|
|
804
719
|
console.log(
|
|
805
|
-
`${YELLOW}⊘ Duplicate: Case #${String(existing.id).padStart(4,
|
|
720
|
+
`${YELLOW}⊘ Duplicate: Case #${String(existing.id).padStart(4, '0')} already records this pattern.${RESET}`,
|
|
806
721
|
);
|
|
807
722
|
return;
|
|
808
723
|
}
|
|
809
724
|
}
|
|
810
725
|
|
|
811
726
|
const delta = semanticDelta(diffText);
|
|
812
|
-
const tags = extractTags(diffText +
|
|
727
|
+
const tags = extractTags(diffText + ' ' + reason);
|
|
813
728
|
const caseId = index.next_id;
|
|
814
729
|
const now = new Date().toISOString().slice(0, 19);
|
|
815
730
|
|
|
@@ -843,16 +758,15 @@ function cmdAutoRecord() {
|
|
|
843
758
|
index.next_id = caseId + 1;
|
|
844
759
|
saveIndex(index);
|
|
845
760
|
console.log(
|
|
846
|
-
`${GREEN}✔ Auto-recorded Case #${String(caseId).padStart(4,
|
|
761
|
+
`${GREEN}✔ Auto-recorded Case #${String(caseId).padStart(4, '0')}${RESET} [${verdict}] domain=${domain}`,
|
|
847
762
|
);
|
|
848
763
|
console.log(` ${DIM}Reason: ${reason.slice(0, 80)}${RESET}`);
|
|
849
|
-
if (stackVersion)
|
|
850
|
-
console.log(` ${DIM}Stack version: ${stackVersion}${RESET}`);
|
|
764
|
+
if (stackVersion) console.log(` ${DIM}Stack version: ${stackVersion}${RESET}`);
|
|
851
765
|
}
|
|
852
766
|
|
|
853
767
|
async function cmdOverrule(args) {
|
|
854
768
|
let caseId = null;
|
|
855
|
-
const ii = args.indexOf(
|
|
769
|
+
const ii = args.indexOf('--id');
|
|
856
770
|
if (ii !== -1 && args[ii + 1]) caseId = parseInt(args[ii + 1], 10);
|
|
857
771
|
if (caseId == null || isNaN(caseId)) {
|
|
858
772
|
console.log(`${RED}✖ Provide a case ID: overrule --id 7${RESET}`);
|
|
@@ -861,25 +775,21 @@ async function cmdOverrule(args) {
|
|
|
861
775
|
|
|
862
776
|
const caseRecord = loadCase(caseId);
|
|
863
777
|
if (!caseRecord) {
|
|
864
|
-
console.log(
|
|
865
|
-
`${RED}✖ Case #${String(caseId).padStart(4, "0")} not found.${RESET}`,
|
|
866
|
-
);
|
|
778
|
+
console.log(`${RED}✖ Case #${String(caseId).padStart(4, '0')} not found.${RESET}`);
|
|
867
779
|
process.exit(1);
|
|
868
780
|
}
|
|
869
|
-
if (caseRecord.verdict ===
|
|
870
|
-
console.log(
|
|
871
|
-
`${YELLOW}Case #${String(caseId).padStart(4, "0")} is already OVERRULED.${RESET}`,
|
|
872
|
-
);
|
|
781
|
+
if (caseRecord.verdict === 'OVERRULED') {
|
|
782
|
+
console.log(`${YELLOW}Case #${String(caseId).padStart(4, '0')} is already OVERRULED.${RESET}`);
|
|
873
783
|
return;
|
|
874
784
|
}
|
|
875
785
|
|
|
876
786
|
let reason = null;
|
|
877
|
-
const ri = args.indexOf(
|
|
787
|
+
const ri = args.indexOf('--reason');
|
|
878
788
|
if (ri !== -1 && args[ri + 1]) reason = args[ri + 1];
|
|
879
789
|
|
|
880
790
|
if (!reason) {
|
|
881
791
|
const rl = createRl();
|
|
882
|
-
reason = await ask(rl,
|
|
792
|
+
reason = await ask(rl, 'Reason for overruling this precedent:');
|
|
883
793
|
rl.close();
|
|
884
794
|
}
|
|
885
795
|
if (!reason || !reason.trim()) {
|
|
@@ -888,7 +798,7 @@ async function cmdOverrule(args) {
|
|
|
888
798
|
}
|
|
889
799
|
|
|
890
800
|
const oldVerdict = caseRecord.verdict;
|
|
891
|
-
caseRecord.verdict =
|
|
801
|
+
caseRecord.verdict = 'OVERRULED';
|
|
892
802
|
caseRecord.overruled_at = new Date().toISOString().slice(0, 19);
|
|
893
803
|
caseRecord.overrule_reason = reason.trim();
|
|
894
804
|
caseRecord.previous_verdict = oldVerdict;
|
|
@@ -897,27 +807,23 @@ async function cmdOverrule(args) {
|
|
|
897
807
|
const index = loadIndex();
|
|
898
808
|
for (const entry of index.cases) {
|
|
899
809
|
if (entry.id === caseId) {
|
|
900
|
-
entry.verdict =
|
|
810
|
+
entry.verdict = 'OVERRULED';
|
|
901
811
|
break;
|
|
902
812
|
}
|
|
903
813
|
}
|
|
904
814
|
saveIndex(index);
|
|
905
815
|
|
|
906
|
-
console.log(
|
|
907
|
-
`\n${GREEN}✔ Case #${String(caseId).padStart(4, "0")} OVERRULED${RESET}`,
|
|
908
|
-
);
|
|
816
|
+
console.log(`\n${GREEN}✔ Case #${String(caseId).padStart(4, '0')} OVERRULED${RESET}`);
|
|
909
817
|
console.log(` ${DIM}Previous verdict : ${oldVerdict}${RESET}`);
|
|
910
818
|
console.log(` ${DIM}Overrule reason : ${reason.trim()}${RESET}`);
|
|
911
|
-
console.log(
|
|
912
|
-
` ${DIM}The case is preserved in history but no longer blocks reviews.${RESET}\n`,
|
|
913
|
-
);
|
|
819
|
+
console.log(` ${DIM}The case is preserved in history but no longer blocks reviews.${RESET}\n`);
|
|
914
820
|
}
|
|
915
821
|
|
|
916
822
|
// ── Main ──────────────────────────────────────────────────────────────────────
|
|
917
823
|
const COMMANDS = {
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
824
|
+
'add-case': cmdAddCase,
|
|
825
|
+
'auto-record': cmdAutoRecord,
|
|
826
|
+
'search-cases': cmdSearchCases,
|
|
921
827
|
list: cmdList,
|
|
922
828
|
show: cmdShow,
|
|
923
829
|
overrule: cmdOverrule,
|
|
@@ -927,7 +833,7 @@ const COMMANDS = {
|
|
|
927
833
|
|
|
928
834
|
async function main() {
|
|
929
835
|
const argv = process.argv.slice(2);
|
|
930
|
-
if (!argv.length || [
|
|
836
|
+
if (!argv.length || ['-h', '--help', 'help'].includes(argv[0])) {
|
|
931
837
|
console.log(`
|
|
932
838
|
${BOLD}case_law_manager.js${RESET} — Tribunal Case Law Engine
|
|
933
839
|
|
|
@@ -941,8 +847,8 @@ ${BOLD}Commands:${RESET}
|
|
|
941
847
|
export [--stdout] Export all cases to Markdown
|
|
942
848
|
stats Show breakdown by domain/verdict
|
|
943
849
|
|
|
944
|
-
${BOLD}Domains:${RESET} ${[...VALID_DOMAINS].sort().join(
|
|
945
|
-
${BOLD}Verdicts:${RESET} ${[...VALID_VERDICTS].sort().join(
|
|
850
|
+
${BOLD}Domains:${RESET} ${[...VALID_DOMAINS].sort().join(', ')}
|
|
851
|
+
${BOLD}Verdicts:${RESET} ${[...VALID_VERDICTS].sort().join(', ')}
|
|
946
852
|
`);
|
|
947
853
|
return;
|
|
948
854
|
}
|
|
@@ -951,7 +857,7 @@ ${BOLD}Verdicts:${RESET} ${[...VALID_VERDICTS].sort().join(", ")}
|
|
|
951
857
|
const rest = argv.slice(1);
|
|
952
858
|
if (!COMMANDS[cmd]) {
|
|
953
859
|
console.log(`${RED}✖ Unknown command: '${cmd}'${RESET}`);
|
|
954
|
-
console.log(` Valid: ${Object.keys(COMMANDS).join(
|
|
860
|
+
console.log(` Valid: ${Object.keys(COMMANDS).join(', ')}`);
|
|
955
861
|
process.exit(1);
|
|
956
862
|
}
|
|
957
863
|
await COMMANDS[cmd](rest);
|
|
@@ -974,7 +880,7 @@ module.exports = {
|
|
|
974
880
|
};
|
|
975
881
|
|
|
976
882
|
if (require.main === module) {
|
|
977
|
-
main().catch(
|
|
883
|
+
main().catch(err => {
|
|
978
884
|
console.error(err);
|
|
979
885
|
process.exit(1);
|
|
980
886
|
});
|