Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
fc73457
feat(picker): match every profile against the repo, not just the 19 w…
Imdeadpool1 Jul 26, 2026
cce1777
fix(test): make cue handoff tests e2e, drop the global mock.module leak
Imdeadpool1 Jul 26, 2026
36e5075
chore: delete 733 lines of verified dead code
Imdeadpool1 Jul 26, 2026
7e337c7
docs(dead-code): mark the report superseded, record the false-positiv…
Imdeadpool1 Jul 26, 2026
7d34aeb
feat(brief): hand the agent verified facts about the directory it lau…
Imdeadpool1 Jul 27, 2026
7973afe
feat(picker): let the model rerank profile matches, without ever wait…
Imdeadpool1 Jul 27, 2026
7c8494a
feat(picker): scope remembered stacks to the repo you launch in
Imdeadpool1 Jul 27, 2026
b029942
feat(picker): scope pair affinity to the repo you launch in
Imdeadpool1 Jul 27, 2026
c157281
feat(picker): scope Recent by repository, not by path prefix
Imdeadpool1 Jul 27, 2026
5eb0ccd
fix(picker): rank suggested stacks by what you actually launch here
Imdeadpool1 Jul 27, 2026
fdd083b
fix(suggest): score skills on what the user actually said
Imdeadpool1 Jul 27, 2026
ae59ebf
fix(auth): keep concurrent sessions from revoking each other's tokens
Imdeadpool1 Jul 27, 2026
cbeeb6b
fix(auth): read the default account's identity where Claude Code keep…
Imdeadpool1 Jul 28, 2026
6385de5
fix(resolver): follow symlinked skill directories
Imdeadpool1 Aug 5, 2026
120e327
feat(core): keep ego-browser loaded in every project
Imdeadpool1 Aug 7, 2026
788d72d
feat(security): gate freshly-fetched skills through NVIDIA SkillSpector
Imdeadpool1 Aug 7, 2026
8112f7b
fix(materializer): stop unresolving the live runtime path mid-swap
Imdeadpool1 Aug 7, 2026
ceebde6
refactor(picker): pull the shared visual primitives out of card and p…
Imdeadpool1 Aug 7, 2026
062b40a
chore: integrity-protocol wording, tag hooks, two new profiles
Imdeadpool1 Aug 7, 2026
0c0f25b
Merge origin/main into fix/oauth-identity-desync
Imdeadpool1 Aug 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .cue.profile
Original file line number Diff line number Diff line change
@@ -1 +1 @@
core
core+skill-writer
1 change: 1 addition & 0 deletions profiles/core/profile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ skills:
# ── Security ──
- security/trivy-scan # /trivy-scan — pre-merge supply-chain gate (Trivy: dependency CVEs + secrets + IaC misconfig). Hard-blocks HIGH/CRITICAL. Invoked by code-review-deep Pass 0 + ship Step 9.0.
# ── Browser ──
- browser/ego-browser # DEFAULT browser path. One JS heredoc drives a real Chromium (navigate, forms, clicks, snapshot, screenshots) instead of many MCP tool-call round trips — chosen for token cost. Reuses the user's real logins. Linux port lives in ~/Documents/ego-lite-linux (package/ego-linux).
- browser/lightpanda # Lightpanda — fast headless browser for scraping, DOM dump, CDP. Pairs with lightpanda MCP.
# ── Source-fetching ──
- tools/opensrc # opensrc — fetch dependency source (npm/PyPI/crates/GitHub) so agents read implementations, not just types
Expand Down
60 changes: 60 additions & 0 deletions profiles/frontend-design/profile.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: frontend-design
icon: "🖌️"
description: Distinctive frontend visual design — Anthropic's frontend-design plugin as the method, plus a browser to verify what actually rendered
inherits: web-frontend-base
recommends:
- frontend # combine to implement the design in React/Next/Svelte
- designer # combine for the heavy kit: brandkit, imagegen, taste-skills, Figma
- medusa-vite # combine to design a Medusa + Vite storefront
- medusa-next # combine to design a Medusa + Next.js storefront
playbooks:
# Inherits ship-feature, triage-bug, sprint from core.
- designer-workflow # brief → layout → build → design-review → polish
persona: |
You design frontends that could not be mistaken for anyone else's. The
`frontend-design` plugin skill is the method — load it and follow its
process (brainstorm → explore → plan → critique → build → critique again)
rather than improvising a look.

