tribunal-kit 5.8.4 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent/ARCHITECTURE.md +219 -215
- package/.agent/agents/accessibility-reviewer.md +66 -151
- package/.agent/agents/ai-code-reviewer.md +22 -2
- package/.agent/agents/anti-pattern-reviewer.md +99 -0
- package/.agent/agents/api-architect.md +19 -2
- package/.agent/agents/backend-specialist.md +25 -3
- package/.agent/agents/cloud-engineer.md +21 -0
- package/.agent/agents/code-archaeologist.md +20 -3
- package/.agent/agents/complexity-reviewer.md +22 -2
- package/.agent/agents/database-architect.md +24 -3
- package/.agent/agents/db-latency-auditor.md +22 -2
- package/.agent/agents/debugger.md +19 -3
- package/.agent/agents/dependency-reviewer.md +19 -2
- package/.agent/agents/devops-engineer.md +24 -3
- package/.agent/agents/documentation-writer.md +21 -3
- package/.agent/agents/explorer-agent.md +21 -3
- package/.agent/agents/frontend-reviewer.md +19 -3
- package/.agent/agents/frontend-specialist.md +72 -224
- package/.agent/agents/game-developer.md +22 -3
- package/.agent/agents/interaction-reviewer.md +118 -0
- package/.agent/agents/logic-reviewer.md +18 -3
- package/.agent/agents/minimalist-reviewer.md +98 -0
- package/.agent/agents/mobile-developer.md +21 -3
- package/.agent/agents/mobile-reviewer.md +22 -2
- package/.agent/agents/orchestrator.md +19 -3
- package/.agent/agents/penetration-tester.md +22 -3
- package/.agent/agents/performance-optimizer.md +21 -3
- package/.agent/agents/performance-reviewer.md +2 -0
- package/.agent/agents/precedence-reviewer.md +19 -5
- package/.agent/agents/product-manager.md +20 -3
- package/.agent/agents/product-owner.md +22 -1
- package/.agent/agents/product-reviewer.md +98 -0
- package/.agent/agents/project-planner.md +19 -3
- package/.agent/agents/qa-automation-engineer.md +24 -3
- package/.agent/agents/resilience-reviewer.md +21 -2
- package/.agent/agents/schema-reviewer.md +21 -2
- package/.agent/agents/security-auditor.md +7 -1
- package/.agent/agents/seo-specialist.md +18 -3
- package/.agent/agents/sql-reviewer.md +22 -2
- package/.agent/agents/supervisor-agent.md +22 -3
- package/.agent/agents/swarm-worker-registry.md +7 -0
- package/.agent/agents/system-architect.md +19 -0
- package/.agent/agents/test-coverage-reviewer.md +22 -2
- package/.agent/agents/test-engineer.md +21 -2
- package/.agent/agents/throughput-optimizer.md +22 -2
- package/.agent/agents/type-safety-reviewer.md +21 -2
- package/.agent/agents/ui-ux-auditor.md +44 -242
- package/.agent/agents/ui-visual-auditor.md +99 -0
- package/.agent/agents/ux-reviewer.md +104 -0
- package/.agent/agents/visual-reviewer.md +98 -0
- package/.agent/agents/vitals-reviewer.md +23 -2
- package/.agent/history/README.md +53 -0
- package/.agent/history/case-law/cases/template-case.json +19 -0
- package/.agent/history/integrity_manifest.json +131 -33
- package/.agent/history/memory/.memory.idx +1581 -1
- package/.agent/history/memory/MEMORY.md +105 -1
- package/.agent/routing_index.json +272 -690
- package/.agent/rules/GEMINI.md +10 -12
- package/.agent/rules/GEMINI_PLANNER.md +84 -0
- package/.agent/scripts/_utils.js +28 -7
- package/.agent/scripts/case_law_manager.js +2 -0
- package/.agent/scripts/context_broker.js +152 -19
- package/.agent/scripts/guardrail_engine.js +60 -5
- package/.agent/scripts/impact_classifier.js +167 -0
- package/.agent/scripts/integrity_manifest.js +10 -4
- package/.agent/scripts/marathon_harness.js +10 -1
- package/.agent/scripts/minimal_change_engine.js +437 -0
- package/.agent/scripts/pipeline_engine.js +856 -0
- package/.agent/scripts/skill_evolution.js +36 -21
- package/.agent/scripts/socratic_gate_policy.js +60 -0
- package/.agent/scripts/swarm_dispatcher.js +65 -6
- package/.agent/scripts/token_budget_broker.js +52 -0
- package/.agent/scripts/verify_all.js +13 -0
- package/.agent/scripts/visual_audit.js +92 -0
- package/.agent/skills/12-principles-of-animation/SKILL.md +19 -10
- package/.agent/skills/60fps-animation/SKILL.md +19 -10
- package/.agent/skills/accessible-animation/SKILL.md +19 -10
- package/.agent/skills/adapt/SKILL.md +19 -10
- package/.agent/skills/advanced-rag-pipelines/SKILL.md +21 -3
- package/.agent/skills/agent-organizer/SKILL.md +21 -7
- package/.agent/skills/agentic-patterns/SKILL.md +19 -7
- package/.agent/skills/ai-prompt-injection-defense/SKILL.md +21 -7
- package/.agent/skills/animation-on-scroll/SKILL.md +19 -10
- package/.agent/skills/animation-systems/SKILL.md +19 -10
- package/.agent/skills/antfu-conventions/SKILL.md +21 -10
- package/.agent/skills/api-patterns/SKILL.md +21 -7
- package/.agent/skills/api-patterns/scripts/__pycache__/api_validator.cpython-311.pyc +0 -0
- package/.agent/skills/api-security-auditor/SKILL.md +21 -7
- package/.agent/skills/app-builder/SKILL.md +21 -7
- package/.agent/skills/apple-design/SKILL.md +19 -10
- package/.agent/skills/architecture/SKILL.md +21 -7
- package/.agent/skills/audit-and-fix/SKILL.md +19 -10
- package/.agent/skills/authentication-best-practices/SKILL.md +21 -7
- package/.agent/skills/backend-security-expert/SKILL.md +21 -7
- package/.agent/skills/baseline-ui/SKILL.md +19 -10
- package/.agent/skills/bash-linux/SKILL.md +21 -7
- package/.agent/skills/behavioral-modes/SKILL.md +19 -7
- package/.agent/skills/better-colors/SKILL.md +31 -14
- package/.agent/skills/better-ui/SKILL.md +19 -10
- package/.agent/skills/bolder/SKILL.md +19 -10
- package/.agent/skills/brainstorming/SKILL.md +17 -7
- package/.agent/skills/browser-native-ai/SKILL.md +19 -3
- package/.agent/skills/build-primitive/SKILL.md +19 -10
- package/.agent/skills/building-native-ui/SKILL.md +21 -7
- package/.agent/skills/cicd-pro/SKILL.md +21 -10
- package/.agent/skills/clarify/SKILL.md +19 -10
- package/.agent/skills/clean-code/SKILL.md +21 -7
- package/.agent/skills/cloud-architect/SKILL.md +21 -10
- package/.agent/skills/cobejs/SKILL.md +19 -10
- package/.agent/skills/code-review-checklist/SKILL.md +21 -7
- package/.agent/skills/codebase-design/SKILL.md +21 -10
- package/.agent/skills/colorize/SKILL.md +19 -10
- package/.agent/skills/compact-landing/SKILL.md +19 -10
- package/.agent/skills/company-logos/SKILL.md +19 -10
- package/.agent/skills/config-validator/SKILL.md +21 -7
- package/.agent/skills/containerization-pro/SKILL.md +21 -10
- package/.agent/skills/create-design-md/SKILL.md +19 -10
- package/.agent/skills/critique/SKILL.md +19 -10
- package/.agent/skills/csharp-developer/SKILL.md +21 -7
- package/.agent/skills/data-validation-schemas/SKILL.md +21 -7
- package/.agent/skills/database-design/SKILL.md +21 -7
- package/.agent/skills/database-design/scripts/__pycache__/schema_validator.cpython-311.pyc +0 -0
- package/.agent/skills/delight/SKILL.md +19 -10
- package/.agent/skills/deployment-procedures/SKILL.md +21 -7
- package/.agent/skills/design-lab/SKILL.md +21 -10
- package/.agent/skills/devops-engineer/SKILL.md +21 -7
- package/.agent/skills/devops-incident-responder/SKILL.md +21 -7
- package/.agent/skills/diagnosing-bugs/SKILL.md +21 -10
- package/.agent/skills/distill/SKILL.md +19 -10
- package/.agent/skills/documentation-templates/SKILL.md +21 -7
- package/.agent/skills/domain-modeling/SKILL.md +21 -10
- package/.agent/skills/edge-computing/SKILL.md +21 -7
- package/.agent/skills/emil-design-eng/SKILL.md +19 -10
- package/.agent/skills/error-resilience/SKILL.md +21 -7
- package/.agent/skills/extract-design-system/SKILL.md +21 -7
- package/.agent/skills/fabel-protocol/SKILL.md +19 -2
- package/.agent/skills/fixing-accessibility/SKILL.md +25 -14
- package/.agent/skills/fixing-metadata/SKILL.md +19 -10
- package/.agent/skills/fixing-motion-performance/SKILL.md +19 -10
- package/.agent/skills/framer-motion-expert/SKILL.md +19 -6
- package/.agent/skills/frontend-design/SKILL.md +66 -204
- package/.agent/skills/frontend-design/scripts/__pycache__/accessibility_checker.cpython-311.pyc +0 -0
- package/.agent/skills/frontend-design/scripts/__pycache__/ux_audit.cpython-311.pyc +0 -0
- package/.agent/skills/frontend-security-expert/SKILL.md +21 -7
- package/.agent/skills/game-design-expert/SKILL.md +21 -7
- package/.agent/skills/game-engineering-expert/SKILL.md +21 -7
- package/.agent/skills/generative-ui-expert/SKILL.md +19 -3
- package/.agent/skills/geo-fundamentals/SKILL.md +21 -7
- package/.agent/skills/geo-fundamentals/scripts/__pycache__/geo_checker.cpython-311.pyc +0 -0
- package/.agent/skills/git-pro/SKILL.md +21 -10
- package/.agent/skills/github-operations/SKILL.md +21 -7
- package/.agent/skills/gpt-taste/SKILL.md +19 -10
- package/.agent/skills/gsap-core/SKILL.md +21 -4
- package/.agent/skills/gsap-frameworks/SKILL.md +21 -4
- package/.agent/skills/gsap-performance/SKILL.md +20 -5
- package/.agent/skills/gsap-plugins/SKILL.md +20 -5
- package/.agent/skills/gsap-react/SKILL.md +20 -5
- package/.agent/skills/gsap-scrolltrigger/SKILL.md +21 -4
- package/.agent/skills/gsap-timeline/SKILL.md +20 -5
- package/.agent/skills/gsap-utils/SKILL.md +20 -5
- package/.agent/skills/harden/SKILL.md +19 -10
- package/.agent/skills/harness-protocol/SKILL.md +20 -4
- package/.agent/skills/i18n-localization/SKILL.md +21 -7
- package/.agent/skills/i18n-localization/scripts/__pycache__/i18n_checker.cpython-311.pyc +0 -0
- package/.agent/skills/impeccable/SKILL.md +21 -10
- package/.agent/skills/improve-codebase-architecture/SKILL.md +21 -10
- package/.agent/skills/improve-ui/SKILL.md +19 -10
- package/.agent/skills/intelligent-routing/SKILL.md +19 -7
- package/.agent/skills/knowledge-graph/SKILL.md +20 -4
- package/.agent/skills/landing-page/SKILL.md +20 -10
- package/.agent/skills/lint-and-validate/SKILL.md +21 -7
- package/.agent/skills/lint-and-validate/scripts/__pycache__/lint_runner.cpython-311.pyc +0 -0
- package/.agent/skills/lint-and-validate/scripts/__pycache__/type_coverage.cpython-311.pyc +0 -0
- package/.agent/skills/llm-engineering/SKILL.md +21 -7
- package/.agent/skills/local-first/SKILL.md +21 -7
- package/.agent/skills/local-first-architecture/SKILL.md +19 -10
- package/.agent/skills/lottie-animation/SKILL.md +19 -10
- package/.agent/skills/marquee-loop/SKILL.md +19 -10
- package/.agent/skills/masked-reveal/SKILL.md +19 -10
- package/.agent/skills/mcp-builder/SKILL.md +21 -7
- package/.agent/skills/micro-interaction/SKILL.md +19 -10
- package/.agent/skills/mobile-design/SKILL.md +21 -6
- package/.agent/skills/mobile-design/scripts/__pycache__/mobile_audit.cpython-311.pyc +0 -0
- package/.agent/skills/monorepo-management/SKILL.md +21 -7
- package/.agent/skills/morphing-icons/SKILL.md +19 -10
- package/.agent/skills/motion-engineering/SKILL.md +86 -171
- package/.agent/skills/nextjs-react-expert/SKILL.md +19 -7
- package/.agent/skills/nextjs-react-expert/scripts/__pycache__/convert_rules.cpython-311.pyc +0 -0
- package/.agent/skills/nextjs-react-expert/scripts/__pycache__/react_performance_checker.cpython-311.pyc +0 -0
- package/.agent/skills/nodejs-best-practices/SKILL.md +21 -7
- package/.agent/skills/observability/SKILL.md +21 -7
- package/.agent/skills/page-transition-animation/SKILL.md +19 -10
- package/.agent/skills/parallel-agents/SKILL.md +23 -7
- package/.agent/skills/performance-profiling/SKILL.md +21 -7
- package/.agent/skills/performance-profiling/scripts/__pycache__/lighthouse_audit.cpython-311.pyc +0 -0
- package/.agent/skills/plan-writing/SKILL.md +21 -7
- package/.agent/skills/platform-engineer/SKILL.md +21 -7
- package/.agent/skills/playwright-best-practices/SKILL.md +21 -7
- package/.agent/skills/polish/SKILL.md +19 -10
- package/.agent/skills/powershell-windows/SKILL.md +21 -7
- package/.agent/skills/pricing-page/SKILL.md +19 -10
- package/.agent/skills/product-aware-heuristics/SKILL.md +358 -0
- package/.agent/skills/progressive-blur/SKILL.md +19 -10
- package/.agent/skills/project-idioms/SKILL.md +20 -8
- package/.agent/skills/python-patterns/SKILL.md +21 -7
- package/.agent/skills/python-pro/SKILL.md +21 -6
- package/.agent/skills/quieter/SKILL.md +19 -10
- package/.agent/skills/react-doctor/SKILL.md +19 -10
- package/.agent/skills/react-specialist/SKILL.md +19 -6
- package/.agent/skills/readme-builder/SKILL.md +21 -7
- package/.agent/skills/realtime-patterns/SKILL.md +21 -7
- package/.agent/skills/red-team-tactics/SKILL.md +21 -7
- package/.agent/skills/redesign-skill/SKILL.md +19 -10
- package/.agent/skills/review-animations/SKILL.md +21 -10
- package/.agent/skills/rust-pro/SKILL.md +21 -7
- package/.agent/skills/seo-fundamentals/SKILL.md +21 -7
- package/.agent/skills/seo-fundamentals/scripts/__pycache__/seo_checker.cpython-311.pyc +0 -0
- package/.agent/skills/server-management/SKILL.md +21 -7
- package/.agent/skills/shadcn-ui-expert/SKILL.md +21 -7
- package/.agent/skills/shape/SKILL.md +19 -10
- package/.agent/skills/skill-creator/SKILL.md +21 -7
- package/.agent/skills/soft-skill/SKILL.md +19 -10
- package/.agent/skills/sounds-on-the-web/SKILL.md +19 -10
- package/.agent/skills/sql-pro/SKILL.md +21 -7
- package/.agent/skills/supabase-postgres-best-practices/SKILL.md +21 -7
- package/.agent/skills/svg-animation/SKILL.md +19 -10
- package/.agent/skills/swiftui-expert/SKILL.md +21 -7
- package/.agent/skills/swiss-design/SKILL.md +19 -10
- package/.agent/skills/system-design-pro/SKILL.md +21 -9
- package/.agent/skills/systematic-debugging/SKILL.md +21 -7
- package/.agent/skills/tailwind-patterns/SKILL.md +21 -7
- package/.agent/skills/taste-skill/SKILL.md +19 -10
- package/.agent/skills/tdd-workflow/SKILL.md +21 -10
- package/.agent/skills/test-result-analyzer/SKILL.md +18 -6
- package/.agent/skills/testing-patterns/SKILL.md +21 -7
- package/.agent/skills/testing-patterns/scripts/__pycache__/test_runner.cpython-311.pyc +0 -0
- package/.agent/skills/thermo-nuclear-code-quality-review/SKILL.md +21 -10
- package/.agent/skills/thinking-protocol/SKILL.md +20 -8
- package/.agent/skills/to-spring-or-not-to-spring/SKILL.md +19 -10
- package/.agent/skills/transitions-dev/SKILL.md +19 -10
- package/.agent/skills/trend-researcher/SKILL.md +17 -6
- package/.agent/skills/typescript-advanced/SKILL.md +21 -7
- package/.agent/skills/typeset/SKILL.md +19 -10
- package/.agent/skills/ui-reasoning-engine/SKILL.md +154 -0
- package/.agent/skills/ui-skill-packs/SKILL.md +77 -0
- package/.agent/skills/ui-skills-root/SKILL.md +21 -9
- package/.agent/skills/ui-ux-pro-max/SKILL.md +62 -583
- package/.agent/skills/ui-ux-researcher/SKILL.md +17 -6
- package/.agent/skills/vue-expert/SKILL.md +19 -6
- package/.agent/skills/vulnerability-scanner/SKILL.md +21 -7
- package/.agent/skills/vulnerability-scanner/scripts/__pycache__/security_scan.cpython-311.pyc +0 -0
- package/.agent/skills/web-accessibility-auditor/SKILL.md +21 -7
- package/.agent/skills/web-design-guidelines/SKILL.md +32 -206
- package/.agent/skills/web-quality-audit/SKILL.md +19 -10
- package/.agent/skills/webapp-testing/SKILL.md +21 -7
- package/.agent/skills/webapp-testing/scripts/__pycache__/playwright_runner.cpython-311.pyc +0 -0
- package/.agent/skills/webgpu-performance/SKILL.md +19 -3
- package/.agent/skills/whimsy-injector/SKILL.md +20 -10
- package/.agent/skills/workflow-optimizer/SKILL.md +16 -6
- package/.agent/templates/DESIGN.md +292 -0
- package/.agent/templates/design-tokens.json +137 -0
- package/.agent/workflows/acf.md +13 -6
- package/.agent/workflows/api-tester.md +15 -8
- package/.agent/workflows/audit.md +19 -8
- package/.agent/workflows/brainstorm.md +14 -9
- package/.agent/workflows/changelog.md +14 -8
- package/.agent/workflows/create.md +16 -8
- package/.agent/workflows/debug.md +15 -10
- package/.agent/workflows/deploy.md +16 -8
- package/.agent/workflows/enhance.md +15 -10
- package/.agent/workflows/fix.md +15 -8
- package/.agent/workflows/generate.md +17 -11
- package/.agent/workflows/marathon.md +15 -8
- package/.agent/workflows/migrate.md +16 -8
- package/.agent/workflows/minimal.md +94 -0
- package/.agent/workflows/orchestrate.md +15 -8
- package/.agent/workflows/performance-benchmarker.md +15 -8
- package/.agent/workflows/pipeline.md +155 -0
- package/.agent/workflows/plan.md +14 -10
- package/.agent/workflows/preview.md +14 -7
- package/.agent/workflows/refactor.md +16 -8
- package/.agent/workflows/review-ai.md +15 -7
- package/.agent/workflows/review.md +15 -7
- package/.agent/workflows/session.md +14 -7
- package/.agent/workflows/status.md +13 -7
- package/.agent/workflows/strengthen-skills.md +15 -6
- package/.agent/workflows/super-prompt.md +19 -10
- package/.agent/workflows/swarm.md +15 -7
- package/.agent/workflows/test.md +15 -8
- package/.agent/workflows/tribunal-backend.md +16 -9
- package/.agent/workflows/tribunal-database.md +15 -8
- package/.agent/workflows/tribunal-frontend.md +26 -11
- package/.agent/workflows/tribunal-full.md +29 -14
- package/.agent/workflows/tribunal-mobile.md +15 -8
- package/.agent/workflows/tribunal-performance.md +15 -8
- package/.agent/workflows/tribunal-speed.md +16 -9
- package/.agent/workflows/tribunal-ui.md +82 -0
- package/.agent/workflows/ui-ux-pro-max.md +51 -107
- package/CONTRIBUTING.md +2 -2
- package/README.md +5 -5
- package/bin/mcp-server.js +126 -76
- package/bin/wrapper.js +67 -31
- package/dist/cli.js +39 -1
- package/dist/commands/compile.js +7 -0
- package/dist/commands/context.js +8 -3
- package/dist/commands/graph.js +8 -2
- package/dist/commands/init.js +37 -2
- package/dist/commands/memory.js +119 -68
- package/dist/commands/minimal.js +71 -0
- package/dist/commands/native.js +228 -0
- package/dist/commands/optimize.js +15 -5
- package/dist/commands/validate.js +67 -0
- package/dist/esm/index.mjs +26 -32
- package/dist/mcp/server.js +4 -138
- package/dist/utils/fs.js +10 -4
- package/dist/utils/helpers.js +10 -1
- package/dist/utils/version.js +9 -2
- package/mcp_config.json +10 -1
- package/package.json +19 -15
- package/scripts/benchmark.js +58 -0
- package/scripts/sync-version.js +129 -76
- package/scripts/visual_audit.js +126 -0
|
@@ -0,0 +1,856 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* pipeline_engine.js — Tribunal Kit Hybrid Pipeline Engine
|
|
4
|
+
* ==========================================================
|
|
5
|
+
* 3-pass decoupled code generation pipeline that separates planning,
|
|
6
|
+
* code synthesis, and validation into distinct phases with minimal
|
|
7
|
+
* context overhead per pass.
|
|
8
|
+
*
|
|
9
|
+
* Architecture:
|
|
10
|
+
* Pass 1 — Planner: Classify task, detect stack, select skills, emit spec JSON
|
|
11
|
+
* Pass 2 — Builder: Assemble minimal prompt (spec + 2-3 key-rules + target file)
|
|
12
|
+
* Pass 3 — Validator: Run inner_loop_validator + guardrail_engine (zero LLM calls)
|
|
13
|
+
*
|
|
14
|
+
* This engine does NOT call LLMs directly. It produces structured prompt payloads
|
|
15
|
+
* consumed by the IDE's native LLM integration. The engine is a prompt assembler
|
|
16
|
+
* and validator orchestrator.
|
|
17
|
+
*
|
|
18
|
+
* Usage:
|
|
19
|
+
* node .agent/scripts/pipeline_engine.js --task "Build a login form" --file Login.tsx
|
|
20
|
+
* node .agent/scripts/pipeline_engine.js --task "..." --phase plan --output json
|
|
21
|
+
* node .agent/scripts/pipeline_engine.js --task "..." --phase build --spec spec.json
|
|
22
|
+
* node .agent/scripts/pipeline_engine.js --task "..." --phase validate --code output.tsx
|
|
23
|
+
* node .agent/scripts/pipeline_engine.js --task "..." --dry-run
|
|
24
|
+
*
|
|
25
|
+
* API:
|
|
26
|
+
* const { planPhase, buildPhase, validatePhase, fullPipeline } = require('./pipeline_engine');
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
"use strict";
|
|
30
|
+
|
|
31
|
+
const fs = require("fs");
|
|
32
|
+
const path = require("path");
|
|
33
|
+
|
|
34
|
+
// ── Shared Utilities ────────────────────────────────────────────────────────
|
|
35
|
+
const { GREEN, YELLOW, CYAN, RED, BLUE, BOLD, DIM, RESET, banner, sectionHeader, timer } = require("./_colors");
|
|
36
|
+
const { findAgentDir, parseArgs, loadJson } = require("./_utils");
|
|
37
|
+
|
|
38
|
+
// ── Lazy-load sibling scripts (avoid circular deps) ─────────────────────────
|
|
39
|
+
let _contextBroker = null;
|
|
40
|
+
function getContextBroker() {
|
|
41
|
+
if (!_contextBroker) {
|
|
42
|
+
_contextBroker = require(path.join(__dirname, "context_broker.js"));
|
|
43
|
+
}
|
|
44
|
+
return _contextBroker;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let _innerLoopValidator = null;
|
|
48
|
+
function getInnerLoopValidator() {
|
|
49
|
+
if (!_innerLoopValidator) {
|
|
50
|
+
try {
|
|
51
|
+
_innerLoopValidator = require(path.join(__dirname, "inner_loop_validator.js"));
|
|
52
|
+
} catch {
|
|
53
|
+
_innerLoopValidator = null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return _innerLoopValidator;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let _guardrailEngine = null;
|
|
60
|
+
function _getGuardrailEngine() {
|
|
61
|
+
if (!_guardrailEngine) {
|
|
62
|
+
try {
|
|
63
|
+
_guardrailEngine = require(path.join(__dirname, "guardrail_engine.js"));
|
|
64
|
+
} catch {
|
|
65
|
+
_guardrailEngine = null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return _guardrailEngine;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let _minimalChangeEngine = null;
|
|
72
|
+
function getMinimalChangeEngine() {
|
|
73
|
+
if (!_minimalChangeEngine) {
|
|
74
|
+
try {
|
|
75
|
+
_minimalChangeEngine = require(path.join(__dirname, "minimal_change_engine.js"));
|
|
76
|
+
} catch {
|
|
77
|
+
_minimalChangeEngine = null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return _minimalChangeEngine;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
// ── Task Classification ─────────────────────────────────────────────────────
|
|
85
|
+
// Lightweight intent detection — no LLM call needed.
|
|
86
|
+
|
|
87
|
+
const TASK_TYPES = {
|
|
88
|
+
frontend_component: {
|
|
89
|
+
keywords: ["component", "page", "layout", "ui", "form", "modal", "dialog", "card", "hero", "nav", "sidebar", "dashboard", "landing"],
|
|
90
|
+
stack_hint: ["react", "vue", "svelte", "html", "css"],
|
|
91
|
+
},
|
|
92
|
+
frontend_page: {
|
|
93
|
+
keywords: ["page", "landing", "dashboard", "home", "about", "pricing", "settings"],
|
|
94
|
+
stack_hint: ["react", "next", "vue", "nuxt"],
|
|
95
|
+
},
|
|
96
|
+
api_endpoint: {
|
|
97
|
+
keywords: ["api", "endpoint", "route", "handler", "middleware", "controller", "rest", "graphql"],
|
|
98
|
+
stack_hint: ["express", "fastapi", "hono", "node"],
|
|
99
|
+
},
|
|
100
|
+
database_query: {
|
|
101
|
+
keywords: ["query", "sql", "migration", "schema", "prisma", "drizzle", "orm", "table", "index"],
|
|
102
|
+
stack_hint: ["prisma", "drizzle", "postgres", "mysql", "sql"],
|
|
103
|
+
},
|
|
104
|
+
auth_flow: {
|
|
105
|
+
keywords: ["auth", "login", "signup", "jwt", "oauth", "session", "password", "rbac", "permission"],
|
|
106
|
+
stack_hint: ["jwt", "oauth", "next-auth"],
|
|
107
|
+
},
|
|
108
|
+
test_suite: {
|
|
109
|
+
keywords: ["test", "spec", "jest", "vitest", "playwright", "e2e", "unit", "mock"],
|
|
110
|
+
stack_hint: ["jest", "vitest", "playwright"],
|
|
111
|
+
},
|
|
112
|
+
refactor: {
|
|
113
|
+
keywords: ["refactor", "clean", "extract", "rename", "move", "split", "merge", "deduplicate"],
|
|
114
|
+
stack_hint: [],
|
|
115
|
+
},
|
|
116
|
+
animation: {
|
|
117
|
+
keywords: ["animation", "motion", "gsap", "framer", "transition", "scroll", "parallax", "hover"],
|
|
118
|
+
stack_hint: ["gsap", "framer-motion", "css"],
|
|
119
|
+
},
|
|
120
|
+
general: {
|
|
121
|
+
keywords: [],
|
|
122
|
+
stack_hint: [],
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// Stack detection keywords
|
|
127
|
+
const STACK_KEYWORDS = {
|
|
128
|
+
react: ["react", "jsx", "tsx", "useState", "useEffect", "component"],
|
|
129
|
+
nextjs: ["next", "nextjs", "server component", "server action", "app router"],
|
|
130
|
+
vue: ["vue", "nuxt", "composition api", "ref(", "computed("],
|
|
131
|
+
typescript: ["typescript", "ts", "interface", "type ", "generic"],
|
|
132
|
+
python: ["python", "fastapi", "django", "flask", "pydantic"],
|
|
133
|
+
node: ["node", "express", "hono", "koa"],
|
|
134
|
+
css: ["css", "tailwind", "style", "responsive", "dark mode"],
|
|
135
|
+
sql: ["sql", "postgres", "mysql", "prisma", "drizzle", "query"],
|
|
136
|
+
rust: ["rust", "cargo", "tokio", "axum"],
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
// ══════════════════════════════════════════════════════════════════════════════
|
|
141
|
+
// PASS 1 — PLANNER
|
|
142
|
+
// Classify the task, detect stack, select minimal skills, emit structured spec.
|
|
143
|
+
// Token budget: ~1,500 tokens (no GEMINI.md, no agent personas)
|
|
144
|
+
// ══════════════════════════════════════════════════════════════════════════════
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Classify user task into a structured spec without any LLM call.
|
|
148
|
+
*
|
|
149
|
+
* @param {string} task - Raw user task description
|
|
150
|
+
* @param {string[]} files - Files being touched (for extension detection)
|
|
151
|
+
* @param {string} agentDir - Path to .agent/ directory
|
|
152
|
+
* @returns {object} Structured spec JSON
|
|
153
|
+
*/
|
|
154
|
+
function planPhase(task, files = [], agentDir = null) {
|
|
155
|
+
const resolvedAgentDir = agentDir || findAgentDir();
|
|
156
|
+
const taskLower = task.toLowerCase();
|
|
157
|
+
|
|
158
|
+
// 1. Classify task type
|
|
159
|
+
let bestType = "general";
|
|
160
|
+
let bestScore = 0;
|
|
161
|
+
for (const [type, config] of Object.entries(TASK_TYPES)) {
|
|
162
|
+
let score = 0;
|
|
163
|
+
for (const kw of config.keywords) {
|
|
164
|
+
if (taskLower.includes(kw)) score += 2;
|
|
165
|
+
}
|
|
166
|
+
if (score > bestScore) {
|
|
167
|
+
bestScore = score;
|
|
168
|
+
bestType = type;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// 2. Detect stack from task text + file extensions
|
|
173
|
+
const detectedStack = [];
|
|
174
|
+
for (const [stack, keywords] of Object.entries(STACK_KEYWORDS)) {
|
|
175
|
+
for (const kw of keywords) {
|
|
176
|
+
if (taskLower.includes(kw)) {
|
|
177
|
+
if (!detectedStack.includes(stack)) detectedStack.push(stack);
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Infer stack from file extensions
|
|
184
|
+
const extToStack = {
|
|
185
|
+
".tsx": "react", ".jsx": "react",
|
|
186
|
+
".ts": "typescript", ".js": "node",
|
|
187
|
+
".vue": "vue", ".py": "python",
|
|
188
|
+
".rs": "rust", ".sql": "sql",
|
|
189
|
+
".css": "css",
|
|
190
|
+
};
|
|
191
|
+
for (const f of files) {
|
|
192
|
+
const ext = path.extname(f).toLowerCase();
|
|
193
|
+
const stack = extToStack[ext];
|
|
194
|
+
if (stack && !detectedStack.includes(stack)) {
|
|
195
|
+
detectedStack.push(stack);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// 3. Select essential skills via context broker (pipeline mode)
|
|
200
|
+
const broker = getContextBroker();
|
|
201
|
+
const selection = broker.selectSkills(
|
|
202
|
+
task,
|
|
203
|
+
files,
|
|
204
|
+
"small", // Use small-model tier for aggressive pruning
|
|
205
|
+
broker.loadSkills(resolvedAgentDir)
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
// Take only top 3 essential skills for Pass 2
|
|
209
|
+
const essentialSkills = selection.essential.slice(0, 3).map(s => ({
|
|
210
|
+
name: s.name,
|
|
211
|
+
score: selection.scores.get(s.name) || 0,
|
|
212
|
+
}));
|
|
213
|
+
|
|
214
|
+
// 4. Extract constraints from task text
|
|
215
|
+
const constraints = {};
|
|
216
|
+
if (taskLower.includes("accessible") || taskLower.includes("a11y") || taskLower.includes("aria")) {
|
|
217
|
+
constraints.accessibility = true;
|
|
218
|
+
}
|
|
219
|
+
if (taskLower.includes("responsive") || taskLower.includes("mobile")) {
|
|
220
|
+
constraints.responsive = true;
|
|
221
|
+
}
|
|
222
|
+
if (taskLower.includes("dark mode") || taskLower.includes("theme")) {
|
|
223
|
+
constraints.dark_mode = true;
|
|
224
|
+
}
|
|
225
|
+
if (taskLower.includes("animation") || taskLower.includes("motion") || taskLower.includes("gsap")) {
|
|
226
|
+
constraints.animation = true;
|
|
227
|
+
}
|
|
228
|
+
if (taskLower.includes("test") || taskLower.includes("spec")) {
|
|
229
|
+
constraints.tests_required = true;
|
|
230
|
+
}
|
|
231
|
+
if (taskLower.includes("typescript") || taskLower.includes("type-safe")) {
|
|
232
|
+
constraints.type_safe = true;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// 5. Determine target file
|
|
236
|
+
const targetFile = files.length > 0 ? files[0] : null;
|
|
237
|
+
|
|
238
|
+
// 5b. Adaptive Governance Impact Tiering
|
|
239
|
+
let impactClassification = null;
|
|
240
|
+
let socraticGatePolicy = null;
|
|
241
|
+
let tokenBudget = null;
|
|
242
|
+
try {
|
|
243
|
+
const { classifyImpact } = require("./impact_classifier.js");
|
|
244
|
+
const { evaluateSocraticGate } = require("./socratic_gate_policy.js");
|
|
245
|
+
const { getTokenBudget } = require("./token_budget_broker.js");
|
|
246
|
+
impactClassification = classifyImpact({ files, task });
|
|
247
|
+
socraticGatePolicy = evaluateSocraticGate({ tier: impactClassification.tier, ambiguityScore: 0.2 });
|
|
248
|
+
tokenBudget = getTokenBudget(impactClassification.tier);
|
|
249
|
+
} catch {
|
|
250
|
+
impactClassification = { tier: 1, score: 0.2, maxReviewers: 1, requireGate: false, fastPass: false };
|
|
251
|
+
socraticGatePolicy = { shouldBlock: false, reason: "Fallback evaluation" };
|
|
252
|
+
tokenBudget = { tier: 1, maxTokens: 2000, maxReviewers: 1 };
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// 6. Minimal Change Governance Gate
|
|
256
|
+
const minEngine = getMinimalChangeEngine();
|
|
257
|
+
let minimalChange = null;
|
|
258
|
+
if (minEngine) {
|
|
259
|
+
try {
|
|
260
|
+
minimalChange = minEngine.evaluateMinimalChange(
|
|
261
|
+
task,
|
|
262
|
+
{
|
|
263
|
+
target_file: targetFile,
|
|
264
|
+
files_added: targetFile ? 0 : 1,
|
|
265
|
+
files_modified: targetFile ? 1 : 0,
|
|
266
|
+
estimated_lines_added: 20,
|
|
267
|
+
},
|
|
268
|
+
{ cwd: path.dirname(resolvedAgentDir) },
|
|
269
|
+
);
|
|
270
|
+
} catch {
|
|
271
|
+
minimalChange = null;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
task_type: bestType,
|
|
277
|
+
stack: detectedStack,
|
|
278
|
+
target_file: targetFile,
|
|
279
|
+
essential_skills: essentialSkills,
|
|
280
|
+
constraints,
|
|
281
|
+
impact_governance: {
|
|
282
|
+
impact: impactClassification,
|
|
283
|
+
socratic_gate: socraticGatePolicy,
|
|
284
|
+
token_budget: tokenBudget,
|
|
285
|
+
},
|
|
286
|
+
minimal_change: minimalChange,
|
|
287
|
+
spec: task,
|
|
288
|
+
timestamp: new Date().toISOString(),
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
// ══════════════════════════════════════════════════════════════════════════════
|
|
294
|
+
// PASS 2 — BUILDER
|
|
295
|
+
// Assemble a minimal, focused prompt for code generation.
|
|
296
|
+
// Token budget: ~2,500 tokens (spec + 2-3 key-rules + target file excerpt)
|
|
297
|
+
// Zero governance overhead — no GEMINI.md, no agent personas, no tribunal rules.
|
|
298
|
+
// ══════════════════════════════════════════════════════════════════════════════
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Assemble a minimal builder prompt from the planner spec.
|
|
302
|
+
*
|
|
303
|
+
* @param {object} spec - Structured spec from planPhase()
|
|
304
|
+
* @param {string} agentDir - Path to .agent/ directory
|
|
305
|
+
* @param {object} [opts] - Options
|
|
306
|
+
* @param {string} [opts.targetFileContent] - Current content of the target file (for modifications)
|
|
307
|
+
* @returns {{ prompt: string, tokenEstimate: number, skillsLoaded: string[] }}
|
|
308
|
+
*/
|
|
309
|
+
function buildPhase(spec, agentDir = null, opts = {}) {
|
|
310
|
+
const resolvedAgentDir = agentDir || findAgentDir();
|
|
311
|
+
const broker = getContextBroker();
|
|
312
|
+
const allSkills = broker.loadSkills(resolvedAgentDir);
|
|
313
|
+
|
|
314
|
+
const lines = [];
|
|
315
|
+
const skillsLoaded = [];
|
|
316
|
+
|
|
317
|
+
// ── Section 1: Task Specification (compact) ────────────────────────────────
|
|
318
|
+
lines.push("# Code Generation Task");
|
|
319
|
+
lines.push("");
|
|
320
|
+
lines.push(`Type: ${spec.task_type}`);
|
|
321
|
+
lines.push(`Stack: ${spec.stack.join(", ") || "detect from context"}`);
|
|
322
|
+
if (spec.target_file) {
|
|
323
|
+
lines.push(`Target: ${spec.target_file}`);
|
|
324
|
+
}
|
|
325
|
+
lines.push("");
|
|
326
|
+
lines.push("## Requirements");
|
|
327
|
+
lines.push("");
|
|
328
|
+
lines.push(spec.spec);
|
|
329
|
+
lines.push("");
|
|
330
|
+
|
|
331
|
+
// Constraints
|
|
332
|
+
const constraintEntries = Object.entries(spec.constraints || {});
|
|
333
|
+
if (constraintEntries.length > 0) {
|
|
334
|
+
lines.push("## Constraints");
|
|
335
|
+
lines.push("");
|
|
336
|
+
for (const [key, val] of constraintEntries) {
|
|
337
|
+
lines.push(`- ${key.replace(/_/g, " ")}: ${val}`);
|
|
338
|
+
}
|
|
339
|
+
lines.push("");
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// ── Section 2: Essential Skill Key-Rules (max 3 skills) ────────────────────
|
|
343
|
+
const skillNames = (spec.essential_skills || []).map(s => s.name);
|
|
344
|
+
if (skillNames.length > 0) {
|
|
345
|
+
lines.push("## Design & Implementation Guidelines");
|
|
346
|
+
lines.push("");
|
|
347
|
+
lines.push("Follow these rules strictly:");
|
|
348
|
+
lines.push("");
|
|
349
|
+
|
|
350
|
+
for (const skillName of skillNames.slice(0, 3)) {
|
|
351
|
+
const skill = allSkills.find(s => s.name === skillName);
|
|
352
|
+
if (!skill) continue;
|
|
353
|
+
|
|
354
|
+
skillsLoaded.push(skillName);
|
|
355
|
+
lines.push(`### ${skillName}`);
|
|
356
|
+
lines.push("");
|
|
357
|
+
// Use condensed key-rules only — not full SKILL.md
|
|
358
|
+
lines.push(skill.keyRules || "");
|
|
359
|
+
lines.push("");
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// ── Section 3: Target File Context (if modifying existing code) ────────────
|
|
364
|
+
if (opts.targetFileContent) {
|
|
365
|
+
lines.push("## Current File Content");
|
|
366
|
+
lines.push("");
|
|
367
|
+
lines.push("```");
|
|
368
|
+
// Truncate to ~200 lines to stay within budget
|
|
369
|
+
const fileLines = opts.targetFileContent.split("\n");
|
|
370
|
+
if (fileLines.length > 200) {
|
|
371
|
+
lines.push(fileLines.slice(0, 200).join("\n"));
|
|
372
|
+
lines.push(`\n... (${fileLines.length - 200} more lines truncated)`);
|
|
373
|
+
} else {
|
|
374
|
+
lines.push(opts.targetFileContent);
|
|
375
|
+
}
|
|
376
|
+
lines.push("```");
|
|
377
|
+
lines.push("");
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// ── Section 4: Output Format Instruction ───────────────────────────────────
|
|
381
|
+
lines.push("## Output Instructions");
|
|
382
|
+
lines.push("");
|
|
383
|
+
lines.push("Generate ONLY the code. No explanations, no preamble, no markdown fences.");
|
|
384
|
+
lines.push("Use self-documenting names. Add error handling on async functions.");
|
|
385
|
+
lines.push("Mark any uncertain API calls with // VERIFY: [reason].");
|
|
386
|
+
lines.push("");
|
|
387
|
+
|
|
388
|
+
const prompt = lines.join("\n");
|
|
389
|
+
|
|
390
|
+
// Rough token estimate: ~4 chars per token for English text
|
|
391
|
+
const tokenEstimate = Math.ceil(prompt.length / 4);
|
|
392
|
+
|
|
393
|
+
return { prompt, tokenEstimate, skillsLoaded };
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
// ══════════════════════════════════════════════════════════════════════════════
|
|
398
|
+
// PASS 3 — VALIDATOR
|
|
399
|
+
// Run deterministic checks on generated code (zero LLM calls).
|
|
400
|
+
// Uses inner_loop_validator.js (OWASP patterns + syntax heuristics)
|
|
401
|
+
// and guardrail_engine.js (phantom references + structural checks).
|
|
402
|
+
// ══════════════════════════════════════════════════════════════════════════════
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Validate generated code using deterministic scanners.
|
|
406
|
+
*
|
|
407
|
+
* @param {string} code - Generated code string
|
|
408
|
+
* @param {object} spec - Structured spec from planPhase()
|
|
409
|
+
* @param {object} [opts] - Options
|
|
410
|
+
* @param {string} [opts.lang] - Language override (ts, js, py, etc.)
|
|
411
|
+
* @returns {{ verdict: string, passed: boolean, issues: object[], summary: string, feedback: string|null }}
|
|
412
|
+
*/
|
|
413
|
+
function validatePhase(code, spec, opts = {}) {
|
|
414
|
+
const issues = [];
|
|
415
|
+
let verdict = "APPROVED";
|
|
416
|
+
|
|
417
|
+
// ── Inner Loop Validator (security patterns + syntax) ──────────────────────
|
|
418
|
+
const innerLoop = getInnerLoopValidator();
|
|
419
|
+
if (innerLoop && typeof innerLoop.validateSnippet === "function") {
|
|
420
|
+
try {
|
|
421
|
+
const ilResult = innerLoop.validateSnippet(code, opts.lang || detectLang(spec));
|
|
422
|
+
if (ilResult && ilResult.issues) {
|
|
423
|
+
for (const issue of ilResult.issues) {
|
|
424
|
+
issues.push({
|
|
425
|
+
source: "inner_loop_validator",
|
|
426
|
+
severity: issue.severity || "medium",
|
|
427
|
+
category: issue.category || "unknown",
|
|
428
|
+
line: issue.line || null,
|
|
429
|
+
message: issue.message || "Unnamed issue",
|
|
430
|
+
fix: issue.fix || null,
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
} catch {
|
|
435
|
+
// Inner loop validator not available or errored — continue without it
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// ── Manual Pattern Checks (always run, no dependencies) ────────────────────
|
|
440
|
+
const manualIssues = runManualChecks(code, spec);
|
|
441
|
+
issues.push(...manualIssues);
|
|
442
|
+
|
|
443
|
+
// ── Classify verdict ───────────────────────────────────────────────────────
|
|
444
|
+
const criticalCount = issues.filter(i => i.severity === "critical").length;
|
|
445
|
+
const highCount = issues.filter(i => i.severity === "high").length;
|
|
446
|
+
|
|
447
|
+
if (criticalCount > 0) {
|
|
448
|
+
verdict = "REJECTED";
|
|
449
|
+
} else if (highCount > 0) {
|
|
450
|
+
verdict = "WARNING";
|
|
451
|
+
} else {
|
|
452
|
+
verdict = "APPROVED";
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// ── Build self-healing feedback for retry ──────────────────────────────────
|
|
456
|
+
let feedback = null;
|
|
457
|
+
if (verdict !== "APPROVED" && issues.length > 0) {
|
|
458
|
+
const feedbackLines = ["The following issues were found in the generated code:"];
|
|
459
|
+
for (const issue of issues.slice(0, 5)) {
|
|
460
|
+
feedbackLines.push(`- [${issue.severity.toUpperCase()}] ${issue.message}`);
|
|
461
|
+
if (issue.fix) feedbackLines.push(` Fix: ${issue.fix}`);
|
|
462
|
+
}
|
|
463
|
+
feedbackLines.push("");
|
|
464
|
+
feedbackLines.push("Please regenerate the code addressing these issues.");
|
|
465
|
+
feedback = feedbackLines.join("\n");
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const summary = verdict === "APPROVED"
|
|
469
|
+
? `✅ Code passed validation (${issues.length} issues found, none blocking)`
|
|
470
|
+
: `${verdict === "REJECTED" ? "❌" : "⚠️"} Code ${verdict.toLowerCase()}: ${criticalCount} critical, ${highCount} high severity issues`;
|
|
471
|
+
|
|
472
|
+
return {
|
|
473
|
+
verdict,
|
|
474
|
+
passed: verdict === "APPROVED",
|
|
475
|
+
issues,
|
|
476
|
+
summary,
|
|
477
|
+
feedback,
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Detect language from spec for validation context.
|
|
483
|
+
*/
|
|
484
|
+
function detectLang(spec) {
|
|
485
|
+
if (!spec || !spec.target_file) return "js";
|
|
486
|
+
const ext = path.extname(spec.target_file).toLowerCase();
|
|
487
|
+
const map = {
|
|
488
|
+
".ts": "ts", ".tsx": "tsx", ".js": "js", ".jsx": "jsx",
|
|
489
|
+
".py": "py", ".rs": "rs", ".sql": "sql", ".vue": "vue",
|
|
490
|
+
".css": "css", ".html": "html",
|
|
491
|
+
};
|
|
492
|
+
return map[ext] || "js";
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Run lightweight manual pattern checks that don't require external scripts.
|
|
497
|
+
* These catch the most common AI code generation mistakes.
|
|
498
|
+
*/
|
|
499
|
+
function runManualChecks(code, spec) {
|
|
500
|
+
const issues = [];
|
|
501
|
+
const lines = code.split("\n");
|
|
502
|
+
|
|
503
|
+
// Check 1: eval() usage
|
|
504
|
+
if (/\beval\s*\(/.test(code)) {
|
|
505
|
+
issues.push({
|
|
506
|
+
source: "pipeline_validator",
|
|
507
|
+
severity: "critical",
|
|
508
|
+
category: "Code Injection",
|
|
509
|
+
line: findLineNumber(lines, /\beval\s*\(/),
|
|
510
|
+
message: "eval() is a code injection vector — never use in production",
|
|
511
|
+
fix: "Use JSON.parse(), new Function(), or a proper parser instead",
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// Check 2: Hardcoded secrets
|
|
516
|
+
if (/(?:password|secret|api[_-]?key)\s*[:=]\s*["'][^"']{4,}["']/i.test(code)) {
|
|
517
|
+
issues.push({
|
|
518
|
+
source: "pipeline_validator",
|
|
519
|
+
severity: "critical",
|
|
520
|
+
category: "Hardcoded Secret",
|
|
521
|
+
line: findLineNumber(lines, /(?:password|secret|api[_-]?key)\s*[:=]\s*["']/i),
|
|
522
|
+
message: "Hardcoded secret detected — use environment variables",
|
|
523
|
+
fix: "Replace with process.env.SECRET_NAME or equivalent",
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Check 3: innerHTML XSS
|
|
528
|
+
if (/\.innerHTML\s*=/.test(code) && !code.includes("DOMPurify")) {
|
|
529
|
+
issues.push({
|
|
530
|
+
source: "pipeline_validator",
|
|
531
|
+
severity: "high",
|
|
532
|
+
category: "XSS",
|
|
533
|
+
line: findLineNumber(lines, /\.innerHTML\s*=/),
|
|
534
|
+
message: "Direct innerHTML assignment without sanitization",
|
|
535
|
+
fix: "Use textContent, DOMPurify.sanitize(), or React's JSX instead",
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// Check 4: SQL injection (string interpolation in queries)
|
|
540
|
+
if (/\$\{.*\}.*(?:SELECT|INSERT|UPDATE|DELETE|WHERE)/i.test(code) ||
|
|
541
|
+
/['"].*\+.*(?:SELECT|INSERT|UPDATE|DELETE|WHERE)/i.test(code)) {
|
|
542
|
+
issues.push({
|
|
543
|
+
source: "pipeline_validator",
|
|
544
|
+
severity: "critical",
|
|
545
|
+
category: "SQL Injection",
|
|
546
|
+
line: null,
|
|
547
|
+
message: "Possible SQL injection — string interpolation in SQL query",
|
|
548
|
+
fix: "Use parameterized queries ($1, ?) or an ORM",
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// Check 5: Console.log in production code (warning, not blocking)
|
|
553
|
+
const consoleCount = (code.match(/console\.(log|debug|info)\(/g) || []).length;
|
|
554
|
+
if (consoleCount > 3) {
|
|
555
|
+
issues.push({
|
|
556
|
+
source: "pipeline_validator",
|
|
557
|
+
severity: "low",
|
|
558
|
+
category: "Code Quality",
|
|
559
|
+
line: null,
|
|
560
|
+
message: `${consoleCount} console.log statements found — remove before production`,
|
|
561
|
+
fix: "Use a proper logger (winston, pino) or remove debug logs",
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// Check 6: Empty catch blocks
|
|
566
|
+
if (/catch\s*\([^)]*\)\s*\{[\s\n]*\}/.test(code)) {
|
|
567
|
+
issues.push({
|
|
568
|
+
source: "pipeline_validator",
|
|
569
|
+
severity: "medium",
|
|
570
|
+
category: "Error Handling",
|
|
571
|
+
line: findLineNumber(lines, /catch\s*\([^)]*\)\s*\{[\s\n]*\}/),
|
|
572
|
+
message: "Empty catch block swallows errors silently",
|
|
573
|
+
fix: "At minimum, log the error: catch(err) { console.error(err); }",
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Check 7: TypeScript `any` usage (if TS file)
|
|
578
|
+
const lang = detectLang(spec);
|
|
579
|
+
if ((lang === "ts" || lang === "tsx") && /:\s*any\b/.test(code)) {
|
|
580
|
+
const anyCount = (code.match(/:\s*any\b/g) || []).length;
|
|
581
|
+
if (anyCount > 2) {
|
|
582
|
+
issues.push({
|
|
583
|
+
source: "pipeline_validator",
|
|
584
|
+
severity: "medium",
|
|
585
|
+
category: "Type Safety",
|
|
586
|
+
line: null,
|
|
587
|
+
message: `${anyCount} uses of 'any' type — reduces type safety`,
|
|
588
|
+
fix: "Replace with specific types or use 'unknown' with type guards",
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
return issues;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Find the first line number matching a regex pattern.
|
|
598
|
+
*/
|
|
599
|
+
function findLineNumber(lines, pattern) {
|
|
600
|
+
for (let i = 0; i < lines.length; i++) {
|
|
601
|
+
if (pattern.test(lines[i])) return i + 1;
|
|
602
|
+
}
|
|
603
|
+
return null;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
// ══════════════════════════════════════════════════════════════════════════════
|
|
608
|
+
// FULL PIPELINE — Orchestrate all 3 passes
|
|
609
|
+
// ══════════════════════════════════════════════════════════════════════════════
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Run the full pipeline: Plan → Build → (code generation happens externally) → Validate.
|
|
613
|
+
* Returns the planner spec and builder prompt. Validation runs after code is generated.
|
|
614
|
+
*
|
|
615
|
+
* @param {string} task - Raw user task
|
|
616
|
+
* @param {string[]} files - Files being touched
|
|
617
|
+
* @param {object} [opts] - Options
|
|
618
|
+
* @param {string} [opts.agentDir] - Path to .agent/ directory
|
|
619
|
+
* @param {string} [opts.targetFileContent] - Current content of target file
|
|
620
|
+
* @returns {{ spec: object, prompt: string, tokenEstimate: number, skillsLoaded: string[], validate: function }}
|
|
621
|
+
*/
|
|
622
|
+
function fullPipeline(task, files = [], opts = {}) {
|
|
623
|
+
const agentDir = opts.agentDir || findAgentDir();
|
|
624
|
+
|
|
625
|
+
// Pass 1: Plan
|
|
626
|
+
const spec = planPhase(task, files, agentDir);
|
|
627
|
+
|
|
628
|
+
// Pass 2: Build prompt
|
|
629
|
+
const builderResult = buildPhase(spec, agentDir, {
|
|
630
|
+
targetFileContent: opts.targetFileContent || null,
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
// Pass 3: Return validator as a callable
|
|
634
|
+
// The caller invokes validate(code) after the LLM generates the code
|
|
635
|
+
const validate = (code) => validatePhase(code, spec, { lang: detectLang(spec) });
|
|
636
|
+
|
|
637
|
+
return {
|
|
638
|
+
spec,
|
|
639
|
+
prompt: builderResult.prompt,
|
|
640
|
+
tokenEstimate: builderResult.tokenEstimate,
|
|
641
|
+
skillsLoaded: builderResult.skillsLoaded,
|
|
642
|
+
validate,
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
// ══════════════════════════════════════════════════════════════════════════════
|
|
648
|
+
// PUBLIC API
|
|
649
|
+
// ══════════════════════════════════════════════════════════════════════════════
|
|
650
|
+
|
|
651
|
+
module.exports = {
|
|
652
|
+
planPhase,
|
|
653
|
+
buildPhase,
|
|
654
|
+
validatePhase,
|
|
655
|
+
fullPipeline,
|
|
656
|
+
detectLang,
|
|
657
|
+
TASK_TYPES,
|
|
658
|
+
STACK_KEYWORDS,
|
|
659
|
+
};
|
|
660
|
+
|
|
661
|
+
|
|
662
|
+
// ══════════════════════════════════════════════════════════════════════════════
|
|
663
|
+
// CLI ENTRY POINT
|
|
664
|
+
// ══════════════════════════════════════════════════════════════════════════════
|
|
665
|
+
|
|
666
|
+
if (require.main === module) {
|
|
667
|
+
const { flags, positional } = parseArgs(process.argv.slice(2), {
|
|
668
|
+
task: { type: "string", default: null },
|
|
669
|
+
file: { type: "string", default: null },
|
|
670
|
+
phase: { type: "string", default: "full" },
|
|
671
|
+
output: { type: "string", default: "report" },
|
|
672
|
+
spec: { type: "string", default: null },
|
|
673
|
+
code: { type: "string", default: null },
|
|
674
|
+
"dry-run": { type: "boolean", default: false },
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
if (flags.help || (!flags.task && positional[0] !== "demo")) {
|
|
678
|
+
console.log(`
|
|
679
|
+
${BOLD}pipeline_engine.js${RESET} — Tribunal Hybrid Pipeline Engine
|
|
680
|
+
|
|
681
|
+
${BOLD}Usage:${RESET}
|
|
682
|
+
node .agent/scripts/pipeline_engine.js --task "<description>" [options]
|
|
683
|
+
node .agent/scripts/pipeline_engine.js demo
|
|
684
|
+
|
|
685
|
+
${BOLD}Options:${RESET}
|
|
686
|
+
--task <text> Task description
|
|
687
|
+
--file <path> Target file being created/modified
|
|
688
|
+
--phase <name> plan | build | validate | full (default: full)
|
|
689
|
+
--output <format> report | json | prompt (default: report)
|
|
690
|
+
--spec <path> JSON spec file (for build phase, output of plan phase)
|
|
691
|
+
--code <path> Code file to validate (for validate phase)
|
|
692
|
+
--dry-run Show what would happen without generating prompts
|
|
693
|
+
|
|
694
|
+
${BOLD}Phases:${RESET}
|
|
695
|
+
plan Classify task, detect stack, select skills → structured spec
|
|
696
|
+
build Assemble minimal builder prompt from spec
|
|
697
|
+
validate Run deterministic checks on generated code
|
|
698
|
+
full Run plan + build (validate returned as callable)
|
|
699
|
+
|
|
700
|
+
${BOLD}Examples:${RESET}
|
|
701
|
+
node .agent/scripts/pipeline_engine.js --task "React login form" --dry-run
|
|
702
|
+
node .agent/scripts/pipeline_engine.js --task "Express JWT API" --phase plan --output json
|
|
703
|
+
node .agent/scripts/pipeline_engine.js --phase validate --code ./output.tsx
|
|
704
|
+
`);
|
|
705
|
+
process.exit(0);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
const agentDir = findAgentDir();
|
|
709
|
+
|
|
710
|
+
// ── Demo Mode ──────────────────────────────────────────────────────────────
|
|
711
|
+
if (positional[0] === "demo") {
|
|
712
|
+
const scenarios = [
|
|
713
|
+
{ task: "Build a React dashboard with charts and dark mode", file: "Dashboard.tsx" },
|
|
714
|
+
{ task: "Create an Express JWT authentication middleware", file: "auth.ts" },
|
|
715
|
+
{ task: "Write a Prisma query for paginated user search", file: "users.ts" },
|
|
716
|
+
{ task: "Design a premium SaaS landing page with scroll animations", file: "Hero.tsx" },
|
|
717
|
+
];
|
|
718
|
+
|
|
719
|
+
console.log(banner("Pipeline Engine — Demo Mode"));
|
|
720
|
+
|
|
721
|
+
for (const scenario of scenarios) {
|
|
722
|
+
const t = timer();
|
|
723
|
+
const result = fullPipeline(scenario.task, [scenario.file], { agentDir });
|
|
724
|
+
const elapsed = t().toFixed(1);
|
|
725
|
+
|
|
726
|
+
console.log(`\n ${BOLD}Task:${RESET} "${scenario.task}"`);
|
|
727
|
+
console.log(` ${DIM}Type:${RESET} ${result.spec.task_type}`);
|
|
728
|
+
console.log(` ${DIM}Stack:${RESET} ${result.spec.stack.join(", ")}`);
|
|
729
|
+
console.log(` ${GREEN}Skills:${RESET} ${result.skillsLoaded.join(", ") || "(none matched)"}`);
|
|
730
|
+
console.log(` ${CYAN}Tokens:${RESET} ~${result.tokenEstimate} (vs ~12,000 monolithic)`);
|
|
731
|
+
console.log(` ${DIM}Time:${RESET} ${elapsed}ms`);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
console.log(`\n${CYAN}${"━".repeat(56)}${RESET}\n`);
|
|
735
|
+
process.exit(0);
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// ── Phase Execution ────────────────────────────────────────────────────────
|
|
739
|
+
const files = flags.file ? [flags.file] : [];
|
|
740
|
+
|
|
741
|
+
if (flags.phase === "plan" || flags.phase === "full") {
|
|
742
|
+
const t = timer();
|
|
743
|
+
const spec = planPhase(flags.task, files, agentDir);
|
|
744
|
+
const elapsed = t().toFixed(1);
|
|
745
|
+
|
|
746
|
+
if (flags.output === "json") {
|
|
747
|
+
console.log(JSON.stringify(spec, null, 2));
|
|
748
|
+
} else {
|
|
749
|
+
console.log(banner("Pipeline — Pass 1: Planner"));
|
|
750
|
+
console.log(` ${BOLD}Task Type:${RESET} ${spec.task_type}`);
|
|
751
|
+
console.log(` ${BOLD}Stack:${RESET} ${spec.stack.join(", ") || "(auto-detect)"}`);
|
|
752
|
+
console.log(` ${BOLD}Skills:${RESET} ${spec.essential_skills.map(s => s.name).join(", ")}`);
|
|
753
|
+
if (Object.keys(spec.constraints).length > 0) {
|
|
754
|
+
console.log(` ${BOLD}Constraints:${RESET} ${Object.keys(spec.constraints).join(", ")}`);
|
|
755
|
+
}
|
|
756
|
+
console.log(` ${DIM}Time: ${elapsed}ms${RESET}`);
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
if (flags.phase === "full" && !flags["dry-run"]) {
|
|
760
|
+
const builderResult = buildPhase(spec, agentDir);
|
|
761
|
+
|
|
762
|
+
if (flags.output === "json") {
|
|
763
|
+
console.log(JSON.stringify({
|
|
764
|
+
spec,
|
|
765
|
+
tokenEstimate: builderResult.tokenEstimate,
|
|
766
|
+
skillsLoaded: builderResult.skillsLoaded,
|
|
767
|
+
}, null, 2));
|
|
768
|
+
} else if (flags.output === "prompt") {
|
|
769
|
+
console.log(builderResult.prompt);
|
|
770
|
+
} else {
|
|
771
|
+
console.log(sectionHeader("Pass 2: Builder Prompt", 2));
|
|
772
|
+
console.log(` ${BOLD}Token Estimate:${RESET} ~${builderResult.tokenEstimate}`);
|
|
773
|
+
console.log(` ${BOLD}Skills Loaded:${RESET} ${builderResult.skillsLoaded.join(", ")}`);
|
|
774
|
+
console.log(` ${GREEN}Savings:${RESET} ~${Math.round((1 - builderResult.tokenEstimate / 14000) * 100)}% vs monolithic prompt`);
|
|
775
|
+
console.log(`\n ${DIM}Use --output prompt to see the full builder prompt${RESET}`);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
if (flags.phase === "build") {
|
|
781
|
+
let spec;
|
|
782
|
+
if (flags.spec) {
|
|
783
|
+
spec = loadJson(flags.spec);
|
|
784
|
+
if (!spec) {
|
|
785
|
+
console.error(`${RED}✖ Could not load spec file: ${flags.spec}${RESET}`);
|
|
786
|
+
process.exit(1);
|
|
787
|
+
}
|
|
788
|
+
} else if (flags.task) {
|
|
789
|
+
spec = planPhase(flags.task, files, agentDir);
|
|
790
|
+
} else {
|
|
791
|
+
console.error(`${RED}✖ --spec or --task required for build phase${RESET}`);
|
|
792
|
+
process.exit(1);
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
const result = buildPhase(spec, agentDir);
|
|
796
|
+
|
|
797
|
+
if (flags.output === "prompt") {
|
|
798
|
+
console.log(result.prompt);
|
|
799
|
+
} else if (flags.output === "json") {
|
|
800
|
+
console.log(JSON.stringify({
|
|
801
|
+
tokenEstimate: result.tokenEstimate,
|
|
802
|
+
skillsLoaded: result.skillsLoaded,
|
|
803
|
+
}, null, 2));
|
|
804
|
+
} else {
|
|
805
|
+
console.log(banner("Pipeline — Pass 2: Builder"));
|
|
806
|
+
console.log(` Token Estimate: ~${result.tokenEstimate}`);
|
|
807
|
+
console.log(` Skills Loaded: ${result.skillsLoaded.join(", ")}`);
|
|
808
|
+
console.log(`\n Use --output prompt to see the full builder prompt`);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
if (flags.phase === "validate") {
|
|
813
|
+
let codeContent;
|
|
814
|
+
if (flags.code) {
|
|
815
|
+
try {
|
|
816
|
+
codeContent = fs.readFileSync(flags.code, "utf8");
|
|
817
|
+
} catch (_err) {
|
|
818
|
+
console.error(`${RED}✖ Could not read code file: ${flags.code}${RESET}`);
|
|
819
|
+
process.exit(1);
|
|
820
|
+
}
|
|
821
|
+
} else {
|
|
822
|
+
console.error(`${RED}✖ --code is required for validate phase${RESET}`);
|
|
823
|
+
process.exit(1);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
const spec = flags.task ? planPhase(flags.task, files, agentDir) : { target_file: flags.code };
|
|
827
|
+
const result = validatePhase(codeContent, spec);
|
|
828
|
+
|
|
829
|
+
if (flags.output === "json") {
|
|
830
|
+
console.log(JSON.stringify(result, null, 2));
|
|
831
|
+
} else {
|
|
832
|
+
console.log(banner("Pipeline — Pass 3: Validator"));
|
|
833
|
+
const icon = result.verdict === "APPROVED" ? `${GREEN}✅` :
|
|
834
|
+
result.verdict === "WARNING" ? `${YELLOW}⚠️` : `${RED}❌`;
|
|
835
|
+
console.log(` ${BOLD}Verdict:${RESET} ${icon} ${result.verdict}${RESET}`);
|
|
836
|
+
console.log(` ${BOLD}Issues:${RESET} ${result.issues.length}`);
|
|
837
|
+
|
|
838
|
+
if (result.issues.length > 0) {
|
|
839
|
+
console.log("");
|
|
840
|
+
for (const issue of result.issues) {
|
|
841
|
+
const sev = issue.severity === "critical" ? RED :
|
|
842
|
+
issue.severity === "high" ? YELLOW : DIM;
|
|
843
|
+
const loc = issue.line ? ` (line ${issue.line})` : "";
|
|
844
|
+
console.log(` ${sev}[${issue.severity.toUpperCase()}]${RESET} ${issue.message}${loc}`);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
if (result.feedback) {
|
|
849
|
+
console.log(`\n ${BLUE}Self-Healing Feedback:${RESET}`);
|
|
850
|
+
console.log(` ${DIM}${result.feedback.split("\n").join("\n ")}${RESET}`);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
process.exit(result.passed ? 0 : 1);
|
|
855
|
+
}
|
|
856
|
+
}
|