tribunal-kit 3.0.0 → 4.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.
Files changed (233) hide show
  1. package/.agent/ARCHITECTURE.md +99 -99
  2. package/.agent/GEMINI.md +52 -52
  3. package/.agent/agents/accessibility-reviewer.md +187 -220
  4. package/.agent/agents/ai-code-reviewer.md +199 -233
  5. package/.agent/agents/backend-specialist.md +215 -238
  6. package/.agent/agents/code-archaeologist.md +161 -181
  7. package/.agent/agents/database-architect.md +184 -207
  8. package/.agent/agents/debugger.md +191 -218
  9. package/.agent/agents/dependency-reviewer.md +103 -136
  10. package/.agent/agents/devops-engineer.md +218 -238
  11. package/.agent/agents/documentation-writer.md +201 -221
  12. package/.agent/agents/explorer-agent.md +160 -180
  13. package/.agent/agents/frontend-reviewer.md +160 -194
  14. package/.agent/agents/frontend-specialist.md +248 -237
  15. package/.agent/agents/game-developer.md +48 -52
  16. package/.agent/agents/logic-reviewer.md +116 -149
  17. package/.agent/agents/mobile-developer.md +200 -223
  18. package/.agent/agents/mobile-reviewer.md +162 -195
  19. package/.agent/agents/orchestrator.md +181 -211
  20. package/.agent/agents/penetration-tester.md +157 -174
  21. package/.agent/agents/performance-optimizer.md +183 -203
  22. package/.agent/agents/performance-reviewer.md +178 -211
  23. package/.agent/agents/precedence-reviewer.md +213 -0
  24. package/.agent/agents/product-manager.md +142 -162
  25. package/.agent/agents/product-owner.md +6 -25
  26. package/.agent/agents/project-planner.md +142 -162
  27. package/.agent/agents/qa-automation-engineer.md +225 -242
  28. package/.agent/agents/security-auditor.md +174 -194
  29. package/.agent/agents/seo-specialist.md +193 -213
  30. package/.agent/agents/sql-reviewer.md +161 -194
  31. package/.agent/agents/supervisor-agent.md +184 -203
  32. package/.agent/agents/swarm-worker-contracts.md +17 -17
  33. package/.agent/agents/swarm-worker-registry.md +46 -46
  34. package/.agent/agents/test-coverage-reviewer.md +160 -193
  35. package/.agent/agents/test-engineer.md +0 -21
  36. package/.agent/agents/type-safety-reviewer.md +175 -208
  37. package/.agent/patterns/generator.md +9 -9
  38. package/.agent/patterns/inversion.md +12 -12
  39. package/.agent/patterns/pipeline.md +9 -9
  40. package/.agent/patterns/reviewer.md +13 -13
  41. package/.agent/patterns/tool-wrapper.md +9 -9
  42. package/.agent/rules/GEMINI.md +63 -63
  43. package/.agent/scripts/append_flow.js +72 -0
  44. package/.agent/scripts/case_law_manager.py +525 -0
  45. package/.agent/scripts/compress_skills.py +167 -0
  46. package/.agent/scripts/consolidate_skills.py +173 -0
  47. package/.agent/scripts/deep_compress.py +202 -0
  48. package/.agent/scripts/minify_context.py +80 -0
  49. package/.agent/scripts/security_scan.py +1 -1
  50. package/.agent/scripts/skill_evolution.py +563 -0
  51. package/.agent/scripts/strip_tribunal.py +41 -0
  52. package/.agent/skills/agent-organizer/SKILL.md +100 -126
  53. package/.agent/skills/agentic-patterns/SKILL.md +0 -70
  54. package/.agent/skills/ai-prompt-injection-defense/SKILL.md +134 -160
  55. package/.agent/skills/api-patterns/SKILL.md +123 -215
  56. package/.agent/skills/api-security-auditor/SKILL.md +143 -177
  57. package/.agent/skills/app-builder/SKILL.md +334 -50
  58. package/.agent/skills/app-builder/templates/SKILL.md +13 -15
  59. package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +16 -16
  60. package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +22 -22
  61. package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +18 -18
  62. package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +20 -20
  63. package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +17 -17
  64. package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +18 -18
  65. package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +21 -21
  66. package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +19 -19
  67. package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +26 -26
  68. package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +26 -26
  69. package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +19 -19
  70. package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +18 -18
  71. package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +20 -20
  72. package/.agent/skills/appflow-wireframe/SKILL.md +95 -121
  73. package/.agent/skills/architecture/SKILL.md +169 -331
  74. package/.agent/skills/authentication-best-practices/SKILL.md +139 -173
  75. package/.agent/skills/bash-linux/SKILL.md +129 -154
  76. package/.agent/skills/behavioral-modes/SKILL.md +8 -69
  77. package/.agent/skills/brainstorming/SKILL.md +436 -104
  78. package/.agent/skills/building-native-ui/SKILL.md +152 -174
  79. package/.agent/skills/clean-code/SKILL.md +331 -360
  80. package/.agent/skills/code-review-checklist/SKILL.md +0 -62
  81. package/.agent/skills/config-validator/SKILL.md +115 -141
  82. package/.agent/skills/csharp-developer/SKILL.md +468 -528
  83. package/.agent/skills/database-design/SKILL.md +104 -369
  84. package/.agent/skills/deployment-procedures/SKILL.md +119 -145
  85. package/.agent/skills/devops-engineer/SKILL.md +295 -332
  86. package/.agent/skills/devops-incident-responder/SKILL.md +87 -113
  87. package/.agent/skills/doc.md +5 -5
  88. package/.agent/skills/documentation-templates/SKILL.md +27 -63
  89. package/.agent/skills/edge-computing/SKILL.md +131 -157
  90. package/.agent/skills/extract-design-system/SKILL.md +108 -134
  91. package/.agent/skills/framer-motion-expert/SKILL.md +111 -855
  92. package/.agent/skills/frontend-design/SKILL.md +151 -499
  93. package/.agent/skills/game-design-expert/SKILL.md +79 -105
  94. package/.agent/skills/game-engineering-expert/SKILL.md +96 -122
  95. package/.agent/skills/geo-fundamentals/SKILL.md +97 -124
  96. package/.agent/skills/github-operations/SKILL.md +279 -314
  97. package/.agent/skills/gsap-expert/SKILL.md +119 -826
  98. package/.agent/skills/i18n-localization/SKILL.md +113 -138
  99. package/.agent/skills/intelligent-routing/SKILL.md +167 -127
  100. package/.agent/skills/lint-and-validate/SKILL.md +16 -52
  101. package/.agent/skills/llm-engineering/SKILL.md +344 -357
  102. package/.agent/skills/local-first/SKILL.md +128 -154
  103. package/.agent/skills/mcp-builder/SKILL.md +92 -118
  104. package/.agent/skills/mobile-design/SKILL.md +213 -219
  105. package/.agent/skills/motion-engineering/SKILL.md +184 -0
  106. package/.agent/skills/nextjs-react-expert/SKILL.md +99 -698
  107. package/.agent/skills/nodejs-best-practices/SKILL.md +498 -559
  108. package/.agent/skills/observability/SKILL.md +293 -330
  109. package/.agent/skills/parallel-agents/SKILL.md +96 -122
  110. package/.agent/skills/performance-profiling/SKILL.md +217 -254
  111. package/.agent/skills/plan-writing/SKILL.md +92 -118
  112. package/.agent/skills/platform-engineer/SKILL.md +97 -123
  113. package/.agent/skills/playwright-best-practices/SKILL.md +137 -162
  114. package/.agent/skills/powershell-windows/SKILL.md +112 -146
  115. package/.agent/skills/project-idioms/SKILL.md +87 -0
  116. package/.agent/skills/python-patterns/SKILL.md +15 -35
  117. package/.agent/skills/python-pro/SKILL.md +148 -754
  118. package/.agent/skills/react-specialist/SKILL.md +123 -827
  119. package/.agent/skills/readme-builder/SKILL.md +23 -85
  120. package/.agent/skills/realtime-patterns/SKILL.md +269 -304
  121. package/.agent/skills/red-team-tactics/SKILL.md +18 -51
  122. package/.agent/skills/rust-pro/SKILL.md +623 -701
  123. package/.agent/skills/seo-fundamentals/SKILL.md +129 -154
  124. package/.agent/skills/server-management/SKILL.md +164 -190
  125. package/.agent/skills/shadcn-ui-expert/SKILL.md +181 -206
  126. package/.agent/skills/skill-creator/SKILL.md +24 -56
  127. package/.agent/skills/sql-pro/SKILL.md +579 -633
  128. package/.agent/skills/supabase-postgres-best-practices/SKILL.md +35 -66
  129. package/.agent/skills/swiftui-expert/SKILL.md +151 -176
  130. package/.agent/skills/systematic-debugging/SKILL.md +92 -118
  131. package/.agent/skills/tailwind-patterns/SKILL.md +516 -576
  132. package/.agent/skills/tdd-workflow/SKILL.md +111 -137
  133. package/.agent/skills/test-result-analyzer/SKILL.md +33 -73
  134. package/.agent/skills/testing-patterns/SKILL.md +512 -573
  135. package/.agent/skills/trend-researcher/SKILL.md +30 -71
  136. package/.agent/skills/ui-ux-pro-max/SKILL.md +8 -41
  137. package/.agent/skills/ui-ux-researcher/SKILL.md +51 -91
  138. package/.agent/skills/vue-expert/SKILL.md +127 -866
  139. package/.agent/skills/vulnerability-scanner/SKILL.md +354 -269
  140. package/.agent/skills/web-accessibility-auditor/SKILL.md +168 -193
  141. package/.agent/skills/web-design-guidelines/SKILL.md +25 -61
  142. package/.agent/skills/webapp-testing/SKILL.md +119 -145
  143. package/.agent/skills/whimsy-injector/SKILL.md +58 -132
  144. package/.agent/skills/workflow-optimizer/SKILL.md +28 -68
  145. package/.agent/workflows/api-tester.md +151 -151
  146. package/.agent/workflows/audit.md +127 -138
  147. package/.agent/workflows/brainstorm.md +110 -110
  148. package/.agent/workflows/changelog.md +112 -112
  149. package/.agent/workflows/create.md +124 -124
  150. package/.agent/workflows/debug.md +165 -189
  151. package/.agent/workflows/deploy.md +180 -189
  152. package/.agent/workflows/enhance.md +128 -151
  153. package/.agent/workflows/fix.md +114 -135
  154. package/.agent/workflows/generate.md +13 -4
  155. package/.agent/workflows/migrate.md +160 -160
  156. package/.agent/workflows/orchestrate.md +168 -168
  157. package/.agent/workflows/performance-benchmarker.md +114 -123
  158. package/.agent/workflows/plan.md +173 -173
  159. package/.agent/workflows/preview.md +80 -80
  160. package/.agent/workflows/refactor.md +161 -183
  161. package/.agent/workflows/review-ai.md +101 -129
  162. package/.agent/workflows/review.md +116 -116
  163. package/.agent/workflows/session.md +94 -94
  164. package/.agent/workflows/status.md +79 -79
  165. package/.agent/workflows/strengthen-skills.md +138 -139
  166. package/.agent/workflows/swarm.md +179 -179
  167. package/.agent/workflows/test.md +189 -211
  168. package/.agent/workflows/tribunal-backend.md +94 -113
  169. package/.agent/workflows/tribunal-database.md +95 -115
  170. package/.agent/workflows/tribunal-frontend.md +96 -118
  171. package/.agent/workflows/tribunal-full.md +93 -133
  172. package/.agent/workflows/tribunal-mobile.md +95 -119
  173. package/.agent/workflows/tribunal-performance.md +110 -133
  174. package/.agent/workflows/ui-ux-pro-max.md +122 -143
  175. package/README.md +30 -1
  176. package/bin/tribunal-kit.js +175 -12
  177. package/package.json +25 -4
  178. package/.agent/skills/api-patterns/api-style.md +0 -42
  179. package/.agent/skills/api-patterns/auth.md +0 -24
  180. package/.agent/skills/api-patterns/documentation.md +0 -26
  181. package/.agent/skills/api-patterns/graphql.md +0 -41
  182. package/.agent/skills/api-patterns/rate-limiting.md +0 -31
  183. package/.agent/skills/api-patterns/response.md +0 -37
  184. package/.agent/skills/api-patterns/rest.md +0 -40
  185. package/.agent/skills/api-patterns/security-testing.md +0 -122
  186. package/.agent/skills/api-patterns/trpc.md +0 -41
  187. package/.agent/skills/api-patterns/versioning.md +0 -22
  188. package/.agent/skills/app-builder/agent-coordination.md +0 -71
  189. package/.agent/skills/app-builder/feature-building.md +0 -53
  190. package/.agent/skills/app-builder/project-detection.md +0 -34
  191. package/.agent/skills/app-builder/scaffolding.md +0 -118
  192. package/.agent/skills/app-builder/tech-stack.md +0 -40
  193. package/.agent/skills/architecture/context-discovery.md +0 -43
  194. package/.agent/skills/architecture/examples.md +0 -94
  195. package/.agent/skills/architecture/pattern-selection.md +0 -68
  196. package/.agent/skills/architecture/patterns-reference.md +0 -50
  197. package/.agent/skills/architecture/trade-off-analysis.md +0 -77
  198. package/.agent/skills/brainstorming/dynamic-questioning.md +0 -360
  199. package/.agent/skills/database-design/database-selection.md +0 -43
  200. package/.agent/skills/database-design/indexing.md +0 -39
  201. package/.agent/skills/database-design/migrations.md +0 -48
  202. package/.agent/skills/database-design/optimization.md +0 -36
  203. package/.agent/skills/database-design/orm-selection.md +0 -30
  204. package/.agent/skills/database-design/schema-design.md +0 -56
  205. package/.agent/skills/frontend-design/animation-guide.md +0 -331
  206. package/.agent/skills/frontend-design/color-system.md +0 -329
  207. package/.agent/skills/frontend-design/decision-trees.md +0 -418
  208. package/.agent/skills/frontend-design/motion-graphics.md +0 -306
  209. package/.agent/skills/frontend-design/typography-system.md +0 -363
  210. package/.agent/skills/frontend-design/ux-psychology.md +0 -1116
  211. package/.agent/skills/frontend-design/visual-effects.md +0 -383
  212. package/.agent/skills/intelligent-routing/router-manifest.md +0 -65
  213. package/.agent/skills/mobile-design/decision-trees.md +0 -516
  214. package/.agent/skills/mobile-design/mobile-backend.md +0 -491
  215. package/.agent/skills/mobile-design/mobile-color-system.md +0 -420
  216. package/.agent/skills/mobile-design/mobile-debugging.md +0 -122
  217. package/.agent/skills/mobile-design/mobile-design-thinking.md +0 -357
  218. package/.agent/skills/mobile-design/mobile-navigation.md +0 -458
  219. package/.agent/skills/mobile-design/mobile-performance.md +0 -767
  220. package/.agent/skills/mobile-design/mobile-testing.md +0 -356
  221. package/.agent/skills/mobile-design/mobile-typography.md +0 -433
  222. package/.agent/skills/mobile-design/platform-android.md +0 -666
  223. package/.agent/skills/mobile-design/platform-ios.md +0 -561
  224. package/.agent/skills/mobile-design/touch-psychology.md +0 -537
  225. package/.agent/skills/nextjs-react-expert/1-async-eliminating-waterfalls.md +0 -312
  226. package/.agent/skills/nextjs-react-expert/2-bundle-bundle-size-optimization.md +0 -240
  227. package/.agent/skills/nextjs-react-expert/3-server-server-side-performance.md +0 -490
  228. package/.agent/skills/nextjs-react-expert/4-client-client-side-data-fetching.md +0 -264
  229. package/.agent/skills/nextjs-react-expert/5-rerender-re-render-optimization.md +0 -581
  230. package/.agent/skills/nextjs-react-expert/6-rendering-rendering-performance.md +0 -432
  231. package/.agent/skills/nextjs-react-expert/7-js-javascript-performance.md +0 -684
  232. package/.agent/skills/nextjs-react-expert/8-advanced-advanced-patterns.md +0 -150
  233. package/.agent/skills/vulnerability-scanner/checklists.md +0 -121
