Add new agents and skills for enhanced project orchestration and review processes

- Introduced `critic`, an independent adversarial reviewer for security and correctness.
- Added `fable-orchestrator` to manage task routing and verification.
- Implemented `gauntlet-critic` for fresh-context evaluation of gauntlet rounds.
- Created `planner` for generating executable implementation plans with dependencies.
- Developed `security-auditor` for application security reviews and audits.
- Established `system-steward` to improve agent prompts and skills based on verified failures.
- Added `dev-loop` skill for autonomous development loops over repositories.
- Implemented `gauntlet-loop` skill for iterative quality benchmarking against reference standards.
- Updated project settings to utilize the new orchestrator agent.
- Created documentation for `GAUNTLET.md`, `PROGRESS.md`, and `REFERENCE_BAR.md` to track project status and quality benchmarks.
- Added detailed prompting style guide to enhance understanding of prompt patterns and agentic loops.
This commit is contained in:
john kevin asprec
2026-08-08 16:49:07 +08:00
parent 6aee260533
commit 444060c3eb
85 changed files with 2717 additions and 171 deletions

View File

@@ -1,9 +1,9 @@
import { defineBackground } from 'wxt/utils/define-background';
import { CONFIG_STORAGE_KEYS } from '@lib/types';
import type { AnalyzePayload, LexAIConfig, LexAIResponse } from '@lib/types';
import { CONTEXT_MENU_ENTRIES, findContextMenuEntry, resolvePromptPersona } from '@lib/actions';
import { CONTEXT_MENU_ENTRIES, findContextMenuEntry, resolvePromptPersona, resolvePromptPattern } from '@lib/actions';
import { decryptApiKey, migratePlaintextApiKey } from '@lib/crypto';
import { callProvider, getSystemPrompt, listModels, providerLabel } from '@lib/providers';
import { callProvider, defaultMaxTokens, getSystemPrompt, listModels, providerLabel } from '@lib/providers';
// Resolve the usable API key from stored config: prefer the encrypted path,
// fall back to plaintext for backward compat. Returns null if none is set.
@@ -37,12 +37,12 @@ async function handleAnalyzeText(payload: AnalyzePayload): Promise<LexAIResponse
// points behave the same. The popup still overrides by sending its own.
if (payload.action === 'prompt' && !payload.promptParams) {
const saved = (await chrome.storage.local.get([
'promptStyle', 'promptPersona', 'customPersona', 'promptFormat', 'promptModel',
'promptPattern', 'promptStyle', 'promptPersona', 'customPersona', 'promptFormat', 'promptModel',
])) as Record<string, string | undefined>;
payload = {
...payload,
promptParams: {
promptStyle: saved.promptStyle,
pattern: resolvePromptPattern(saved.promptPattern, saved.promptStyle),
persona: resolvePromptPersona(saved.promptPersona, saved.customPersona),
format: saved.promptFormat,
},
@@ -64,7 +64,13 @@ async function handleAnalyzeText(payload: AnalyzePayload): Promise<LexAIResponse
...(payload.model ? { model: payload.model } : {}),
};
const systemPrompt = getSystemPrompt(payload.action, payload.style, payload.promptParams);
return callProvider(resolvedConfig, payload.text, systemPrompt);
// The Prompt Builder's engineered prompts (react/pev/gauntlet skeletons
// especially) run well past defaultMaxTokens' input-scaled floor for a
// short input idea — give the 'prompt' action a higher floor.
const callOpts = payload.action === 'prompt'
? { maxTokens: Math.max(2048, defaultMaxTokens(payload.text)) }
: undefined;
return callProvider(resolvedConfig, payload.text, systemPrompt, callOpts);
}
// ─── Background entry ─────────────────────────────────────────────────────────

View File

