@gmickel/gno 0.9.3 → 0.9.5

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/README.md CHANGED
@@ -332,14 +332,14 @@ Models auto-download on first use to `~/.cache/gno/models/`.
332
332
 
333
333
  ### Model Presets
334
334
 
335
- | Preset | Disk | Best For |
336
- | :--------- | :----- | :--------------------- |
337
- | `slim` | ~1GB | Fast, lower quality |
338
- | `balanced` | ~2GB | Good balance (default) |
339
- | `quality` | ~2.5GB | Best answers |
335
+ | Preset | Disk | Best For |
336
+ | :--------- | :----- | :--------------------------- |
337
+ | `slim` | ~1GB | Fast, good quality (default) |
338
+ | `balanced` | ~2GB | Slightly larger model |
339
+ | `quality` | ~2.5GB | Best answers |
340
340
 
341
341
  ```bash
342
- gno models use balanced
342
+ gno models use slim
343
343
  gno models pull --all # Optional: pre-download models (auto-downloads on first use)
344
344
  ```
345
345
 
@@ -1,25 +1,27 @@
1
1
  ---
2
2
  name: gno
3
- description: Search local documents, files, notes, and knowledge bases. Supports PDFs, Markdown, Word docs, code files. Use when user wants to search files, find documents, query their notes, look up information in local folders, index a directory, set up document search, build a knowledge base, or needs RAG/semantic search over their files.
3
+ description: Search local documents, files, notes, and knowledge bases. Index directories, search with BM25/vector/hybrid, get AI answers with citations. Use when user wants to search files, find documents, query notes, look up information in local folders, index a directory, set up document search, build a knowledge base, needs RAG/semantic search, or wants to start a local web UI for their docs.
4
4
  allowed-tools: Bash(gno:*), Read
5
5
  ---
6
6
 
7
- # GNO - Local Document Search
7
+ # GNO - Local Knowledge Engine
8
8
 
9
- Fast local semantic search for your documents. Index once, search instantly.
9
+ Fast local semantic search for your documents. Index once, search instantly. No cloud, no API keys.
10
10
 
11
- **Role**: document search assistant
12
- **Goal**: help users index, search, and query their documents
11
+ **Role**: Document search and knowledge retrieval assistant
12
+ **Goal**: Help users index, search, query, and get answers from their local documents
13
13
 
14
14
  ## When to Use This Skill
15
15
 
16
16
  - User asks to **search files, documents, or notes**
17
17
  - User wants to **find information** in local folders
18
18
  - User needs to **index a directory** for searching
19
- - User mentions **PDFs, markdown, docs** they want to search
19
+ - User mentions **PDFs, markdown, Word docs, code** they want to search
20
20
  - User asks about **knowledge base** or **RAG** setup
21
21
  - User wants **semantic/vector search** over their files
22
22
  - User needs to **set up MCP** for document access
23
+ - User wants a **web UI** to browse/search documents
24
+ - User asks to **get AI answers** from their documents
23
25
 
24
26
  ## Quick Start
25
27
 
@@ -39,6 +41,52 @@ gno search "your query"
39
41
 
40
42
  ## Core Commands
41
43
 
44
+ ### Search Commands
45
+
46
+ | Command | Description |
47
+ | --------------------- | ------------------------------------------- |
48
+ | `gno search <query>` | BM25 keyword search (fast, exact terms) |
49
+ | `gno vsearch <query>` | Vector semantic search (meaning-based) |
50
+ | `gno query <query>` | Hybrid search (BM25 + vector + rerank) |
51
+ | `gno ask <question>` | Get AI answer with citations from your docs |
52
+
53
+ ### Search Options
54
+
55
+ ```bash
56
+ # Limit results
57
+ gno search "api" -n 10
58
+
59
+ # Filter by collection
60
+ gno query "auth" --collection work
61
+
62
+ # Search modes (for query/ask)
63
+ gno query "topic" --fast # ~0.7s, skip expansion/rerank
64
+ gno query "topic" # ~2-3s, default with rerank
65
+ gno query "topic" --thorough # ~5-8s, full pipeline
66
+
67
+ # Get AI answer
68
+ gno ask "how does auth work" --answer
69
+
70
+ # Output formats
71
+ gno search "test" --json # JSON for parsing
72
+ gno search "test" --files # URIs for piping
73
+ ```
74
+
75
+ ### Document Retrieval
76
+
77
+ | Command | Description |
78
+ | ------------------------- | ---------------------------- |
79
+ | `gno get <ref>` | Get document by URI or docid |
80
+ | `gno multi-get <refs...>` | Get multiple documents |
81
+ | `gno ls [scope]` | List indexed documents |
82
+
83
+ ```bash
84
+ gno get gno://work/readme.md
85
+ gno get "#a1b2c3d4" --line-numbers
86
+ gno ls notes
87
+ gno ls --json
88
+ ```
89
+
42
90
  ### Indexing
