tribunal-kit 5.8.0 → 5.8.2

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.
@@ -487,50 +487,209 @@ async function callLlmApi(prompt, provider, apiKey) {
487
487
  return null;
488
488
  }
489
489
 
490
+ // ── Commands ──────────────────────────────────────────────────────────────────
491
+ // ── Strategy Filtering ────────────────────────────────────────────────────────
492
+ function applyStrategyFilter(signals, strategy) {
493
+ if (strategy === "repair-only") {
494
+ return signals.filter((s) => s.type === "log_error");
495
+ }
496
+ return signals;
497
+ }
498
+
499
+ // ── Log Reflection Prompt ─────────────────────────────────────────────────────
500
+ function generateLogReflectionPrompt(delta) {
501
+ return `You are analyzing runtime log signals (errors, tracebacks, and warnings) from an AI agent's execution.
502
+ Your job is to identify systemic coding issues and extract evolution assets:
503
+ 1. **Idioms (Genes)**: Coding rules/conventions to prevent the AI from generating this mistake again.
504
+ 2. **Cases (Capsules)**: Rejection precedents (offending code pattern and why it failed) to block reviewers from approving it.
505
+
506
+ Rules:
507
+ - Return ONLY a YAML structure containing 'idioms:' and 'cases:'. No markdown boxes, no conversational prose.
508
+ - Maximum 3 idioms and 3 cases.
509
+ - If an asset category is not applicable, leave it empty (e.g., 'idioms: []').
510
+
511
+ Log Signals:
512
+ \`\`\`
513
+ ${delta.slice(0, 1800)}
514
+ \`\`\`
515
+
516
+ Output format (YAML only):
517
+ idioms:
518
+ - pattern: "<code convention or rule to avoid this error>"
519
+ reason: "<why it is needed based on the error>"
520
+ domain: "<backend|frontend|database|security|performance|general>"
521
+ cases:
522
+ - pattern: "<offending code snippet or error-triggering pattern>"
523
+ reason: "<specific runtime error / traceback reason>"
524
+ domain: "<backend|frontend|database|security|performance|general>"
525
+ verdict: "REJECTED"
526
+ `;
527
+ }
528
+
529
+ // ── Combined YAML Parser ──────────────────────────────────────────────────────
530
+ function parseEvolutionYaml(response) {
531
+ const idioms = [];
532
+ const cases = [];
533
+ let currentSection = null; // 'idioms' | 'cases'
534
+ let current = null;
535
+
536
+ const lines = response.split("\n");
537
+ for (const line of lines) {
538
+ const trimmed = line.trim();
539
+ if (trimmed === "idioms:") {
540
+ if (current) {
541
+ if (currentSection === "idioms") idioms.push(current);
542
+ if (currentSection === "cases") cases.push(current);
543
+ }
544
+ currentSection = "idioms";
545
+ current = null;
546
+ continue;
547
+ }
548
+ if (trimmed === "cases:") {
549
+ if (current) {
550
+ if (currentSection === "idioms") idioms.push(current);
551
+ if (currentSection === "cases") cases.push(current);
552
+ }
553
+ currentSection = "cases";
554
+ current = null;
555
+ continue;
556
+ }
557
+
558
+ if (trimmed.startsWith("- pattern:") || (trimmed.startsWith("- ") && trimmed.includes("pattern:"))) {
559
+ if (current) {
560
+ if (currentSection === "idioms") idioms.push(current);
561
+ if (currentSection === "cases") cases.push(current);
562
+ }
563
+ let pat = "";
564
+ if (trimmed.startsWith("- pattern:")) {
565
+ pat = trimmed.substring("- pattern:".length).trim();
566
+ } else {
567
+ pat = trimmed.split("pattern:", 2)[1].trim();
568
+ }
569
+ current = { pattern: pat.replace(/^['"]|['"]$/g, "") };
570
+ } else if (trimmed.startsWith("reason:") && current) {
571
+ current.reason = trimmed.substring("reason:".length).trim().replace(/^['"]|['"]$/g, "");
572
+ } else if (trimmed.startsWith("domain:") && current) {
573
+ current.domain = trimmed.substring("domain:".length).trim().replace(/^['"]|['"]$/g, "");
574
+ } else if (trimmed.startsWith("verdict:") && current) {
575
+ current.verdict = trimmed.substring("verdict:".length).trim().replace(/^['"]|['"]$/g, "");
576
+ }
577
+ }
578
+
579
+ if (current) {
580
+ if (currentSection === "idioms") idioms.push(current);
581
+ if (currentSection === "cases") cases.push(current);
582
+ }
583
+
584
+ return { idioms, cases };
585
+ }
586
+
490
587
  // ── Commands ──────────────────────────────────────────────────────────────────
491
588
  async function cmdDigest(args) {
492
589
  const dryRun = args.includes("--dry-run");
493
590
  const diffMode = args.includes("--head") ? "head" : "staged";
494
591
 
592
+ const logArg = args.find((a) => a.startsWith("--log="));
593
+ const logFile = logArg ? logArg.split("=").slice(1).join("=") : null;
594
+
595
+ const strategyArg = args.find((a) => a.startsWith("--strategy="));
596
+ const strategy = strategyArg ? strategyArg.split("=").slice(1).join("=") : "balanced";
597
+
495
598
  console.log(
496
599
  `\n${BOLD}${CYAN}━━━ Skill Evolution — Digest Cycle ━━━━━━━━━━━━━━━━${RESET}`,
497
600
  );
498
601
  if (dryRun)
499
602
  console.log(` ${YELLOW}DRY RUN — no files will be written${RESET}\n`);
500
603
 
501
- console.log(` ${DIM}[1/5] Fetching git diff (${diffMode})...${RESET}`);
502
- const rawDiff = getGitDiff(diffMode);
503
- if (!rawDiff.trim()) {
604
+ let rawDiff = "";
605
+ let delta = "";
606
+ let rawTokens = 0;
607
+ let deltaTokens = 0;
608
+ let logSignals = [];
609
+ const isLogMode = !!logFile;
610
+
611
+ if (isLogMode) {
612
+ console.log(` ${DIM}[1/5] Reading log file: ${logFile}...${RESET}`);
613
+ if (!fs.existsSync(logFile)) {
614
+ console.log(` ${RED}✖ Log file not found: ${logFile}${RESET}\n`);
615
+ return;
616
+ }
617
+ const logText = fs.readFileSync(logFile, "utf8");
618
+ rawTokens = countTokensEstimate(logText);
619
+
504
620
  console.log(
505
- ` ${YELLOW} No diff found. Commit or stage changes first.${RESET}`,
621
+ ` ${DIM}[2/5] Extracting signals from log (Signal Detector)...${RESET}`,
506
622
  );
623
+ const { detectSignals } = require("./signal_detector");
624
+ const signals = detectSignals(logText);
625
+
626
+ if (signals.length === 0) {
627
+ console.log(
628
+ ` ${GREEN}✔ No errors or performance signals found in logs.${RESET}`,
629
+ );
630
+ console.log(` ${DIM} No self-evolution reflection needed.${RESET}\n`);
631
+ return;
632
+ }
633
+
634
+ logSignals = applyStrategyFilter(signals, strategy);
635
+ if (logSignals.length === 0) {
636
+ console.log(
637
+ ` ${YELLOW}⚠ Strategy filter [${strategy}] filtered out all signals.${RESET}\n`,
638
+ );
639
+ return;
640
+ }
641
+
507
642
  console.log(
508
- ` ${DIM}Tip: Use --head to diff against the last commit.${RESET}\n`,
643
+ ` ${GREEN} Filtered to ${logSignals.length} signal(s) using [${strategy}] strategy.${RESET}`,
509
644
  );
510
- return;
511
- }
512
645
 
513
- const rawTokens = countTokensEstimate(rawDiff);
514
- console.log(
515
- ` ${DIM} Raw diff: ~${rawTokens} tokens (${rawDiff.length} chars)${RESET}`,
516
- );
646
+ delta = logSignals
647
+ .map((s) => {
648
+ let chunk = `--- SIGNAL: ${s.type} ---\n`;
649
+ if (s.file) chunk += `File: ${s.file}${s.line ? `:${s.line}` : ""}\n`;
650
+ chunk += `Message: ${s.message}\n`;
651
+ chunk += `Context:\n${s.context}\n`;
652
+ return chunk;
653
+ })
654
+ .join("\n\n");
655
+
656
+ deltaTokens = countTokensEstimate(delta);
657
+ } else {
658
+ console.log(` ${DIM}[1/5] Fetching git diff (${diffMode})...${RESET}`);
659
+ rawDiff = getGitDiff(diffMode);
660
+ if (!rawDiff.trim()) {
661
+ console.log(
662
+ ` ${YELLOW}⚠ No diff found. Commit or stage changes first.${RESET}`,
663
+ );
664
+ console.log(
665
+ ` ${DIM}Tip: Use --head to diff against the last commit.${RESET}\n`,
666
+ );
667
+ return;
668
+ }
517
669
 
518
- console.log(
519
- ` ${DIM}[2/5] Extracting architectural delta (Semantic Filter)...${RESET}`,
520
- );
521
- const delta = semanticDelta(rawDiff, 2);
522
- if (!delta.trim()) {
670
+ rawTokens = countTokensEstimate(rawDiff);
523
671
  console.log(
524
- ` ${GREEN} Delta is 100% trivial (whitespace/comments/imports only).${RESET}`,
672
+ ` ${DIM} Raw diff: ~${rawTokens} tokens (${rawDiff.length} chars)${RESET}`,
525
673
  );
674
+
526
675
  console.log(
527
- ` ${DIM} No LLM call needed. Zero tokens consumed.${RESET}\n`,
676
+ ` ${DIM}[2/5] Extracting architectural delta (Semantic Filter)...${RESET}`,
528
677
  );
529
- return;
678
+ delta = semanticDelta(rawDiff, 2);
679
+ if (!delta.trim()) {
680
+ console.log(
681
+ ` ${GREEN}✔ Delta is 100% trivial (whitespace/comments/imports only).${RESET}`,
682
+ );
683
+ console.log(
684
+ ` ${DIM} No LLM call needed. Zero tokens consumed.${RESET}\n`,
685
+ );
686
+ return;
687
+ }
688
+
689
+ deltaTokens = countTokensEstimate(delta);
530
690
  }
531
691
 
532
- const deltaTokens = countTokensEstimate(delta);
533
- const savedTokens = rawTokens - deltaTokens;
692
+ const savedTokens = Math.max(0, rawTokens - deltaTokens);
534
693
  const savedPct = Math.floor((savedTokens / Math.max(rawTokens, 1)) * 100);
535
694
  console.log(
536
695
  ` ${GREEN}✔ Filtered to ~${deltaTokens} tokens (${savedPct}% reduction, saved ~${savedTokens} tokens)${RESET}`,
@@ -546,7 +705,7 @@ async function cmdDigest(args) {
546
705
  }
547
706
  if (delta.split("\n").length > 20)
548
707
  console.log(
549
- ` ${DIM}... (${delta.split("\n").length - 20} more lines)${RESET}`,
708
+ ` ... (${delta.split("\n").length - 20} more lines)`,
550
709
  );
551
710
 
552
711
  if (dryRun) {
@@ -559,8 +718,10 @@ async function cmdDigest(args) {
559
718
  return;
560
719
  }
561
720
 
562
- // GENERATE: Auto-LLM call. Tries API first, falls back to manual paste if no key.
563
- const reflectionPrompt = generateReflectionPrompt(delta);
721
+ // GENERATE: Auto-LLM call.
722
+ const reflectionPrompt = isLogMode
723
+ ? generateLogReflectionPrompt(delta)
724
+ : generateReflectionPrompt(delta);
564
725
  let llmResponse = "";
565
726
 
566
727
  let llmCreds = detectLlmProvider();
@@ -582,12 +743,12 @@ async function cmdDigest(args) {
582
743
  console.log(
583
744
  ` ${YELLOW}⚠ API call failed — falling back to manual mode${RESET}`,
584
745
  );
585
- llmCreds = null; // triggers manual fallback below
746
+ llmCreds = null;
586
747
  }
587
748
  }
588
749
 
589
750
  if (!llmCreds || !llmResponse) {
590
- // Manual fallback: copy-paste mode (no API key configured)
751
+ // Manual fallback: copy-paste mode
591
752
  console.log(
592
753
  `\n ${DIM}[3/5] LLM Reflection — copy the prompt below and paste the response${RESET}`,
593
754
  );
@@ -619,21 +780,42 @@ async function cmdDigest(args) {
619
780
  llmResponse = responseLines.join("\n");
620
781
  }
621
782
 
622
- console.log(`\n ${DIM}[4/5] Parsing idioms...${RESET}`);
623
- const newIdioms = parseLlmYamlResponse(llmResponse);
624
- if (!newIdioms.length) {
625
- console.log(` ${YELLOW}⚠ No idioms extracted from LLM response.${RESET}`);
783
+ console.log(`\n ${DIM}[4/5] Parsing idioms/cases...${RESET}`);
784
+ let newIdioms = [];
785
+ let newCases = [];
786
+
787
+ if (isLogMode) {
788
+ const parsed = parseEvolutionYaml(llmResponse);
789
+ newIdioms = parsed.idioms;
790
+ newCases = parsed.cases;
791
+ } else {
792
+ newIdioms = parseLlmYamlResponse(llmResponse);
793
+ }
794
+
795
+ if (!newIdioms.length && !newCases.length) {
626
796
  console.log(
627
- ` ${DIM} The LLM may have returned idioms: [] no architectural pattern detected.${RESET}\n`,
797
+ ` ${YELLOW} No idioms or cases extracted from LLM response.${RESET}`,
628
798
  );
799
+ console.log(`\n`);
629
800
  return;
630
801
  }
631
802
 
632
- console.log(` ${GREEN}✔ Extracted ${newIdioms.length} idiom(s)${RESET}`);
633
- for (const idiom of newIdioms) {
634
- console.log(
635
- ` ${CYAN}• ${idiom.pattern || "?"}${RESET} — ${idiom.reason || ""}`,
636
- );
803
+ if (newIdioms.length > 0) {
804
+ console.log(` ${GREEN}✔ Extracted ${newIdioms.length} idiom(s)${RESET}`);
805
+ for (const idiom of newIdioms) {
806
+ console.log(
807
+ ` ${CYAN}• ${idiom.pattern || "?"}${RESET} — ${idiom.reason || ""}`,
808
+ );
809
+ }
810
+ }
811
+
812
+ if (newCases.length > 0) {
813
+ console.log(` ${GREEN}✔ Extracted ${newCases.length} case precedent(s)${RESET}`);
814
+ for (const c of newCases) {
815
+ console.log(
816
+ ` ${RED}• ${c.pattern || "?"}${RESET} — ${c.reason || ""}`,
817
+ );
818
+ }
637
819
  }
638
820
 
639
821
  console.log(
@@ -647,10 +829,8 @@ async function cmdDigest(args) {
647
829
  let added = 0;
648
830
 
649
831
  for (const idiom of newIdioms) {
650
- // FIX: Use Levenshtein normalised similarity (threshold 0.80) instead of
651
- // substring .includes() which was over-aggressive and blocked valid idioms.
652
832
  if (isDuplicateIdiom(idiom.pattern || "", existing)) {
653
- console.log(` ${DIM} Skipped near-duplicate: ${idiom.pattern}${RESET}`);
833
+ console.log(` ${DIM} Skipped near-duplicate idiom: ${idiom.pattern}${RESET}`);
654
834
  continue;
655
835
  }
656
836
  merged.push({
@@ -664,17 +844,86 @@ async function cmdDigest(args) {
664
844
  added++;
665
845
  }
666
846
 
667
- if (added === 0) {
668
- console.log(
669
- ` ${YELLOW}⚠ All extracted idioms were duplicates. SKILL.md unchanged.${RESET}\n`,
670
- );
671
- return;
847
+ if (added > 0) {
848
+ log.total_idioms = merged.length;
849
+ const skillMd = renderSkillMd(merged, (log.cycles || []).length + 1);
850
+ fs.mkdirSync(SKILL_DIR, { recursive: true });
851
+ fs.writeFileSync(SKILL_FILE, skillMd, "utf8");
852
+ console.log(` ${GREEN}✔ ${added} new idiom(s) added to SKILL.md${RESET}`);
853
+ } else {
854
+ console.log(` ${DIM} No new unique idioms added to SKILL.md.${RESET}`);
672
855
  }
673
856
 
674
- log.total_idioms = merged.length;
675
- const skillMd = renderSkillMd(merged, (log.cycles || []).length + 1);
676
- fs.mkdirSync(SKILL_DIR, { recursive: true });
677
- fs.writeFileSync(SKILL_FILE, skillMd, "utf8");
857
+ // Record Case Precedents if present
858
+ if (newCases && newCases.length > 0) {
859
+ console.log(`\n ${DIM}Merging into Case Law precedents...${RESET}`);
860
+ const caseLawManager = require("./case_law_manager");
861
+ const clIndex = caseLawManager.loadIndex();
862
+ let clNextId = clIndex.next_id;
863
+ let clAdded = 0;
864
+
865
+ for (const c of newCases) {
866
+ const diffText = c.pattern || "";
867
+ const reason = c.reason || "";
868
+ const domain = c.domain || "general";
869
+ const verdict = c.verdict || "REJECTED";
870
+ const fingerprint = caseLawManager.contentHash(diffText);
871
+
872
+ let isDup = false;
873
+ for (const existing of clIndex.cases) {
874
+ if (existing.fingerprint === fingerprint) {
875
+ isDup = true;
876
+ break;
877
+ }
878
+ }
879
+ if (isDup) {
880
+ console.log(
881
+ ` ${DIM} Skipped duplicate case: ${reason.slice(0, 50)}...${RESET}`,
882
+ );
883
+ continue;
884
+ }
885
+
886
+ const deltaCase = caseLawManager.semanticDelta(diffText);
887
+ const tags = caseLawManager.extractTags(diffText + " " + reason);
888
+ const caseRecord = {
889
+ id: clNextId,
890
+ fingerprint,
891
+ timestamp: new Date().toISOString().slice(0, 19),
892
+ domain,
893
+ verdict,
894
+ reason: reason.trim(),
895
+ pr_ref: "auto-evolution",
896
+ reviewer: "skill-evolution",
897
+ tags,
898
+ stack_version: null,
899
+ diff_raw: diffText.trim(),
900
+ diff_delta: deltaCase,
901
+ auto_recorded: true,
902
+ };
903
+
904
+ caseLawManager.saveCase(caseRecord);
905
+ clIndex.cases.push({
906
+ id: clNextId,
907
+ fingerprint,
908
+ domain,
909
+ verdict,
910
+ tags,
911
+ timestamp: caseRecord.timestamp,
912
+ reason_summary: reason.trim().slice(0, 120),
913
+ stack_version: null,
914
+ });
915
+ clNextId++;
916
+ clAdded++;
917
+ }
918
+
919
+ if (clAdded > 0) {
920
+ clIndex.next_id = clNextId;
921
+ caseLawManager.saveIndex(clIndex);
922
+ console.log(` ${GREEN}✔ ${clAdded} new Case Law precedent(s) recorded.${RESET}`);
923
+ } else {
924
+ console.log(` ${DIM} No new unique Case Law precedents recorded.${RESET}`);
925
+ }
926
+ }
678
927
 
679
928
  log.cycles = log.cycles || [];
680
929
  log.cycles.push({
@@ -687,15 +936,11 @@ async function cmdDigest(args) {
687
936
  log.total_tokens_saved = (log.total_tokens_saved || 0) + savedTokens;
688
937
  saveLog(log);
689
938
 
690
- console.log(`\n ${GREEN}✔ ${added} new idiom(s) added to SKILL.md${RESET}`);
691
- console.log(` ${DIM} File: ${SKILL_FILE}${RESET}`);
939
+ console.log(`\n ${GREEN}✔ Learn cycle complete.${RESET}`);
692
940
  console.log(` ${DIM} Total idioms: ${merged.length}${RESET}`);
693
941
  console.log(
694
942
  ` ${DIM} Lifetime tokens saved: ${log.total_tokens_saved}${RESET}\n`,
695
943
  );
696
- console.log(
697
- ` ${CYAN}Commit SKILL.md to share your Engineering Culture with the team.${RESET}\n`,
698
- );
699
944
  }
700
945
 
701
946
  function cmdShow() {
@@ -32,6 +32,15 @@ CONFIDENCE CHECK:
32
32
  → Standard library (Node, Python, Rust) → Low risk. Proceed with confidence.
33
33
  ```
34
34
 
35
+ ### Epistemic Confidence Levels (L1-L5)
36
+
37
+ Rate the certainty of your implementation decisions using this hierarchy:
38
+ - **L1: Absolute Certainty (Verified Truth)**: Code is fully checked against active files in the workspace or verified in up-to-date documentation.
39
+ - **L2: High Confidence (Standard API)**: Using standard library or stable, unchanged language features (e.g. standard Node `fs` methods, basic Python functions).
40
+ - **L3: Moderate Confidence (Likely but Unverified)**: Custom utilities or package features that are likely correct but not actively verified. Must add `// VERIFY: [reason]` tags.
41
+ - **L4: Low Confidence (Speculative)**: Unstable APIs, recently modified dependencies, or legacy components. Search or audit first.
42
+ - **L5: Pure Speculation (Guessed / Blind)**: Complete guesswork. Strictly prohibited from code generation. Must stop and research or ask.
43
+
35
44
  ### Uncertainty Markers
36
45
 
37
46
  When uncertain, never silently guess. Use explicit markers:
@@ -193,7 +202,25 @@ ALWAYS:
193
202
 
194
203
  ---
195
204
 
196
- ## 6. Anti-Hallucination Quick Reference
205
+ ## 6. Fabel-5 Cognitive Boundaries (Wellbeing, Evenhandedness, Memory)
206
+
207
+ ### User Wellbeing & Safety
208
+ * **No Psychoanalysis / Diagnosis**: Reflect what is said without diagnosing or assigning psychological narratives (e.g. "you restrict because of trauma"). Suggest professional help without clinical labels.
209
+ * **Self-Harm Interruptions**: Never suggest physical substitutes (holding ice, snapping rubber bands, drawing lines) or mimic self-harm. They reinforce the self-harm loop.
210
+ * **No Over-reliance**: Do not thank the user for reaching out, encourage them to stay, or reiterate willingness to continue. Avoid conversational dependencies.
211
+ * **Positive Paths**: Acknowledge distress without reflective listening that amplifies negative spirals. Keep paths to external help open.
212
+
213
+ ### Moral & Political Evenhandedness
214
+ * **Nuance Over Brevity**: Reject requests for simple yes/no or one-word answers on contested political, ethical, or policy issues. Give a fair, balanced overview of existing positions.
215
+ * **Opposing Perspectives**: Conclude arguments for positions by presenting opposing viewpoints or empirical disputes even if the user/AI agrees with the primary view.
216
+
217
+ ### Memory & Preference Boundaries
218
+ * **Invisible Integration**: Integrate remembered user context silently without attribution or observation verbs ("I notice in your profile...", "Based on your memory...").
219
+ * **Expertise Tuning**: Match language and technical depth to the user's stated background without lecturing.
220
+
221
+ ---
222
+
223
+ ## 7. Anti-Hallucination Quick Reference
197
224
 
198
225
  High-risk hallucination zones (verify before using):
199
226
 
@@ -211,6 +238,15 @@ When in doubt: **search the official docs**. Never trust training data for API s
211
238
 
212
239
  ---
213
240
 
241
+ ## ⚡ Hallucination Heatmap (High-Risk Zones)
242
+
243
+ - **Next.js 15+ Route Handlers**: Dynamic functions (`headers()`, `cookies()`, `params`) are now async and must be awaited. Unawaited calls throw runtime errors.
244
+ - **React 19 Hooks**: `useFormState` was renamed to `useActionState`. Direct context creation using `React.createServerContext()` was removed.
245
+ - **Drizzle ORM Queries**: `db.select().from().filter()` does not exist; Drizzle uses `.where()` for filtering.
246
+ - **OpenAI / Anthropic SDKs**: Model strings (e.g., trying to use `gpt-5` or `claude-4-opus` which do not exist or are incorrect).
247
+
248
+ ---
249
+
214
250
  ## LLM Traps — Self-Audit
215
251
 
216
252
  ```
@@ -109,6 +109,7 @@ When unsure: write `// VERIFY: [specific reason]` instead of hallucinating.
109
109
  precedence-reviewer→ Enforces repository Case Law and past rejections (Runs First)
110
110
  logic-reviewer → Hallucinated methods, undefined refs, impossible logic
111
111
  security-auditor → OWASP vulnerabilities, hardcoded secrets, injection
112
+ complexity-reviewer→ Enforces the Dependency Ladder to prevent over-engineering
112
113
  ```
113
114
 
114
115
  **Auto-activated by keywords:**
@@ -1,9 +1,9 @@
1
1
  ---
2
- description: Run ALL 19 Tribunal reviewer agents simultaneously. Maximum hallucination coverage. Use before merging any AI-generated code, before production deployments, or when maximum confidence is required.
2
+ description: Run ALL 20 Tribunal reviewer agents simultaneously. Maximum hallucination coverage. Use before merging any AI-generated code, before production deployments, or when maximum confidence is required.
3
3
  required-skills: all domain skills auto-loaded
4
4
  ---
5
5
 
6
- # /tribunal-full — Complete 19-Reviewer Audit
6
+ # /tribunal-full — Complete 20-Reviewer Audit
7
7
 
8
8
  $ARGUMENTS
9
9
 
@@ -32,7 +32,7 @@ Read BEFORE full review:
32
32
 
33
33
  ---
34
34
 
35
- ## 19 Reviewers — All Active Simultaneously
35
+ ## 20 Reviewers — All Active Simultaneously
36
36
 
37
37
  ```
38
38
  Tier 1: Always active (universal concerns)
@@ -44,6 +44,7 @@ Tier 1: Always active (universal concerns)
44
44
  Tier 2: Code quality
45
45
  ├── dependency-reviewer → Fabricated packages, supply chain, version compatibility
46
46
  ├── type-safety-reviewer → 'any' epidemic, Zod parse vs cast, unguarded access
47
+ ├── complexity-reviewer → Enforces the Dependency Ladder to prevent over-engineering
47
48
  ├── schema-reviewer → Missing input validation, loose schemas, raw req.body
48
49
  └── sql-reviewer → Injection, N+1, missing indexes, unscoped mutations
49
50
 
@@ -0,0 +1,134 @@
1
+ # Contributing to Tribunal Kit
2
+
3
+ Thank you for your interest in contributing to Tribunal Kit! This guide will help you get started.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ # Clone the repository
9
+ git clone https://github.com/Harmitx7/tribunal-kit.git
10
+ cd tribunal-kit
11
+
12
+ # Install dependencies
13
+ npm install
14
+
15
+ # Run tests
16
+ npm run test:unit
17
+
18
+ # Run the CLI locally
19
+ node bin/wrapper.js --help
20
+ ```
21
+
22
+ ## Development Setup
23
+
24
+ ### Prerequisites
25
+
26
+ - **Node.js** >= 18.0.0 (LTS recommended)
27
+ - **npm** >= 9
28
+ - **Rust** (optional) — Only needed if modifying the Rust core engine in `crates/`
29
+
30
+ ### Project Structure
31
+
32
+ ```
33
+ tribunal-kit/
34
+ ├── bin/ # CLI entry points (JS monolith + MCP server)
35
+ │ ├── wrapper.js # Main entry: routes to Rust binary or JS fallback
36
+ │ ├── tribunal-kit.js # Legacy JS CLI (1,500+ lines)
37
+ │ └── mcp-server.js # MCP server over JSON-RPC 2.0 / stdio
38
+ ├── dist/ # Modular CLI (lazy-loaded commands)
39
+ │ ├── cli.js # CLI core with command routing
40
+ │ ├── commands/ # Individual command modules
41
+ │ ├── utils/ # Logger, helpers, version checker, hasher
42
+ │ └── index.d.ts # TypeScript declarations
43
+ ├── crates/core/ # Rust core engine (Tokio-based)
44
+ ├── .agent/ # The intelligence payload (agents, skills, workflows)
45
+ │ ├── agents/ # 43 specialist agent definitions
46
+ │ ├── skills/ # Reusable skill packs
47
+ │ ├── workflows/ # 34 workflow definitions
48
+ │ └── scripts/ # Automation scripts
49
+ ├── test/
50
+ │ ├── unit/ # Unit tests (Jest)
51
+ │ └── integration/ # Integration tests
52
+ └── scripts/ # Build & release scripts
53
+ ```
54
+
55
+ ### Running Tests
56
+
57
+ ```bash
58
+ # Unit tests with coverage
59
+ npm run test:unit
60
+
61
+ # All tests
62
+ npm test
63
+
64
+ # Run a specific test file
65
+ npx jest test/unit/memory.test.js
66
+ ```
67
+
68
+ ### Building the Rust Core (Optional)
69
+
70
+ ```bash
71
+ # Build in release mode
72
+ cargo build --release
73
+
74
+ # Run benchmarks comparing Rust vs JS
75
+ npm run benchmark:rust
76
+ ```
77
+
78
+ ## How to Contribute
79
+
80
+ ### Reporting Bugs
81
+
82
+ 1. Check [existing issues](https://github.com/Harmitx7/tribunal-kit/issues) first
83
+ 2. Use the bug report template
84
+ 3. Include: Node.js version, OS, steps to reproduce, expected vs actual behavior
85
+
86
+ ### Suggesting Features
87
+
88
+ 1. Open a [GitHub Discussion](https://github.com/Harmitx7/tribunal-kit/discussions) first
89
+ 2. Describe the use case, not just the solution
90
+ 3. Be specific about which command or workflow this affects
91
+
92
+ ### Submitting Pull Requests
93
+
94
+ 1. **Fork** the repository and create a branch from `main`
95
+ 2. **Write tests** for any new functionality
96
+ 3. **Run the test suite** — all tests must pass: `npm run test:unit`
97
+ 4. **Follow existing code style** — the project uses ESLint
98
+ 5. **Write clear commit messages** following [Conventional Commits](https://www.conventionalcommits.org/):
99
+ ```
100
+ feat: add --json flag to status command
101
+ fix: handle symlinks in init --force
102
+ docs: update CLI reference for memory command
103
+ test: add coverage for marathon harness
104
+ ```
105
+ 6. **Open a PR** with a clear description of what changed and why
106
+
107
+ ### Contributing Agents or Skills
108
+
109
+ Tribunal Kit's value comes from its agent and skill library. To contribute:
110
+
111
+ 1. **Agents** go in `.agent/agents/` as markdown files
112
+ 2. **Skills** go in `.agent/skills/<skill-name>/SKILL.md`
113
+ 3. Follow the existing format — check any existing agent/skill for the structure
114
+ 4. Include the YAML frontmatter (`name`, `description`)
115
+ 5. Add practical, non-obvious guidance — not generic advice
116
+
117
+ ### Code Style
118
+
119
+ - **JavaScript**: CommonJS (`require`/`module.exports`), no transpilation needed
120
+ - **Naming**: `camelCase` for functions/variables, `UPPER_SNAKE` for constants
121
+ - **Error handling**: Always handle errors in async functions
122
+ - **Comments**: Explain *why*, not *what*
123
+ - **No new dependencies**: Zero production dependencies is a feature, not a limitation
124
+
125
+ ## Review Process
126
+
127
+ 1. All PRs are reviewed by a maintainer
128
+ 2. CI must pass (tests + lint)
129
+ 3. Changes to `.agent/` content are reviewed for accuracy and usefulness
130
+ 4. Breaking changes require a discussion before implementation
131
+
132
+ ## License
133
+
134
+ By contributing, you agree that your contributions will be licensed under the [MIT License](LICENSE).