- **Name the subject before you design.** If the brief doesn't pin down what
the product is, who it's for, and the page's single job, pin it yourself
and say so. Distinctive choices come from the subject's own world — its
materials, vernacular, artifacts — not from a palette generator.
- **The template answer is the one to beat.** A big number with a small
label, a gradient accent, 01/02/03 markers: use them only when the content
genuinely is a sequence or a statistic. Otherwise they read as defaults.
- **Typography carries the personality.** Pair display and body faces
deliberately, set an intentional scale. Not a neutral delivery vehicle.
- **Take one real aesthetic risk you can justify.** Then justify it out loud.
- **Motion is deliberate or absent.** One orchestrated moment beats scattered
effects; excess animation is itself a tell that a machine made this.
- **Match complexity to the vision.** Maximalist needs elaborate execution;
minimal needs precision in spacing, type, and detail.
- **No em-dashes in UI copy.** Copy makes a design feel templated as fast as
the layout does — write it with the same intent.

## Verify what rendered, not what you wrote

A design claim is unproven until you have looked at it. `web-frontend-base`
supplies `agent-browser` and the screenshot skill — open the page, capture
it, and check the result against the brief before calling it done. Reading
your own CSS is not verification.

## When combined with other profiles

- With **frontend**: you own the visual layer, that profile owns the
component/state/a11y correctness bar.
- With **designer**: reach for that profile's brandkit, image generation, and
Figma MCP when the job needs assets or an existing design file. This
profile stays lean on purpose — combine rather than duplicate.
skills:
local:
# Creation is the plugin's job (see `plugins:` below); this covers the
# plugin's own "critique again" step with a structured review pass.
- gstack/design-review
plugins:
# Anthropic's official frontend-design plugin — the aesthetic-direction
# method this profile is built around. Already shipped by designer/commerce/
# studio/webshop; here it is the centrepiece rather than an add-on.
- frontend-design@claude-plugins-official
mcps: [] # browser automation via the agent-browser CLI (no MCP needed)
53 changes: 47 additions & 6 deletions resources/hooks/liedetector-tag-density.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
#
# The integrity protocol asks the model to mark decision-relevant claims with
# confidence tags (🟢 [VERIFIED], 🟡 [INFERRED ~80%], 🟠 [GUESSED ~30%],
# 🔴 [UNKNOWN], etc.). Two failure modes degrade that signal:
# 🔴 [UNKNOWN], etc.). Three failure modes degrade that signal:
# (a) a long, substantive response with ZERO tags — no confidence signal at
# all where the reader most needs one;
# (b) tag-spam — a tag on nearly every clause, which trains the reader to
# (b) a yellow/orange tag with no ~N%, or an ~N% that isn't on its tier's
# ladder — the tier alone can't order claims against each other;
# (c) tag-spam — a tag on nearly every clause, which trains the reader to
# ignore the tags entirely.
# This hook nudges on both. It NEVER blocks the Stop; it only prints one line
# to stderr (which Claude Code surfaces) so the model can self-correct.
# This hook nudges on all three. It NEVER blocks the Stop; it only prints one
# line to stderr (which Claude Code surfaces) so the model can self-correct.
#
# Honest about its limits: this is a crude heuristic. It cannot tell whether a
# response was actually "decision-relevant" — it uses response length (>1500
Expand All @@ -18,7 +20,9 @@
# narrative explanation). To keep the false-positive rate low it only fires on
# CLEARLY long, completely tag-free responses, and stays silent otherwise. The
# density check needs at least 4 tags before it can call something "spam".
# Treat every nudge as a question ("did this response need tags?"), not a verdict.
# Treat those two nudges as a question ("did this response need tags?"), not a
# verdict. Check (b) is the exception: the protocol names the legal ~N% values,
# so a missing or off-ladder percent is a fact, not a heuristic.
#
# Reliability: parsing the transcript can fail for many reasons (missing file,
# truncated JSONL, schema drift). Every failure path FAILS OPEN — any error
Expand Down Expand Up @@ -55,6 +59,18 @@ TAGS = ("VERIFIED", "KNOWN", "INFERRED", "ASSUMED",
"GUESSED", "STALE", "UNKNOWN", "CORRECTION")
TAG_RE = re.compile(r"\[(?:%s)[^\]]*\]" % "|".join(TAGS))