43
91
 
44
92
  | Command | Description |
@@ -47,62 +95,146 @@ gno search "your query"
47
95
  | `gno collection add <path> --name <name>` | Add folder to index |
48
96
  | `gno index` | Full index (ingest + embed) |
49
97
  | `gno update` | Sync files without embedding |
98
+ | `gno embed` | Generate embeddings only |
99
+
100
+ ```bash
101
+ gno init
102
+ gno collection add ~/notes --name notes --pattern "**/*.md"
103
+ gno index
104
+ gno index --collection notes # Single collection
105
+ ```
106
+
107
+ ### Model Management
108
+
109
+ | Command | Description |
110
+ | ------------------ | ------------------------------------- |
111
+ | `gno models list` | Show available model presets |
112
+ | `gno models use` | Switch preset (slim/balanced/quality) |
113
+ | `gno models pull` | Download models |
114
+ | `gno models clear` | Remove cached models |
115
+ | `gno models path` | Show model cache directory |
116
+
117
+ ```bash
118
+ gno models use slim # Default, fast (~1GB)
119
+ gno models use balanced # Larger model (~2GB)
120
+ gno models use quality # Best answers (~2.5GB)
121
+ gno models pull --all
122
+ ```
123
+
124
+ ### Context Hints
50
125
 
51
- ### Searching
126
+ | Command | Description |
127
+ | -------------------------------- | ---------------------- |
128
+ | `gno context add <scope> "text"` | Add context for scope |
129
+ | `gno context list` | List all contexts |
130
+ | `gno context check` | Validate configuration |
131
+ | `gno context rm <scope>` | Remove a context |
52
132
 
53
- | Command | Description |
54
- | --------------------- | -------------------------------------- |
55
- | `gno search <query>` | BM25 keyword search |
56
- | `gno vsearch <query>` | Vector semantic search |
57
- | `gno query <query>` | Hybrid search (BM25 + vector + rerank) |
58
- | `gno ask <question>` | AI-powered Q&A with citations |
133
+ ```bash
134
+ gno context add "/" "Corporate knowledge base"
135
+ gno context add "work:" "Work documents and contracts"
136
+ ```
59
137
 
60
- ### Common Options
138
+ ### Web UI
61
139
 
62
- - `-n <num>` — Max results (default: 5)
63
- - `-c, --collection <name>` — Filter to collection
64
- - `--json` JSON output
65
- - `--full` Include full content (not snippets)
140
+ | Command | Description |
141
+ | ------------------- | ------------------------ |
142
+ | `gno serve` | Start web UI (port 3000) |
143
+ | `gno serve -p 8080` | Custom port |
66
144
 
67
- ## Usage Patterns
145
+ Features: Dashboard, search, browse, document editor, AI Q&A with citations.
68
146
 
69
- ### Search & Retrieve
147
+ ### MCP Server
148
+
149
+ | Command | Description |
150
+ | ------------------- | ------------------------------- |
151
+ | `gno mcp` | Start MCP server (stdio) |
152
+ | `gno mcp install` | Install for Claude Desktop, etc |
153
+ | `gno mcp uninstall` | Remove MCP configuration |
154
+ | `gno mcp status` | Show installation status |
70
155
 
