@intentic/iq 1.310.0 → 1.311.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.
Files changed (2) hide show
  1. package/README.md +27 -142
  2. package/package.json +6 -6
package/README.md CHANGED
@@ -1,153 +1,38 @@
1
1
  # iq
2
2
 
3
- **Agent-native workspace search: one intent-first CLI over lexical, structural, semantic, and git engines.**
4
-
5
- `iq` replaces the `grep`/`find`/`Glob` chains a coding agent (or you) would otherwise run. One call returns ranked, token-budgeted, answer-shaped results where every line is a `path:line` anchor you can open directly. It auto-detects intent, fuses several search engines, and fits the answer to a token budget so it drops cleanly into an LLM's context.
6
-
7
- ```
8
- iq "where do we enforce the secrets floor?"
3
+ The `iq` CLI, agent-native workspace search that answers a symbol, path, regex or plain question with ranked `path:line` anchors in one call.
4
+
5
+ ```mermaid
6
+ flowchart LR
7
+ plugin["plugin/<br/>skill + hooks"] -. "teaches" .-> agent["Agent or shell"]
8
+ agent -- "iq '…' · def · refs · outline" --> iq(["iq"])
9
+ iq -- "createEngine" --> engine["iq-engine<br/>index + ranking"]
10
+ iq -- "sessions" --> recall["iq-recall<br/>past transcripts"]
11
+ iq -- "capsule + code<br/>within --budget" --> agent
9
12
  ```
10
13
 
11
- ## Install
12
-
13
- ```sh
14
- npm i -g @intentic/iq # or: pnpm add -g @intentic/iq
15
- ```
16
-
17
- **Requirements**
18
-
19
- - **Node ≥ 24** (uses `node:sqlite`).
20
- - **ripgrep** on `PATH`: the lexical engine shells into `rg`. Install via your package manager (`apt install ripgrep`, `brew install ripgrep`, …) or point `IQ_RG_PATH` at a binary.
21
- - **Semantic search is optional.** Natural-language queries use a local embedding + cross-encoder model. Set `IQ_MODEL_DIR` to a directory holding them (fetch once with the bundled `node_modules/@intentic/iq-engine/scripts/fetch-model.mjs <dir>`). Without it, they degrade to keyword-expanded lexical search: everything else works unchanged.
22
-
23
- The index self-manages: it builds on first query and revalidates against disk on every run. Nothing to set up.
24
-
25
- ## Quickstart: pick the verb by what you already know
26
-
27
- - **You know which symbol you want to read** → `iq read X`, or `iq read path::X` / `iq read path::Class::method` when the name is not unique. One call returns the body, where `def` then `context path:line` took two and needed a line number in between. Open the file itself when you want the whole file: a `Read` with no offset hands back up to 2000 lines whether you wanted them or not.
28
- - **You know the file** (a stack trace or failing test names it) → `iq outline <path>` for its shape, then `iq read <path>::<symbol>`; `iq context path:line` / `iq who path:line` when a line is all you have.
29
- - **You know the exact identifier** → `iq def X` / `iq refs X`: one call replaces a grep-then-filter chain.
30
- - **You don't know where it lives**, or your words may not match the code's vocabulary → bare `iq "…"`, phrased as a question. Natural language has no verb of its own. This is where `iq` decisively beats grep.
31
- - **You have error-message text** → `iq find 'literal text'`, then `iq context` on the hit.
32
- - **You know nothing yet**: a repo you've never opened → `iq map --budget 4000` for the shape, `iq hotspots` for where the risk sits. These answer "what is this and where do I start", which no amount of searching does.
33
-
34
- | I want… | Run |
35
- |---|---|
36
- | the repo's shape | `iq map --budget 4000` |
37
- | where risk concentrates | `iq hotspots --in _apps` |
38
- | what my change reaches | `iq impact` |
39
- | text/regex match | `iq find 'createServer\(' --lang ts` |
40
- | a file by name | `iq files wkignore` (`--exact` for globs) |
41
- | where X is defined | `iq def createIgnoreScope` |
42
- | who uses X | `iq refs createIgnoreScope --kind call` |
43
- | symbols by pattern | `iq sym 'Workspace*Schema' --kind type` |
44
- | structural AST pattern | `iq ast 'await $FN($$$)' --lang ts` |
45
- | a natural-language answer | `iq "how does the daemon expose tools?"` |
46
- | a file's skeleton | `iq outline src/workspace/workspace-ignore.ts` |
47
- | a symbol's body | `iq read createIgnoreScope`, `iq read src/app.ts::Server::start` |
48
- | code around a hit | `iq context src/workspace/workspace-tree.ts:48` |
49
- | recent changes | `iq recent --since 2d` |
50
- | history of a string | `iq log "MAX_TOTAL_MATCHES" --path src/workspace` |
51
- | blame a line | `iq who src/file.ts:15` |
52
- | several queries, one spawn | `iq multi "how is auth refreshed" "def refreshToken"` |
53
-
54
- Regex is **rust syntax**: alternation is `a|b` (not `a\|b`); for literal text use `--literal`.
55
-
56
- ## Output contract
57
-
58
- - **Answer first.** Every response opens with a capsule that precedes the code: `answer:` names the top anchor, its enclosing symbol and whether the top result is `confident` or `ambiguous`; `candidates:` names the ranked `path:line` anchors that did not fit; `more:` gives the exact `--after <cursor>` command. A reader that keeps only the first few lines keeps everything actionable.
59
- - **Token budget is first-class.** Output fits `--budget` (default 1500 tokens); the tool decides how to spend it and truncates whole groups from the tail.
60
- - **Every hit is a `path:line` anchor** into the live file; hits show their enclosing symbol (`⟨in createWidget (fn)⟩`), and natural-language answers deliver the top hits' full enclosing bodies plus `related:` definition anchors for follow-up.
61
- - **Scope** with `--in <dir>`, `--repo <name>`, `--lang ts,py`, `--glob`/`--not-glob`, `--only tests|src|docs|config`; `--ignored` includes gitignored files (the security floor (secrets, `.git`) never lifts).
62
- - **Exit codes** follow grep convention: `0` hits, `1` none, `2` usage error.
63
- - **Machine output**: `--json` (one result document) or `--ndjson` (one line per group).
64
- - **Zero hits are never a dead end**: an identifier, path or pattern that matches nothing exactly is re-run semantically (the header says so), and a genuine zero carries a `hint:` diagnosing the likely cause.
65
-
66
- ## Configuration (environment)
67
-
68
- | Var | Effect |
69
- |---|---|
70
- | `WORKSPACE_ROOT` | Directory to search (default: current directory). |
71
- | `IQ_MODEL_DIR` | Embedding/reranker model directory; unset → natural-language queries degrade to lexical. |
72
- | `IQ_RG_PATH` | Override the ripgrep binary resolved from `PATH`. |
73
- | `INTENTIC_OUTPUT` | Default output mode when no flag is given: `text` (default), `json`, `ndjson`. |
74
- | `IQ_FEATURES` | Retrieval-stage toggles (see below); the `--features` flag overrides it. |
75
- | `IQ_DEBUG` | Keep the full JS stack on a thrown error instead of the one-line message. |
76
-
77
- ## How it works
78
-
79
- A bare query is classified (identifier / regex / path / natural-language) and routed through a fused pipeline:
80
-
81
- **ripgrep** (exact) + **FTS5 BM25** (sparse relevance) + **RM3 pseudo-relevance feedback** (query expansion) + **dense embeddings** (semantic) → **reciprocal-rank fusion** with **defboost** / **pathboost** / **recency** multipliers (one toggle each; pathboost matches path *words*, so `indexer.ts` answers "index" while `_textwrap.py` does not answer "wrap") and a **source-first** class prior (implementation over its tests and docs, natural-language answers only) → **cross-encoder rerank** (blended into the fused order via RRF: it *votes*, it doesn't veto) → **enclosing-symbol context** + **graph** neighbor anchors + **pack** (the top groups arrive as the actual code slice, not just a pointer, so the reader usually needs no follow-up open).
82
-
83
- All twelve stages are independently toggleable for benchmarking and deployment tuning, via `--features` or `IQ_FEATURES` (allow-list `bm25` = only BM25; default-minus `-rerank,-prf` = all except those):
84
-
85
- `bm25`, `semantic`, `rerank`, `prf`, `confidence`, `symctx`, `graph`, `defboost`, `pathboost`, `recency`, `srcfirst`, `pack`.
14
+ - A bare query picks its own strategy: a path, an identifier, a regex or natural language, falling back to semantic search when nothing matches exactly. There is no separate verb for questions.
15
+ - Every answer opens with a capsule: `answer:` names the top anchor, its enclosing symbol and whether it is confident or ambiguous; `candidates:` and `more:` follow. Output fits `--budget` tokens, capsule included.
16
+ - Exit codes follow grep (0 hits, 1 none, 2 error). Common grep flags get a one-line redirect instead of a usage dump.
17
+ - The index lives in `.intentic/local/cache/iq` and maintains itself; `iq index rebuild` is for a stale index only.
18
+ - `plugin/` is a Claude Code plugin: the `iq` skill, a SessionStart nudge that ingests transcripts, and a prompt hook that suggests matching past sessions. The sandbox image bakes it and loads it for every agent.
86
19
 
87
- Structural search (`ast`) uses ast-grep; history verbs (`log`, `who`, `recent`) use git.
88
-
89
- ### Orientation verbs
90
-
91
- `map` ranks files by **PageRank over the import graph** (an edge A→B means A imports B) then prints each file's exported signatures until `--budget` runs out. Specifiers are extracted at index time and resolved at query time against the indexed file set: relative paths directly (including TypeScript's `./x.js` → `x.ts` rule), bare specifiers via each workspace `package.json`'s `name`, so cross-package imports don't fragment a monorepo into disconnected islands. Unresolved specifiers (node_modules, stdlib) are simply not edges. Generated files contribute no definitions; a re-export shim would otherwise look like the most depended-upon file in the repo.
92
-
93
- An earlier version built edges from exported-symbol name matches instead. It doesn't work at any weighting: text can't tell a reference from a coincidence, so modules exporting ordinary words (`App`, `Host`, `Repo`) top the list.
94
-
95
- `hotspots` multiplies **git churn** (commits touching a file) by **complexity** (branch points, counted at index time from the same ast-grep parse that extracts symbols, with a lexical fallback for languages that have no grammar). Neither half is interesting alone: a churning config file is trivial, a gnarly file nobody edits costs nobody anything. Data and markup score zero, keyword scans there read content, not code paths.
96
-
97
- Complexity is reported as a **raw branch count**, not a composite score: counts are comparable to the file in front of you, and you can recount them by hand.
98
-
99
- `impact` answers "what does this change reach?" over the same import graph. With no argument it reads your uncommitted changes; given paths, it answers about those. Each result carries its distance and whether it is a test, and the header names what the walk could **not** answer: seeds the index has never seen, how many reachable files the cap dropped, and (the useful half) which changed files no test reaches at all.
100
-
101
- It walks exactly **one hop, in both directions**, and that is a measured choice rather than a cautious one. Against 762 co-change cases mined from this repo's own history (`iq-bench impact`), one hop each way beat the no-graph "same folder" baseline 444 wins to 168 and beat one hop of importers alone 318 to 159, both at p < 0.001. Depth made it worse, not better: two hops of importers *lost* to one hop, and two hops in both directions reach hundreds of files per seed at 0.06 precision. Coverage depth is deliberately separate and deliberately timid (a test that imports the file directly counts, nothing further) because the co-change benchmark measured which files change together, which is not the same question as which tests exercise what, and borrowing its answer would be borrowing evidence never collected.
102
-
103
- ## Benchmarks
104
-
105
- `iq` is measured by a two-tier harness in [`_search/iq-bench`](../../_search/iq-bench): tier 1 scores retrieval quality against golden query→anchor datasets with no LLM (free, deterministic); tier 2 runs real coding-agent CLIs on real tasks, paired with and without `iq`, graded automatically (answer must name the ground-truth `path:line`, or the repo's tests must pass). All numbers below are reproducible by re-running the committed harness and datasets.
106
-
107
- ### Tier 1, retrieval quality (90 golden queries, 3 repos: this workspace + `hono` + `click`)
108
-
109
- The full pipeline is the best configuration on aggregate (recall@5 **0.81**, recall@10 **0.88**, nDCG@10 **0.65**). Ablations, paired per query vs the full pipeline (exact sign test):
110
-
111
- | Configuration | vs full | Δ recall@10 | Verdict |
112
- |---|---|---:|---|
113
- | drop **semantic** | loses 24 / wins 3 (p<0.001) | −0.04 | significant: dense retrieval earns its place |
114
- | **lexical only** (grep-grade) | loses 27 / wins 3 (p<0.001) | −0.09 | significantly worse on natural-language queries |
115
- | **bm25 only** | loses 29 / wins 7 (p<0.001) | −0.07 | significantly worse |
116
- | drop **rerank** | loses 5 / wins 4 (p=1.0) | ±0.00 | neutral on ranking (helps agents downstream) |
117
- | drop **pack** | loses 0 / wins 3 (p=0.25) | +0.02 | ranking-neutral (marginally trims recall); payoff is agent-side (below) |
118
-
119
- The gap over grep-grade retrieval concentrates exactly on the synonym-gap queries where an agent's phrasing differs from the code's vocabulary: the case `iq` exists for.
120
-
121
- ### Tier 2: does it help a real agent? (Claude Sonnet, 8 tasks, `iq` vs plain grep/find)
122
-
123
- Both arms solved every task, so at this difficulty `iq` doesn't change *whether* the problem is solved: it changes the *cost* of getting there, and the gap widens with search difficulty:
124
-
125
- - **Aggregate: iq $2.19 vs grep $3.29 across 8 tasks (−33% cost).**
126
- - **The search-hardest task** (trace an off-by-2 bug through a URL parser): grep baseline burned **21 turns / $1.27 / 306 s**; with `iq`, **10 turns / $0.33 / 50 s**, **−74% cost, −52% turns, −84% wall.** The baseline thrashed through ad-hoc probes and repeated reads; `iq` located the mechanism directly.
127
-
128
- On trivial single-file lookups `iq` is roughly break-even (when the answer is one obvious grep away, the index fetch is overhead it can't earn back), the value is concentrated where retrieval is genuinely hard. Full methodology, per-task tables, paired statistics, and transcripts: [`_search/iq-bench`](../../_search/iq-bench).
129
-
130
- ## Using it with a coding agent
131
-
132
- `iq` ships as a **Claude Code plugin** ([`plugin/`](plugin)) that teaches an agent to prefer `iq` over grep/find/Glob by default: a bundled skill plus a SessionStart nudge. The plugin installs the *teaching*, not the binary, so install both:
20
+ ## Usage
133
21
 
134
22
  ```sh