# The protocol requires a ~N% on every yellow and orange tag, drawn from that
# tier's ladder. Yellow spans ~50-85%, orange ~20-45%, so the ladders don't
# overlap each other or green (>=90%).
LADDER = {
"INFERRED": {"50", "60", "70", "80"},
"ASSUMED": {"50", "60", "70", "80"},
"GUESSED": {"20", "30", "40"},
"STALE": {"20", "30", "40"},
}
CAL_RE = re.compile(r"\[(%s)([^\]]*)\]" % "|".join(LADDER))
PCT_RE = re.compile(r"~\s*(\d+)\s*%")

LONG_CHARS = 1500 # proxy for "substantive response"
SPAM_MIN_TAGS = 4 # need real density before calling it spam
SPAM_WORDS_PER_TAG = 25 # > 1 tag / 25 words = spam
Expand Down Expand Up @@ -106,7 +122,32 @@ if n_chars > LONG_CHARS and n_tags == 0:
"Skip this nudge with [skip-tag-density]." % n_chars)
sys.exit(0)

# (b) tag-spam → density trains the reader to ignore the tags.
# (b) calibration format → a yellow/orange tag with no ~N%, or with one that
# isn't on its tier's ladder. Unlike the two heuristics around it this is
# an exact check: the protocol names the legal values, so a miss is a
# violation rather than a guess about intent.
missing, offladder = [], []
for tag, rest in CAL_RE.findall(last):
pct = PCT_RE.search(rest)
if not pct:
missing.append(tag)
elif pct.group(1) not in LADDER[tag]:
offladder.append("%s ~%s%%" % (tag, pct.group(1)))

if missing or offladder:
parts = []
if missing:
parts.append("%d tag(s) with no ~N%% (%s)"
% (len(missing), ", ".join(sorted(set(missing)))))
if offladder:
parts.append("%d off-ladder (%s)"
% (len(offladder), ", ".join(sorted(set(offladder)))))
print("liedetector: %s. Yellow ([INFERRED]/[ASSUMED]) takes ~50/60/70/80%%, "
"orange ([GUESSED]/[STALE]) takes ~20/30/40%% — nothing else. "
"Skip with [skip-tag-density]." % "; ".join(parts))
sys.exit(0)

# (c) tag-spam → density trains the reader to ignore the tags.
if n_tags >= SPAM_MIN_TAGS and n_words > 0:
words_per_tag = n_words / n_tags
if words_per_tag < SPAM_WORDS_PER_TAG:
Expand Down
58 changes: 48 additions & 10 deletions resources/hooks/tag-audit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@
# - For every [KNOWN] claim that mentions a time-sensitive subject
# (versions, "latest", "current"), warn — training data goes stale.
#
# It also reports the turn's TAG MIX — the green/yellow/orange/red split of
# the claims, with a "% grounded" and "% guess-or-worse" readout. The audits
# above catch violations; the mix answers the plainer question the tags exist
# for: how much of this answer did the model actually check? Prints on turns
# with >=3 tags. Disable with CUE_TAG_MIX_OFF=1.
#
# When mismatches are detected, the hook emits a "⚠ Tag audit" block to
# stderr (which Claude Code surfaces). It never blocks; it only flags.
# Suppress per-turn via [skip-tag-audit] anywhere in the assistant response.
Expand Down Expand Up @@ -46,8 +52,10 @@ touch "$throttle"
last_user_line=$(awk 'BEGIN{n=0; last=0} {n++} /"type":"user"/{last=n} END{print last}' "$transcript_path")
[ "$last_user_line" = "0" ] && exit 0

# Slice the transcript: just this turn's lines.
turn_jsonl="$CACHE_DIR/turn.jsonl"
# Slice the transcript: just this turn's lines. Session-scoped — concurrent
# Claude sessions share $CACHE_DIR, and an unscoped path lets one session's
# Stop hook overwrite another's slice mid-read.
turn_jsonl="$CACHE_DIR/turn.${session_id:-default}.jsonl"
tail -n +"$last_user_line" "$transcript_path" > "$turn_jsonl"