71
156
  ```bash
72
- # Keyword search
73
- gno search "termination clause" -n 10
157
+ gno mcp install --target claude-desktop
158
+ gno mcp install --target cursor
159
+ gno mcp install --target claude-code --scope project
160
+ ```
74
161
 
75
- # Semantic search (similar meaning)
76
- gno vsearch "how to cancel contract"
162
+ ### Skill Management
77
163
 
78
- # Hybrid search (best quality)
79
- gno query "deployment process" --collection work
164
+ | Command | Description |
165
+ | --------------------- | --------------------------- |
166
+ | `gno skill install` | Install skill for AI agents |
167
+ | `gno skill uninstall` | Remove skill |
168
+ | `gno skill show` | Preview skill files |
169
+ | `gno skill paths` | Show installation paths |
80
170
 
81
- # Get answer with citations
82
- gno ask "what are the payment terms"
171
+ ```bash
172
+ gno skill install --target claude --scope project
173
+ gno skill install --target codex --scope user
83
174
  ```
84
175
 
85
- ### Inspect Documents
176
+ ### Admin Commands
177
+
178
+ | Command | Description |
179
+ | ---------------- | ----------------------------- |
180
+ | `gno status` | Show index status |
181
+ | `gno doctor` | Check system health |
182
+ | `gno cleanup` | Remove orphaned data |
183
+ | `gno reset` | Delete all data (use caution) |
184
+ | `gno completion` | Shell tab completion |
86
185
 
87
186
  ```bash
88
- # Get document by URI
89
- gno get gno://docs/readme.md
187
+ gno status --json
188
+ gno doctor
189
+ gno completion install
190
+ ```
90
191
 
91
- # Get with line numbers
92
- gno get "#a1b2c3d4" --line-numbers
192
+ ## Global Flags
93
193
 
94
- # List documents
95
- gno ls docs
96
194
  ```
195
+ --index <name> Use alternate index (default: "default")
196
+ --config <path> Override config file path
197
+ --no-color Disable colored output
198
+ --no-pager Disable automatic paging
199
+ --verbose Enable verbose logging
200
+ --yes Non-interactive mode
201
+ --offline Use cached models only
202
+ --json JSON output (where supported)
203
+ ```
204
+
205
+ ## Common Patterns
97
206
 
98
- ### JSON Output (for scripts)
207
+ ### Search & Get Full Content
99
208
 
100
209
  ```bash
101
- # Search results as JSON
102
- gno search "api" --json | jq '.[] | .uri'
210
+ # Find documents, get full content
211
+ gno search "api design" --files | head -1 | cut -d, -f3 | xargs gno get
103
212
 
104
- # Status check
105
- gno status --json
213
+ # JSON pipeline
214
+ gno query "auth" --json | jq -r '.results[0].uri' | xargs gno get
215
+ ```
216
+
217
+ ### AI-Powered Q&A
218
+
219
+ ```bash
220
+ # Get answer with sources
221
+ gno ask "what are the deployment steps" --answer
222
+
223
+ # Show all retrieved sources
224
+ gno ask "summarize the auth discussion" --answer --show-sources
225
+ ```
226
+
227
+ ### Scripting
228
+
229
+ ```bash
230
+ # Check if indexed
231
+ gno status --json | jq '.healthy'
232
+
233
+ # List all URIs
234
+ gno ls --files
235
+
236
+ # Batch get
237
+ gno multi-get abc123 def456 ghi789 --max-bytes 10000
106
238
  ```
107
239
 
108
240
  ## Reference
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gmickel/gno",
3
- "version": "0.9.3",
3
+ "version": "0.9.5",
4
4
  "description": "Local semantic search for your documents. Index Markdown, PDF, and Office files with hybrid BM25 + vector search.",
5
5
  "keywords": [
6
6
  "embeddings",
@@ -48,6 +48,9 @@
48
48
  "test:coverage": "bun test --coverage",
49
49
  "test:coverage:html": "bun test --coverage --html",
50
50
  "test:fixtures": "bun scripts/generate-test-fixtures.ts",
51
+ "evals": "bun scripts/update-eval-scores.ts",
52
+ "eval": "bun --bun evalite",
53
+ "eval:watch": "bun --bun evalite watch",
51
54
  "reset": "bun run src/index.ts reset --confirm",
52
55
  "docs:verify": "bun run scripts/docs-verify.ts",
53
56
  "website:install": "cd website && bundle install",
@@ -110,6 +113,7 @@
110
113
  "zod": "^4.3.4"
111
114
  },
