Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
37ac793
docs: RepoLens brand-identity spec (Mono Ink, Vee-eye icon, voice)
New1Direction Jun 16, 2026
795747b
docs: brand-identity implementation plan (17 tasks, 4 phases)
New1Direction Jun 16, 2026
5dab908
feat(brand): icon-draw.js — pure canvas draw of the Mono Ink Vee icon
New1Direction Jun 16, 2026
8a2669e
feat(brand): tools/make-icons.html — export icon PNGs from icon-draw.js
New1Direction Jun 16, 2026
195c5c5
feat(brand): real Mono Ink icon PNGs (16/32/48/128) + manifest wiring
New1Direction Jun 16, 2026
86bc4f7
feat(brand): icon-anim.js — scan-state toolbar animation + pure frame…
New1Direction Jun 16, 2026
2925884
feat(brand): animate the toolbar icon during an active scan
New1Direction Jun 16, 2026
de59287
feat(brand): animateIcon setting + persisted reduced-motion flag for …
New1Direction Jun 16, 2026
4592f5a
feat(brand): add the Mono Ink theme to themes.css
New1Direction Jun 16, 2026
068750f
feat(brand): make Mono Ink the default theme
New1Direction Jun 16, 2026
4598212
test(brand): assert themes.css defines the Mono Ink token vocabulary
New1Direction Jun 16, 2026
103807c
feat(brand): assets/wordmark.svg — lens + RepoLens lockup
New1Direction Jun 16, 2026
1ad652f
feat(brand): new tagline in manifest + README header (drop emoji)
New1Direction Jun 16, 2026
4a19bb8
docs(style): vendor stop-slop as the RepoLens writing standard
New1Direction Jun 16, 2026
1500781
refactor(brand): re-voice Vee's onboarding copy to warm-human
New1Direction Jun 16, 2026
36357ea
docs(brand): de-slop README intro + Vee lines, add Mono Ink changelog…
New1Direction Jun 16, 2026
ea020a6
fix(brand): options.html smart-quote crash + matchMedia guard + voice…
New1Direction Jun 16, 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
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@ Every release of RepoLens, newest first. Want the friendly highlights instead of
the full detail? See **[What's new](README.md)** in the README.

This project follows [Semantic Versioning](https://semver.org/) and groups changes
by theme. Dates are when the release landed on `main` 1.1.0 through 1.6.0 shipped
by theme. Dates are when the release landed on `main`. 1.1.0 through 1.6.0 shipped
the same day, as a rapid burst of improvements, so they share a date.

## [Unreleased]

### Added

- **Mono Ink identity.** RepoLens ships a new dark-tile lens icon, a "Mono Ink" default theme (cool near-black, white, and cobalt), and a wordmark lockup. The toolbar icon now animates only while a scan runs: the aperture grows and spins and the ring breathes grey to blue, then it resets to static. Turn the animation off in **Options**, and it honors your OS reduced-motion setting. The other 13 themes stay one click away.
- **A warmer Vee.** Vee's onboarding copy reads like a person now. The repo also vendors the stop-slop writing standard under `docs/style/` so the voice stays consistent.
- **Vee-guided first-run walkthrough.** New users are met by Vee on their first Library open; the coachmark steps through a seeded demo repo (Library card → Verdict tab → Blueprint canvas) with plain narration and a spotlight on each target element. Implemented in `onboarding.js` / `coachmark.js`; copy lives in `onboarding-copy.js`.
- **Milestone "power tour"** offered after approximately five real scans: a second coachmark sequence introducing the cross-library tools Ask, Corkboard (Alternatives / Synergies), multi-select Compare, Radar / auto-organize, and Discover.
- **Milestone "power tour"** offered after approximately five real scans: a second coachmark sequence introducing the cross-library tools: Ask, Corkboard (Alternatives / Synergies), multi-select Compare, Radar / auto-organize, and Discover.

---

Expand Down
26 changes: 13 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<div align="center">

# 🔭 RepoLens
# RepoLens

### One click opens the case file on any repo.
### Click any repo. Get a straight answer on whether to use it.

**The verdict · the evidence · the red flags · how it's actually built — in plain English, before the README's pitch.**
**The verdict, the evidence, the red flags, and how it's built. In plain English, before the README's pitch.**

![Chrome Manifest V3](https://img.shields.io/badge/Chrome-Manifest_V3-1a73e8?logo=googlechrome&logoColor=white)
![Zero build](https://img.shields.io/badge/build-none-0e1722)
Expand All @@ -17,9 +17,9 @@

---

RepoLens is a **Manifest V3 Chrome extension**. Land on a GitHub, GitLab, npm, or PyPI page, click the toolbar icon, and it reads the repo, runs it past the AI provider of your choice, and opens a tab with a **verdict-first** breakdown — it opens with a straight answer (*should you use this?*) before any prose, not the README's marketing.
RepoLens is a **Manifest V3 Chrome extension**. Open a GitHub, GitLab, npm, or PyPI page and click the toolbar icon. RepoLens reads the repo, runs it past the AI provider you picked, and opens a tab that leads with a straight answer: should you use this? You see the verdict before any of the README's pitch.

> Stars tell you a project is popular. They don't tell you whether it fits *your* problem. RepoLens answers the question you actually have: **should I use this, and what am I signing up for?**
> Stars tell you a project is popular. They don't tell you whether it fits your problem. RepoLens answers the question you have: should I use this, and what am I signing up for?

---

Expand All @@ -41,7 +41,7 @@ A scan opens to a **verdict landing** and fans out into focused tabs:

Plus **SKTPG** (a one-tap State / Known-pitfalls / Trajectory / Proof / Growth read), framework lenses, and capability re-tagging.

**First run:** Vee walks new users through a seeded demo repo (Library Verdict Blueprint) via a coachmark tour. After roughly five real scans a second "power tour" introduces the cross-library tools: Ask, Corkboard analysis, multi-select compare, Radar, and Discover.
**First run:** Vee, the lens mascot, walks you through a seeded demo repo (Library, then Verdict, then Blueprint) with a short coachmark tour. After about five real scans, a second power tour shows you the cross-library tools: Ask, Corkboard, multi-select compare, Radar, and Discover.

---

Expand All @@ -61,7 +61,7 @@ A correctness, security, and tooling pass from a full code audit — fixes only,
### v1.7.0 — Boards, Vee & a motion pass

- 🗂️ **Collections ("Boards").** Group the repos you're evaluating together and filter the Library by board — with live counts, per-card membership dots, and a one-click assignment popover. Boards travel in your library export/import.
- 🔭 **Meet "Vee", an optional lens mascot** that reacts to your scans (scanning, wide-open on a strong fit, eyes-narrowed on a risky one, resting on an empty library). One theme-aware SVG, reduced-motion-safe; turn it off in **Options → Interface**.
- **Meet Vee, an optional lens mascot** that reacts to your scans: scanning, wide-open on a strong fit, narrowed on a risky one, resting on an empty library. One theme-aware SVG, reduced-motion safe. Turn it off in **Options → Interface**.
- ✨ **Subtle motion, everywhere it helps** — tactile press states, a staged tab reveal, a verdict health-bar fill, a smoother toast and modal — all respecting reduced-motion.
- 🧭 **Errors that tell you what to do** — a failed scan now offers **Open Settings** (bad key / wrong model) or **Retry** (transient), and the loading copy names the provider it's actually using.

Expand Down Expand Up @@ -119,15 +119,15 @@ No accounts. No backend. Your keys, your machine.

## Models — your keys, your call

Bring your own provider. Five are **first-class** (one-click sign-in where the vendor allows it**Grok**, **OpenRouter**, and **OpenAI/ChatGPT** otherwise an API key; **Claude** is API-key only) and fan out across a **smart fallback chain**: RepoLens tries them in order and drops to the next if one errors, so a single key is enough to start.
Bring your own provider. Five are **first-class** (one-click sign-in where the vendor allows it: **Grok**, **OpenRouter**, and **OpenAI/ChatGPT**; otherwise an API key; **Claude** is API-key only) and fan out across a **smart fallback chain**: RepoLens tries them in order and drops to the next if one errors, so a single key is enough to start.

**Nous** (Nous Research) **→ Gemini → OpenRouter → Grok → Anthropic**

On top of those, RepoLens works with **almost any other AI service** through one registry**OpenAI, DeepSeek, Groq, NVIDIA NIM, Kimi (Moonshot), Zhipu GLM, Qwen (Aliyun), Xiaomi MiMo, Volcengine Ark, Ollama Cloud, MiniMax, Azure OpenAI**, local **Ollama** (no key needed), and a universal **Custom** endpoint. Each keeps its **own key** (switching never loses data), has a model picker, an optional **endpoint override**, and built-in **connection / function self-tests**. Connect just one and it works — it joins the fallback chain automatically.
On top of those, RepoLens works with **almost any other AI service** through one registry: **OpenAI, DeepSeek, Groq, NVIDIA NIM, Kimi (Moonshot), Zhipu GLM, Qwen (Aliyun), Xiaomi MiMo, Volcengine Ark, Ollama Cloud, MiniMax, Azure OpenAI**, local **Ollama** (no key needed), and a universal **Custom** endpoint. Each keeps its **own key** (switching never loses data), has a model picker, an optional **endpoint override**, and built-in **connection / function self-tests**. Connect just one and it works. It joins the fallback chain automatically.

> **Sign in with ChatGPT.** The OpenAI card also offers a one-click **ChatGPT login**the same OAuth the **Codex CLI** uses so you can connect without pasting a key (it needs API access on your ChatGPT plan; otherwise paste a key).
> **Sign in with ChatGPT.** The OpenAI card also offers a one-click **ChatGPT login**, the same OAuth the **Codex CLI** uses, so you can connect without pasting a key (it needs API access on your ChatGPT plan; otherwise paste a key).

> Local-only? Point at **Ollama** on `localhost` — no key, no cloud. (Spawning a local *CLI* binary like `claude`/`codex` still isn't possiblea browser extension is sandboxed and can't launch a program — but it can do those CLIs' **OAuth logins**, and talk to a local HTTP model server like Ollama.)
> Local-only? Point at **Ollama** on `localhost`. No key, no cloud. (Spawning a local *CLI* binary like `claude`/`codex` still isn't possible: a browser extension is sandboxed and can't launch a program. But it can do those CLIs' **OAuth logins**, and talk to a local HTTP model server like Ollama.)

Each provider has a model dropdown (★ marks the recommended pick), and you can **route each part of a scan to a different model**:

Expand All @@ -139,9 +139,9 @@ Any per-part pick still falls back to the full chain if that provider errors or

## Storage — nothing to install

Your whole library lives **in the browser** (IndexedDB). No database, no daemon, no setup — it works the moment you load the extension, and it's Web-Store-ready.
Your whole library lives **in the browser** (IndexedDB). No database, no daemon, no setup. It works the moment you load the extension, and it's Web-Store-ready.

Because it's *your* data, you can take it with you: **Library → Export** writes your whole library analyzed repos, the semantic graph, and the local scan cacheto one portable JSON file, and **Import** restores it (merge or replace) on any machine. Backups are validated and bounded on import, so a bad file fails safe. Your settings travel too: **Options → Back up your settings** exports your theme, voice, model picks and per-part routing never your API keys.
Because it's *your* data, you can take it with you: **Library → Export** writes your whole library (analyzed repos, the semantic graph, and the local scan cache) to one portable JSON file, and **Import** restores it (merge or replace) on any machine. Backups are validated and bounded on import, so a bad file fails safe. Your settings travel too: **Options → Back up your settings** exports your theme, voice, model picks and per-part routing, but never your API keys.

Migrating from an old VelesDB server? **Options → Import from VelesDB** pulls your library across in one click.

Expand Down
19 changes: 19 additions & 0 deletions assets/wordmark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 7 additions & 3 deletions background.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
import { buildStackPrompt, parseStack } from './stack-prompt.js';
import { buildAskRepoPrompt, parseAskRepoAnswer } from './ask-repo.js';
import { buildComparePrompt, parseCompareResult } from './compare-repos.js';
import { startScanAnim, stopScanAnim } from './icon-anim.js';

// Notify when a scan completes — clicking the notification focuses the result tab.
chrome.notifications.onClicked.addListener(async (notifId) => {
Expand Down Expand Up @@ -199,7 +200,7 @@
.set({ [msg.sessionKey]: { loading: true, status: 'fetching', ...detected } })
.then(() => {
sendResponse({ ok: true });
runAnalysis(msg.sessionKey, detected); // fire and forget; tab polls the session
runAnalysis(msg.sessionKey, detected, sender.tab?.id); // fire and forget; tab polls the session
})
.catch((err) => sendResponse({ ok: false, error: err?.message || 'Could not start the scan' }));
return true; // keep the message channel open for the async sendResponse
Expand Down Expand Up @@ -261,7 +262,7 @@
if (msg.type === 'PIN_IDEA' && msg.sessionKey && msg.idea && Array.isArray(msg.idea.sources)) {
sendResponse({ ok: true });
(async () => {
const cur = (await chrome.storage.session.get(msg.sessionKey))[msg.sessionKey] || {};

Check warning on line 265 in background.js

View workflow job for this annotation

GitHub Actions / test

'cur' is assigned a value but never used. Allowed unused vars must match /^_/u
await pinIdea({ ...msg.idea, createdIso: new Date().toISOString() });
})();
return true;
Expand Down Expand Up @@ -319,7 +320,7 @@
try {
const persisted = await chrome.storage.local.get(`repolens_ask_${cur.repoId}`);
sessionHistory = persisted[`repolens_ask_${cur.repoId}`] || [];
} catch (_) {}

Check warning on line 323 in background.js

View workflow job for this annotation

GitHub Actions / test

'_' is defined but never used
}
const history = sessionHistory.slice(-4); // keep last 4 completed pairs for AI context
await setAsk({ pending: { status: 'thinking', question: msg.question }, history });
Expand Down Expand Up @@ -466,7 +467,7 @@

if (error) {
const msg = errorDesc || error;
console.warn('[RepoLens OAuth] OpenAI provider returned error:', msg);

Check warning on line 470 in background.js

View workflow job for this annotation

GitHub Actions / test

Unexpected console statement
await chrome.storage.local.set({ [OPENAI_OAUTH_ERROR_KEY]: `ChatGPT sign-in error: ${msg}` });
await cleanupFlowMarkers();
if (tabId) chrome.tabs.remove(tabId).catch(() => {});
Expand All @@ -483,7 +484,7 @@
const storedState = stored[OPENAI_OAUTH_STATE_KEY];

if (!verifier) {
console.warn('[RepoLens OAuth] No stored OpenAI verifier — flow interrupted or for another extension');

Check warning on line 487 in background.js

View workflow job for this annotation

GitHub Actions / test

Unexpected console statement
await cleanupFlowMarkers(); // clear any stale state marker from an interrupted flow
if (tabId) chrome.tabs.remove(tabId).catch(() => {});
return;
Expand All @@ -497,7 +498,7 @@
await cleanupFlowMarkers();
if (tabId) chrome.tabs.remove(tabId).catch(() => {});
} catch (err) {
console.error('[RepoLens OAuth] OpenAI exchange error:', err.message);

Check warning on line 501 in background.js

View workflow job for this annotation

GitHub Actions / test

Unexpected console statement
// No usable key ⇒ don't leave half-finished OAuth state that reads as "connected".
await clearOpenAICredentials().catch(() => {});
await chrome.storage.local.set({ [OPENAI_OAUTH_ERROR_KEY]: err.message });
Expand Down Expand Up @@ -555,7 +556,7 @@
// Open the output tab immediately with a loading state, then run the analysis.
await chrome.storage.session.set({ [sessionKey]: { loading: true, status: 'fetching', ...detected } });
await chrome.tabs.create({ url: `output-tab.html?key=${sessionKey}` });
runAnalysis(sessionKey, detected);
runAnalysis(sessionKey, detected, tab.id);
});

// Every provider credential + model-selector key, read together wherever an AI
Expand Down Expand Up @@ -645,14 +646,15 @@
}

// Fetch → AI → parse → store. Used by the initial click and by RERUN (retry).
async function runAnalysis(sessionKey, detected) {
async function runAnalysis(sessionKey, detected, tabId) {
// Load every provider credential + model + routing in one read; pass the whole
// object to callAI so registry (compat) providers are reachable too — not just
// the five first-class ones. Extra keys (autoSave/tone) are ignored downstream.
const settings = await chrome.storage.local.get([...PROVIDER_KEYS, 'autoSave', 'tone']);
const { autoSave = true, tone } = settings;

try {
startScanAnim(tabId); // fire-and-forget; no-ops without a tabId / when disabled / reduced motion
// Snapshot the previous cached analysis for diff comparison (before it's overwritten).
const prevCached = await getCached(detected.platform, detected.repoId).catch(() => null);

Expand Down Expand Up @@ -749,7 +751,9 @@
});
} catch { /* notifications are best-effort */ }

stopScanAnim(tabId); // success: reset to the static icon
} catch (err) {
stopScanAnim(tabId); // error: reset to the static icon
// AI failures already carry a humanized message + kind; other failures (fetch,
// parse) get classified here so the tab can still route the error CTA.
const errorKind = err.kind || categorizeError(err).kind;
Expand Down Expand Up @@ -1572,7 +1576,7 @@
throw new Error('xAI returned no text content');
}
const err = await res.json().catch(() => ({}));
console.warn('[RepoLens xAI]', endpoint, res.status, JSON.stringify(err));

Check warning on line 1579 in background.js

View workflow job for this annotation

GitHub Actions / test

Unexpected console statement
lastErr = err.error?.message || ('xAI API error ' + res.status + ' at ' + endpoint);
if (res.status === 401 && isOAuth) {
await chrome.storage.local.remove(['xaiKey', 'xaiRefresh', 'xaiExpiry', 'xaiCredentials']);
Expand Down
11 changes: 11 additions & 0 deletions docs/style/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Writing standard

RepoLens copy follows **stop-slop** (Hardik Pandya's de-slop ruleset), vendored in
[`stop-slop/`](stop-slop/SKILL.md). It is the standard for product copy and docs:
Vee's onboarding lines, the README, the CHANGELOG, and any future store-listing or
website copy.

The short version: cut filler openers and adverbs, write active voice with a human
subject, name the specific thing, vary sentence rhythm, no em dashes, no emoji on
product surfaces. Score a draft against the rubric in
[`stop-slop/SKILL.md`](stop-slop/SKILL.md); below 35/50, revise.
68 changes: 68 additions & 0 deletions docs/style/stop-slop/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
name: stop-slop
description: Remove AI writing patterns from prose. Use when drafting, editing, or reviewing text to eliminate predictable AI tells.
metadata:
trigger: Writing prose, editing drafts, reviewing content for AI patterns
author: Hardik Pandya (https://hvpandya.com)
---

# Stop Slop

Eliminate predictable AI writing patterns from prose.

## Core Rules

1. **Cut filler phrases.** Remove throat-clearing openers, emphasis crutches, and all adverbs. See [references/phrases.md](references/phrases.md).

2. **Break formulaic structures.** Avoid binary contrasts, negative listings, dramatic fragmentation, rhetorical setups, false agency. See [references/structures.md](references/structures.md).

3. **Use active voice.** Every sentence needs a human subject doing something. No passive constructions. No inanimate objects performing human actions ("the complaint becomes a fix").

4. **Be specific.** No vague declaratives ("The reasons are structural"). Name the specific thing. No lazy extremes ("every," "always," "never") doing vague work.

5. **Put the reader in the room.** No narrator-from-a-distance voice. "You" beats "People." Specifics beat abstractions.

6. **Vary rhythm.** Mix sentence lengths. Two items beat three. End paragraphs differently. No em dashes.

7. **Trust readers.** State facts directly. Skip softening, justification, hand-holding.

8. **Cut quotables.** If it sounds like a pull-quote, rewrite it.

## Quick Checks

Before delivering prose:

- Any adverbs? Kill them.
- Any passive voice? Find the actor, make them the subject.
- Inanimate thing doing a human verb ("the decision emerges")? Name the person.
- Sentence starts with a Wh- word? Restructure it.
- Any "here's what/this/that" throat-clearing? Cut to the point.
- Any "not X, it's Y" contrasts? State Y directly.
- Three consecutive sentences match length? Break one.
- Paragraph ends with punchy one-liner? Vary it.
- Em-dash anywhere? Remove it.
- Vague declarative ("The implications are significant")? Name the specific implication.
- Narrator-from-a-distance ("Nobody designed this")? Put the reader in the scene.
- Meta-joiners ("The rest of this essay...")? Delete. Let the essay move.

## Scoring

Rate 1-10 on each dimension:

| Dimension | Question |
|-----------|----------|
| Directness | Statements or announcements? |
| Rhythm | Varied or metronomic? |
| Trust | Respects reader intelligence? |
| Authenticity | Sounds human? |
| Density | Anything cuttable? |

Below 35/50: revise.

## Examples

See [references/examples.md](references/examples.md) for before/after transformations.

## License

MIT
Loading
Loading