domainstorm 0.2.6 → 0.3.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/README.md +30 -2
- package/check-domains.mjs +232 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,12 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/domainstorm)
|
|
4
4
|
[](https://www.npmjs.com/package/domainstorm)
|
|
5
|
-
[](https://github.com/tanishqsh/domain-cli/actions/workflows/npm-publish.yml)
|
|
6
5
|
|
|
7
6
|
Domainstorm is the fastest way to go from "we need a name" to "these are the domains we can buy now."
|
|
8
7
|
|
|
9
8
|
It is built for AI and agent products where naming cycles happen fast and every good domain disappears quickly.
|
|
10
|
-
|
|
11
9
|
## Why Domainstorm
|
|
12
10
|
|
|
13
11
|
- One command for ideation and availability checks
|
|
@@ -78,6 +76,18 @@ Compatibility alias:
|
|
|
78
76
|
domain-check --help
|
|
79
77
|
```
|
|
80
78
|
|
|
79
|
+
## Zero Install — Just `npx`
|
|
80
|
+
|
|
81
|
+
Agents don't need to install anything globally. Domainstorm works out of the box with `npx`:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
npx domainstorm --brainstorm "agent runtime" --tld com --only-available --table
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
No setup, no config, no API keys. One command and the agent has domain availability data. This makes it trivial to add to any agent's tool chain — just shell out to `npx domainstorm` and parse the output.
|
|
88
|
+
|
|
89
|
+
Works with Claude Code, Codex, OpenClaw, Cursor, and any agent that can run shell commands.
|
|
90
|
+
|
|
81
91
|
## Example Output
|
|
82
92
|
|
|
83
93
|
```txt
|
|
@@ -112,6 +122,24 @@ Output columns:
|
|
|
112
122
|
- `--timeout-ms <n>`
|
|
113
123
|
- `--raw`
|
|
114
124
|
|
|
125
|
+
## OpenClaw / AI Agent Skill
|
|
126
|
+
|
|
127
|
+
Domainstorm ships as an [OpenClaw](https://openclaw.ai) skill. Install it with a single `npx` command — no global install needed:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
npx domainstorm --install-skill
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
This launches an interactive installer that lets you pick where to install the skill (Claude Code, OpenClaw, Cursor, or a custom path). Once installed, your agent will automatically use Domainstorm when you ask it to brainstorm or check domains.
|
|
134
|
+
|
|
135
|
+
You can also print the skill file to stdout for manual setup:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
npx domainstorm --skill
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Works with any agent that supports OpenClaw skills — Claude, Codex, Cursor, and others.
|
|
142
|
+
|
|
115
143
|
## Notes
|
|
116
144
|
|
|
117
145
|
- WHOIS formats vary by registry; treat `likely_available` as a pre-check, not final registrar checkout.
|
package/check-domains.mjs
CHANGED
|
@@ -101,6 +101,228 @@ const TRADEMARK_KEYWORDS = [
|
|
|
101
101
|
"spacex",
|
|
102
102
|
];
|
|
103
103
|
|
|
104
|
+
import { createInterface } from "node:readline";
|
|
105
|
+
import { homedir } from "node:os";
|
|
106
|
+
|
|
107
|
+
const SKILL_DIRS = [
|
|
108
|
+
{ name: "OpenClaw", path: path.join(homedir(), ".openclaw", "workspace", "skills") },
|
|
109
|
+
{ name: "OpenClaw (global)", path: path.join(homedir(), ".openclaw", "skills") },
|
|
110
|
+
{ name: "Claude Code", path: path.join(homedir(), ".claude", "skills") },
|
|
111
|
+
{ name: "Cursor", path: path.join(homedir(), ".cursor", "skills") },
|
|
112
|
+
{ name: "Codex", path: path.join(homedir(), ".codex", "skills") },
|
|
113
|
+
{ name: "Local ./skills", path: path.resolve("skills") },
|
|
114
|
+
];
|
|
115
|
+
|
|
116
|
+
const DIM = "\x1b[2m";
|
|
117
|
+
const RESET = "\x1b[0m";
|
|
118
|
+
const BOLD = "\x1b[1m";
|
|
119
|
+
const GREEN = "\x1b[32m";
|
|
120
|
+
const CYAN = "\x1b[36m";
|
|
121
|
+
const YELLOW = "\x1b[33m";
|
|
122
|
+
const WHITE = "\x1b[37m";
|
|
123
|
+
const HIDE_CURSOR = "\x1b[?25l";
|
|
124
|
+
const SHOW_CURSOR = "\x1b[?25h";
|
|
125
|
+
const CLEAR_LINE = "\x1b[2K";
|
|
126
|
+
const UP = (n) => `\x1b[${n}A`;
|
|
127
|
+
|
|
128
|
+
function multiSelect(items) {
|
|
129
|
+
return new Promise((resolve) => {
|
|
130
|
+
const selected = new Set();
|
|
131
|
+
let cursor = 0;
|
|
132
|
+
const stdin = process.stdin;
|
|
133
|
+
const out = process.stdout;
|
|
134
|
+
|
|
135
|
+
// Pre-select detected items
|
|
136
|
+
items.forEach((item, i) => {
|
|
137
|
+
if (item.exists) selected.add(i);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
function render() {
|
|
141
|
+
let output = "";
|
|
142
|
+
for (let i = 0; i < items.length; i++) {
|
|
143
|
+
const item = items[i];
|
|
144
|
+
const isSelected = selected.has(i);
|
|
145
|
+
const isCursor = i === cursor;
|
|
146
|
+
const checkbox = isSelected ? `${GREEN}◉${RESET}` : `${DIM}○${RESET}`;
|
|
147
|
+
const pointer = isCursor ? `${CYAN}❯${RESET} ` : " ";
|
|
148
|
+
const label = isCursor ? `${WHITE}${BOLD}${item.name}${RESET}` : `${WHITE}${item.name}${RESET}`;
|
|
149
|
+
const pathStr = `${DIM}${item.path}${RESET}`;
|
|
150
|
+
const badge = item.exists ? ` ${GREEN}● found${RESET}` : "";
|
|
151
|
+
output += `${CLEAR_LINE}${pointer}${checkbox} ${label} ${pathStr}${badge}\n`;
|
|
152
|
+
}
|
|
153
|
+
output += `${CLEAR_LINE}\n`;
|
|
154
|
+
output += `${CLEAR_LINE}${DIM}↑↓ navigate ⎵ select ↵ confirm a toggle all q cancel${RESET}`;
|
|
155
|
+
out.write(output);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function clearRender() {
|
|
159
|
+
const totalLines = items.length + 2;
|
|
160
|
+
out.write(UP(totalLines) + "\r");
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
out.write(`\n${BOLD}📦 Where do you want to install?${RESET}\n\n`);
|
|
164
|
+
out.write(HIDE_CURSOR);
|
|
165
|
+
render();
|
|
166
|
+
|
|
167
|
+
stdin.setRawMode(true);
|
|
168
|
+
stdin.resume();
|
|
169
|
+
stdin.setEncoding("utf8");
|
|
170
|
+
|
|
171
|
+
function cleanup() {
|
|
172
|
+
stdin.setRawMode(false);
|
|
173
|
+
stdin.pause();
|
|
174
|
+
stdin.removeListener("data", onKey);
|
|
175
|
+
out.write(SHOW_CURSOR + "\n");
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function onKey(key) {
|
|
179
|
+
// Ctrl+C
|
|
180
|
+
if (key === "\x03") {
|
|
181
|
+
cleanup();
|
|
182
|
+
process.exit(0);
|
|
183
|
+
}
|
|
184
|
+
// q / Escape
|
|
185
|
+
if (key === "q" || key === "\x1b" && key.length === 1) {
|
|
186
|
+
cleanup();
|
|
187
|
+
resolve([]);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
// Enter
|
|
191
|
+
if (key === "\r" || key === "\n") {
|
|
192
|
+
cleanup();
|
|
193
|
+
resolve(items.filter((_, i) => selected.has(i)));
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
// Space
|
|
197
|
+
if (key === " ") {
|
|
198
|
+
if (selected.has(cursor)) {
|
|
199
|
+
selected.delete(cursor);
|
|
200
|
+
} else {
|
|
201
|
+
selected.add(cursor);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// a — toggle all
|
|
205
|
+
if (key === "a") {
|
|
206
|
+
if (selected.size === items.length) {
|
|
207
|
+
selected.clear();
|
|
208
|
+
} else {
|
|
209
|
+
items.forEach((_, i) => selected.add(i));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// Up arrow
|
|
213
|
+
if (key === "\x1b[A" || key === "k") {
|
|
214
|
+
cursor = (cursor - 1 + items.length) % items.length;
|
|
215
|
+
}
|
|
216
|
+
// Down arrow
|
|
217
|
+
if (key === "\x1b[B" || key === "j") {
|
|
218
|
+
cursor = (cursor + 1) % items.length;
|
|
219
|
+
}
|
|
220
|
+
clearRender();
|
|
221
|
+
render();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
stdin.on("data", onKey);
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async function installSkill() {
|
|
229
|
+
const scriptDir = path.dirname(new URL(import.meta.url).pathname);
|
|
230
|
+
const skillSrc = path.join(scriptDir, "SKILL.md");
|
|
231
|
+
|
|
232
|
+
try {
|
|
233
|
+
await fs.access(skillSrc);
|
|
234
|
+
} catch {
|
|
235
|
+
console.error("Error: SKILL.md not found in domainstorm package. Try updating: npm i -g domainstorm@latest");
|
|
236
|
+
process.exit(1);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
console.log(`\n${BOLD}${CYAN}domainstorm${RESET} ${DIM}— skill installer${RESET}\n`);
|
|
240
|
+
|
|
241
|
+
const items = [];
|
|
242
|
+
for (const dir of SKILL_DIRS) {
|
|
243
|
+
let exists = false;
|
|
244
|
+
try {
|
|
245
|
+
await fs.access(dir.path);
|
|
246
|
+
exists = true;
|
|
247
|
+
} catch {}
|
|
248
|
+
items.push({ ...dir, exists });
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
let selections;
|
|
252
|
+
|
|
253
|
+
if (process.stdin.isTTY) {
|
|
254
|
+
selections = await multiSelect(items);
|
|
255
|
+
} else {
|
|
256
|
+
// Fallback for non-TTY (piped input, CI, agents)
|
|
257
|
+
console.log(`${BOLD}📦 Where do you want to install?${RESET}\n`);
|
|
258
|
+
items.forEach((item, i) => {
|
|
259
|
+
const badge = item.exists ? ` ${GREEN}● found${RESET}` : "";
|
|
260
|
+
console.log(` ${i + 1}. ${item.name} ${DIM}${item.path}${RESET}${badge}`);
|
|
261
|
+
});
|
|
262
|
+
console.log(` ${items.length + 1}. Enter custom path\n`);
|
|
263
|
+
|
|
264
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
265
|
+
const answer = await new Promise((resolve) => {
|
|
266
|
+
rl.question(`Select (1-${items.length + 1}, comma-separated): `, resolve);
|
|
267
|
+
});
|
|
268
|
+
rl.close();
|
|
269
|
+
|
|
270
|
+
const indices = answer.split(",").map((s) => Number(s.trim()) - 1).filter((i) => i >= 0 && i < items.length);
|
|
271
|
+
selections = indices.map((i) => items[i]);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (selections.length === 0) {
|
|
275
|
+
console.log(`${DIM}No directories selected. Exiting.${RESET}`);
|
|
276
|
+
process.exit(0);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
console.log("");
|
|
280
|
+
const skillContent = await fs.readFile(skillSrc, "utf8");
|
|
281
|
+
|
|
282
|
+
const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
283
|
+
const out = process.stdout;
|
|
284
|
+
|
|
285
|
+
for (const item of selections) {
|
|
286
|
+
const destDir = path.join(item.path, "domainstorm");
|
|
287
|
+
const destFile = path.join(destDir, "SKILL.md");
|
|
288
|
+
|
|
289
|
+
// Spinner
|
|
290
|
+
let frameIdx = 0;
|
|
291
|
+
const spinner = setInterval(() => {
|
|
292
|
+
out.write(`\r${CLEAR_LINE} ${CYAN}${frames[frameIdx]}${RESET} Installing to ${BOLD}${item.name}${RESET}${DIM}...${RESET}`);
|
|
293
|
+
frameIdx = (frameIdx + 1) % frames.length;
|
|
294
|
+
}, 80);
|
|
295
|
+
|
|
296
|
+
try {
|
|
297
|
+
await fs.mkdir(destDir, { recursive: true });
|
|
298
|
+
await fs.writeFile(destFile, skillContent, "utf8");
|
|
299
|
+
// Small delay so spinner is visible
|
|
300
|
+
await new Promise((r) => setTimeout(r, 400));
|
|
301
|
+
clearInterval(spinner);
|
|
302
|
+
out.write(`\r${CLEAR_LINE} ${GREEN}✓${RESET} ${BOLD}${item.name}${RESET} ${DIM}→ ${destFile}${RESET}\n`);
|
|
303
|
+
} catch (err) {
|
|
304
|
+
clearInterval(spinner);
|
|
305
|
+
out.write(`\r${CLEAR_LINE} ${YELLOW}✗${RESET} ${BOLD}${item.name}${RESET} ${DIM}— ${err.message}${RESET}\n`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
console.log("");
|
|
310
|
+
console.log(` ${GREEN}${BOLD}✨ Done!${RESET} Your agent can now discover domainstorm as a skill.`);
|
|
311
|
+
console.log(` ${DIM}Restart your agent or start a new session to pick it up.${RESET}\n`);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
async function printSkill() {
|
|
315
|
+
const scriptDir = path.dirname(new URL(import.meta.url).pathname);
|
|
316
|
+
const skillSrc = path.join(scriptDir, "SKILL.md");
|
|
317
|
+
try {
|
|
318
|
+
const content = await fs.readFile(skillSrc, "utf8");
|
|
319
|
+
console.log(content);
|
|
320
|
+
} catch {
|
|
321
|
+
console.error("Error: SKILL.md not found in domainstorm package.");
|
|
322
|
+
process.exit(1);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
104
326
|
function printUsage() {
|
|
105
327
|
console.log(
|
|
106
328
|
[
|
|
@@ -123,6 +345,8 @@ function printUsage() {
|
|
|
123
345
|
" --plain Emit machine-friendly TSV output",
|
|
124
346
|
" --only-available Print only likely available domains",
|
|
125
347
|
" --raw Include WHOIS snippet in console output",
|
|
348
|
+
" --install-skill Install SKILL.md to your agent's skill directory",
|
|
349
|
+
" --skill Print SKILL.md to stdout",
|
|
126
350
|
" --help Show this help",
|
|
127
351
|
"",
|
|
128
352
|
"Notes:",
|
|
@@ -135,7 +359,7 @@ function printUsage() {
|
|
|
135
359
|
);
|
|
136
360
|
}
|
|
137
361
|
|
|
138
|
-
function parseArgs(argv) {
|
|
362
|
+
async function parseArgs(argv) {
|
|
139
363
|
const options = {
|
|
140
364
|
input: null,
|
|
141
365
|
output: null,
|
|
@@ -201,6 +425,12 @@ function parseArgs(argv) {
|
|
|
201
425
|
case "--raw":
|
|
202
426
|
options.raw = true;
|
|
203
427
|
break;
|
|
428
|
+
case "--install-skill":
|
|
429
|
+
await installSkill();
|
|
430
|
+
process.exit(0);
|
|
431
|
+
case "--skill":
|
|
432
|
+
await printSkill();
|
|
433
|
+
process.exit(0);
|
|
204
434
|
case "--help":
|
|
205
435
|
case "-h":
|
|
206
436
|
printUsage();
|
|
@@ -894,7 +1124,7 @@ function shouldAutoThrottleWhois(domains) {
|
|
|
894
1124
|
}
|
|
895
1125
|
|
|
896
1126
|
async function main() {
|
|
897
|
-
const options = parseArgs(process.argv.slice(2));
|
|
1127
|
+
const options = await parseArgs(process.argv.slice(2));
|
|
898
1128
|
let rawLabels = await loadLabels(options);
|
|
899
1129
|
let storyByLabel = new Map();
|
|
900
1130
|
|