112
115
  "devDependencies": {
116
+ "@ai-sdk/openai": "^3.0.2",
113
117
  "@biomejs/biome": "2.3.10",
114
118
  "@tailwindcss/cli": "^4.1.18",
115
119
  "@types/bun": "latest",
@@ -126,7 +130,8 @@
126
130
  "oxlint-tsgolint": "^0.10.1",
127
131
  "pdf-lib": "^1.17.1",
128
132
  "pptxgenjs": "^4.0.1",
129
- "ultracite": "7.0.4"
133
+ "ultracite": "7.0.4",
134
+ "vitest": "^4.0.16"
130
135
  },
131
136
  "peerDependencies": {
132
137
  "typescript": "^5"
package/src/cli/run.ts CHANGED
@@ -6,6 +6,8 @@
6
6
  */
7
7
 
8
8
  import { CommanderError } from "commander";
9
+ import { dirname, join } from "node:path";
10
+ import { fileURLToPath } from "node:url";
9
11
 
10
12
  import { CLI_NAME, PRODUCT_NAME } from "../app/constants";
11
13
  import { CliError, exitCodeFor, formatErrorForOutput } from "./errors";
@@ -54,6 +56,32 @@ function isKnownValueFlag(arg: string): boolean {
54
56
  return false;
55
57
  }
56
58
 
59
+ /**
60
+ * Check if --skill flag is present in argv (before end-of-options marker).
61
+ */
62
+ function argvWantsSkill(argv: string[]): boolean {
63
+ for (const arg of argv) {
64
+ if (arg === "--") {
65
+ break;
66
+ }
67
+ if (arg === "--skill") {
68
+ return true;
69
+ }
70
+ }
71
+ return false;
72
+ }
73
+
74
+ /**
75
+ * Output SKILL.md content for agent discovery.
76
+ * Clean output with no extra text (suitable for piping/parsing).
77
+ */
78
+ async function printSkillFile(): Promise<void> {
79
+ const __dirname = dirname(fileURLToPath(import.meta.url));
80
+ const skillPath = join(__dirname, "../../assets/skill/SKILL.md");
81
+ const content = await Bun.file(skillPath).text();
82
+ process.stdout.write(content);
83
+ }
84
+
57
85
  /**
58
86
  * Check if argv has no subcommand (only known global flags).
59
87
  * Returns true for: gno, gno --json, gno --quiet --verbose
@@ -146,6 +174,12 @@ export async function runCli(argv: string[]): Promise<number> {
146
174
 
147
175
  const isJson = argvWantsJson(argv);
148
176
 
177
+ // Handle --skill flag: output SKILL.md for agent discovery
178
+ if (argvWantsSkill(argv)) {
179
+ await printSkillFile();
180
+ return 0;
181
+ }
182
+
149
183
  // Handle "no subcommand" case before Commander (avoids full help display)
150
184
  if (hasNoSubcommand(argv)) {
151
185
  printConciseHelp({ json: isJson });
@@ -176,7 +176,7 @@ export type ModelPreset = z.infer<typeof ModelPresetSchema>;
176
176
  export const DEFAULT_MODEL_PRESETS: ModelPreset[] = [
177
177
  {
178
178
  id: "slim",
179
- name: "Slim (Fast, ~1GB)",
179
+ name: "Slim (Default, ~1GB)",
180
180
  embed: "hf:gpustack/bge-m3-GGUF/bge-m3-Q4_K_M.gguf",
181
181
  rerank:
182
182
  "hf:ggml-org/Qwen3-Reranker-0.6B-Q8_0-GGUF/qwen3-reranker-0.6b-q8_0.gguf",
@@ -184,11 +184,11 @@ export const DEFAULT_MODEL_PRESETS: ModelPreset[] = [
184
184
  },
185
185
  {
186
186
  id: "balanced",
187
- name: "Balanced (Default, ~2GB)",
187
+ name: "Balanced (~2GB)",
188
188
  embed: "hf:gpustack/bge-m3-GGUF/bge-m3-Q4_K_M.gguf",
189
189
  rerank:
190
190
  "hf:ggml-org/Qwen3-Reranker-0.6B-Q8_0-GGUF/qwen3-reranker-0.6b-q8_0.gguf",
191
- gen: "hf:ggml-org/SmolLM3-3B-GGUF/SmolLM3-Q4_K_M.gguf",
191
+ gen: "hf:bartowski/Qwen2.5-3B-Instruct-GGUF/Qwen2.5-3B-Instruct-Q4_K_M.gguf",
192
192
  },
193
193
  {
194
194
  id: "quality",
@@ -202,7 +202,7 @@ export const DEFAULT_MODEL_PRESETS: ModelPreset[] = [
202
202
 
203
203
  export const ModelConfigSchema = z.object({
204
204
  /** Active preset ID */
205
- activePreset: z.string().default("balanced"),
205
+ activePreset: z.string().default("slim"),
206
206
  /** Model presets */
207
207
  presets: z.array(ModelPresetSchema).default(DEFAULT_MODEL_PRESETS),
208
208
  /** Model load timeout in ms */
@@ -19,7 +19,7 @@ import { DEFAULT_MODEL_PRESETS } from "../config/types";
19
19
  */
20
20
  export function getModelConfig(config: Config): ModelConfig {
21
21
  return {
22
- activePreset: config.models?.activePreset ?? "balanced",
22
+ activePreset: config.models?.activePreset ?? "slim",
23
23
  presets: config.models?.presets?.length
24
24
  ? config.models.presets
25
25
  : DEFAULT_MODEL_PRESETS,
@@ -13,21 +13,24 @@ import type { Citation, SearchResult } from "./types";
13
13
  // Constants
14
14
  // ─────────────────────────────────────────────────────────────────────────────
15
15
 
16
- const ANSWER_PROMPT = `You are answering a question using ONLY the provided context blocks.
16
+ const ANSWER_PROMPT = `Answer the question using ONLY the context blocks below. Cite sources with [1], [2], etc.
17
17
 
18
- Rules you MUST follow:
19
- 1) Use ONLY facts stated in the context blocks. Do NOT use outside knowledge.
20
- 2) Every factual statement must include an inline citation like [1] or [2] referring to a context block.
21
- 3) If the context does not contain enough information to answer, reply EXACTLY:
22
- "I don't have enough information in the provided sources to answer this question."
23
- 4) Do not cite sources you did not use. Do not invent citation numbers.
18
+ Example:
19
+ Q: What is the capital of France?
20
+ Context:
21
+ [1] France is a country in Western Europe. Paris is the capital and largest city.
22
+ [2] The Eiffel Tower, built in 1889, is located in Paris.
24
23
 
25
- Question: {query}
24
+ Answer: Paris is the capital of France [1]. It is home to the Eiffel Tower [2].
26
25
 
27
- Context blocks:
26
+ ---
27
+
28
+ Q: {query}
29
+
30
+ Context:
28
31
  {context}
29
32
 
30
- Write a concise answer (1-3 paragraphs).`;
33
+ Answer:`;
31
34
 
32
35
  /** Abstention message when LLM cannot ground answer */
33
36
  export const ABSTENTION_MESSAGE =
@@ -223,7 +223,11 @@ export async function searchBm25(
223
223
 
224
224
  // For --full, fetch full content and build results
225
225
  if (options.full) {
226
- for (const { fts, chunk } of bestByDocid.values()) {
226
+ // Sort by raw BM25 score (smaller = better) before building results
227
+ const sortedEntries = [...bestByDocid.values()].sort(
228
+ (a, b) => a.score - b.score
229
+ );
230
+ for (const { fts, chunk } of sortedEntries) {
227
231
  let fullContent: string | undefined;
228
232
  if (fts.mirrorHash) {
229
233
  const contentResult = await store.getContent(fts.mirrorHash);