@howdoi-cli/core 1.0.1
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/LICENSE +21 -0
- package/README.md +15 -0
- package/dist/cli.js +446 -0
- package/dist/package.json +50 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 howdoi-cli contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# @howdoi-cli/core
|
|
2
|
+
|
|
3
|
+
The engine, renderer, and `howdoi` binary.
|
|
4
|
+
|
|
5
|
+
You don't install this directly — it's pulled in automatically when you install any knowledge base package.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @howdoi-cli/unix # core comes along for free
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
See the [main README](https://github.com/SiphoChris/howdoi-cli) for full documentation.
|
|
12
|
+
|
|
13
|
+
## License
|
|
14
|
+
|
|
15
|
+
MIT
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/engine/loader.ts
|
|
4
|
+
import { readFileSync, readdirSync, existsSync } from "fs";
|
|
5
|
+
import { join, dirname } from "path";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
import { homedir } from "os";
|
|
8
|
+
import yaml from "js-yaml";
|
|
9
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
function getXdgDataDir() {
|
|
11
|
+
const xdgBase = process.env.XDG_DATA_HOME ?? join(homedir(), ".local", "share");
|
|
12
|
+
return join(xdgBase, "howdoi");
|
|
13
|
+
}
|
|
14
|
+
function discoverFromNodeModules() {
|
|
15
|
+
const dataDirs = [];
|
|
16
|
+
const candidates = [
|
|
17
|
+
join(__dirname, "..", "..", "..", ".."),
|
|
18
|
+
// global: node_modules/@howdoi-cli/core/dist -> ../../../../
|
|
19
|
+
join(__dirname, "..", "..", "..", "..", "..")
|
|
20
|
+
// deeper nesting
|
|
21
|
+
];
|
|
22
|
+
for (const base of candidates) {
|
|
23
|
+
const scope = join(base, "node_modules", "@howdoi-cli");
|
|
24
|
+
if (!existsSync(scope)) continue;
|
|
25
|
+
for (const pkg of readdirSync(scope)) {
|
|
26
|
+
if (pkg === "core") continue;
|
|
27
|
+
const pkgJson = join(scope, pkg, "package.json");
|
|
28
|
+
if (!existsSync(pkgJson)) continue;
|
|
29
|
+
try {
|
|
30
|
+
const manifest = JSON.parse(readFileSync(pkgJson, "utf-8"));
|
|
31
|
+
const dataDir = manifest?.howdoi?.dataDir;
|
|
32
|
+
if (dataDir) {
|
|
33
|
+
dataDirs.push(join(scope, pkg, dataDir));
|
|
34
|
+
}
|
|
35
|
+
} catch {
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return dataDirs;
|
|
40
|
+
}
|
|
41
|
+
function loadFromDir(dataDir, tools) {
|
|
42
|
+
if (!existsSync(dataDir)) return;
|
|
43
|
+
const categories = readdirSync(dataDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
44
|
+
for (const category of categories) {
|
|
45
|
+
const categoryDir = join(dataDir, category);
|
|
46
|
+
const files = readdirSync(categoryDir).filter((f) => f.endsWith(".yaml"));
|
|
47
|
+
for (const file of files) {
|
|
48
|
+
try {
|
|
49
|
+
const raw = readFileSync(join(categoryDir, file), "utf-8");
|
|
50
|
+
const parsed = yaml.load(raw);
|
|
51
|
+
tools.push(parsed);
|
|
52
|
+
} catch {
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function loadAllTools() {
|
|
58
|
+
const tools = [];
|
|
59
|
+
const xdgDir = getXdgDataDir();
|
|
60
|
+
if (existsSync(xdgDir)) {
|
|
61
|
+
for (const pkg of readdirSync(xdgDir, { withFileTypes: true }).filter((d) => d.isDirectory())) {
|
|
62
|
+
loadFromDir(join(xdgDir, pkg.name), tools);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (tools.length === 0) {
|
|
66
|
+
for (const dir of discoverFromNodeModules()) {
|
|
67
|
+
loadFromDir(dir, tools);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (tools.length === 0) {
|
|
71
|
+
console.error(
|
|
72
|
+
"\nNo knowledge bases found. Install one to get started:\n\n npm install -g @howdoi-cli/unix\n npm install -g @howdoi-cli/git\n"
|
|
73
|
+
);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
return tools;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/engine/search.ts
|
|
80
|
+
import Fuse from "fuse.js";
|
|
81
|
+
var SearchEngine = class {
|
|
82
|
+
fuse;
|
|
83
|
+
intentFuse;
|
|
84
|
+
index;
|
|
85
|
+
exampleIntents;
|
|
86
|
+
constructor(tools) {
|
|
87
|
+
this.index = [];
|
|
88
|
+
for (const tool of tools) {
|
|
89
|
+
const exampleIntents = new Set(tool.examples.map((e) => e.intent));
|
|
90
|
+
this.index.push({ intent: tool.tool, tool, isExampleIntent: false });
|
|
91
|
+
this.index.push({ intent: tool.description, tool, isExampleIntent: false });
|
|
92
|
+
for (const intent of tool.intents) {
|
|
93
|
+
this.index.push({
|
|
94
|
+
intent,
|
|
95
|
+
tool,
|
|
96
|
+
isExampleIntent: exampleIntents.has(intent)
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
this.fuse = new Fuse(this.index, {
|
|
101
|
+
keys: ["intent"],
|
|
102
|
+
threshold: 0.45,
|
|
103
|
+
distance: 100,
|
|
104
|
+
minMatchCharLength: 2,
|
|
105
|
+
includeScore: true
|
|
106
|
+
});
|
|
107
|
+
this.exampleIntents = [
|
|
108
|
+
...new Set(
|
|
109
|
+
this.index.filter((i) => i.isExampleIntent).map((i) => i.intent)
|
|
110
|
+
)
|
|
111
|
+
].sort();
|
|
112
|
+
this.intentFuse = new Fuse(
|
|
113
|
+
this.exampleIntents.map((i) => ({ intent: i })),
|
|
114
|
+
{
|
|
115
|
+
keys: ["intent"],
|
|
116
|
+
threshold: 0.35,
|
|
117
|
+
distance: 100,
|
|
118
|
+
minMatchCharLength: 2,
|
|
119
|
+
includeScore: true
|
|
120
|
+
}
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
search(query) {
|
|
124
|
+
const raw = this.fuse.search(query);
|
|
125
|
+
const seen = /* @__PURE__ */ new Map();
|
|
126
|
+
for (const r of raw) {
|
|
127
|
+
const toolName = r.item.tool.tool;
|
|
128
|
+
const score = r.score ?? 1;
|
|
129
|
+
if (!seen.has(toolName) || score < seen.get(toolName).score) {
|
|
130
|
+
seen.set(toolName, {
|
|
131
|
+
tool: r.item.tool,
|
|
132
|
+
matchedIntent: r.item.isExampleIntent ? r.item.intent : void 0,
|
|
133
|
+
score
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return Array.from(seen.values()).sort((a, b) => a.score - b.score);
|
|
138
|
+
}
|
|
139
|
+
suggestIntents(query) {
|
|
140
|
+
if (!query || query.trim().length < 2) return this.exampleIntents.slice(0, 10);
|
|
141
|
+
return this.intentFuse.search(query).slice(0, 10).map((r) => r.item.intent);
|
|
142
|
+
}
|
|
143
|
+
getAllIntents() {
|
|
144
|
+
return this.exampleIntents;
|
|
145
|
+
}
|
|
146
|
+
getToolsByCategory() {
|
|
147
|
+
const map = /* @__PURE__ */ new Map();
|
|
148
|
+
const seen = /* @__PURE__ */ new Set();
|
|
149
|
+
for (const entry of this.index) {
|
|
150
|
+
const tool = entry.tool;
|
|
151
|
+
if (seen.has(tool.tool)) continue;
|
|
152
|
+
seen.add(tool.tool);
|
|
153
|
+
const list = map.get(tool.category) ?? [];
|
|
154
|
+
list.push(tool);
|
|
155
|
+
map.set(tool.category, list);
|
|
156
|
+
}
|
|
157
|
+
return map;
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// src/renderer/display.ts
|
|
162
|
+
import chalk from "chalk";
|
|
163
|
+
|
|
164
|
+
// src/engine/types.ts
|
|
165
|
+
var CATEGORY_LABELS = {
|
|
166
|
+
"file-management": "File Management",
|
|
167
|
+
"text-processing": "Text Processing",
|
|
168
|
+
"file-inspection": "File Inspection",
|
|
169
|
+
"git": "Git",
|
|
170
|
+
"ssh": "SSH",
|
|
171
|
+
"docker": "Docker",
|
|
172
|
+
"networking": "Networking"
|
|
173
|
+
};
|
|
174
|
+
var CATEGORY_ORDER = [
|
|
175
|
+
"file-management",
|
|
176
|
+
"text-processing",
|
|
177
|
+
"file-inspection",
|
|
178
|
+
"git",
|
|
179
|
+
"ssh",
|
|
180
|
+
"docker",
|
|
181
|
+
"networking"
|
|
182
|
+
];
|
|
183
|
+
var PACKAGE_CATEGORY_MAP = {
|
|
184
|
+
"@howdoi-cli/unix": ["file-management", "text-processing", "file-inspection"],
|
|
185
|
+
"@howdoi-cli/git": ["git"],
|
|
186
|
+
"@howdoi-cli/ssh": ["ssh"],
|
|
187
|
+
"@howdoi-cli/docker": ["docker"],
|
|
188
|
+
"@howdoi-cli/networking": ["networking"]
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// src/renderer/display.ts
|
|
192
|
+
function rule(width = 56) {
|
|
193
|
+
return chalk.dim("\u2500".repeat(width));
|
|
194
|
+
}
|
|
195
|
+
function badge(category) {
|
|
196
|
+
const labels = {
|
|
197
|
+
"file-management": chalk.bgYellow.black(" FILE "),
|
|
198
|
+
"text-processing": chalk.bgCyan.black(" TEXT "),
|
|
199
|
+
"file-inspection": chalk.bgMagenta.black(" INSPECT "),
|
|
200
|
+
"git": chalk.bgRed.black(" GIT "),
|
|
201
|
+
"ssh": chalk.bgBlue.black(" SSH "),
|
|
202
|
+
"docker": chalk.bgBlue.black(" DOCKER "),
|
|
203
|
+
"networking": chalk.bgGreen.black(" NET ")
|
|
204
|
+
};
|
|
205
|
+
return labels[category] ?? chalk.bgWhite.black(` ${category.toUpperCase()} `);
|
|
206
|
+
}
|
|
207
|
+
function osBadge(os) {
|
|
208
|
+
if (!os) return "";
|
|
209
|
+
const labels = {
|
|
210
|
+
"linux": chalk.dim(" [linux/macos]"),
|
|
211
|
+
"macos": chalk.dim(" [linux/macos]"),
|
|
212
|
+
"linux-only": chalk.yellow(" [linux only]"),
|
|
213
|
+
"macos-only": chalk.yellow(" [macos only]")
|
|
214
|
+
};
|
|
215
|
+
return labels[os] ?? "";
|
|
216
|
+
}
|
|
217
|
+
function renderToolCard(tool, intentFilter) {
|
|
218
|
+
const examples = intentFilter ? tool.examples.filter((e) => e.intent === intentFilter) : tool.examples;
|
|
219
|
+
const displayExamples = examples.length > 0 ? examples : tool.examples;
|
|
220
|
+
console.log();
|
|
221
|
+
console.log(
|
|
222
|
+
`${badge(tool.category)} ${chalk.bold.white(tool.tool)} ${chalk.dim(tool.description)}`
|
|
223
|
+
);
|
|
224
|
+
console.log(rule());
|
|
225
|
+
for (const example of displayExamples) {
|
|
226
|
+
renderExample(example);
|
|
227
|
+
}
|
|
228
|
+
console.log(rule());
|
|
229
|
+
console.log();
|
|
230
|
+
}
|
|
231
|
+
function renderExample(example) {
|
|
232
|
+
console.log();
|
|
233
|
+
console.log(` ${chalk.dim("\u25B8")} ${chalk.white(example.title)}${osBadge(example.os)}`);
|
|
234
|
+
console.log(` ${chalk.green.bold(example.command)}`);
|
|
235
|
+
}
|
|
236
|
+
function renderNoResults(query) {
|
|
237
|
+
console.log();
|
|
238
|
+
console.log(chalk.yellow(" No results found for: ") + chalk.bold(`"${query}"`));
|
|
239
|
+
console.log(chalk.dim(" Try rephrasing \u2014 e.g. 'search file for text' or 'undo last commit'"));
|
|
240
|
+
console.log(chalk.dim(" Or browse with: howdoi (no args)"));
|
|
241
|
+
console.log();
|
|
242
|
+
}
|
|
243
|
+
function renderWelcome() {
|
|
244
|
+
console.log();
|
|
245
|
+
console.log(chalk.bold.white(" howdoi") + chalk.dim(" \u2014 intent-based command discovery"));
|
|
246
|
+
console.log(chalk.dim(" ") + rule(52));
|
|
247
|
+
console.log();
|
|
248
|
+
}
|
|
249
|
+
function renderList(tools, packageFilter) {
|
|
250
|
+
const packageMap = /* @__PURE__ */ new Map();
|
|
251
|
+
for (const tool of tools) {
|
|
252
|
+
const pkg = tool.package ?? "unknown";
|
|
253
|
+
if (packageFilter) {
|
|
254
|
+
const short = pkg.replace("@howdoi-cli/", "");
|
|
255
|
+
if (short !== packageFilter && pkg !== packageFilter) continue;
|
|
256
|
+
}
|
|
257
|
+
if (!packageMap.has(pkg)) packageMap.set(pkg, /* @__PURE__ */ new Map());
|
|
258
|
+
const catMap = packageMap.get(pkg);
|
|
259
|
+
if (!catMap.has(tool.category)) catMap.set(tool.category, []);
|
|
260
|
+
catMap.get(tool.category).push(tool);
|
|
261
|
+
}
|
|
262
|
+
const filteredTools = packageFilter ? tools.filter((t) => {
|
|
263
|
+
const short = (t.package ?? "").replace("@howdoi-cli/", "");
|
|
264
|
+
return short === packageFilter || t.package === packageFilter;
|
|
265
|
+
}) : tools;
|
|
266
|
+
const packageCount = packageMap.size;
|
|
267
|
+
if (packageMap.size === 0) {
|
|
268
|
+
console.log();
|
|
269
|
+
if (packageFilter) {
|
|
270
|
+
console.log(chalk.yellow(` No tools found for package: `) + chalk.bold(`"${packageFilter}"`));
|
|
271
|
+
console.log(chalk.dim(` Available packages: ${Object.keys(PACKAGE_CATEGORY_MAP).map((p) => p.replace("@howdoi-cli/", "")).join(", ")}`));
|
|
272
|
+
} else {
|
|
273
|
+
console.log(chalk.yellow(" No tools found."));
|
|
274
|
+
}
|
|
275
|
+
console.log();
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
console.log();
|
|
279
|
+
const packageOrder = Object.keys(PACKAGE_CATEGORY_MAP);
|
|
280
|
+
const sortedPackages = [...packageMap.keys()].sort(
|
|
281
|
+
(a, b) => packageOrder.indexOf(a) - packageOrder.indexOf(b)
|
|
282
|
+
);
|
|
283
|
+
for (const pkg of sortedPackages) {
|
|
284
|
+
const catMap = packageMap.get(pkg);
|
|
285
|
+
console.log(chalk.bold.white(` ${pkg}`));
|
|
286
|
+
console.log(chalk.dim(" " + "\u2500".repeat(50)));
|
|
287
|
+
const sortedCats = [...catMap.keys()].sort(
|
|
288
|
+
(a, b) => CATEGORY_ORDER.indexOf(a) - CATEGORY_ORDER.indexOf(b)
|
|
289
|
+
);
|
|
290
|
+
for (const cat of sortedCats) {
|
|
291
|
+
const catTools = catMap.get(cat);
|
|
292
|
+
console.log();
|
|
293
|
+
console.log(` ${chalk.bold.yellow(CATEGORY_LABELS[cat] ?? cat)}`);
|
|
294
|
+
for (const tool of catTools) {
|
|
295
|
+
console.log(
|
|
296
|
+
` ${chalk.green(tool.tool.padEnd(22))} ${chalk.dim(tool.description)}`
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
console.log();
|
|
301
|
+
}
|
|
302
|
+
console.log(
|
|
303
|
+
chalk.dim(` ${filteredTools.length} tool${filteredTools.length !== 1 ? "s" : ""} across ${packageCount} package${packageCount !== 1 ? "s" : ""}`)
|
|
304
|
+
);
|
|
305
|
+
console.log();
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// src/cli/index.ts
|
|
309
|
+
import chalk2 from "chalk";
|
|
310
|
+
import { select } from "@inquirer/prompts";
|
|
311
|
+
var VERSION = "__HOWDOI_VERSION__";
|
|
312
|
+
async function promptSelect(message, choices) {
|
|
313
|
+
try {
|
|
314
|
+
return await select({
|
|
315
|
+
message,
|
|
316
|
+
choices: choices.map((c) => ({
|
|
317
|
+
name: c.description ? `${c.name} ${chalk2.dim(c.description)}` : c.name,
|
|
318
|
+
value: c.value
|
|
319
|
+
})),
|
|
320
|
+
pageSize: 12
|
|
321
|
+
});
|
|
322
|
+
} catch {
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
async function guidedMode(engine) {
|
|
327
|
+
renderWelcome();
|
|
328
|
+
const categoryMap = engine.getToolsByCategory();
|
|
329
|
+
const selectedCategory = await promptSelect(
|
|
330
|
+
"What area are you working in?",
|
|
331
|
+
CATEGORY_ORDER.filter((c) => categoryMap.has(c)).map((c) => ({
|
|
332
|
+
name: CATEGORY_LABELS[c] ?? c,
|
|
333
|
+
value: c,
|
|
334
|
+
description: `${categoryMap.get(c).length} tools`
|
|
335
|
+
}))
|
|
336
|
+
);
|
|
337
|
+
if (!selectedCategory) {
|
|
338
|
+
console.log(chalk2.dim("\n Bye!\n"));
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
const tools = categoryMap.get(selectedCategory) ?? [];
|
|
342
|
+
const selectedTool = await promptSelect(
|
|
343
|
+
`Which ${CATEGORY_LABELS[selectedCategory]} tool?`,
|
|
344
|
+
tools.map((t) => ({
|
|
345
|
+
name: t.tool,
|
|
346
|
+
value: t.tool,
|
|
347
|
+
description: t.description
|
|
348
|
+
}))
|
|
349
|
+
);
|
|
350
|
+
if (!selectedTool) {
|
|
351
|
+
console.log(chalk2.dim("\n Bye!\n"));
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
const tool = tools.find((t) => t.tool === selectedTool);
|
|
355
|
+
if (tool) renderToolCard(tool);
|
|
356
|
+
}
|
|
357
|
+
function intentSearch(query, engine) {
|
|
358
|
+
const results = engine.search(query);
|
|
359
|
+
if (results.length === 0) {
|
|
360
|
+
renderNoResults(query);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
const top = results[0];
|
|
364
|
+
renderToolCard(top.tool, top.matchedIntent);
|
|
365
|
+
if (results.length > 1) {
|
|
366
|
+
console.log(chalk2.dim(" Also relevant:"));
|
|
367
|
+
for (const r of results.slice(1, 4)) {
|
|
368
|
+
console.log(
|
|
369
|
+
` ${chalk2.cyan(r.tool.tool.padEnd(22))} ${chalk2.dim(r.tool.description)}`
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
console.log();
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
function printVersion() {
|
|
376
|
+
console.log(`howdoi v${VERSION}`);
|
|
377
|
+
}
|
|
378
|
+
function printHelp() {
|
|
379
|
+
console.log();
|
|
380
|
+
console.log(chalk2.bold.white(" howdoi") + " \u2014 intent-based command discovery");
|
|
381
|
+
console.log();
|
|
382
|
+
console.log(" " + chalk2.bold("Usage"));
|
|
383
|
+
console.log(` ${chalk2.green("howdoi")} ${chalk2.dim("guided category browser")}`);
|
|
384
|
+
console.log(` ${chalk2.green("howdoi")} ${chalk2.yellow("<intent>")} ${chalk2.dim("search by what you want to do")}`);
|
|
385
|
+
console.log(` ${chalk2.green("howdoi")} ${chalk2.yellow("<tool>")} ${chalk2.dim("show all examples for a tool")}`);
|
|
386
|
+
console.log(` ${chalk2.green("howdoi")} ${chalk2.yellow("--list")} ${chalk2.dim("list all tools grouped by package")}`);
|
|
387
|
+
console.log(` ${chalk2.green("howdoi")} ${chalk2.yellow("--list <package>")} ${chalk2.dim("list tools for a specific package")}`);
|
|
388
|
+
console.log(` ${chalk2.green("howdoi")} ${chalk2.yellow("--version")} ${chalk2.dim("show installed version")}`);
|
|
389
|
+
console.log(` ${chalk2.green("howdoi")} ${chalk2.yellow("--help")} ${chalk2.dim("show this help message")}`);
|
|
390
|
+
console.log();
|
|
391
|
+
console.log(" " + chalk2.bold("Examples"));
|
|
392
|
+
console.log(` ${chalk2.green("howdoi")} search for text in file`);
|
|
393
|
+
console.log(` ${chalk2.green("howdoi")} undo last commit`);
|
|
394
|
+
console.log(` ${chalk2.green("howdoi")} add ssh key to agent`);
|
|
395
|
+
console.log(` ${chalk2.green("howdoi")} run container in background`);
|
|
396
|
+
console.log(` ${chalk2.green("howdoi")} grep`);
|
|
397
|
+
console.log(` ${chalk2.green("howdoi")} --list`);
|
|
398
|
+
console.log(` ${chalk2.green("howdoi")} --list unix`);
|
|
399
|
+
console.log(` ${chalk2.green("howdoi")} --list git`);
|
|
400
|
+
console.log(` ${chalk2.green("howdoi")} --version`);
|
|
401
|
+
console.log();
|
|
402
|
+
console.log(" " + chalk2.bold("Install knowledge bases"));
|
|
403
|
+
console.log(` ${chalk2.cyan("npm install -g @howdoi-cli/unix")}`);
|
|
404
|
+
console.log(` ${chalk2.cyan("npm install -g @howdoi-cli/git")}`);
|
|
405
|
+
console.log(` ${chalk2.cyan("npm install -g @howdoi-cli/ssh")}`);
|
|
406
|
+
console.log(` ${chalk2.cyan("npm install -g @howdoi-cli/docker")}`);
|
|
407
|
+
console.log(` ${chalk2.cyan("npm install -g @howdoi-cli/networking")}`);
|
|
408
|
+
console.log(` ${chalk2.cyan("npm install -g @howdoi-cli/all")} ${chalk2.dim("\u2190 everything")}`);
|
|
409
|
+
console.log();
|
|
410
|
+
}
|
|
411
|
+
async function main() {
|
|
412
|
+
const args = process.argv.slice(2);
|
|
413
|
+
if (args[0] === "--version" || args[0] === "-v") {
|
|
414
|
+
printVersion();
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
if (args[0] === "--help" || args[0] === "-h") {
|
|
418
|
+
printHelp();
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
const tools = loadAllTools();
|
|
422
|
+
const engine = new SearchEngine(tools);
|
|
423
|
+
if (args[0] === "--list" || args[0] === "-l") {
|
|
424
|
+
const packageFilter = args[1];
|
|
425
|
+
renderWelcome();
|
|
426
|
+
renderList(tools, packageFilter);
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
if (args.length === 0) {
|
|
430
|
+
await guidedMode(engine);
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
const query = args.join(" ").trim();
|
|
434
|
+
const exactTool = tools.find(
|
|
435
|
+
(t) => t.tool.toLowerCase() === query.toLowerCase()
|
|
436
|
+
);
|
|
437
|
+
if (exactTool) {
|
|
438
|
+
renderToolCard(exactTool);
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
intentSearch(query, engine);
|
|
442
|
+
}
|
|
443
|
+
main().catch((err) => {
|
|
444
|
+
console.error(chalk2.red("Error: ") + err.message);
|
|
445
|
+
process.exit(1);
|
|
446
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@howdoi-cli/core",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "The howdoi CLI engine — intent-based command discovery for the terminal.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"howdoi": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md",
|
|
12
|
+
"LICENSE"
|
|
13
|
+
],
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"dev": "bun run src/cli/index.ts",
|
|
19
|
+
"build": "tsup",
|
|
20
|
+
"prepublishOnly": "bun run build",
|
|
21
|
+
"publish:npm": "npm publish"
|
|
22
|
+
},
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=18.0.0"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@inquirer/prompts": "^7.0.0",
|
|
28
|
+
"chalk": "^5.3.0",
|
|
29
|
+
"fuse.js": "^7.0.0",
|
|
30
|
+
"js-yaml": "^4.1.0"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"cli",
|
|
34
|
+
"unix",
|
|
35
|
+
"terminal",
|
|
36
|
+
"git",
|
|
37
|
+
"commands",
|
|
38
|
+
"developer-tools",
|
|
39
|
+
"howdoi"
|
|
40
|
+
],
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "https://github.com/SiphoChris/howdoi-cli"
|
|
45
|
+
},
|
|
46
|
+
"homepage": "https://github.com/SiphoChris/howdoi-cli#readme",
|
|
47
|
+
"bugs": {
|
|
48
|
+
"url": "https://github.com/SiphoChris/howdoi-cli/issues"
|
|
49
|
+
}
|
|
50
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@howdoi-cli/core",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "The howdoi CLI engine — intent-based command discovery for the terminal.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"howdoi": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md",
|
|
12
|
+
"LICENSE"
|
|
13
|
+
],
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"dev": "bun run src/cli/index.ts",
|
|
19
|
+
"build": "tsup",
|
|
20
|
+
"prepublishOnly": "bun run build",
|
|
21
|
+
"publish:npm": "npm publish"
|
|
22
|
+
},
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=18.0.0"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@inquirer/prompts": "^7.0.0",
|
|
28
|
+
"chalk": "^5.3.0",
|
|
29
|
+
"fuse.js": "^7.0.0",
|
|
30
|
+
"js-yaml": "^4.1.0"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"cli",
|
|
34
|
+
"unix",
|
|
35
|
+
"terminal",
|
|
36
|
+
"git",
|
|
37
|
+
"commands",
|
|
38
|
+
"developer-tools",
|
|
39
|
+
"howdoi"
|
|
40
|
+
],
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "https://github.com/SiphoChris/howdoi-cli"
|
|
45
|
+
},
|
|
46
|
+
"homepage": "https://github.com/SiphoChris/howdoi-cli#readme",
|
|
47
|
+
"bugs": {
|
|
48
|
+
"url": "https://github.com/SiphoChris/howdoi-cli/issues"
|
|
49
|
+
}
|
|
50
|
+
}
|