@liustack/modlens 2.8.0 → 3.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/CHANGELOG.md +186 -0
- package/README.md +79 -15
- package/README.zh-CN.md +83 -19
- package/SECURITY.md +17 -0
- package/dist/main.js +798 -401
- package/docs/commit.md +76 -0
- package/docs/harness-setup.md +43 -0
- package/docs/research-gemini-claude-skills.md +48 -0
- package/docs/security.md +31 -0
- package/docs/testing.md +29 -0
- package/docs/troubleshooting.md +132 -0
- package/package.json +13 -4
- package/skills/modlens/references/configure.md +2 -0
package/docs/commit.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: 'Git Commit Conventions: Conventional Commits format, atomic commits, HEREDOC usage'
|
|
3
|
+
read_when:
|
|
4
|
+
- Preparing to commit code
|
|
5
|
+
- Writing a commit message
|
|
6
|
+
- Need to understand commit conventions
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Git Commit Conventions
|
|
10
|
+
|
|
11
|
+
## Commit Message Format
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
<type>[optional scope]: <imperative summary>
|
|
15
|
+
|
|
16
|
+
[optional body]
|
|
17
|
+
|
|
18
|
+
[optional footer(s)]
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
- Use imperative mood in summary, max 72 characters
|
|
22
|
+
- Do not end with a period
|
|
23
|
+
- Use scope only when it adds clarity (e.g., `auth`, `api`, `ui`)
|
|
24
|
+
- Mark breaking changes with `!` (e.g., `feat!:`) or a `BREAKING CHANGE:` footer
|
|
25
|
+
|
|
26
|
+
## Available Types
|
|
27
|
+
|
|
28
|
+
| Type | Description |
|
|
29
|
+
|------|-------------|
|
|
30
|
+
| `feat` | New feature |
|
|
31
|
+
| `fix` | Bug fix |
|
|
32
|
+
| `docs` | Documentation update |
|
|
33
|
+
| `style` | Formatting (no logic changes) |
|
|
34
|
+
| `refactor` | Code restructuring (no behavior changes) |
|
|
35
|
+
| `perf` | Performance improvement |
|
|
36
|
+
| `test` | Testing related |
|
|
37
|
+
| `build` | Build system or dependency changes |
|
|
38
|
+
| `ci` | CI/CD changes |
|
|
39
|
+
| `chore` | Maintenance tasks |
|
|
40
|
+
| `revert` | Revert a commit |
|
|
41
|
+
|
|
42
|
+
## Atomic Commit Principles
|
|
43
|
+
|
|
44
|
+
1. One commit does one thing
|
|
45
|
+
2. The repository should remain buildable/runnable after each commit
|
|
46
|
+
3. Each commit should be independently revertible, affecting only that specific change
|
|
47
|
+
4. Include related tests in the same commit when behavior changes
|
|
48
|
+
5. Do not mix refactoring/formatting with behavior changes
|
|
49
|
+
|
|
50
|
+
## Commit Workflow
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# 1. Review changes
|
|
54
|
+
git status --short
|
|
55
|
+
git diff --staged --stat
|
|
56
|
+
|
|
57
|
+
# 2. Stage selectively (avoid git add -A)
|
|
58
|
+
git add <specific-files>
|
|
59
|
+
|
|
60
|
+
# 3. Review what will be committed
|
|
61
|
+
git diff --staged
|
|
62
|
+
|
|
63
|
+
# 4. Commit (HEREDOC format)
|
|
64
|
+
git commit -m "$(cat <<'EOF'
|
|
65
|
+
<type>[scope]: <summary>
|
|
66
|
+
|
|
67
|
+
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
68
|
+
EOF
|
|
69
|
+
)"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Notes
|
|
73
|
+
|
|
74
|
+
- Do not commit sensitive files such as `.env` or credentials
|
|
75
|
+
- After a pre-commit hook failure, fix the issue and create a **new commit** (do not use `--amend`)
|
|
76
|
+
- Do not push automatically; wait for user instructions
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: 'Harness setup: how images reach the model in Codex, Claude Code, Pi, and OpenCode'
|
|
3
|
+
read_when:
|
|
4
|
+
- Setting modlens up inside a specific coding agent
|
|
5
|
+
- A pasted image is not reaching the model
|
|
6
|
+
- Understanding what recover-paste does per harness
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Harness setup
|
|
10
|
+
|
|
11
|
+
Where a pasted image ends up differs per harness, and modlens takes a different route in each. `recover-paste` detects which harness it runs inside (process ancestry, then environment fingerprints) and reads only that harness's storage.
|
|
12
|
+
|
|
13
|
+
## Codex
|
|
14
|
+
|
|
15
|
+
Pasted images become real temp files, and the message carries a tag like `<image name=[Image #1] path="/tmp/xxxx.png">`. The skill reads the path out of the tag. `recover-paste` detects Codex and refuses, pointing back at the tag.
|
|
16
|
+
|
|
17
|
+
One catch with text-only models: once `models.json` declares `input_modalities: ["text"]`, the Codex TUI blocks Ctrl+V paste outright. Drag the file into the terminal, type its path, or use `codex exec -i image.png "..."`.
|
|
18
|
+
|
|
19
|
+
## Claude Code, Pi, OpenCode
|
|
20
|
+
|
|
21
|
+
None of them writes a pasted image to a regular temp file, but all three persist the user message locally before any gateway strips it:
|
|
22
|
+
|
|
23
|
+
| Harness | Storage | Notes |
|
|
24
|
+
| :-- | :-- | :-- |
|
|
25
|
+
| Claude Code | `~/.claude/projects/<slug>/<session>.jsonl` | images as base64. The injected `CLAUDE_CODE_SESSION_ID` targets the exact session |
|
|
26
|
+
| Pi | `~/.pi/agent/sessions/--<encoded-cwd>--/*.jsonl` | same shape as Claude Code |
|
|
27
|
+
| OpenCode | `~/.local/share/opencode/opencode.db` | SQLite, images as data URLs (read via `node:sqlite`) |
|
|
28
|
+
|
|
29
|
+
Running a text-only model behind `ANTHROPIC_BASE_URL` in Claude Code, a pasted image arrives as a pathless `[Unsupported Image]` placeholder (on lenient gateways) or breaks the request outright ([#62009](https://github.com/anthropics/claude-code/issues/62009)). The bytes are not gone, and that is what `recover-paste` retrieves.
|
|
30
|
+
|
|
31
|
+
## Skill locations
|
|
32
|
+
|
|
33
|
+
| Harness | Reads skills from |
|
|
34
|
+
| :-- | :-- |
|
|
35
|
+
| Claude Code | `~/.claude/skills/` |
|
|
36
|
+
| Codex | `~/.codex/skills/` |
|
|
37
|
+
| Pi, OpenCode | `~/.agents/skills/` |
|
|
38
|
+
|
|
39
|
+
Symlinks work in all of them, so linking the skill folder once keeps every agent on the latest version.
|
|
40
|
+
|
|
41
|
+
## Gateway setups
|
|
42
|
+
|
|
43
|
+
OpenCode with DeepSeek: `opencode auth login`, pick DeepSeek and paste the key (it lands in `~/.local/share/opencode/auth.json`), then set the default model in `~/.config/opencode/opencode.jsonc` to `deepseek/deepseek-v4-flash`. Pi reads its key from `~/.pi/agent/auth.json`.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: 'Research Notes: Gemini CLI invocation mechanics + Claude Code skills model'
|
|
3
|
+
read_when:
|
|
4
|
+
- Designing or updating ModLens runtime behavior
|
|
5
|
+
- Updating skill metadata/trigger strategy
|
|
6
|
+
- Verifying compatibility with Claude Code and Gemini CLI
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
> **HISTORICAL.** Written in the Gemini CLI era, before that free tier was shut
|
|
10
|
+
> down in June 2026 and ModLens moved to Antigravity CLI. Kept for the reasoning
|
|
11
|
+
> behind the skill-trigger design. For how things work now, read the README and
|
|
12
|
+
> `troubleshooting.md`.
|
|
13
|
+
|
|
14
|
+
# Research Notes: Gemini CLI + Claude Code Skills
|
|
15
|
+
|
|
16
|
+
Date: 2026-02-22
|
|
17
|
+
|
|
18
|
+
## 1) Gemini CLI Findings (v1 implementation impact)
|
|
19
|
+
|
|
20
|
+
- Local environment verified: `gemini --version` is `0.24.0`.
|
|
21
|
+
- CLI help shows `-p/--prompt` is deprecated but still supported; positional prompt is the recommended long-term mode.
|
|
22
|
+
- `--output-format` supports `text | json | stream-json`; `json` is suitable for machine parsing.
|
|
23
|
+
- Gemini CLI supports `@path` references in prompt. For file references, docs specify that `@` can force full content inclusion.
|
|
24
|
+
- Package source inspection confirms images are handled as `inlineData` (base64 + MIME) when explicitly referenced by `@path` and passed through `read_many_files`.
|
|
25
|
+
|
|
26
|
+
Implementation decision:
|
|
27
|
+
- v1 keeps `gemini -p` (matching current project requirement) + `--output-format json`.
|
|
28
|
+
- To avoid workspace-bound path misses, include `--include-directories <image-dir>`.
|
|
29
|
+
|
|
30
|
+
## 2) Claude Code Skills Findings
|
|
31
|
+
|
|
32
|
+
- Official docs define skills as foldered instruction packs with frontmatter metadata.
|
|
33
|
+
- Triggering relies heavily on `name` and `description` in frontmatter.
|
|
34
|
+
- Supported frontmatter controls include `allowed-tools`, `disable-model-invocation`, `model`, and `context` behaviors.
|
|
35
|
+
- Skills can be project-level and chained by the agent when descriptions match user intent.
|
|
36
|
+
|
|
37
|
+
Implementation decision:
|
|
38
|
+
- `skills/modlens/SKILL.md` uses explicit trigger description for image tasks + non-vision model scenarios.
|
|
39
|
+
- `allowed-tools: Bash` is set so the agent can deterministically call `modlens`.
|
|
40
|
+
- Scope is intentionally limited to image parsing; no modsearch/modfetch logic is included.
|
|
41
|
+
|
|
42
|
+
## 3) Primary Sources
|
|
43
|
+
|
|
44
|
+
- Gemini CLI repo README: https://github.com/google-gemini/gemini-cli
|
|
45
|
+
- Gemini CLI file management docs (`@` references): https://gemini-cli.xyz/docs/cli/file-management/
|
|
46
|
+
- Claude Code skills docs: https://docs.claude.com/en/docs/claude-code/agent-skills
|
|
47
|
+
- Anthropic skills examples repo: https://github.com/anthropics/skills
|
|
48
|
+
- Claude help center (skills overview): https://support.claude.com/en/articles/11817219-what-are-skills
|
package/docs/security.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: 'Security: what modlens runs, how recovered images are protected, image content as untrusted input'
|
|
3
|
+
read_when:
|
|
4
|
+
- Reviewing what this tool does on your machine
|
|
5
|
+
- Recovering pastes on a shared machine
|
|
6
|
+
- Deciding how much to trust what a vision engine reports
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Security
|
|
10
|
+
|
|
11
|
+
## Recovered images are private
|
|
12
|
+
|
|
13
|
+
Images pulled out of session storage are written 0600 into a 0700 directory. By default that directory is a fresh, unpredictable `<tmpdir>/modlens-paste-*` minted per run, so nobody on a shared machine can pre-create a known path (`recursive` mkdir leaves an existing directory's mode alone) and read the bytes. A pasted screenshot can hold anything. An explicit `--out-dir` is honoured but refused when unsafe: it must be a real directory, not a symlink, owned by you, with no group or world access.
|
|
14
|
+
|
|
15
|
+
Recovery is also scoped to one project: the working directory recorded inside the transcript is checked, not just the directory name, because directory slugs collide (`/tmp/a.b` and `/tmp/a-b` produce the same one). A neighbouring project's images are never handed over.
|
|
16
|
+
|
|
17
|
+
## Permissions passed to engines
|
|
18
|
+
|
|
19
|
+
ModLens invokes `agy` with `--dangerously-skip-permissions` because prompt mode fails in some environments without it. The prompt restricts the agent to reading the one image it was given, and instructs it to treat image content strictly as data.
|
|
20
|
+
|
|
21
|
+
The `claude-cli` provider runs with `--allowedTools Read` only, so it can read local files and nothing else.
|
|
22
|
+
|
|
23
|
+
Both subprocess providers also run in a throwaway directory containing only the one image, created fresh per call and removed afterward. Text inside an image is untrusted, so an injection could otherwise steer a broadly-permissioned agent into reading files that sit next to the original. A directory of one removes that reach. Passing `--workdir` opts out and runs where you point it.
|
|
24
|
+
|
|
25
|
+
## Image content is untrusted input
|
|
26
|
+
|
|
27
|
+
Text inside an image is untrusted, the same as a web page. A screenshot can contain instructions aimed at whatever reads it. The prompt says so explicitly, but that is mitigation, not a guarantee: analyze images you are willing to open, and prefer a sandboxed working directory when they came from elsewhere.
|
|
28
|
+
|
|
29
|
+
## Evidence, not invention
|
|
30
|
+
|
|
31
|
+
What the engine cannot read goes into `uncertainty` rather than being filled in. v2 dropped pixel coordinates and confidence scores entirely, because those are the two fields models fabricate most convincingly.
|
package/docs/testing.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: 'Testing Guide: Vitest usage, co-located layout, conventions'
|
|
3
|
+
read_when:
|
|
4
|
+
- Running tests
|
|
5
|
+
- Writing tests
|
|
6
|
+
- Troubleshooting test failures
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Testing Guide
|
|
10
|
+
|
|
11
|
+
## Layout
|
|
12
|
+
|
|
13
|
+
Tests are co-located with sources: every module gets an adjacent `*.test.ts` (for example `src/config.ts` and `src/config.test.ts`, `src/providers/geminiApi.ts` and `src/providers/geminiApi.test.ts`). There is no separate `test/` directory. Vite's lib build only follows the import graph from `src/main.ts`, so test files never enter `dist/`.
|
|
14
|
+
|
|
15
|
+
## Commands
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm test # run everything
|
|
19
|
+
pnpm exec vitest run src/config.test.ts # run a single file
|
|
20
|
+
pnpm typecheck # tsc --noEmit, run before tests
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Conventions
|
|
24
|
+
|
|
25
|
+
- No network in unit tests: stub `fetch` with `vi.stubGlobal('fetch', ...)` and clean up in `afterEach` via `vi.unstubAllGlobals()`.
|
|
26
|
+
- ESM module namespaces cannot be spied on (`vi.spyOn(fs, ...)` throws). Use real temp files via `fs.mkdtempSync(path.join(os.tmpdir(), ...))` and remove them in the test.
|
|
27
|
+
- To fake the home directory (config, transcripts), set `process.env.HOME` and restore it in `finally`; `os.homedir()` follows it on POSIX.
|
|
28
|
+
- Providers with subprocess transports test `buildInvocation`/`parseOutput` as pure functions; API providers test `execute` against a stubbed `fetch`, asserting both the request body and the parsed result.
|
|
29
|
+
- Real provider calls (agy, API keys, Claude login) are end-to-end verification, not unit tests. Keep them out of `pnpm test`.
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: 'Troubleshooting: every error modlens can print, what causes it, what to do'
|
|
3
|
+
read_when:
|
|
4
|
+
- A run failed and the message is not self-explanatory
|
|
5
|
+
- recover-paste found nothing, or found the wrong image
|
|
6
|
+
- Deciding whether a failure is setup, quota, or a bug
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Troubleshooting
|
|
10
|
+
|
|
11
|
+
Start with `modlens doctor`: it checks your Node version, which providers are ready, which one will be selected and why, and the detected harness, all without spending quota or making a network request. It catches most setup problems before you read any further.
|
|
12
|
+
|
|
13
|
+
Every message below is one modlens actually prints. Search this file for the words you saw.
|
|
14
|
+
|
|
15
|
+
## Antigravity CLI cannot read its stored login token
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
Antigravity CLI cannot read its stored login token.
|
|
19
|
+
|
|
20
|
+
On Linux this usually means the OS keyring is locked, which is normal for headless
|
|
21
|
+
sessions (agents, cron, systemd, SSH without a desktop login) ...
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
agy keeps its token in the OS keyring. When the keyring is locked, agy reports itself as signed out and tries a browser sign-in that cannot finish without a display. Three ways forward:
|
|
25
|
+
|
|
26
|
+
- Unlock the keyring, or run modlens from a desktop session.
|
|
27
|
+
- Sign in again with `agy`.
|
|
28
|
+
- Switch to a provider that needs no interactive login:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
modlens config set gemini-api.apiKey <key> # free key: https://aistudio.google.com
|
|
32
|
+
modlens config set provider gemini-api
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quota exhausted
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
Individual quota reached. ... Resets in 94h19m9s.
|
|
39
|
+
|
|
40
|
+
agy's free tier is one weekly bucket shared by the desktop app, the CLI, and the SDK ...
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Wait for the reset, or move to `gemini-api`, which has its own budget. Parallel subagents drain the shared bucket fast, so a heavy day can end it.
|
|
44
|
+
|
|
45
|
+
## Provider CLI not found
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
Provider CLI not found: agy. Install it and sign in first.
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The binary is not on PATH, or `--provider-bin` points somewhere wrong.
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
Working directory does not exist: /some/path
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Different cause, same underlying error code from the OS: `--workdir` points at a directory that is not there. The binary is fine.
|
|
58
|
+
|
|
59
|
+
## recover-paste found nothing
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
No pasted images found in any session storage for this directory (looked in: ...)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
In order of likelihood:
|
|
66
|
+
|
|
67
|
+
- **You are in the wrong directory.** Recovery is scoped to the project the conversation is happening in. Pass `--cwd /path/to/project`.
|
|
68
|
+
- **Nothing was pasted.** Dragged files and typed paths are already real files, so there is nothing to recover: use the path directly.
|
|
69
|
+
- **A setup problem is blocking one harness.** Anything blocking appears after `Blocked:` in the same message, for example OpenCode needing Node 22.13+ for `node:sqlite`.
|
|
70
|
+
|
|
71
|
+
## recover-paste returned an image from another project
|
|
72
|
+
|
|
73
|
+
This should not happen any more, and if it does it is a bug worth reporting. Recovery checks the working directory recorded inside the transcript, not just the directory name, because directory slugs collide (`/tmp/a.b` and `/tmp/a-b` produce the same one). Include the `harness` and `transcript` fields from the output in the issue.
|
|
74
|
+
|
|
75
|
+
## Recovered the wrong image from the right project
|
|
76
|
+
|
|
77
|
+
The output lists images oldest to newest, so the **last** entry is the most recent paste. Entries carry `filename` when the harness stored one: match on that when the user mentioned a name. `--count 3` gives you more to choose from.
|
|
78
|
+
|
|
79
|
+
## recover-paste: overriding detection and output location
|
|
80
|
+
|
|
81
|
+
`recover-paste` auto-detects which harness it runs inside (process ancestry first, then environment fingerprints) and reads only that harness's storage. Two knobs override it:
|
|
82
|
+
|
|
83
|
+
- **`MODLENS_HARNESS`** forces the storage scope without a flag: `claude-code`, `pi`, `opencode`, `codex`, or `none` (scan every store, no scoping). Detection reads it first, so it wins over ancestry and env fingerprints. `--harness` does the same for a single run.
|
|
84
|
+
- **`--out-dir`** sets where recovered images land. By default each run mints a fresh, unpredictable `<tmpdir>/modlens-paste-*` directory (0700, holding 0600 files), so nobody can pre-create a shared path to intercept the bytes. Point it elsewhere when the system temp dir is not where you want them. An explicit `--out-dir` that already exists is rejected unless it is a real directory (not a symlink), owned by you, with no group or world access.
|
|
85
|
+
|
|
86
|
+
## This is a Codex session
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
This is a Codex session: pasted images already exist as temp files, and each image
|
|
90
|
+
tag in the message carries its path.
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Working as intended. Codex writes pasted images to disk and puts the path in the message, so read the path out of the tag instead of recovering anything.
|
|
94
|
+
|
|
95
|
+
## The openai provider rejected a result
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
OpenAI-compatible API returned JSON that does not match the vision schema
|
|
99
|
+
(missing: ocr, ocr.full_text, ...)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
That endpoint returned a partial result. Only agy, gemini-api, anthropic, and claude-cli enforce the schema server-side, so weaker gateways can produce half a result. Retry once, then switch:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
modlens -i <image> -p gemini-api
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Config file problems
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
Cannot read /Users/you/.modlens/config.json: EACCES ... Fix the file or its permissions.
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The file exists but is unreadable. A missing file is fine, so this is a real problem rather than something to ignore.
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
Failed to parse ... Fix or delete the file.
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Invalid JSON. `modlens config init --force` writes a clean one, losing the old contents.
|
|
121
|
+
|
|
122
|
+
## Timeouts
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
antigravity-cli provider timed out after 210000 ms.
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Retry once with `--timeout 300000`. Dense images on agy legitimately take 15-40 seconds, and `-m gemini-3.1-pro-high` is slower still. Engines that ignore SIGTERM are escalated to SIGKILL, so a timeout returns promptly regardless.
|
|
129
|
+
|
|
130
|
+
## Still stuck
|
|
131
|
+
|
|
132
|
+
Include the exact command and the full error in an issue: https://github.com/liustack/modlens/issues
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@liustack/modlens",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Plug-in vision for text-only LLMs, powered by the free Antigravity CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -8,8 +8,11 @@
|
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"dist",
|
|
11
|
+
"docs",
|
|
11
12
|
"skills/modlens/SKILL.md",
|
|
12
|
-
"skills/modlens/references"
|
|
13
|
+
"skills/modlens/references",
|
|
14
|
+
"CHANGELOG.md",
|
|
15
|
+
"SECURITY.md"
|
|
13
16
|
],
|
|
14
17
|
"keywords": [
|
|
15
18
|
"cli",
|
|
@@ -34,22 +37,28 @@
|
|
|
34
37
|
},
|
|
35
38
|
"homepage": "https://github.com/liustack/modlens#readme",
|
|
36
39
|
"engines": {
|
|
37
|
-
"node": ">=
|
|
40
|
+
"node": ">=22.13"
|
|
38
41
|
},
|
|
39
42
|
"dependencies": {
|
|
40
43
|
"commander": "^13.1.0"
|
|
41
44
|
},
|
|
42
45
|
"devDependencies": {
|
|
46
|
+
"@biomejs/biome": "^2.5.7",
|
|
43
47
|
"@types/node": "^22.19.7",
|
|
48
|
+
"@vitest/coverage-v8": "^3.2.7",
|
|
44
49
|
"typescript": "^5.9.3",
|
|
45
50
|
"vite": "^6.4.1",
|
|
46
|
-
"vitest": "^3.2.
|
|
51
|
+
"vitest": "^3.2.7"
|
|
47
52
|
},
|
|
48
53
|
"scripts": {
|
|
49
54
|
"dev": "vite build --watch",
|
|
50
55
|
"build": "vite build",
|
|
51
56
|
"typecheck": "tsc --noEmit",
|
|
52
57
|
"test": "vitest run",
|
|
58
|
+
"coverage": "vitest run --coverage",
|
|
59
|
+
"lint": "biome check src scripts",
|
|
60
|
+
"format": "biome check --write src scripts",
|
|
61
|
+
"eval": "node evals/run.mjs",
|
|
53
62
|
"release": "node scripts/release.mjs",
|
|
54
63
|
"docs:list": "node scripts/docs-list.js"
|
|
55
64
|
}
|
|
@@ -61,6 +61,8 @@ modlens config set anthropic.apiKey <sk-ant-key>
|
|
|
61
61
|
|
|
62
62
|
Default model is Claude Haiku (`claude-haiku-4-5-20251001`). Schema is enforced through a forced tool call.
|
|
63
63
|
|
|
64
|
+
**`ANTHROPIC_BASE_URL` trap.** modlens binds `ANTHROPIC_BASE_URL` to `anthropic.baseUrl`, so it inherits whatever that variable points at. If the user set it in their shell to route Claude Code through a text-only gateway (a common way to run a non-Claude model behind the Claude Code UI), then `-p anthropic` silently sends the vision request to that gateway too, where it fails or comes back blind, with no hint that the endpoint was swapped. Check `echo $ANTHROPIC_BASE_URL` when anthropic vision misbehaves. Fixes: unset it for the modlens call, pin the real endpoint with `modlens config set anthropic.baseUrl https://api.anthropic.com`, or use `-p gemini-api` instead.
|
|
65
|
+
|
|
64
66
|
### claude-cli (Claude Code login, no key)
|
|
65
67
|
|
|
66
68
|
Rides an existing `claude` sign-in, so it costs the user's Claude subscription quota, not a separate API bill. Requires Claude Code installed and logged in (`claude --version` to check). Runs with `--allowedTools Read` only. Local image files only; for remote URLs use gemini-api instead. Default model alias `haiku`.
|