@@ -0,0 +1,563 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ skill_evolution.py — Tribunal Kit Skill Evolution Forge
4
+ =========================================================
5
+ Analyzes the delta between what the AI proposed and what the developer
6
+ actually committed, then distills those decisions into evolving
7
+ project-specific SKILL idioms — WITHOUT sending full files to any LLM.
8
+
9
+ Core Strategy: Semantic Delta Extraction
10
+ 1. Read the raw git diff of staged/recent changes
11
+ 2. Strip trivial noise (whitespace, comments, import renames)
12
+ 3. Score remaining lines for "Architectural Weight"
13
+ 4. Only high-weight deltas reach the LLM reflection prompt
14
+ 5. LLM returns structured YAML idiom entries (not prose)
15
+ 6. Idioms are merged into .agent/skills/project-idioms/SKILL.md
16
+
17
+ This keeps token consumption minimal — typically < 500 tokens per digest.
18
+
19
+ Usage:
20
+ python .agent/scripts/skill_evolution.py digest
21
+ python .agent/scripts/skill_evolution.py digest --dry-run
22
+ python .agent/scripts/skill_evolution.py show
23
+ python .agent/scripts/skill_evolution.py reset
24
+ python .agent/scripts/skill_evolution.py status
25
+ """
26
+
27
+ import os
28
+ import sys
29
+ import re
30
+ import json
31
+ import subprocess
32
+ from pathlib import Path
33
+ from datetime import datetime
34
+
35
+ # ── Colours ──────────────────────────────────────────────────────────────────
36
+ GREEN = "\033[92m"
37
+ YELLOW = "\033[93m"
38
+ CYAN = "\033[96m"
39
+ RED = "\033[91m"
40
+ BLUE = "\033[94m"
41
+ BOLD = "\033[1m"
42
+ DIM = "\033[2m"
43
+ RESET = "\033[0m"
44
+
45
+ # ── Paths ─────────────────────────────────────────────────────────────────────
46
+ def find_agent_dir() -> Path:
47
+ current = Path.cwd()
48
+ while current != current.parent:
49
+ candidate = current / ".agent"
50
+ if candidate.is_dir():
51
+ return candidate
52
+ current = current.parent
53
+
54
+ print("\033[91m✖ Error: '.agent' directory not found. Please run 'npx tribunal-kit init' first.\033[0m")
55
+ sys.exit(1)
56
+
57
+ AGENT_DIR = find_agent_dir()
58
+ SKILL_DIR = AGENT_DIR / "skills" / "project-idioms"
59
+ SKILL_FILE = SKILL_DIR / "SKILL.md"
60
+ HISTORY_DIR = AGENT_DIR / "history" / "skill-evolution"
61
+ LOG_FILE = HISTORY_DIR / "digest-log.json"
62
+
63
+ # ── Semantic Delta Thresholds ─────────────────────────────────────────────────
64
+ # Lines with any of these patterns score HIGH architectural weight
65
+ HIGH_WEIGHT_PATTERNS = [
66
+ r"\bclass\b",
67
+ r"\binterface\b",
68
+ r"\btype\s+\w+\s*=",
69
+ r"\bextends\b",
70
+ r"\bimplements\b",
71
+ r"\bthrow\b",
72
+ r"\bcatch\b",
73
+ r"\btry\b",
74
+ r"\bprisma\.\w+\(",
75
+ r"\bsupabase\.",
76
+ r"\bfetch\(",
77
+ r"\baxios\.",
78
+ r"\bReturnType\b",
79
+ r"\bPromise<",
80
+ r"\basync\s+function",
81
+ r"\bawait\b",
82
+ r"\bexport\s+(default\s+)?(class|function|const)",
83
+ r"\bmodule\.exports\b",
84
+ r"\bRouter\b|\bapp\.(get|post|put|delete|patch)\(",
85
+ r"\buse[A-Z]\w+\(", # React hooks
86
+ r"\bcreateContext\(",
87
+ r"\bz\.object\(", # Zod schemas
88
+ r"\bPrisma\b|\bdrizzle\b",
89
+ r"\benv\.\w+",
90
+ r"\bprocess\.env\.",
91
+ ]
92
+
93
+ # Lines that are definitely noise — never escalate to LLM
94
+ NOISE_PATTERNS = [
95
+ r"^\s*$",
96
+ r"^\s*(//|#|/\*).*$",
97
+ r"^\s*\*",
98
+ r"^\s*import\s+\{[^}]+\}\s+from\s+['\"](?!\.)",
99
+ r"^\s*(console\.(log|warn|error)|print\()",
100
+ r"^\s*\w+\s*[:,]?\s*$",
101
+ ]
102
+
103
+ def architectural_weight(line: str) -> int:
104
+ """Return 0 (noise), 1 (low), or 2 (high) for a diff line."""
105
+ code = line.lstrip("+-").strip()
106
+ for p in NOISE_PATTERNS:
107
+ if re.match(p, code):
108
+ return 0
109
+ for p in HIGH_WEIGHT_PATTERNS:
110
+ if re.search(p, code):
111
+ return 2
112
+ return 1
113
+
114
+ def semantic_delta(diff_text: str, min_weight: int = 2) -> str:
115
+ """
116
+ Filter diff to only architectural lines. Returns the trimmed delta
117
+ that will be sent to the LLM — minimal tokens, maximum signal.
118
+ """
119
+ lines = diff_text.splitlines()
120
+ kept = []
121
+ current_hunk_has_high = False
122
+ hunk_lines: list[str] = []
123
+
124
+ for line in lines:
125
+ if line.startswith(("---", "+++", "diff --git")):
126
+ kept.append(line)
127
+ continue
128
+ if line.startswith("@@"):
129
+ # Flush previous hunk if it had high-weight lines
130
+ if current_hunk_has_high:
131
+ kept.extend(hunk_lines)
132
+ current_hunk_has_high = False
133
+ hunk_lines = [line]
134
+ continue
135
+ if line.startswith(("+", "-")):
136
+ w = architectural_weight(line)
137
+ hunk_lines.append(line)
138
+ if w >= min_weight:
139
+ current_hunk_has_high = True
140
+ else:
141
+ hunk_lines.append(line)
142
+
143
+ # Flush final hunk
144
+ if current_hunk_has_high:
145
+ kept.extend(hunk_lines)
146
+
147
+ result = "\n".join(kept)
148
+ # Collapse 3+ blank context lines
149
+ result = re.sub(r"\n([ ]{0,1}\n){3,}", "\n\n", result)
150
+ return result.strip()
151
+
152
+ # ── Git helpers ────────────────────────────────────────────────────────────────
153
+ def get_git_diff(mode: str = "staged") -> str:
154
+ """
155
+ Get the current diff. mode = 'staged' | 'head' | 'all'
156
+ Returns empty string if git is unavailable.
157
+ """
158
+ try:
159
+ if mode == "staged":
160
+ cmd = ["git", "diff", "--cached", "--unified=3"]
161
+ elif mode == "head":
162
+ cmd = ["git", "diff", "HEAD~1", "HEAD", "--unified=3"]
163
+ else:
164
+ cmd = ["git", "diff", "--unified=3"]
165
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
166
+ return result.stdout if result.returncode == 0 else ""
167
+ except (subprocess.SubprocessError, FileNotFoundError):
168
+ return ""
169
+
170
+ def count_tokens_estimate(text: str) -> int:
171
+ """Rough estimate: 1 token ≈ 4 chars for code."""
172
+ return max(1, len(text) // 4)
173
+
174
+ # ── Idiom management ──────────────────────────────────────────────────────────
175
+ def load_existing_idioms() -> list[dict]:
176
+ """Parse the SKILL.md idiom table into structured dicts."""
177
+ if not SKILL_FILE.exists():
178
+ return []
179
+
180
+ content = SKILL_FILE.read_text(encoding="utf-8")
181
+ idioms = []
182
+ # Match rows in the idiom table: | ID | Pattern | Reason | Domain | Since |
183
+ pattern = re.compile(
184
+ r"\|\s*(\d+)\s*\|\s*`([^`]+)`\s*\|\s*([^|]+)\|\s*([^|]+)\|\s*([^|]+)\|"
185
+ )
186
+ for m in pattern.finditer(content):
187
+ idioms.append({
188
+ "id": int(m.group(1)),
189
+ "pattern": m.group(2).strip(),
190
+ "reason": m.group(3).strip(),
191
+ "domain": m.group(4).strip(),
192
+ "since": m.group(5).strip(),
193
+ })
194
+ return idioms
195
+
196
+ def next_idiom_id(idioms: list[dict]) -> int:
197
+ if not idioms:
198
+ return 1
199
+ return max(i["id"] for i in idioms) + 1
200
+
201
+ def render_skill_md(idioms: list[dict], digest_count: int) -> str:
202
+ """Render the full SKILL.md content from idiom list."""
203
+ now = datetime.now().strftime("%Y-%m-%d")
204
+ rows = []
205
+ for idiom in idioms:
206
+ rows.append(
207
+ f"| {idiom['id']} | `{idiom['pattern']}` "
208
+ f"| {idiom['reason']} "
209
+ f"| {idiom['domain']} "
210
+ f"| {idiom['since']} |"
211
+ )
212
+ table = "\n".join(rows) if rows else "_No idioms recorded yet._"
213
+
214
+ return f"""---
215
+ name: project-idioms
216
+ description: >
217
+ Auto-evolved skill containing project-specific architectural idioms.
218
+ Generated by skill_evolution.py — do not edit manually. Commit this
219
+ file to share your Engineering Culture across the team.
220
+ version: auto
221
+ last-updated: {now}
222
+ digest-cycles: {digest_count}
223
+ pattern: generator
224
+ ---
225
+
226
+ # Project Idioms — Auto-Evolved Skill
227
+
228
+ > **Authority Level: ABSOLUTE**
229
+ > These idioms were extracted from the developer's own code decisions.
230
+ > They override generic agent defaults. Every agent MUST respect them.
231
+
232
+ ---
233
+
234
+ ## How Idioms Are Born
235
+
236
+ 1. Developer commits code that differs from the AI proposal.
237
+ 2. `skill_evolution.py digest` extracts architectural deltas only.
238
+ 3. A minimal LLM reflection prompt (< 500 tokens) identifies the "WHY."
239
+ 4. The idiom is recorded here with a stable pattern + reason pair.
240
+
241
+ ---
242
+
243
+ ## Recorded Idioms
244
+
245
+ | ID | Pattern | Why This Project Uses It | Domain | Since |
246
+ |:---|:--------|:-------------------------|:-------|:------|
247
+ {table}
248
+
249
+ ---
250
+
251
+ ## Enforcement Rules for All Agents
252
+
253
+ ```
254
+ □ Before proposing code: scan this skill's idiom table
255
+ □ If your proposal contradicts an idiom → flag it explicitly
256
+ □ Never override an idiom silently — always ask the developer first
257
+ □ When citing an idiom: "Per Project Idiom #N: [pattern] — [reason]"
258
+ ```
259
+
260
+ ---
261
+
262
+ ## Digest History
263
+
264
+ Last digest: `{now}`
265
+ Total cycles: `{digest_count}`
266
+
267
+ Run `python .agent/scripts/skill_evolution.py status` to see the full log.
268
+ """
269
+
270
+ def generate_reflection_prompt(delta: str) -> str:
271
+ """
272
+ Minimal, structured prompt for the LLM. Returns YAML idioms only.
273
+ Designed to consume < 500 tokens total (prompt + response).
274
+ """
275
+ return f"""You are analyzing a code delta from a developer who changed an AI-proposed solution.
276
+ Your only job: identify the ARCHITECTURAL IDIOM this change reveals about their project.
277
+
278
+ Rules:
279
+ - Return ONLY a YAML list of idioms. No prose. No explanation outside YAML.
280
+ - Each idiom: pattern (code signature), reason (1 sentence WHY), domain (backend/frontend/database/general)
281
+ - Ignore whitespace, comment, import changes — only architectural choices
282
+ - If no meaningful idiom can be extracted, return: "idioms: []"
283
+ - Maximum 3 idioms per delta.
284
+
285
+ Delta:
286
+ ```
287
+ {delta[:1500]}
288
+ ```
289
+
290
+ Output format (YAML only):
291
+ idioms:
292
+ - pattern: "<code pattern or convention>"
293
+ reason: "<why this project uses this pattern>"
294
+ domain: "<backend|frontend|database|security|performance|general>"
295
+ """
296
+
297
+ def parse_llm_yaml_response(response: str) -> list[dict]:
298
+ """Parse structured YAML from LLM response without pyyaml dependency."""
299
+ idioms = []
300
+ in_idioms = False
301
+ current: dict = {}
302
+
303
+ for line in response.splitlines():
304
+ stripped = line.strip()
305
+ if stripped == "idioms:":
306
+ in_idioms = True
307
+ continue
308
+ if not in_idioms:
309
+ continue
310
+ if stripped.startswith("- pattern:"):
311
+ if current:
312
+ idioms.append(current)
313
+ current = {"pattern": stripped.split(":", 1)[1].strip().strip('"')}
314
+ elif stripped.startswith("reason:") and current:
315
+ current["reason"] = stripped.split(":", 1)[1].strip().strip('"')
316
+ elif stripped.startswith("domain:") and current:
317
+ current["domain"] = stripped.split(":", 1)[1].strip().strip('"')
318
+
319
+ if current and "pattern" in current:
320
+ idioms.append(current)
321
+
322
+ return idioms
323
+
324
+ # ── Log helpers ────────────────────────────────────────────────────────────────
325
+ def load_log() -> dict:
326
+ HISTORY_DIR.mkdir(parents=True, exist_ok=True)
327
+ if LOG_FILE.exists():
328
+ try:
329
+ return json.loads(LOG_FILE.read_text(encoding="utf-8"))
330
+ except Exception:
331
+ pass
332
+ return {"cycles": [], "total_tokens_saved": 0, "total_idioms": 0}
333
+
334
+ def save_log(log: dict) -> None:
335
+ HISTORY_DIR.mkdir(parents=True, exist_ok=True)
336
+ LOG_FILE.write_text(json.dumps(log, indent=2), encoding="utf-8")
337
+
338
+ # ── Commands ──────────────────────────────────────────────────────────────────
339
+ def cmd_digest(args: list[str]) -> None:
340
+ dry_run = "--dry-run" in args
341
+ diff_mode = "head" if "--head" in args else "staged"
342
+
343
+ print(f"\n{BOLD}{CYAN}━━━ Skill Evolution — Digest Cycle ━━━━━━━━━━━━━━━━{RESET}")
344
+ if dry_run:
345
+ print(f" {YELLOW}DRY RUN — no files will be written{RESET}\n")
346
+
347
+ # Step 1: Get diff
348
+ print(f" {DIM}[1/5] Fetching git diff ({diff_mode})...{RESET}")
349
+ raw_diff = get_git_diff(diff_mode)
350
+ if not raw_diff.strip():
351
+ print(f" {YELLOW}⚠ No diff found. Commit or stage changes first.{RESET}")
352
+ print(f" {DIM}Tip: Use --head to diff against the last commit.{RESET}\n")
353
+ return
354
+
355
+ raw_tokens = count_tokens_estimate(raw_diff)
356
+ print(f" {DIM} Raw diff: ~{raw_tokens} tokens ({len(raw_diff)} chars){RESET}")
357
+
358
+ # Step 2: Extract semantic delta
359
+ print(f" {DIM}[2/5] Extracting architectural delta (Semantic Filter)...{RESET}")
360
+ delta = semantic_delta(raw_diff, min_weight=2)
361
+ if not delta.strip():
362
+ print(f" {GREEN}✔ Delta is 100% trivial (whitespace/comments/imports only).{RESET}")
363
+ print(f" {DIM} No LLM call needed. Zero tokens consumed.{RESET}\n")
364
+ return
365
+
366
+ delta_tokens = count_tokens_estimate(delta)
367
+ saved_tokens = raw_tokens - delta_tokens
368
+ saved_pct = int((saved_tokens / max(raw_tokens, 1)) * 100)
369
+ print(f" {GREEN}✔ Filtered to ~{delta_tokens} tokens "
370
+ f"({saved_pct}% reduction, saved ~{saved_tokens} tokens){RESET}")
371
+
372
+ # Step 3: Show delta preview
373
+ print(f"\n {BOLD}Architectural Delta Preview:{RESET}")
374
+ preview_lines = delta.splitlines()[:20]
375
+ for line in preview_lines:
376
+ if line.startswith("+"):
377
+ print(f" {GREEN}{line}{RESET}")
378
+ elif line.startswith("-"):
379
+ print(f" {RED}{line}{RESET}")
380
+ elif line.startswith("@@"):
381
+ print(f" {BLUE}{line}{RESET}")
382
+ else:
383
+ print(f" {DIM}{line}{RESET}")
384
+ if len(delta.splitlines()) > 20:
385
+ print(f" {DIM}... ({len(delta.splitlines()) - 20} more lines){RESET}")
386
+
387
+ if dry_run:
388
+ print(f"\n {YELLOW}[DRY RUN] Would send {delta_tokens} tokens to LLM for reflection.{RESET}")
389
+ print(f" {DIM}Run without --dry-run to complete the digest.{RESET}\n")
390
+ return
391
+
392
+ # Step 4: LLM reflection (user pastes response)
393
+ print(f"\n {DIM}[3/5] LLM Reflection — copy the prompt below and paste the response{RESET}")
394
+ print(f"\n {BOLD}{'─'*60}{RESET}")
395
+ prompt = generate_reflection_prompt(delta)
396
+ print(prompt)
397
+ print(f" {BOLD}{'─'*60}{RESET}")
398
+ print(f"\n {BOLD}Paste LLM response below (type END_RESPONSE when done):{RESET}")
399
+
400
+ response_lines = []
401
+ while True:
402
+ try:
403
+ line = input()
404
+ except EOFError:
405
+ break
406
+ if line.strip() == "END_RESPONSE":
407
+ break
408
+ response_lines.append(line)
409
+ llm_response = "\n".join(response_lines)
410
+
411
+ # Step 5: Parse + merge
412
+ print(f"\n {DIM}[4/5] Parsing idioms...{RESET}")
413
+ new_idioms = parse_llm_yaml_response(llm_response)
414
+ if not new_idioms:
415
+ print(f" {YELLOW}⚠ No idioms extracted from LLM response.{RESET}")
416
+ print(f" {DIM} The LLM may have returned idioms: [] — no architectural pattern detected.{RESET}\n")
417
+ return
418
+
419
+ print(f" {GREEN}✔ Extracted {len(new_idioms)} idiom(s){RESET}")
420
+ for idiom in new_idioms:
421
+ print(f" {CYAN}• {idiom.get('pattern', '?')}{RESET} — {idiom.get('reason', '')}")
422
+
423
+ print(f"\n {DIM}[5/5] Merging into project-idioms/SKILL.md...{RESET}")
424
+ existing = load_existing_idioms()
425
+ log = load_log()
426
+ next_id = next_idiom_id(existing)
427
+
428
+ today = datetime.now().strftime("%Y-%m-%d")
429
+ merged = existing.copy()
430
+ added = 0
431
+ for idiom in new_idioms:
432
+ # Deduplicate: skip if pattern is highly similar (simple substring check)
433
+ pattern = idiom.get("pattern", "").lower()
434
+ if any(pattern in ex["pattern"].lower() or ex["pattern"].lower() in pattern
435
+ for ex in existing):
436
+ print(f" {DIM} Skipped duplicate: {idiom.get('pattern')}{RESET}")
437
+ continue
438
+ merged.append({
439
+ "id": next_id,
440
+ "pattern": idiom.get("pattern", "?"),
441
+ "reason": idiom.get("reason", "No reason provided."),
442
+ "domain": idiom.get("domain", "general"),
443
+ "since": today,
444
+ })
445
+ next_id += 1
446
+ added += 1
447
+
448
+ if added == 0:
449
+ print(f" {YELLOW}⚠ All extracted idioms were duplicates. SKILL.md unchanged.{RESET}\n")
450
+ return
451
+
452
+ # Write SKILL.md
453
+ log["total_idioms"] = len(merged)
454
+ skill_md = render_skill_md(merged, len(log["cycles"]) + 1)
455
+ SKILL_DIR.mkdir(parents=True, exist_ok=True)
456
+ SKILL_FILE.write_text(skill_md, encoding="utf-8")
457
+
458
+ # Update log
459
+ log["cycles"].append({
460
+ "timestamp": datetime.now().isoformat(timespec="seconds"),
461
+ "raw_tokens": raw_tokens,
462
+ "delta_tokens": delta_tokens,
463
+ "tokens_saved": saved_tokens,
464
+ "idioms_added": added,
465
+ })
466
+ log["total_tokens_saved"] = log.get("total_tokens_saved", 0) + saved_tokens
467
+ save_log(log)
468
+
469
+ print(f"\n {GREEN}✔ {added} new idiom(s) added to SKILL.md{RESET}")
470
+ print(f" {DIM} File: {SKILL_FILE}{RESET}")
471
+ print(f" {DIM} Total idioms: {len(merged)}{RESET}")
472
+ print(f" {DIM} Lifetime tokens saved: {log['total_tokens_saved']}{RESET}\n")
473
+ print(f" {CYAN}Commit {SKILL_FILE.name} to share your Engineering Culture with the team.{RESET}\n")
474
+
475
+
476
+ def cmd_show(args: list[str]) -> None:
477
+ if not SKILL_FILE.exists():
478
+ print(f"{YELLOW}No project-idioms skill found. Run 'digest' first.{RESET}")
479
+ return
480
+ print(SKILL_FILE.read_text(encoding="utf-8"))
481
+
482
+
483
+ def cmd_reset(args: list[str]) -> None:
484
+ if SKILL_FILE.exists():
485
+ SKILL_FILE.unlink()
486
+ print(f"{GREEN}✔ project-idioms/SKILL.md deleted.{RESET}")
487
+ if LOG_FILE.exists():
488
+ LOG_FILE.unlink()
489
+ print(f"{GREEN}✔ Digest log cleared.{RESET}")
490
+ print(f"{DIM}Run 'digest' to start a fresh evolution cycle.{RESET}")
491
+
492
+
493
+ def cmd_status(args: list[str]) -> None:
494
+ log = load_log()
495
+ cycles = log.get("cycles", [])
496
+ total_saved = log.get("total_tokens_saved", 0)
497
+ total_idioms = log.get("total_idioms", 0)
498
+
499
+ idioms_exist = SKILL_FILE.exists()
500
+
501
+ print(f"\n{BOLD}{CYAN}━━━ Skill Evolution Status ━━━━━━━━━━━━━━━━━━━━━━━━{RESET}")
502
+ print(f" Digest cycles : {BOLD}{len(cycles)}{RESET}")
503
+ print(f" Total idioms : {BOLD}{total_idioms}{RESET}")
504
+ print(f" Tokens saved : {GREEN}{total_saved:,} tokens{RESET} "
505
+ f"(≈ ${total_saved / 1_000_000 * 3:.4f} at $3/M)")
506
+ print(f" SKILL.md exists : {'✔' if idioms_exist else '✗'}")
507
+
508
+ if cycles:
509
+ print(f"\n {BOLD}Last 5 digest cycles:{RESET}")
510
+ for cycle in reversed(cycles[-5:]):
511
+ ts = cycle.get("timestamp", "?")[:16]
512
+ delta_t = cycle.get("delta_tokens", 0)
513
+ saved = cycle.get("tokens_saved", 0)
514
+ added = cycle.get("idioms_added", 0)
515
+ pct = int((saved / max(cycle.get("raw_tokens", 1), 1)) * 100)
516
+ print(f" {DIM}{ts}{RESET} "
517
+ f"delta={delta_t}tok saved={saved}tok ({pct}%) "
518
+ f"idioms+={added}")
519
+
520
+ print(f"{CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━{RESET}\n")
521
+
522
+
523
+ # ── Main ──────────────────────────────────────────────────────────────────────
524
+ COMMANDS = {
525
+ "digest": cmd_digest,
526
+ "show": cmd_show,
527
+ "reset": cmd_reset,
528
+ "status": cmd_status,
529
+ }
530
+
531
+ def main() -> None:
532
+ # Ensure Unicode output works on Windows terminals
533
+ if hasattr(sys.stdout, "reconfigure"):
534
+ sys.stdout.reconfigure(encoding="utf-8", errors="replace")
535
+ argv = sys.argv[1:]
536
+ if not argv or argv[0] in ("-h", "--help", "help"):
537
+ print(f"""
538
+ {BOLD}skill_evolution.py{RESET} — Tribunal Skill Evolution Forge
539
+
540
+ {BOLD}Commands:{RESET}
541
+ digest [--dry-run] [--head] Analyze latest git diff and evolve SKILL.md
542
+ --dry-run : preview without writing
543
+ --head : diff last commit instead of staged
544
+ show Print current project-idioms/SKILL.md
545
+ status Show digest history and token savings
546
+ reset Clear all idioms and start fresh
547
+
548
+ {BOLD}Token Budget:{RESET}
549
+ Raw diff -> Semantic Filter -> Only architectural lines -> LLM
550
+ Typical savings: 70–90% of tokens. Most trivial commits = 0 tokens.
551
+ """)
552
+ return
553
+
554
+ cmd = argv[0]
555
+ rest = argv[1:]
556
+ if cmd not in COMMANDS:
557
+ print(f"{RED}✖ Unknown command: '{cmd}'{RESET}")
558
+ sys.exit(1)
559
+ COMMANDS[cmd](rest)
560
+
561
+
562
+ if __name__ == "__main__":
563
+ main()
@@ -0,0 +1,41 @@
1
+ import os
2
+ import re
3
+
4
+ def strip_boilerplate(file_path):
5
+ with open(file_path, 'r', encoding='utf-8') as f:
6
+ content = f.read()
7
+
8
+ original_len = len(content)
9
+
10
+ # Regex to find everything from "## 🏛️ Tribunal Integration" (or without emojis) to the end of the file or next major H1
11
+ # Often it appears at the end of agents
12
+ content = re.sub(r'## 🏛️ Tribunal Integration[\s\S]*?(?=\n# |\Z)', '', content)
13
+ content = re.sub(r'## Tribunal Integration[\s\S]*?(?=\n# |\Z)', '', content)
14
+
15
+ # Strip Cross-Workflow Navigation since GEMINI.md has global handling
16
+ content = re.sub(r'## Cross-Workflow Navigation[\s\S]*?(?=\n# |\Z)', '', content)
17
+
18
+ # Strip What the Maker Is Not Allowed to Do (it's in GEMINI.md)
19
+ content = re.sub(r'## What the Maker Is Not Allowed to Do[\s\S]*?(?=\n# |\Z)', '', content)
20
+
21
+ with open(file_path, 'w', encoding='utf-8') as f:
22
+ f.write(content.strip() + '\n')
23
+
24
+ return original_len, len(content)
25
+
26
+ def main():
27
+ dirs_to_check = ['.agent/agents', '.agent/workflows']
28
+ total_stripped = 0
29
+
30
+ for d in dirs_to_check:
31
+ if not os.path.exists(d): continue
32
+ for file in os.listdir(d):
33
+ if file.endswith('.md'):
34
+ file_path = os.path.join(d, file)
35
+ orig, new = strip_boilerplate(file_path)
36
+ total_stripped += (orig - new)
37
+
38
+ print(f"Stripped {total_stripped} bytes of repetitive boilerplate.")
39
+
40
+ if __name__ == '__main__':
41
+ main()