# ─── Extract assistant text + tool_use names from this turn ────────────────
Expand All @@ -73,10 +81,22 @@ if grep -qF "[skip-tag-audit]" <<< "$assistant_text"; then exit 0; fi
# ─── Count tags in the response ────────────────────────────────────────────
# Match [VERIFIED], 🟢 [VERIFIED], `[VERIFIED]`, etc. Single regex with
# optional brackets/backticks.
verified_count=$(grep -oE '\[VERIFIED[^]]*\]' <<< "$assistant_text" | wc -l | tr -d '\n')
known_count=$(grep -oE '\[KNOWN[^]]*\]' <<< "$assistant_text" | wc -l | tr -d '\n')
verified_count=${verified_count:-0}
known_count=${known_count:-0}
count_tag() { grep -oE "\[$1[^]]*\]" <<< "$assistant_text" | wc -l | tr -d '\n'; }

verified_count=$(count_tag VERIFIED); verified_count=${verified_count:-0}
known_count=$(count_tag KNOWN); known_count=${known_count:-0}
inferred_count=$(count_tag INFERRED); inferred_count=${inferred_count:-0}
assumed_count=$(count_tag ASSUMED); assumed_count=${assumed_count:-0}
guessed_count=$(count_tag GUESSED); guessed_count=${guessed_count:-0}
stale_count=$(count_tag STALE); stale_count=${stale_count:-0}
unknown_count=$(count_tag UNKNOWN); unknown_count=${unknown_count:-0}
correction_count=$(count_tag CORRECTION); correction_count=${correction_count:-0}

green_count=$((verified_count + known_count))
yellow_count=$((inferred_count + assumed_count))
orange_count=$((guessed_count + stale_count))
red_count=$unknown_count
claim_count=$((green_count + yellow_count + orange_count + red_count))

# ─── Count verification tool calls ─────────────────────────────────────────
# A "verification action" is one of:
Expand Down Expand Up @@ -157,7 +177,22 @@ if [ "$stale_known" -gt 0 ]; then
warnings+=("⚠ Tag audit: ${stale_known}× [KNOWN] tag on time-sensitive subject(s) (versions / 'latest' / 'current'). Training data goes stale. Downgrade to [STALE] or re-verify via web search.")
fi

[ "${#warnings[@]}" -eq 0 ] && exit 0
# ─── Tag mix: how much of this turn was grounded vs guessed ────────────────
# Everything above detects protocol *violations*. This block answers the
# plainer question the tags exist for: how much of what I just said did I
# actually check? Prints whenever the turn carries enough tags to form a
# distribution (>=3), so one-tag asides stay quiet. Disable: CUE_TAG_MIX_OFF=1.
mix_line=""
if [ "${CUE_TAG_MIX_OFF:-}" != "1" ] && [ "$claim_count" -ge 3 ]; then
green_pct=$((green_count * 100 / claim_count))
soft_pct=$(((orange_count + red_count) * 100 / claim_count))
mix_line="$(printf '🕵 Tag mix (%d claims): 🟢%d 🟡%d 🟠%d 🔴%d — %d%% grounded, %d%% guess-or-worse' \
"$claim_count" "$green_count" "$yellow_count" "$orange_count" "$red_count" \
"$green_pct" "$soft_pct")"
[ "$correction_count" -gt 0 ] && mix_line="${mix_line} | ${correction_count}x [CORRECTION]"
fi

[ "${#warnings[@]}" -eq 0 ] && [ -z "$mix_line" ] && exit 0

# ─── Opt-in: auto-log detected miscalibrations to the calibration scoreboard ─
# The always-on audit detects exactly the events the scoreboard wants to tally
Expand Down Expand Up @@ -190,9 +225,12 @@ fi
# ─── Emit warnings to stderr (Claude Code surfaces) ────────────────────────
{
printf '\n'
for w in "${warnings[@]}"; do printf '%s\n' "$w"; done
printf ' (turn tool calls: %d verification, %d non-verification | suppress with [skip-tag-audit])\n' \
"$verification_count" "$non_verification_count"
[ -n "$mix_line" ] && printf '%s\n' "$mix_line"
if [ "${#warnings[@]}" -gt 0 ]; then
for w in "${warnings[@]}"; do printf '%s\n' "$w"; done
printf ' (turn tool calls: %d verification, %d non-verification | suppress with [skip-tag-audit])\n' \
"$verification_count" "$non_verification_count"
fi
} >&2

