@gmickel/gno 0.9.1 → 0.9.3
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 +71 -59
- package/assets/screenshots/claudecodeskill.jpg +0 -0
- package/assets/screenshots/cli.jpg +0 -0
- package/assets/screenshots/raycast-mcp.jpg +0 -0
- package/assets/screenshots/webui-ask-answer.jpg +0 -0
- package/assets/screenshots/webui-collections.jpg +0 -0
- package/assets/screenshots/webui-editor.jpg +0 -0
- package/assets/screenshots/webui-home.jpg +0 -0
- package/assets/screenshots/webui-search.jpg +0 -0
- package/package.json +2 -1
- package/src/cli/commands/completion/completion.ts +201 -0
- package/src/cli/commands/completion/index.ts +8 -0
- package/src/cli/commands/completion/scripts.ts +365 -0
- package/src/cli/commands/skill/paths.ts +47 -28
- package/src/cli/context.ts +3 -0
- package/src/cli/pager.ts +200 -0
- package/src/cli/program.ts +125 -40
- package/src/serve/public/components/editor/MarkdownPreview.tsx +61 -22
- package/src/serve/public/globals.built.css +1 -1
- package/src/serve/public/pages/Ask.tsx +67 -3
- package/src/serve/public/pages/DocView.tsx +48 -4
- package/src/serve/public/pages/DocumentEditor.tsx +2 -2
- package/src/serve/routes/api.ts +4 -0
- package/assets/screenshots/webui-ask-answer.png +0 -0
- package/assets/screenshots/webui-collections.png +0 -0
- package/assets/screenshots/webui-editor.png +0 -0
- package/assets/screenshots/webui-home.png +0 -0
package/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# GNO
|
|
2
2
|
|
|
3
|
-
**Your Local Second Brain
|
|
3
|
+
**Your Local Second Brain**: Index, search, and synthesize your entire digital life.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@gmickel/gno)
|
|
6
6
|
[](./LICENSE)
|
|
7
7
|
[](https://gno.sh)
|
|
8
8
|
[](https://twitter.com/gmickel)
|
|
9
9
|
|
|
10
|
-
GNO is a local knowledge engine for privacy-conscious developers and AI agents. Index your notes, code, PDFs, and Office docs. Get hybrid search (BM25 + vector + reranking) and AI-powered answers
|
|
10
|
+
GNO is a local knowledge engine for privacy-conscious developers and AI agents. Index your notes, code, PDFs, and Office docs. Get hybrid search (BM25 + vector + reranking) and AI-powered answers, all running 100% on your machine.
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
@@ -16,9 +16,9 @@ GNO is a local knowledge engine for privacy-conscious developers and AI agents.
|
|
|
16
16
|
- [Quick Start](#quick-start)
|
|
17
17
|
- [Installation](#installation)
|
|
18
18
|
- [Search Modes](#search-modes)
|
|
19
|
+
- [Agent Integration](#agent-integration)
|
|
19
20
|
- [Web UI](#web-ui)
|
|
20
21
|
- [REST API](#rest-api)
|
|
21
|
-
- [Agent Integration](#agent-integration)
|
|
22
22
|
- [How It Works](#how-it-works)
|
|
23
23
|
- [Features](#features)
|
|
24
24
|
- [Local Models](#local-models)
|
|
@@ -85,7 +85,7 @@ Check status: `gno mcp status`
|
|
|
85
85
|
|
|
86
86
|
#### Skills (Claude Code, Codex, OpenCode)
|
|
87
87
|
|
|
88
|
-
Skills integrate via CLI
|
|
88
|
+
Skills integrate via CLI with no MCP overhead:
|
|
89
89
|
|
|
90
90
|
```bash
|
|
91
91
|
gno skill install --scope user # User-wide
|
|
@@ -106,7 +106,7 @@ gno skill install --target all # Both Claude + Codex
|
|
|
106
106
|
| `gno query` | Hybrid | Best accuracy (BM25 + vector + reranking) |
|
|
107
107
|
| `gno ask --answer` | RAG | Direct answers with citations |
|
|
108
108
|
|
|
109
|
-
**BM25** indexes full documents (not chunks) with Snowball stemming
|
|
109
|
+
**BM25** indexes full documents (not chunks) with Snowball stemming, so "running" matches "run".
|
|
110
110
|
**Vector** embeds chunks with document titles for context awareness.
|
|
111
111
|
|
|
112
112
|
```bash
|
|
@@ -120,29 +120,76 @@ Output formats: `--json`, `--files`, `--csv`, `--md`, `--xml`
|
|
|
120
120
|
|
|
121
121
|
---
|
|
122
122
|
|
|
123
|
+
## Agent Integration
|
|
124
|
+
|
|
125
|
+
Give your local LLM agents a long-term memory. GNO integrates as a Claude Code skill or MCP server, allowing agents to search, read, and cite your local files.
|
|
126
|
+
|
|
127
|
+
### Skills
|
|
128
|
+
|
|
129
|
+
Skills add GNO search to Claude Code/Codex without MCP protocol overhead:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
gno skill install --scope user
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+

|
|
136
|
+
|
|
137
|
+
Then ask your agent: _"Search my notes for the auth discussion"_
|
|
138
|
+
|
|
139
|
+
[Skill setup guide →](https://gno.sh/docs/integrations/skills/)
|
|
140
|
+
|
|
141
|
+
### MCP Server
|
|
142
|
+
|
|
143
|
+
Connect GNO to Claude Desktop, Cursor, Raycast, and more:
|
|
144
|
+
|
|
145
|
+

|
|
146
|
+
|
|
147
|
+
GNO exposes 6 tools via [Model Context Protocol](https://modelcontextprotocol.io):
|
|
148
|
+
|
|
149
|
+
| Tool | Description |
|
|
150
|
+
| :-------------- | :-------------------------- |
|
|
151
|
+
| `gno_search` | BM25 keyword search |
|
|
152
|
+
| `gno_vsearch` | Vector semantic search |
|
|
153
|
+
| `gno_query` | Hybrid search (recommended) |
|
|
154
|
+
| `gno_get` | Retrieve document by ID |
|
|
155
|
+
| `gno_multi_get` | Batch document retrieval |
|
|
156
|
+
| `gno_status` | Index health check |
|
|
157
|
+
|
|
158
|
+
**Design**: MCP tools are retrieval-only. Your AI assistant (Claude, GPT-4) synthesizes answers from retrieved context. Best retrieval (GNO) + best reasoning (your LLM).
|
|
159
|
+
|
|
160
|
+
[MCP setup guide →](https://gno.sh/docs/MCP/)
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
123
164
|
## Web UI
|
|
124
165
|
|
|
125
|
-
Visual dashboard for search, browsing, editing, and AI answers
|
|
166
|
+
Visual dashboard for search, browsing, editing, and AI answers. Right in your browser.
|
|
126
167
|
|
|
127
168
|
```bash
|
|
128
169
|
gno serve # Start on port 3000
|
|
129
170
|
gno serve --port 8080 # Custom port
|
|
130
171
|
```
|
|
131
172
|
|
|
132
|
-

|
|
133
174
|
|
|
134
175
|
Open `http://localhost:3000` to:
|
|
135
176
|
|
|
136
|
-
- **Search
|
|
137
|
-
- **Browse
|
|
138
|
-
- **Edit
|
|
139
|
-
- **Ask
|
|
140
|
-
- **Manage Collections
|
|
141
|
-
- **Switch presets
|
|
177
|
+
- **Search**: BM25, vector, or hybrid modes with visual results
|
|
178
|
+
- **Browse**: Paginated document list, filter by collection
|
|
179
|
+
- **Edit**: Create, edit, and delete documents with live preview
|
|
180
|
+
- **Ask**: AI-powered Q&A with citations
|
|
181
|
+
- **Manage Collections**: Add, remove, and re-index collections
|
|
182
|
+
- **Switch presets**: Change models live without restart
|
|
183
|
+
|
|
184
|
+
### Search
|
|
185
|
+
|
|
186
|
+

|
|
187
|
+
|
|
188
|
+
Three retrieval modes: BM25 (keyword), Vector (semantic), or Hybrid (best of both). Adjust search depth for speed vs thoroughness.
|
|
142
189
|
|
|
143
190
|
### Document Editing
|
|
144
191
|
|
|
145
|
-

|
|
146
193
|
|
|
147
194
|
Full-featured markdown editor with:
|
|
148
195
|
|
|
@@ -156,7 +203,7 @@ Full-featured markdown editor with:
|
|
|
156
203
|
|
|
157
204
|
### Collections Management
|
|
158
205
|
|
|
159
|
-

|
|
160
207
|
|
|
161
208
|
- Add collections with folder path input
|
|
162
209
|
- View document count, chunk count, embedding status
|
|
@@ -165,9 +212,9 @@ Full-featured markdown editor with:
|
|
|
165
212
|
|
|
166
213
|
### AI Answers
|
|
167
214
|
|
|
168
|
-

|
|
169
216
|
|
|
170
|
-
Ask questions in natural language
|
|
217
|
+
Ask questions in natural language. GNO searches your documents and synthesizes answers with inline citations linking to sources.
|
|
171
218
|
|
|
172
219
|
Everything runs locally. No cloud, no accounts, no data leaving your machine.
|
|
173
220
|
|
|
@@ -219,41 +266,6 @@ No authentication. No rate limits. Build custom tools, automate workflows, integ
|
|
|
219
266
|
|
|
220
267
|
---
|
|
221
268
|
|
|
222
|
-
## Agent Integration
|
|
223
|
-
|
|
224
|
-
### MCP Server
|
|
225
|
-
|
|
226
|
-

|
|
227
|
-
|
|
228
|
-
GNO exposes 6 tools via [Model Context Protocol](https://modelcontextprotocol.io):
|
|
229
|
-
|
|
230
|
-
| Tool | Description |
|
|
231
|
-
| :-------------- | :-------------------------- |
|
|
232
|
-
| `gno_search` | BM25 keyword search |
|
|
233
|
-
| `gno_vsearch` | Vector semantic search |
|
|
234
|
-
| `gno_query` | Hybrid search (recommended) |
|
|
235
|
-
| `gno_get` | Retrieve document by ID |
|
|
236
|
-
| `gno_multi_get` | Batch document retrieval |
|
|
237
|
-
| `gno_status` | Index health check |
|
|
238
|
-
|
|
239
|
-
**Design**: MCP tools are retrieval-only. Your AI assistant (Claude, GPT-4) synthesizes answers from retrieved context—best retrieval (GNO) + best reasoning (your LLM).
|
|
240
|
-
|
|
241
|
-
### Skills
|
|
242
|
-
|
|
243
|
-
Skills add GNO search to Claude Code/Codex without MCP protocol overhead:
|
|
244
|
-
|
|
245
|
-
```bash
|
|
246
|
-
gno skill install --scope user
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-

|
|
250
|
-
|
|
251
|
-
Then ask your agent: _"Search my notes for the auth discussion"_
|
|
252
|
-
|
|
253
|
-
> **Detailed docs**: [MCP Integration](https://gno.sh/docs/MCP/) · [Use Cases](https://gno.sh/docs/USE-CASES/)
|
|
254
|
-
|
|
255
|
-
---
|
|
256
|
-
|
|
257
269
|
## How It Works
|
|
258
270
|
|
|
259
271
|
```mermaid
|
|
@@ -279,11 +291,11 @@ graph TD
|
|
|
279
291
|
M --> N[Final Results]
|
|
280
292
|
```
|
|
281
293
|
|
|
282
|
-
0. **Strong Signal Check
|
|
283
|
-
1. **Query Expansion
|
|
284
|
-
2. **Parallel Retrieval
|
|
285
|
-
3. **Fusion
|
|
286
|
-
4. **Reranking
|
|
294
|
+
0. **Strong Signal Check**: Skip expansion if BM25 has confident match (saves 1-3s)
|
|
295
|
+
1. **Query Expansion**: LLM generates lexical variants, semantic rephrases, and a [HyDE](https://arxiv.org/abs/2212.10496) passage
|
|
296
|
+
2. **Parallel Retrieval**: Document-level BM25 + chunk-level vector search on all variants
|
|
297
|
+
3. **Fusion**: RRF with 2× weight for original query, tiered bonus for top ranks
|
|
298
|
+
4. **Reranking**: Qwen3-Reranker scores best chunk per document (4K), blended with fusion
|
|
287
299
|
|
|
288
300
|
> **Deep dive**: [How Search Works](https://gno.sh/docs/HOW-SEARCH-WORKS/)
|
|
289
301
|
|
|
@@ -298,12 +310,12 @@ graph TD
|
|
|
298
310
|
| **Web UI** | Visual dashboard for search, browse, edit, and AI Q&A |
|
|
299
311
|
| **REST API** | HTTP API for custom tools and integrations |
|
|
300
312
|
| **Multi-Format** | Markdown, PDF, DOCX, XLSX, PPTX, plain text |
|
|
301
|
-
| **Local LLM** | AI answers via llama.cpp
|
|
313
|
+
| **Local LLM** | AI answers via llama.cpp, no API keys |
|
|
302
314
|
| **Privacy First** | 100% offline, zero telemetry, your data stays yours |
|
|
303
315
|
| **MCP Server** | Works with Claude Desktop, Cursor, Zed, + 8 more |
|
|
304
316
|
| **Collections** | Organize sources with patterns, excludes, contexts |
|
|
305
317
|
| **Multilingual** | 30+ languages, auto-detection, cross-lingual search |
|
|
306
|
-
| **Incremental** | SHA-256 tracking
|
|
318
|
+
| **Incremental** | SHA-256 tracking, only changed files re-indexed |
|
|
307
319
|
| **Keyboard First** | ⌘N capture, ⌘K search, ⌘/ shortcuts, ⌘S save |
|
|
308
320
|
|
|
309
321
|
---
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gmickel/gno",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.3",
|
|
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",
|
|
@@ -100,6 +100,7 @@
|
|
|
100
100
|
"react-dom": "^19.2.3",
|
|
101
101
|
"react-markdown": "^10.1.0",
|
|
102
102
|
"rehype-sanitize": "^6.0.0",
|
|
103
|
+
"remark-gfm": "^4.0.1",
|
|
103
104
|
"shiki": "^3.20.0",
|
|
104
105
|
"sqlite-vec": "^0.1.7-alpha.2",
|
|
105
106
|
"streamdown": "^1.6.10",
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell completion commands - output scripts or auto-install.
|
|
3
|
+
*
|
|
4
|
+
* @module src/cli/commands/completion/completion
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// node:fs/promises - no Bun equivalent for mkdir/appendFile/writeFile
|
|
8
|
+
import { appendFile, mkdir, writeFile } from "node:fs/promises";
|
|
9
|
+
import { homedir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
|
|
12
|
+
import { CLI_NAME } from "../../../app/constants.js";
|
|
13
|
+
import { CliError } from "../../errors.js";
|
|
14
|
+
import {
|
|
15
|
+
getCompletionScript,
|
|
16
|
+
SUPPORTED_SHELLS,
|
|
17
|
+
type Shell,
|
|
18
|
+
} from "./scripts.js";
|
|
19
|
+
|
|
20
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
21
|
+
// Types
|
|
22
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
export interface OutputOptions {
|
|
25
|
+
shell: Shell;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface InstallOptions {
|
|
29
|
+
/** Override shell detection */
|
|
30
|
+
shell?: Shell;
|
|
31
|
+
/** JSON output */
|
|
32
|
+
json?: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface InstallResult {
|
|
36
|
+
shell: Shell;
|
|
37
|
+
path: string;
|
|
38
|
+
action: "installed" | "already_installed";
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
42
|
+
// Shell Detection
|
|
43
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Detect user's current shell.
|
|
47
|
+
*/
|
|
48
|
+
function detectShell(): Shell | undefined {
|
|
49
|
+
// Check $SHELL env
|
|
50
|
+
const shellEnv = process.env.SHELL || "";
|
|
51
|
+
if (shellEnv.includes("zsh")) return "zsh";
|
|
52
|
+
if (shellEnv.includes("bash")) return "bash";
|
|
53
|
+
if (shellEnv.includes("fish")) return "fish";
|
|
54
|
+
|
|
55
|
+
// Check parent process name on Unix
|
|
56
|
+
// This is less reliable but can help
|
|
57
|
+
const parentName = process.env._ || "";
|
|
58
|
+
if (parentName.includes("zsh")) return "zsh";
|
|
59
|
+
if (parentName.includes("bash")) return "bash";
|
|
60
|
+
if (parentName.includes("fish")) return "fish";
|
|
61
|
+
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Get the appropriate rc file for a shell.
|
|
67
|
+
*/
|
|
68
|
+
function getShellRcPath(shell: Shell): string {
|
|
69
|
+
const home = homedir();
|
|
70
|
+
switch (shell) {
|
|
71
|
+
case "bash":
|
|
72
|
+
// Prefer .bashrc, but .bash_profile on macOS
|
|
73
|
+
return process.platform === "darwin"
|
|
74
|
+
? join(home, ".bash_profile")
|
|
75
|
+
: join(home, ".bashrc");
|
|
76
|
+
case "zsh":
|
|
77
|
+
return join(home, ".zshrc");
|
|
78
|
+
case "fish":
|
|
79
|
+
return join(home, ".config", "fish", "completions", `${CLI_NAME}.fish`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Check if completion is already installed.
|
|
85
|
+
*/
|
|
86
|
+
async function isCompletionInstalled(shell: Shell): Promise<boolean> {
|
|
87
|
+
const path = getShellRcPath(shell);
|
|
88
|
+
|
|
89
|
+
// For fish, check if file exists
|
|
90
|
+
if (shell === "fish") {
|
|
91
|
+
return await Bun.file(path).exists();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// For bash/zsh, check for shell-specific patterns
|
|
95
|
+
try {
|
|
96
|
+
const content = await Bun.file(path).text();
|
|
97
|
+
// Generic header comment
|
|
98
|
+
const header = `# ${CLI_NAME}`;
|
|
99
|
+
// Bash function name
|
|
100
|
+
const bashFn = `_${CLI_NAME}_completions`;
|
|
101
|
+
// Zsh-specific patterns (function definition or completion header)
|
|
102
|
+
const zshFn = `_${CLI_NAME}()`;
|
|
103
|
+
|
|
104
|
+
if (shell === "bash") {
|
|
105
|
+
return content.includes(header) || content.includes(bashFn);
|
|
106
|
+
}
|
|
107
|
+
if (shell === "zsh") {
|
|
108
|
+
return content.includes(header) || content.includes(zshFn);
|
|
109
|
+
}
|
|
110
|
+
return false;
|
|
111
|
+
} catch {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
117
|
+
// Commands
|
|
118
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Output completion script to stdout.
|
|
122
|
+
*/
|
|
123
|
+
export function completionOutput(options: OutputOptions): string {
|
|
124
|
+
const { shell } = options;
|
|
125
|
+
|
|
126
|
+
if (!SUPPORTED_SHELLS.includes(shell)) {
|
|
127
|
+
throw new CliError(
|
|
128
|
+
"VALIDATION",
|
|
129
|
+
`Unsupported shell: ${shell}. Supported: ${SUPPORTED_SHELLS.join(", ")}`
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return getCompletionScript(shell);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Auto-install completion to user's shell config.
|
|
138
|
+
*/
|
|
139
|
+
export async function completionInstall(
|
|
140
|
+
options: InstallOptions
|
|
141
|
+
): Promise<void> {
|
|
142
|
+
const { json = false } = options;
|
|
143
|
+
let { shell } = options;
|
|
144
|
+
|
|
145
|
+
// Auto-detect shell if not specified
|
|
146
|
+
if (!shell) {
|
|
147
|
+
shell = detectShell();
|
|
148
|
+
if (!shell) {
|
|
149
|
+
throw new CliError(
|
|
150
|
+
"VALIDATION",
|
|
151
|
+
"Could not detect shell. Please specify: gno completion install --shell <bash|zsh|fish>"
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!SUPPORTED_SHELLS.includes(shell)) {
|
|
157
|
+
throw new CliError(
|
|
158
|
+
"VALIDATION",
|
|
159
|
+
`Unsupported shell: ${shell}. Supported: ${SUPPORTED_SHELLS.join(", ")}`
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Check if already installed
|
|
164
|
+
const alreadyInstalled = await isCompletionInstalled(shell);
|
|
165
|
+
const rcPath = getShellRcPath(shell);
|
|
166
|
+
const script = getCompletionScript(shell);
|
|
167
|
+
|
|
168
|
+
let result: InstallResult;
|
|
169
|
+
|
|
170
|
+
if (alreadyInstalled) {
|
|
171
|
+
result = { shell, path: rcPath, action: "already_installed" };
|
|
172
|
+
} else {
|
|
173
|
+
// Install completion
|
|
174
|
+
if (shell === "fish") {
|
|
175
|
+
// Fish uses a separate file in completions dir
|
|
176
|
+
const dir = join(homedir(), ".config", "fish", "completions");
|
|
177
|
+
await mkdir(dir, { recursive: true });
|
|
178
|
+
await writeFile(rcPath, script, "utf-8");
|
|
179
|
+
} else {
|
|
180
|
+
// Bash/zsh append to rc file
|
|
181
|
+
const separator = "\n\n";
|
|
182
|
+
await appendFile(rcPath, separator + script, "utf-8");
|
|
183
|
+
}
|
|
184
|
+
result = { shell, path: rcPath, action: "installed" };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Output result
|
|
188
|
+
if (json) {
|
|
189
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
190
|
+
} else {
|
|
191
|
+
if (result.action === "already_installed") {
|
|
192
|
+
process.stdout.write(`Completion already installed for ${shell}.\n`);
|
|
193
|
+
process.stdout.write(`Config: ${result.path}\n`);
|
|
194
|
+
} else {
|
|
195
|
+
process.stdout.write(`Completion installed for ${shell}.\n`);
|
|
196
|
+
process.stdout.write(`Config: ${result.path}\n`);
|
|
197
|
+
process.stdout.write(`\nRestart your shell or run:\n`);
|
|
198
|
+
process.stdout.write(` source ${result.path}\n`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|