135
- npm i -g @intentic/iq # the CLI (also needs Node ≥ 24 + ripgrep, see Install)
136
- /plugin marketplace add intentic/intentic # the marketplace (or the repo's https URL)
137
- /plugin install iq # the skill + nudge
23
+ iq "where do we enforce the secrets floor?" # anything; intent is auto-detected
24
+ iq def createIgnoreScope # where a symbol is defined
25
+ iq refs createIgnoreScope --kind call # who calls it
26
+ iq outline src/app.ts # a file's shape without reading it
27
+ iq read src/app.ts::Server::start # one symbol's body
28
+ iq impact # what the uncommitted change reaches, and its tests
29
+ iq sessions files "auth refresh" # files past sessions touched for a topic
138
30
  ```
139
31
 
140
- The plugin's skill lives at [`plugin/skills/iq/SKILL.md`](plugin/skills/iq/SKILL.md) and doubles as a `CLAUDE.md` / `AGENTS.md` note if you'd rather paste it in directly. The Agent SDK loads the same plugin via `plugins: [{ type: "local", path: "…/_search/iq/plugin" }]`. The intentic sandbox bakes this exact plugin into its image, so the sandbox agent and an external user get identical behavior.
141
-
142
- ## License
143
-
144
- MIT
145
-
146
32
  ## Key files
147
33
 
148
- - [src/commands](src/commands): one file per verb; the CLI surface an agent actually types.
149
- - [src/app.ts](src/app.ts): verb dispatch and intent detection for a bare query.
150
- - [src/lib](src/lib): rendering results inside a token budget.
151
- - [src/cli.ts](src/cli.ts): the entry point — the grep-dialect redirects, over
152
- [`@intentic/agent-cli`](../../_tools/agent-cli)'s process contract (EPIPE, errors on stdout, exit-code
153
- clamp), which `fileq` and `webq` keep the same way.
34
+ - [src/app.ts](src/app.ts) — the verb table and the `--help` text agents read.
35
+ - [src/lib/run.ts](src/lib/run.ts) — the executor every search verb shares: engine, path resolution, output mode, exit code.
36
+ - [src/commands/q.command.ts](src/commands/q.command.ts) — the default verb behind a bare `iq "…"`.
37
+ - [src/commands/sessions/sessions.routes.ts](src/commands/sessions/sessions.routes.ts) — session recall verbs over `iq-recall`.
38
+ - [plugin/skills/iq/SKILL.md](plugin/skills/iq/SKILL.md) — which verb to reach for, as agents are taught it.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentic/iq",
3
- "version": "1.310.0",
3
+ "version": "1.311.0",
4
4
  "description": "iq, agent-native workspace search CLI: one intent-first entry point over lexical, structural, semantic, and git engines",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -33,11 +33,11 @@
33
33
  }
34
34
  },
35
35
  "dependencies": {
36
- "@intentic/agent-cli": "1.310.0",
37
- "@intentic/base": "1.310.0",
38
- "@intentic/constants": "1.310.0",
39
- "@intentic/iq-engine": "1.310.0",
40
- "@intentic/iq-recall": "1.310.0",
36
+ "@intentic/agent-cli": "1.311.0",
37
+ "@intentic/base": "1.311.0",
38
+ "@intentic/constants": "1.311.0",
39
+ "@intentic/iq-engine": "1.311.0",
40
+ "@intentic/iq-recall": "1.311.0",
41
41
  "@puristic/env": "1.6.1",
42
42
  "@stricli/core": "1.3.0",
43
43
  "tslib": "2.8.1",