@@ -1,6 +1,6 @@
import { defineContentScript } from 'wxt/utils/define-content-script';
import { isExtensionValid, safeSendMessage } from '@lib/messaging';
import { MIN_SELECTION_LENGTH, WRITING_STYLES, PROMPT_STYLES, PROMPT_PERSONAS, PROMPT_FORMATS } from '@lib/actions';
import { MIN_SELECTION_LENGTH, WRITING_STYLES, PROMPT_PATTERNS, PROMPT_PERSONAS, PROMPT_FORMATS, resolvePromptPattern } from '@lib/actions';
export default defineContentScript({
matches: ['<all_urls>'],
@@ -654,11 +654,6 @@ export default defineContentScript({
header.appendChild(closeX);
panel.appendChild(header);
const hint = document.createElement('div');
hint.textContent = '"Auto" lets it decide from your selected text.';
Object.assign(hint.style, { fontSize: '11px', color: '#6c7086', marginBottom: '10px' });
panel.appendChild(hint);
const selectStyle = {
flex: '1', background: 'rgba(49,50,68,0.95)', border: '1px solid rgba(205,214,244,0.2)',
borderRadius: '6px', color: '#cdd6f4', fontSize: '12px', padding: '5px 8px',
@@ -670,19 +665,50 @@ export default defineContentScript({
Object.assign(row.style, { display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '8px' });
const label = document.createElement('span');
label.textContent = labelText;
Object.assign(label.style, { fontSize: '12px', color: '#a6adc8', width: '58px', flexShrink: '0' });
Object.assign(label.style, { fontSize: '12px', color: '#a6adc8', width: '64px', flexShrink: '0' });
row.appendChild(label);
row.appendChild(control);
panel.appendChild(row);
return row;
}
function makeSelect(options: readonly string[]): HTMLSelectElement {
// Plain flat-list select (Persona/Format), or a grouped select (Pattern)
// when `grouped` is true — 'auto' renders first as a bare option, then
// one <optgroup> per PROMPT_PATTERNS group.
function makeSelect(options: readonly string[]): HTMLSelectElement;
function makeSelect(options: typeof PROMPT_PATTERNS, grouped: true): HTMLSelectElement;
function makeSelect(options: readonly string[] | typeof PROMPT_PATTERNS, grouped?: true): HTMLSelectElement {
const sel = document.createElement('select');
sel.setAttribute('data-lexai', 'true');
Object.assign(sel.style, selectStyle);
options.forEach((o) => {
if (grouped) {
const defs = options as typeof PROMPT_PATTERNS;
const addOption = (parent: Element, p: (typeof PROMPT_PATTERNS)[number]) => {
const opt = document.createElement('option');
opt.setAttribute('data-lexai', 'true');
opt.value = p.id;
opt.textContent = p.label;
opt.title = p.hint;
parent.appendChild(opt);
};
const auto = defs.find((p) => p.id === 'auto');
if (auto) addOption(sel, auto);
(['Direct', 'Reasoning', 'Agentic'] as const).forEach((group) => {
const inGroup = defs.filter((p) => p.id !== 'auto' && p.group === group);
if (inGroup.length === 0) return;
const optgroup = document.createElement('optgroup');
optgroup.setAttribute('data-lexai', 'true');
optgroup.label = group;
inGroup.forEach((p) => addOption(optgroup, p));
sel.appendChild(optgroup);
});
return sel;
}
(options as readonly string[]).forEach((o) => {
const opt = document.createElement('option');
opt.setAttribute('data-lexai', 'true');
opt.value = o;
opt.textContent = o;
sel.appendChild(opt);
@@ -690,8 +716,21 @@ export default defineContentScript({
return sel;
}
const selPromptStyle = makeSelect(PROMPT_STYLES);
makeRow('Style:', selPromptStyle);
const selPattern = makeSelect(PROMPT_PATTERNS, true);
makeRow('Pattern:', selPattern);
const patternHint = document.createElement('div');
patternHint.setAttribute('data-lexai', 'true');
Object.assign(patternHint.style, {
fontSize: '11px', color: '#6c7086', margin: '2px 0 8px 72px',
minHeight: '28px', lineHeight: '1.3',
});
panel.appendChild(patternHint);
const updatePatternHint = () => {
patternHint.textContent = PROMPT_PATTERNS.find((p) => p.id === selPattern.value)?.hint ?? '';
};
selPattern.addEventListener('change', updatePatternHint);
updatePatternHint();
const selPersona = makeSelect(PROMPT_PERSONAS);
makeRow('Persona:', selPersona);
@@ -720,9 +759,10 @@ export default defineContentScript({
// Prefill from saved settings, then fetch the model list.
chrome.storage.local.get(
['promptStyle', 'promptPersona', 'customPersona', 'promptFormat', 'promptModel'],
['promptPattern', 'promptStyle', 'promptPersona', 'customPersona', 'promptFormat', 'promptModel'],
(saved) => {
if (saved.promptStyle) selPromptStyle.value = saved.promptStyle as string;
selPattern.value = resolvePromptPattern(saved.promptPattern as string | undefined, saved.promptStyle as string | undefined);
updatePatternHint();
if (saved.promptPersona) {
selPersona.value = saved.promptPersona as string;
customRow.style.display = selPersona.value === 'Custom…' ? 'flex' : 'none';
@@ -771,7 +811,7 @@ export default defineContentScript({
// Persist selections (shared with the popup), then run — the background
// reads these same keys for prompt requests without explicit params.
chrome.storage.local.set({
promptStyle: selPromptStyle.value,
promptPattern: selPattern.value,
promptPersona: selPersona.value,
customPersona: customInput.value,
promptFormat: selFormat.value,

View File

@@ -2,7 +2,7 @@ import React, { useEffect, useRef, useState } from 'react';
import { createRoot } from 'react-dom/client';
import { safeSendMessage, safeStorageGet, safeStorageSet } from '@lib/messaging';
import { WRITING_STYLES, PROMPT_STYLES, PROMPT_PERSONAS, PROMPT_FORMATS, resolvePromptPersona } from '@lib/actions';
import { WRITING_STYLES, PROMPT_PATTERNS, PROMPT_PERSONAS, PROMPT_FORMATS, resolvePromptPersona, resolvePromptPattern } from '@lib/actions';
import type { PromptParams } from '@lib/types';
// ─── Types ───────────────────────────────────────────────────────────────────
@@ -202,7 +202,7 @@ function Popup() {
const [copied, setCopied] = useState(false);
const [writingStyle, setWritingStyle] = useState('Default');
// Prompt Builder parameters — persisted so they survive popup close/open.
const [promptStyle, setPromptStyle] = useState('Auto');
const [promptPattern, setPromptPattern] = useState('auto');
const [promptPersona, setPromptPersona] = useState('Auto');
const [customPersona, setCustomPersona] = useState('');
const [promptFormat, setPromptFormat] = useState('Auto');
@@ -217,7 +217,7 @@ function Popup() {
// Load config + restore session input + load writing style
useEffect(() => {
safeStorageGet(
['provider', 'apiKey', 'apiKeyEnc', 'model', 'writingStyle', 'promptStyle', 'promptPersona', 'customPersona', 'promptFormat', 'promptModel', 'popupTab'],
['provider', 'apiKey', 'apiKeyEnc', 'model', 'writingStyle', 'promptPattern', 'promptStyle', 'promptPersona', 'customPersona', 'promptFormat', 'promptModel', 'popupTab'],
(result) => {
if (result.apiKey || result.apiKeyEnc) {
setConfigured(true);
@@ -227,7 +227,9 @@ function Popup() {
if (result.writingStyle) {
setWritingStyle(result.writingStyle);
}
if (result.promptStyle) setPromptStyle(result.promptStyle);
if (result.promptPattern || result.promptStyle) {
setPromptPattern(resolvePromptPattern(result.promptPattern, result.promptStyle));
}
if (result.promptPersona) setPromptPersona(result.promptPersona);
if (result.customPersona) setCustomPersona(result.customPersona);
if (result.promptFormat) setPromptFormat(result.promptFormat);
@@ -273,7 +275,7 @@ function Popup() {
// Resolve the Prompt Builder selections into message params. 'Custom…'
// uses the free-text persona (falls back to Auto when left empty).
const resolvePromptParams = (): PromptParams => ({
promptStyle,
pattern: promptPattern,
persona: resolvePromptPersona(promptPersona, customPersona),
format: promptFormat,
});
@@ -473,18 +475,28 @@ function Popup() {
</div>
<div style={{ ...S.styleRow, marginTop: '6px' }}>
<span style={{ ...S.styleLabel, width: '64px' }}>Style:</span>
<span style={{ ...S.styleLabel, width: '64px' }}>Pattern:</span>
<select
style={S.styleSelect}
value={promptStyle}
onChange={(e) => setParam('promptStyle', e.target.value, setPromptStyle)}
value={promptPattern}
onChange={(e) => setParam('promptPattern', e.target.value, setPromptPattern)}
disabled={processing}
>
{PROMPT_STYLES.map((s) => (
<option key={s} value={s}>{s}</option>
{PROMPT_PATTERNS.filter((p) => p.id === 'auto').map((p) => (
<option key={p.id} value={p.id} title={p.hint}>{p.label}</option>
))}
{(['Direct', 'Reasoning', 'Agentic'] as const).map((group) => (
<optgroup key={group} label={group}>
{PROMPT_PATTERNS.filter((p) => p.id !== 'auto' && p.group === group).map((p) => (
<option key={p.id} value={p.id} title={p.hint}>{p.label}</option>
))}
</optgroup>
))}
</select>
</div>
<div style={{ fontSize: '11px', color: '#6c7086', margin: '2px 0 0 72px' }}>
{PROMPT_PATTERNS.find((p) => p.id === promptPattern)?.hint}
</div>
<div style={{ ...S.styleRow, marginTop: '6px' }}>
<span style={{ ...S.styleLabel, width: '64px' }}>Persona:</span>