tribunal-kit 4.2.0 → 4.3.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/ARCHITECTURE.md +21 -14
- package/.agent/agents/swarm-worker-contracts.md +5 -5
- package/.agent/agents/ui-ux-auditor.md +292 -0
- package/.agent/rules/GEMINI.md +8 -8
- package/.agent/scripts/__pycache__/_colors.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/_utils.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/case_law_manager.cpython-311.pyc +0 -0
- package/.agent/scripts/_colors.js +18 -0
- package/.agent/scripts/_utils.js +42 -0
- package/.agent/scripts/auto_preview.js +197 -0
- package/.agent/scripts/bundle_analyzer.js +290 -0
- package/.agent/scripts/case_law_manager.js +684 -0
- package/.agent/scripts/checklist.js +266 -0
- package/.agent/scripts/colors.js +17 -0
- package/.agent/scripts/compress_skills.js +141 -0
- package/.agent/scripts/consolidate_skills.js +149 -0
- package/.agent/scripts/context_broker.js +609 -0
- package/.agent/scripts/deep_compress.js +150 -0
- package/.agent/scripts/dependency_analyzer.js +272 -0
- package/.agent/scripts/inner_loop_validator.js +465 -0
- package/.agent/scripts/lint_runner.js +187 -0
- package/.agent/scripts/minify_context.js +100 -0
- package/.agent/scripts/patch_skills_meta.js +156 -0
- package/.agent/scripts/patch_skills_output.js +244 -0
- package/.agent/scripts/schema_validator.js +297 -0
- package/.agent/scripts/security_scan.js +303 -0
- package/.agent/scripts/session_manager.js +276 -0
- package/.agent/scripts/skill_evolution.js +644 -0
- package/.agent/scripts/skill_integrator.js +313 -0
- package/.agent/scripts/strengthen_skills.js +193 -0
- package/.agent/scripts/strip_tribunal.js +47 -0
- package/.agent/scripts/swarm_dispatcher.js +360 -0
- package/.agent/scripts/test_runner.js +193 -0
- package/.agent/scripts/utils.js +32 -0
- package/.agent/scripts/verify_all.js +256 -0
- package/.agent/skills/agent-organizer/SKILL.md +3 -3
- package/.agent/skills/agentic-patterns/SKILL.md +3 -3
- package/.agent/skills/ai-prompt-injection-defense/SKILL.md +3 -3
- package/.agent/skills/api-patterns/SKILL.md +3 -3
- package/.agent/skills/api-security-auditor/SKILL.md +3 -3
- package/.agent/skills/app-builder/SKILL.md +3 -3
- package/.agent/skills/app-builder/templates/SKILL.md +1 -1
- package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +1 -1
- package/.agent/skills/appflow-wireframe/SKILL.md +3 -3
- package/.agent/skills/architecture/SKILL.md +3 -3
- package/.agent/skills/authentication-best-practices/SKILL.md +3 -3
- package/.agent/skills/bash-linux/SKILL.md +3 -3
- package/.agent/skills/behavioral-modes/SKILL.md +3 -3
- package/.agent/skills/brainstorming/SKILL.md +3 -3
- package/.agent/skills/building-native-ui/SKILL.md +3 -3
- package/.agent/skills/clean-code/SKILL.md +3 -3
- package/.agent/skills/code-review-checklist/SKILL.md +3 -3
- package/.agent/skills/config-validator/SKILL.md +3 -3
- package/.agent/skills/csharp-developer/SKILL.md +3 -3
- package/.agent/skills/data-validation-schemas/SKILL.md +3 -3
- package/.agent/skills/database-design/SKILL.md +3 -3
- package/.agent/skills/deployment-procedures/SKILL.md +3 -3
- package/.agent/skills/devops-engineer/SKILL.md +3 -3
- package/.agent/skills/devops-incident-responder/SKILL.md +3 -3
- package/.agent/skills/doc.md +1 -1
- package/.agent/skills/documentation-templates/SKILL.md +3 -3
- package/.agent/skills/edge-computing/SKILL.md +3 -3
- package/.agent/skills/error-resilience/SKILL.md +3 -3
- package/.agent/skills/extract-design-system/SKILL.md +3 -3
- package/.agent/skills/framer-motion-expert/SKILL.md +3 -4
- package/.agent/skills/frontend-design/SKILL.md +3 -3
- package/.agent/skills/game-design-expert/SKILL.md +3 -3
- package/.agent/skills/game-engineering-expert/SKILL.md +3 -3
- package/.agent/skills/geo-fundamentals/SKILL.md +3 -3
- package/.agent/skills/github-operations/SKILL.md +3 -3
- package/.agent/skills/gsap-core/SKILL.md +0 -2
- package/.agent/skills/gsap-frameworks/SKILL.md +0 -2
- package/.agent/skills/gsap-performance/SKILL.md +0 -2
- package/.agent/skills/gsap-plugins/SKILL.md +0 -2
- package/.agent/skills/gsap-react/SKILL.md +0 -2
- package/.agent/skills/gsap-scrolltrigger/SKILL.md +0 -2
- package/.agent/skills/gsap-timeline/SKILL.md +0 -2
- package/.agent/skills/gsap-utils/SKILL.md +0 -2
- package/.agent/skills/i18n-localization/SKILL.md +3 -3
- package/.agent/skills/intelligent-routing/SKILL.md +3 -3
- package/.agent/skills/lint-and-validate/SKILL.md +3 -3
- package/.agent/skills/llm-engineering/SKILL.md +3 -3
- package/.agent/skills/local-first/SKILL.md +3 -3
- package/.agent/skills/mcp-builder/SKILL.md +3 -3
- package/.agent/skills/mobile-design/SKILL.md +3 -3
- package/.agent/skills/monorepo-management/SKILL.md +3 -3
- package/.agent/skills/motion-engineering/SKILL.md +4 -4
- package/.agent/skills/nextjs-react-expert/SKILL.md +3 -3
- package/.agent/skills/nodejs-best-practices/SKILL.md +3 -3
- package/.agent/skills/observability/SKILL.md +3 -3
- package/.agent/skills/parallel-agents/SKILL.md +3 -3
- package/.agent/skills/performance-profiling/SKILL.md +3 -3
- package/.agent/skills/plan-writing/SKILL.md +3 -3
- package/.agent/skills/platform-engineer/SKILL.md +3 -3
- package/.agent/skills/playwright-best-practices/SKILL.md +3 -3
- package/.agent/skills/powershell-windows/SKILL.md +3 -3
- package/.agent/skills/project-idioms/SKILL.md +3 -3
- package/.agent/skills/python-patterns/SKILL.md +3 -3
- package/.agent/skills/python-pro/SKILL.md +3 -3
- package/.agent/skills/react-specialist/SKILL.md +3 -3
- package/.agent/skills/readme-builder/SKILL.md +3 -3
- package/.agent/skills/realtime-patterns/SKILL.md +3 -3
- package/.agent/skills/red-team-tactics/SKILL.md +3 -3
- package/.agent/skills/rust-pro/SKILL.md +3 -3
- package/.agent/skills/seo-fundamentals/SKILL.md +3 -3
- package/.agent/skills/server-management/SKILL.md +3 -3
- package/.agent/skills/shadcn-ui-expert/SKILL.md +3 -3
- package/.agent/skills/skill-creator/SKILL.md +3 -3
- package/.agent/skills/sql-pro/SKILL.md +3 -3
- package/.agent/skills/supabase-postgres-best-practices/SKILL.md +3 -3
- package/.agent/skills/swiftui-expert/SKILL.md +3 -3
- package/.agent/skills/systematic-debugging/SKILL.md +3 -3
- package/.agent/skills/tailwind-patterns/SKILL.md +3 -3
- package/.agent/skills/tdd-workflow/SKILL.md +3 -3
- package/.agent/skills/test-result-analyzer/SKILL.md +3 -3
- package/.agent/skills/testing-patterns/SKILL.md +3 -3
- package/.agent/skills/trend-researcher/SKILL.md +3 -3
- package/.agent/skills/typescript-advanced/SKILL.md +3 -3
- package/.agent/skills/ui-ux-pro-max/SKILL.md +3 -3
- package/.agent/skills/ui-ux-researcher/SKILL.md +3 -3
- package/.agent/skills/vue-expert/SKILL.md +3 -3
- package/.agent/skills/vulnerability-scanner/SKILL.md +3 -3
- package/.agent/skills/web-accessibility-auditor/SKILL.md +3 -3
- package/.agent/skills/web-design-guidelines/SKILL.md +3 -3
- package/.agent/skills/webapp-testing/SKILL.md +3 -3
- package/.agent/skills/whimsy-injector/SKILL.md +3 -3
- package/.agent/skills/workflow-optimizer/SKILL.md +3 -3
- package/.agent/workflows/audit.md +6 -6
- package/.agent/workflows/deploy.md +1 -1
- package/.agent/workflows/generate.md +23 -6
- package/.agent/workflows/session.md +5 -5
- package/.agent/workflows/swarm.md +2 -2
- package/README.md +64 -8
- package/bin/tribunal-kit.js +277 -45
- package/package.json +9 -6
- package/scripts/changelog.js +167 -0
- package/scripts/sync-version.js +81 -0
- package/.agent/scripts/__pycache__/auto_preview.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/bundle_analyzer.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/checklist.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/dependency_analyzer.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/security_scan.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/session_manager.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/skill_integrator.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/swarm_dispatcher.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/test_runner.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/verify_all.cpython-311.pyc +0 -0
- package/.agent/scripts/auto_preview.py +0 -180
- package/.agent/scripts/bundle_analyzer.py +0 -259
- package/.agent/scripts/case_law_manager.py +0 -755
- package/.agent/scripts/checklist.py +0 -209
- package/.agent/scripts/compress_skills.py +0 -167
- package/.agent/scripts/consolidate_skills.py +0 -173
- package/.agent/scripts/deep_compress.py +0 -202
- package/.agent/scripts/dependency_analyzer.py +0 -247
- package/.agent/scripts/lint_runner.py +0 -188
- package/.agent/scripts/minify_context.py +0 -80
- package/.agent/scripts/patch_skills_meta.py +0 -177
- package/.agent/scripts/patch_skills_output.py +0 -285
- package/.agent/scripts/schema_validator.py +0 -279
- package/.agent/scripts/security_scan.py +0 -224
- package/.agent/scripts/session_manager.py +0 -261
- package/.agent/scripts/skill_evolution.py +0 -563
- package/.agent/scripts/skill_integrator.py +0 -234
- package/.agent/scripts/strengthen_skills.py +0 -220
- package/.agent/scripts/strip_tribunal.py +0 -41
- package/.agent/scripts/swarm_dispatcher.py +0 -350
- package/.agent/scripts/test_runner.py +0 -192
- package/.agent/scripts/test_swarm_dispatcher.py +0 -163
- package/.agent/scripts/verify_all.py +0 -195
|
@@ -0,0 +1,609 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* context_broker.js — Tribunal Kit Context Density Broker
|
|
4
|
+
* =========================================================
|
|
5
|
+
* "Focus without Compromise" — Intelligent skill selection for all model sizes.
|
|
6
|
+
*
|
|
7
|
+
* Philosophy:
|
|
8
|
+
* This is NOT a filter that removes context. It is a PRIORITIZER that
|
|
9
|
+
* ensures the most relevant rules occupy the highest-attention positions
|
|
10
|
+
* in the AI's context window. Supplementary context is condensed, not cut.
|
|
11
|
+
*
|
|
12
|
+
* For LARGER models (Claude Opus, Gemini 2.5 Pro, GPT-4o):
|
|
13
|
+
* → Level 0 (Essential) skills are injected with full fidelity at the top.
|
|
14
|
+
* → Level 1 (Supplementary) skills are condensed to their key rules only.
|
|
15
|
+
* → Nothing is removed — the model gets everything, ordered optimally.
|
|
16
|
+
*
|
|
17
|
+
* For SMALLER/FASTER models (Gemini Flash, GPT-4o-mini):
|
|
18
|
+
* → Only Level 0 (Essential) skills are included.
|
|
19
|
+
* → This prevents context overflow and attention dilution.
|
|
20
|
+
* → Quality gates remain uncompromised — just fewer rules to track.
|
|
21
|
+
*
|
|
22
|
+
* Tiered Context Priority:
|
|
23
|
+
* Level 0 — Essential: Top matches, full SKILL.md text, injected first
|
|
24
|
+
* Level 1 — Supplementary: Medium matches, condensed to "key rules" section
|
|
25
|
+
* Level 2 — Available: Low matches, listed by name only (for reference)
|
|
26
|
+
*
|
|
27
|
+
* Scoring Algorithm:
|
|
28
|
+
* - Task keyword TF-IDF match against skill frontmatter + description
|
|
29
|
+
* - File type affinity (e.g., .tsx → react-specialist gets +2 boost)
|
|
30
|
+
* - Domain tag match (e.g., "sql" in task → sql-pro gets +3 boost)
|
|
31
|
+
* - Recency boost: skills referenced in the last 3 sessions rank higher
|
|
32
|
+
* - Tribunal alignment: skills matching active reviewers rank higher
|
|
33
|
+
*
|
|
34
|
+
* Usage:
|
|
35
|
+
* node .agent/scripts/context_broker.js --task "Build a login API with JWT"
|
|
36
|
+
* node .agent/scripts/context_broker.js --task "Design a premium landing page" --file Login.tsx
|
|
37
|
+
* node .agent/scripts/context_broker.js --task "..." --model large --output json
|
|
38
|
+
* node .agent/scripts/context_broker.js --task "..." --model small --output names
|
|
39
|
+
* node .agent/scripts/context_broker.js demo
|
|
40
|
+
*
|
|
41
|
+
* Output modes:
|
|
42
|
+
* report (default) — human-readable tiered selection report
|
|
43
|
+
* json — JSON with tiered skill lists
|
|
44
|
+
* names — newline-separated skill names only (for piping)
|
|
45
|
+
* prompt — full injected prompt text ready for an LLM
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
'use strict';
|
|
49
|
+
|
|
50
|
+
const fs = require('fs');
|
|
51
|
+
const path = require('path');
|
|
52
|
+
|
|
53
|
+
// ── Colours ───────────────────────────────────────────────────────────────────
|
|
54
|
+
const GREEN = '\x1b[92m';
|
|
55
|
+
const YELLOW = '\x1b[93m';
|
|
56
|
+
const CYAN = '\x1b[96m';
|
|
57
|
+
const BLUE = '\x1b[94m';
|
|
58
|
+
const RED = '\x1b[91m';
|
|
59
|
+
const BOLD = '\x1b[1m';
|
|
60
|
+
const DIM = '\x1b[2m';
|
|
61
|
+
const RESET = '\x1b[0m';
|
|
62
|
+
|
|
63
|
+
// ── Domain → Skill affinity map ───────────────────────────────────────────────
|
|
64
|
+
// Keywords in the user's task that strongly indicate specific skills.
|
|
65
|
+
// Higher weight = stronger signal.
|
|
66
|
+
const DOMAIN_AFFINITIES = [
|
|
67
|
+
// Backend / API
|
|
68
|
+
{ keywords: ['api', 'rest', 'route', 'endpoint', 'handler', 'express', 'fastapi', 'hono'],
|
|
69
|
+
skills: ['api-patterns', 'nodejs-best-practices', 'backend-specialist', 'error-resilience'], weight: 3 },
|
|
70
|
+
// Database
|
|
71
|
+
{ keywords: ['sql', 'query', 'database', 'prisma', 'drizzle', 'orm', 'postgres', 'mysql', 'schema'],
|
|
72
|
+
skills: ['database-design', 'sql-pro', 'supabase-postgres-best-practices'], weight: 3 },
|
|
73
|
+
// Authentication
|
|
74
|
+
{ keywords: ['auth', 'jwt', 'login', 'oauth', 'session', 'password', 'token', 'rbac'],
|
|
75
|
+
skills: ['authentication-best-practices', 'vulnerability-scanner', 'api-security-auditor'], weight: 3 },
|
|
76
|
+
// React / Next.js
|
|
77
|
+
{ keywords: ['react', 'next', 'nextjs', 'component', 'hook', 'jsx', 'tsx', 'server component', 'server action'],
|
|
78
|
+
skills: ['react-specialist', 'nextjs-react-expert', 'frontend-design'], weight: 3 },
|
|
79
|
+
// Frontend / UI
|
|
80
|
+
{ keywords: ['ui', 'design', 'landing', 'page', 'layout', 'responsive', 'tailwind', 'css', 'style'],
|
|
81
|
+
skills: ['frontend-design', 'ui-ux-pro-max', 'tailwind-patterns', 'web-design-guidelines'], weight: 2 },
|
|
82
|
+
// Animation / Motion
|
|
83
|
+
{ keywords: ['animation', 'gsap', 'framer', 'motion', 'scroll', 'transition', 'parallax'],
|
|
84
|
+
skills: ['motion-engineering', 'framer-motion-expert', 'gsap-core', 'gsap-scrolltrigger'], weight: 3 },
|
|
85
|
+
// AI / LLM
|
|
86
|
+
{ keywords: ['llm', 'openai', 'anthropic', 'gemini', 'embedding', 'ai', 'rag', 'vector', 'chat', 'prompt'],
|
|
87
|
+
skills: ['llm-engineering', 'ai-prompt-injection-defense', 'agentic-patterns'], weight: 3 },
|
|
88
|
+
// Security
|
|
89
|
+
{ keywords: ['security', 'xss', 'injection', 'owasp', 'vulnerability', 'csrf', 'sanitize', 'audit'],
|
|
90
|
+
skills: ['vulnerability-scanner', 'api-security-auditor', 'authentication-best-practices'], weight: 3 },
|
|
91
|
+
// Testing
|
|
92
|
+
{ keywords: ['test', 'spec', 'jest', 'vitest', 'playwright', 'unit test', 'e2e', 'mock'],
|
|
93
|
+
skills: ['testing-patterns', 'playwright-best-practices', 'tdd-workflow'], weight: 3 },
|
|
94
|
+
// Performance
|
|
95
|
+
{ keywords: ['performance', 'optimize', 'bundle', 'cache', 'speed', 'slow', 'lighthouse', 'cwv'],
|
|
96
|
+
skills: ['performance-profiling', 'motion-engineering', 'edge-computing'], weight: 2 },
|
|
97
|
+
// Mobile
|
|
98
|
+
{ keywords: ['mobile', 'react native', 'expo', 'ios', 'android', 'gesture', 'haptic'],
|
|
99
|
+
skills: ['building-native-ui', 'mobile-design', 'agentic-patterns'], weight: 3 },
|
|
100
|
+
// DevOps / CI
|
|
101
|
+
{ keywords: ['docker', 'ci', 'cd', 'deploy', 'pipeline', 'k8s', 'kubernetes', 'github actions'],
|
|
102
|
+
skills: ['devops-engineer', 'deployment-procedures', 'observability'], weight: 2 },
|
|
103
|
+
// Real-time
|
|
104
|
+
{ keywords: ['realtime', 'websocket', 'sse', 'socket', 'live', 'multiplayer', 'collaborative'],
|
|
105
|
+
skills: ['realtime-patterns', 'error-resilience'], weight: 3 },
|
|
106
|
+
// TypeScript
|
|
107
|
+
{ keywords: ['typescript', 'type', 'generic', 'interface', 'satisfies', 'zod', 'pydantic'],
|
|
108
|
+
skills: ['typescript-advanced', 'data-validation-schemas'], weight: 2 },
|
|
109
|
+
// Python
|
|
110
|
+
{ keywords: ['python', 'fastapi', 'django', 'flask', 'pydantic', 'asyncio'],
|
|
111
|
+
skills: ['python-pro', 'python-patterns'], weight: 3 },
|
|
112
|
+
// Architecture
|
|
113
|
+
{ keywords: ['architecture', 'refactor', 'clean', 'solid', 'design pattern', 'monorepo', 'microservice'],
|
|
114
|
+
skills: ['architecture', 'clean-code', 'monorepo-management'], weight: 2 },
|
|
115
|
+
// C# / .NET
|
|
116
|
+
{ keywords: ['csharp', 'c#', 'dotnet', '.net', 'blazor', 'aspnet', 'entity framework'],
|
|
117
|
+
skills: ['csharp-developer'], weight: 3 },
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
// ── File extension → skill boost map ─────────────────────────────────────────
|
|
121
|
+
const EXT_AFFINITIES = {
|
|
122
|
+
'.tsx': ['react-specialist', 'nextjs-react-expert', 'typescript-advanced'],
|
|
123
|
+
'.jsx': ['react-specialist', 'frontend-design'],
|
|
124
|
+
'.ts': ['typescript-advanced', 'nodejs-best-practices'],
|
|
125
|
+
'.vue': ['vue-expert'],
|
|
126
|
+
'.py': ['python-pro', 'python-patterns'],
|
|
127
|
+
'.cs': ['csharp-developer'],
|
|
128
|
+
'.sql': ['sql-pro', 'database-design'],
|
|
129
|
+
'.css': ['tailwind-patterns', 'frontend-design'],
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// ── Core baseline skills — always available to all model sizes ────────────────
|
|
133
|
+
// These are injected for every request at a condensed level.
|
|
134
|
+
const BASELINE_SKILLS = [
|
|
135
|
+
'clean-code',
|
|
136
|
+
'systematic-debugging',
|
|
137
|
+
'error-resilience',
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
// ── Skill catalogue (loaded from disk) ───────────────────────────────────────
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Find the .agent directory by walking up from cwd.
|
|
144
|
+
* @returns {string} path to .agent/
|
|
145
|
+
*/
|
|
146
|
+
function findAgentDir() {
|
|
147
|
+
let current = path.resolve(process.cwd());
|
|
148
|
+
const root = path.parse(current).root;
|
|
149
|
+
while (current !== root) {
|
|
150
|
+
const candidate = path.join(current, '.agent');
|
|
151
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) return candidate;
|
|
152
|
+
current = path.dirname(current);
|
|
153
|
+
}
|
|
154
|
+
console.error(`${RED}✖ .agent/ not found. Run: npx tribunal-kit init${RESET}`);
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Parse the YAML frontmatter from a SKILL.md file.
|
|
160
|
+
* Returns { name, description, ... } or null on parse failure.
|
|
161
|
+
* @param {string} content - Full SKILL.md file text
|
|
162
|
+
*/
|
|
163
|
+
function parseFrontmatter(content) {
|
|
164
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
165
|
+
if (!match) return null;
|
|
166
|
+
const yaml = match[1];
|
|
167
|
+
const obj = {};
|
|
168
|
+
for (const line of yaml.split('\n')) {
|
|
169
|
+
const sep = line.indexOf(':');
|
|
170
|
+
if (sep === -1) continue;
|
|
171
|
+
const key = line.slice(0, sep).trim();
|
|
172
|
+
const val = line.slice(sep + 1).trim().replace(/^["']|["']$/g, '');
|
|
173
|
+
obj[key] = val;
|
|
174
|
+
}
|
|
175
|
+
return obj;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Extract the "key rules" section from a SKILL.md for condensed Level-1 context.
|
|
180
|
+
* Falls back to first 800 chars of content if no section found.
|
|
181
|
+
* @param {string} content
|
|
182
|
+
*/
|
|
183
|
+
function extractKeyRules(content) {
|
|
184
|
+
// Try to find sections named: Key Rules, Rules, Core Rules, Critical Rules, Guardrails
|
|
185
|
+
const sectionMatch = content.match(
|
|
186
|
+
/##\s+(?:Key Rules?|Core Rules?|Critical Rules?|Guardrails?|Rules?)\n([\s\S]*?)(?=\n##\s|$)/i
|
|
187
|
+
);
|
|
188
|
+
if (sectionMatch) return sectionMatch[1].trim().slice(0, 1200);
|
|
189
|
+
// Fallback: strip frontmatter and take first 800 chars
|
|
190
|
+
const bodyStart = content.indexOf('---', 3);
|
|
191
|
+
const body = bodyStart !== -1 ? content.slice(bodyStart + 3).trim() : content;
|
|
192
|
+
return body.slice(0, 800).trim();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Load all skills from .agent/skills/ directory.
|
|
197
|
+
* Returns an array of { name, file, frontmatter, content, keyRules } objects.
|
|
198
|
+
* @param {string} agentDir
|
|
199
|
+
*/
|
|
200
|
+
function loadSkills(agentDir) {
|
|
201
|
+
const skillsDir = path.join(agentDir, 'skills');
|
|
202
|
+
if (!fs.existsSync(skillsDir)) return [];
|
|
203
|
+
|
|
204
|
+
const skills = [];
|
|
205
|
+
const entries = fs.readdirSync(skillsDir, { withFileTypes: true });
|
|
206
|
+
|
|
207
|
+
for (const entry of entries) {
|
|
208
|
+
if (!entry.isDirectory()) continue;
|
|
209
|
+
const skillFile = path.join(skillsDir, entry.name, 'SKILL.md');
|
|
210
|
+
if (!fs.existsSync(skillFile)) continue;
|
|
211
|
+
|
|
212
|
+
try {
|
|
213
|
+
const content = fs.readFileSync(skillFile, 'utf8');
|
|
214
|
+
const frontmatter = parseFrontmatter(content) || {};
|
|
215
|
+
const keyRules = extractKeyRules(content);
|
|
216
|
+
skills.push({
|
|
217
|
+
name: entry.name,
|
|
218
|
+
file: skillFile,
|
|
219
|
+
frontmatter,
|
|
220
|
+
content,
|
|
221
|
+
keyRules,
|
|
222
|
+
description: frontmatter.description || '',
|
|
223
|
+
});
|
|
224
|
+
} catch {
|
|
225
|
+
// Skip unreadable skills silently
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return skills;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// ── Scoring engine ────────────────────────────────────────────────────────────
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Tokenize text into lowercase words (3+ chars).
|
|
235
|
+
* @param {string} text
|
|
236
|
+
* @returns {string[]}
|
|
237
|
+
*/
|
|
238
|
+
function tokenize(text) {
|
|
239
|
+
return (text.match(/\b[a-zA-Z_][a-zA-Z0-9_]{2,}\b/g) || []).map(t => t.toLowerCase());
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Compute a relevance score for a skill against the user's task.
|
|
244
|
+
*
|
|
245
|
+
* Scoring breakdown:
|
|
246
|
+
* - Text overlap between task tokens and skill description/name: up to +5
|
|
247
|
+
* - Domain affinity keyword match: +weight (2 or 3) per match
|
|
248
|
+
* - File extension affinity: +2 per match
|
|
249
|
+
* - Baseline skill bonus: +1 (always present)
|
|
250
|
+
*
|
|
251
|
+
* @param {{ name, description, content }} skill
|
|
252
|
+
* @param {string} task - Raw task text from the user
|
|
253
|
+
* @param {string[]} fileExts - File extensions being touched
|
|
254
|
+
* @param {string[]} taskTokens - Pre-tokenized task
|
|
255
|
+
* @returns {number} score
|
|
256
|
+
*/
|
|
257
|
+
function scoreSkill(skill, task, fileExts, taskTokens) {
|
|
258
|
+
let score = 0;
|
|
259
|
+
const taskLower = task.toLowerCase();
|
|
260
|
+
const skillText = (skill.name + ' ' + skill.description).toLowerCase();
|
|
261
|
+
const skillTokens = tokenize(skillText);
|
|
262
|
+
|
|
263
|
+
// 1. Token overlap (lightweight TF match — no IDF needed at this scale)
|
|
264
|
+
const skillSet = new Set(skillTokens);
|
|
265
|
+
for (const token of taskTokens) {
|
|
266
|
+
if (skillSet.has(token)) score += 1;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// 2. Domain affinity boost
|
|
270
|
+
for (const affinity of DOMAIN_AFFINITIES) {
|
|
271
|
+
const keywordMatch = affinity.keywords.some(k => taskLower.includes(k));
|
|
272
|
+
if (!keywordMatch) continue;
|
|
273
|
+
if (affinity.skills.includes(skill.name)) {
|
|
274
|
+
score += affinity.weight;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// 3. File extension boost
|
|
279
|
+
for (const ext of fileExts) {
|
|
280
|
+
const extSkills = EXT_AFFINITIES[ext] || [];
|
|
281
|
+
if (extSkills.includes(skill.name)) score += 2;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// 4. Baseline skill safety net
|
|
285
|
+
if (BASELINE_SKILLS.includes(skill.name)) score += 1;
|
|
286
|
+
|
|
287
|
+
return score;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Run the tiered selection algorithm.
|
|
292
|
+
*
|
|
293
|
+
* @param {string} task - Raw user task description
|
|
294
|
+
* @param {string[]} files - Affected filenames (for ext detection)
|
|
295
|
+
* @param {string} model - 'large' | 'small' | 'auto'
|
|
296
|
+
* @param {object[]} skills - Loaded skills array from loadSkills()
|
|
297
|
+
* @returns {{ essential: object[], supplementary: object[], available: object[], scores: Map }}
|
|
298
|
+
*/
|
|
299
|
+
function selectSkills(task, files, model, skills) {
|
|
300
|
+
const taskTokens = tokenize(task);
|
|
301
|
+
const fileExts = files.map(f => path.extname(f).toLowerCase()).filter(Boolean);
|
|
302
|
+
|
|
303
|
+
// Score every available skill
|
|
304
|
+
const scored = skills.map(skill => ({
|
|
305
|
+
...skill,
|
|
306
|
+
score: scoreSkill(skill, task, fileExts, taskTokens),
|
|
307
|
+
}));
|
|
308
|
+
scored.sort((a, b) => b.score - a.score);
|
|
309
|
+
|
|
310
|
+
// Determine tier thresholds
|
|
311
|
+
const maxScore = scored[0]?.score || 1;
|
|
312
|
+
const tier0Cut = Math.max(maxScore * 0.65, 2); // Essential: top 65%+ of max score
|
|
313
|
+
const tier1Cut = Math.max(maxScore * 0.3, 1); // Supplementary: 30–65%
|
|
314
|
+
|
|
315
|
+
const essential = scored.filter(s => s.score >= tier0Cut).slice(0, 10);
|
|
316
|
+
const supplementary = scored.filter(s => s.score < tier0Cut && s.score >= tier1Cut).slice(0, 8);
|
|
317
|
+
const available = scored.filter(s => s.score < tier1Cut && s.score > 0).slice(0, 10);
|
|
318
|
+
|
|
319
|
+
// Ensure baseline skills always appear at minimum in supplementary
|
|
320
|
+
for (const base of BASELINE_SKILLS) {
|
|
321
|
+
const inEssential = essential.find(s => s.name === base);
|
|
322
|
+
const inSupplementary = supplementary.find(s => s.name === base);
|
|
323
|
+
if (!inEssential && !inSupplementary) {
|
|
324
|
+
const baseSkill = skills.find(s => s.name === base);
|
|
325
|
+
if (baseSkill) supplementary.push({ ...baseSkill, score: 0.5 });
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// For small models: collapse supplementary into available
|
|
330
|
+
if (model === 'small') {
|
|
331
|
+
return {
|
|
332
|
+
essential: essential.slice(0, 6),
|
|
333
|
+
supplementary: [],
|
|
334
|
+
available: [...supplementary, ...available].slice(0, 8),
|
|
335
|
+
scores: buildScoreMap(scored),
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return { essential, supplementary, available, scores: buildScoreMap(scored) };
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function buildScoreMap(scored) {
|
|
343
|
+
const m = new Map();
|
|
344
|
+
for (const s of scored) m.set(s.name, s.score);
|
|
345
|
+
return m;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// ── Output formatters ─────────────────────────────────────────────────────────
|
|
349
|
+
|
|
350
|
+
function formatReport(task, model, selection, elapsed) {
|
|
351
|
+
const { essential, supplementary, available } = selection;
|
|
352
|
+
const total = essential.length + supplementary.length + available.length;
|
|
353
|
+
|
|
354
|
+
console.log(`\n${BOLD}${CYAN}━━━ Context Broker — Skill Selection ━━━━━━━━━━━━━━━${RESET}`);
|
|
355
|
+
console.log(` Task : ${BOLD}${task.slice(0, 80)}${task.length > 80 ? '...' : ''}${RESET}`);
|
|
356
|
+
console.log(` Model : ${model === 'large' ? GREEN : YELLOW}${model}${RESET} ${DIM}(Focus without Compromise)${RESET}`);
|
|
357
|
+
console.log(` Skills : ${GREEN}${essential.length} essential${RESET} + ${YELLOW}${supplementary.length} supplementary${RESET} + ${DIM}${available.length} available${RESET} of ${total} matched`);
|
|
358
|
+
console.log(` Time : ${elapsed}ms\n`);
|
|
359
|
+
|
|
360
|
+
if (essential.length) {
|
|
361
|
+
console.log(` ${GREEN}${BOLD}▶ Level 0 — Essential (Full Context, Top Priority):${RESET}`);
|
|
362
|
+
for (const s of essential) {
|
|
363
|
+
const score = selection.scores.get(s.name) || 0;
|
|
364
|
+
console.log(` ${GREEN}✦${RESET} ${BOLD}${s.name}${RESET} ${DIM}score=${score.toFixed(1)}${RESET}`);
|
|
365
|
+
if (s.description) console.log(` ${DIM}${s.description.slice(0, 90)}${RESET}`);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (supplementary.length) {
|
|
370
|
+
console.log(`\n ${YELLOW}${BOLD}▶ Level 1 — Supplementary (Key Rules Only):${RESET}`);
|
|
371
|
+
for (const s of supplementary) {
|
|
372
|
+
const score = selection.scores.get(s.name) || 0;
|
|
373
|
+
console.log(` ${YELLOW}◆${RESET} ${s.name} ${DIM}score=${score.toFixed(1)}${RESET}`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (available.length) {
|
|
378
|
+
console.log(`\n ${DIM}▶ Level 2 — Available (Name Reference Only):${RESET}`);
|
|
379
|
+
console.log(` ${DIM}${available.map(s => s.name).join(', ')}${RESET}`);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (model === 'small') {
|
|
383
|
+
console.log(`\n ${YELLOW}⚡ Small model mode: supplementary collapsed. Essential only injected.${RESET}`);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
console.log(`\n${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}\n`);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function formatJson(task, model, selection) {
|
|
390
|
+
return JSON.stringify({
|
|
391
|
+
task,
|
|
392
|
+
model,
|
|
393
|
+
timestamp: new Date().toISOString(),
|
|
394
|
+
essential: selection.essential.map(s => ({ name: s.name, score: selection.scores.get(s.name), description: s.description })),
|
|
395
|
+
supplementary: selection.supplementary.map(s => ({ name: s.name, score: selection.scores.get(s.name) })),
|
|
396
|
+
available: selection.available.map(s => s.name),
|
|
397
|
+
}, null, 2);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function formatNames(selection, model) {
|
|
401
|
+
const all = model === 'large'
|
|
402
|
+
? [...selection.essential, ...selection.supplementary]
|
|
403
|
+
: selection.essential;
|
|
404
|
+
return all.map(s => s.name).join('\n');
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Build a full LLM-ready context prompt string.
|
|
409
|
+
* This is the full output to be injected into an AI system prompt.
|
|
410
|
+
*
|
|
411
|
+
* For large models: Essential = full SKILL.md, Supplementary = key rules section.
|
|
412
|
+
* For small models: Essential = key rules section only.
|
|
413
|
+
*
|
|
414
|
+
* @param {string} task
|
|
415
|
+
* @param {string} model
|
|
416
|
+
* @param {object} selection
|
|
417
|
+
* @returns {string}
|
|
418
|
+
*/
|
|
419
|
+
function formatPrompt(task, model, selection) {
|
|
420
|
+
const lines = [
|
|
421
|
+
`# Tribunal Context Broker — Injected Skills`,
|
|
422
|
+
`# Task: ${task}`,
|
|
423
|
+
`# Model tier: ${model}`,
|
|
424
|
+
`# Generated: ${new Date().toISOString()}`,
|
|
425
|
+
'',
|
|
426
|
+
'## Instructions for the AI',
|
|
427
|
+
'The following skills are ordered by relevance to the current task.',
|
|
428
|
+
'Level 0 skills contain full rule sets. Level 1 skills contain key rules only.',
|
|
429
|
+
'Treat ALL injected rules as mandatory constraints, not suggestions.',
|
|
430
|
+
'',
|
|
431
|
+
'---',
|
|
432
|
+
'',
|
|
433
|
+
];
|
|
434
|
+
|
|
435
|
+
if (selection.essential.length) {
|
|
436
|
+
lines.push('## Level 0 — Essential Skills (Full Context)');
|
|
437
|
+
lines.push('');
|
|
438
|
+
for (const s of selection.essential) {
|
|
439
|
+
lines.push(`### Skill: ${s.name}`);
|
|
440
|
+
lines.push('');
|
|
441
|
+
if (model === 'large') {
|
|
442
|
+
lines.push(s.content || s.keyRules);
|
|
443
|
+
} else {
|
|
444
|
+
lines.push(s.keyRules);
|
|
445
|
+
}
|
|
446
|
+
lines.push('');
|
|
447
|
+
lines.push('---');
|
|
448
|
+
lines.push('');
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
if (model === 'large' && selection.supplementary.length) {
|
|
453
|
+
lines.push('## Level 1 — Supplementary Skills (Key Rules)');
|
|
454
|
+
lines.push('');
|
|
455
|
+
for (const s of selection.supplementary) {
|
|
456
|
+
lines.push(`### Skill: ${s.name} (condensed)`);
|
|
457
|
+
lines.push('');
|
|
458
|
+
lines.push(s.keyRules);
|
|
459
|
+
lines.push('');
|
|
460
|
+
lines.push('---');
|
|
461
|
+
lines.push('');
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if (selection.available.length) {
|
|
466
|
+
lines.push('## Level 2 — Available Skills (Reference Names Only)');
|
|
467
|
+
lines.push('');
|
|
468
|
+
lines.push('The following skills are relevant but not injected to maintain context density.');
|
|
469
|
+
lines.push('Request their full content if needed: ' + selection.available.map(s => s.name).join(', '));
|
|
470
|
+
lines.push('');
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
return lines.join('\n');
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// ── Built-in demo ─────────────────────────────────────────────────────────────
|
|
477
|
+
|
|
478
|
+
function runDemo(agentDir) {
|
|
479
|
+
const skills = loadSkills(agentDir);
|
|
480
|
+
|
|
481
|
+
const scenarios = [
|
|
482
|
+
{ task: 'Build a JWT authentication API with Express.js and Zod validation', file: 'auth.ts', model: 'large' },
|
|
483
|
+
{ task: 'Design a premium landing page with GSAP scroll animations', file: 'Hero.tsx', model: 'large' },
|
|
484
|
+
{ task: 'Write a Prisma query for paginated user orders', file: 'orders.ts', model: 'small' },
|
|
485
|
+
{ task: 'Add RAG pipeline to an OpenAI-powered chat interface', file: 'chat.ts', model: 'large' },
|
|
486
|
+
];
|
|
487
|
+
|
|
488
|
+
console.log(`\n${BOLD}${CYAN}━━━ Context Broker — Demo Mode ━━━━━━━━━━━━━━━━━━━━━${RESET}`);
|
|
489
|
+
console.log(` Loaded ${skills.length} skills from .agent/skills/\n`);
|
|
490
|
+
|
|
491
|
+
for (const scenario of scenarios) {
|
|
492
|
+
const t0 = Date.now();
|
|
493
|
+
const model = scenario.model;
|
|
494
|
+
const selection = selectSkills(scenario.task, [scenario.file], model, skills);
|
|
495
|
+
const elapsed = Date.now() - t0;
|
|
496
|
+
|
|
497
|
+
console.log(`\n ${BOLD}Task: "${scenario.task.slice(0, 70)}"${RESET} ${DIM}[${model} model]${RESET}`);
|
|
498
|
+
console.log(` Essential : ${GREEN}${selection.essential.map(s => s.name).join(', ')}${RESET}`);
|
|
499
|
+
console.log(` Supplementary : ${YELLOW}${selection.supplementary.map(s => s.name).join(', ') || '(small mode)'}${RESET}`);
|
|
500
|
+
console.log(` Time : ${DIM}${elapsed}ms${RESET}`);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
console.log(`\n${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}\n`);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// ── Public API ────────────────────────────────────────────────────────────────
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Programmatic API for use by other Tribunal scripts.
|
|
510
|
+
*
|
|
511
|
+
* @param {string} task - User task description
|
|
512
|
+
* @param {string[]} files - Files being touched
|
|
513
|
+
* @param {string} model - 'large' | 'small' | 'auto'
|
|
514
|
+
* @param {string} agentDir - Path to .agent/ directory
|
|
515
|
+
* @returns {{ essential, supplementary, available, promptText }}
|
|
516
|
+
*/
|
|
517
|
+
function broker(task, files = [], model = 'large', agentDir = null) {
|
|
518
|
+
const resolvedAgentDir = agentDir || findAgentDir();
|
|
519
|
+
const skills = loadSkills(resolvedAgentDir);
|
|
520
|
+
const selection = selectSkills(task, files, model, skills);
|
|
521
|
+
const promptText = formatPrompt(task, model, selection);
|
|
522
|
+
return { ...selection, promptText };
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
module.exports = { broker, selectSkills, loadSkills, scoreSkill, findAgentDir };
|
|
526
|
+
|
|
527
|
+
// ── CLI Entry ─────────────────────────────────────────────────────────────────
|
|
528
|
+
|
|
529
|
+
if (require.main === module) {
|
|
530
|
+
const argv = process.argv.slice(2);
|
|
531
|
+
|
|
532
|
+
if (!argv.length || argv.includes('--help') || argv.includes('-h')) {
|
|
533
|
+
console.log(`
|
|
534
|
+
${BOLD}context_broker.js${RESET} — Tribunal Focus-without-Compromise Context Engine
|
|
535
|
+
|
|
536
|
+
${BOLD}Usage:${RESET}
|
|
537
|
+
node .agent/scripts/context_broker.js --task "<description>" [options]
|
|
538
|
+
node .agent/scripts/context_broker.js demo
|
|
539
|
+
|
|
540
|
+
${BOLD}Options:${RESET}
|
|
541
|
+
--task <text> Task description to match against skill catalogue
|
|
542
|
+
--file <path> File being touched (repeat for multiple files)
|
|
543
|
+
--model <size> large (default) | small — model tier
|
|
544
|
+
--output <format> report (default) | json | names | prompt
|
|
545
|
+
|
|
546
|
+
${BOLD}Model tiers:${RESET}
|
|
547
|
+
large Full Essential + condensed Supplementary (Claude Opus, Gemini 2.5 Pro, GPT-4o)
|
|
548
|
+
small Essential only (Gemini Flash, GPT-4o-mini)
|
|
549
|
+
|
|
550
|
+
${BOLD}Examples:${RESET}
|
|
551
|
+
node .agent/scripts/context_broker.js --task "JWT auth API" --model large
|
|
552
|
+
node .agent/scripts/context_broker.js --task "landing page" --file Hero.tsx --output names
|
|
553
|
+
node .agent/scripts/context_broker.js --task "RAG pipeline" --output prompt > context.md
|
|
554
|
+
node .agent/scripts/context_broker.js demo
|
|
555
|
+
`);
|
|
556
|
+
process.exit(0);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const agentDir = findAgentDir();
|
|
560
|
+
|
|
561
|
+
if (argv[0] === 'demo') {
|
|
562
|
+
runDemo(agentDir);
|
|
563
|
+
process.exit(0);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// Parse args
|
|
567
|
+
const taskIdx = argv.indexOf('--task');
|
|
568
|
+
const modelIdx = argv.indexOf('--model');
|
|
569
|
+
const outputIdx = argv.indexOf('--output');
|
|
570
|
+
|
|
571
|
+
const task = taskIdx !== -1 && argv[taskIdx + 1] ? argv[taskIdx + 1] : '';
|
|
572
|
+
const model = modelIdx !== -1 && argv[modelIdx + 1] ? argv[modelIdx + 1] : 'large';
|
|
573
|
+
const output = outputIdx !== -1 && argv[outputIdx + 1] ? argv[outputIdx + 1] : 'report';
|
|
574
|
+
|
|
575
|
+
if (!task) {
|
|
576
|
+
console.error(`${RED}✖ --task is required${RESET}`);
|
|
577
|
+
process.exit(1);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// Collect --file arguments (may appear multiple times)
|
|
581
|
+
const files = [];
|
|
582
|
+
for (let i = 0; i < argv.length; i++) {
|
|
583
|
+
if (argv[i] === '--file' && argv[i + 1]) files.push(argv[i + 1]);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
const t0 = Date.now();
|
|
587
|
+
const skills = loadSkills(agentDir);
|
|
588
|
+
const selection = selectSkills(task, files, model, skills);
|
|
589
|
+
const elapsed = Date.now() - t0;
|
|
590
|
+
|
|
591
|
+
if (!['large', 'small'].includes(model)) {
|
|
592
|
+
console.error(`${YELLOW}⚠ Unknown model tier "${model}" — defaulting to "large"${RESET}`);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
switch (output) {
|
|
596
|
+
case 'json':
|
|
597
|
+
console.log(formatJson(task, model, selection));
|
|
598
|
+
break;
|
|
599
|
+
case 'names':
|
|
600
|
+
console.log(formatNames(selection, model));
|
|
601
|
+
break;
|
|
602
|
+
case 'prompt':
|
|
603
|
+
console.log(formatPrompt(task, model, selection));
|
|
604
|
+
break;
|
|
605
|
+
default: // 'report'
|
|
606
|
+
formatReport(task, model, selection, elapsed);
|
|
607
|
+
break;
|
|
608
|
+
}
|
|
609
|
+
}
|