exit 0
4 changes: 3 additions & 1 deletion resources/personas/integrity-protocol-compact.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ Applies to every response. Flag uncertainty *before* the claim, never bury hedge
- 🟠 `[STALE]` — true at training cutoff; re-check current docs.
- 🔴 `[UNKNOWN]` — outside reliable knowledge; say so instead of fabricating.

Pick the *most specific* tag and **downgrade when in doubt** — false confidence hurts more than false hedging. Optional decile calibration on yellow/orange (`🟡 [INFERRED ~80%]`), required when the user must rank two of your suggestions.
Pick the *most specific* tag and **downgrade when in doubt** — false confidence hurts more than false hedging.

**Every yellow and orange tag carries a `~N%`** drawn from its tier's ladder — yellow `~50/60/70/80%`, orange `~20/30/40%`, nothing else. A bare `[INFERRED]` or `[GUESSED]` is a protocol violation; so is `~67%` (false precision), `~90%` on yellow (green's range), or `~50%` on orange (yellow's). Skip the % on green and red — the tier already says it. Can't pick a value? You're in the wrong tier: downgrade. The number orders claims *within one response*; it is not a calibrated absolute probability.

**Confidence audit** when a response has 2+ yellow-or-worse claims, recommends an action, or summarizes external evidence: end with Evidence quality (Strong/Moderate/Weak/Insufficient), the biggest confidence limiter, and one thing to verify externally.

Expand Down
9 changes: 6 additions & 3 deletions resources/personas/integrity-protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ Rewritten by Claude (Opus 4.7) from your hallucination-reduction draft. Applies
**Red tier — don't trust, don't fabricate (~0–10%)**
- 🔴 `[UNKNOWN]` — outside my reliable knowledge. I'm saying so instead of fabricating an answer. Hand off to a search or to the user.

**Optional percentage calibration on yellow/orange tags.** When a claim sits at a notable edge of its tier (or stakes warrant more precision), append a decile-snapped estimate with a tilde to signal it's a rough self-calibration, not a true probability: `🟡 [INFERRED ~80%]`, `🟠 [GUESSED ~30%]`. Rules:
- Snap to deciles (20 / 30 / 40 / 60 / 80 / 90), never `~67%` or `~73%` — that's false precision
**Required percentage calibration on yellow/orange tags.** Every yellow and orange tag carries a `~N%` drawn from its tier's ladder, with a tilde to signal it's a rough self-calibration rather than a true probability: `🟡 [INFERRED ~80%]`, `🟠 [GUESSED ~30%]`. Rules:
- Yellow (`[INFERRED]`, `[ASSUMED]`) → one of `~50%` `~60%` `~70%` `~80%`
- Orange (`[GUESSED]`, `[STALE]`) → one of `~20%` `~30%` `~40%`
- Nothing else on the ladder. Never `~67%` or `~73%` (false precision), never `~90%` on yellow (that's green's range) or `~50%` on orange (that's yellow's)
- A bare `[INFERRED]` / `[ASSUMED]` / `[GUESSED]` / `[STALE]` is a protocol violation
- Always prefix `~` so the reader knows it's an estimate
- Skip the % on green and red — the tier already says it
- Required when the user has to decide between two of your suggestions and the order of confidence matters more than the tier itself
- If you can't pick a value, you're in the wrong tier — downgrade to the one where the range fits
- The number is meaningful as *relative* ordering across claims in the same response, *not* as a literal calibrated probability

**Picking the tag.** Choose the *most specific* fit, never grade-inflate:
Expand Down
2 changes: 1 addition & 1 deletion src/commands/optimizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ const KNOWN_CLIS = new Set([
"release-plz", "typos", "cargo-chef", "cargo-msrv", "cargo-readme",
"maturin", "napi", "uniffi-bindgen", "bindgen", "cbindgen",
"probe-rs", "cargo-embed", "cargo-binutils", "chisel",
"chromium", "chrome", "google-chrome", "microsoft-edge",
"chromium", "chrome", "google-chrome", "microsoft-edge", "ego-browser",
"openssl", "ssh", "ncat", "netcat", "socat",
"splunk", "elastic", "kibana", "logstash",
"peepdf", "pdfid", "pdf-parser", "olevba", "oletools",
Expand Down
Loading
Loading