domainstorm 0.2.1 → 0.2.2
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 +12 -1
- package/check-domains.mjs +214 -82
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ npx --yes domainstorm --help
|
|
|
33
33
|
Brainstorm + check + return only likely available:
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
|
-
domainstorm --brainstorm "agent orchestration" "mcp broker" --tld md --server whois.nic.md --only-available
|
|
36
|
+
domainstorm --brainstorm "agent orchestration" "mcp broker" --tld md --server whois.nic.md --only-available --table
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
Check exact domains:
|
|
@@ -42,12 +42,21 @@ Check exact domains:
|
|
|
42
42
|
domainstorm agentmesh.ai agentmesh.com agentmesh.md
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
+
Default output is plain English (for example: `broker.md is available.`).
|
|
46
|
+
Use `--table` if you want a structured table view.
|
|
47
|
+
|
|
45
48
|
Check from file and export CSV:
|
|
46
49
|
|
|
47
50
|
```bash
|
|
48
51
|
domainstorm --input domains.txt --output /tmp/domainstorm.csv
|
|
49
52
|
```
|
|
50
53
|
|
|
54
|
+
Try immediately with the bundled candidate list:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
domainstorm --input agent-candidates.txt --tld md --server whois.nic.md --only-available --table
|
|
58
|
+
```
|
|
59
|
+
|
|
51
60
|
## Built For Agent Workflows
|
|
52
61
|
|
|
53
62
|
Use Domainstorm when your coding/research agent needs to propose names and validate them in the same run.
|
|
@@ -56,6 +65,7 @@ Use Domainstorm when your coding/research agent needs to propose names and valid
|
|
|
56
65
|
- Works with shell pipelines
|
|
57
66
|
- No external AI API keys required
|
|
58
67
|
- Easy to trigger in CI on release or branch workflows
|
|
68
|
+
- No seed ideas? Ask your agent of choice (Codex, Claude, etc.) for seed phrases and pass them to `--brainstorm`
|
|
59
69
|
|
|
60
70
|
Compatibility alias:
|
|
61
71
|
|
|
@@ -83,6 +93,7 @@ Output columns:
|
|
|
83
93
|
|
|
84
94
|
- `--brainstorm` / `--storm`
|
|
85
95
|
- `--max-suggestions <n>` (default: `120`)
|
|
96
|
+
- `--table`
|
|
86
97
|
- `--input <file>`
|
|
87
98
|
- `--output <file>`
|
|
88
99
|
- `--tld <tld>` (default: `md`)
|
package/check-domains.mjs
CHANGED
|
@@ -75,59 +75,6 @@ const STOP_WORDS = new Set([
|
|
|
75
75
|
"agents",
|
|
76
76
|
]);
|
|
77
77
|
|
|
78
|
-
const DEFAULT_STORM_SEEDS = [
|
|
79
|
-
"agent",
|
|
80
|
-
"mcp",
|
|
81
|
-
"ai",
|
|
82
|
-
"broker",
|
|
83
|
-
"router",
|
|
84
|
-
"runtime",
|
|
85
|
-
"ops",
|
|
86
|
-
"stack",
|
|
87
|
-
"forge",
|
|
88
|
-
"mesh",
|
|
89
|
-
"pilot",
|
|
90
|
-
"flow",
|
|
91
|
-
];
|
|
92
|
-
|
|
93
|
-
const STORM_SUFFIXES = [
|
|
94
|
-
"storm",
|
|
95
|
-
"ops",
|
|
96
|
-
"hub",
|
|
97
|
-
"mesh",
|
|
98
|
-
"forge",
|
|
99
|
-
"stack",
|
|
100
|
-
"pilot",
|
|
101
|
-
"route",
|
|
102
|
-
"router",
|
|
103
|
-
"broker",
|
|
104
|
-
"runtime",
|
|
105
|
-
"engine",
|
|
106
|
-
"works",
|
|
107
|
-
"cloud",
|
|
108
|
-
"grid",
|
|
109
|
-
"terminal",
|
|
110
|
-
"dock",
|
|
111
|
-
"labs",
|
|
112
|
-
"studio",
|
|
113
|
-
];
|
|
114
|
-
|
|
115
|
-
const STORM_PREFIXES = [
|
|
116
|
-
"agent",
|
|
117
|
-
"ai",
|
|
118
|
-
"mcp",
|
|
119
|
-
"task",
|
|
120
|
-
"flow",
|
|
121
|
-
"auto",
|
|
122
|
-
"tool",
|
|
123
|
-
"prompt",
|
|
124
|
-
"context",
|
|
125
|
-
"trace",
|
|
126
|
-
"guard",
|
|
127
|
-
"secure",
|
|
128
|
-
"fleet",
|
|
129
|
-
];
|
|
130
|
-
|
|
131
78
|
// Small, non-authoritative heuristic list for quick legal-risk triage.
|
|
132
79
|
const TRADEMARK_KEYWORDS = [
|
|
133
80
|
"openai",
|
|
@@ -169,12 +116,16 @@ function printUsage() {
|
|
|
169
116
|
" --timeout-ms <n> WHOIS timeout per domain (default: 12000)",
|
|
170
117
|
" --brainstorm Generate domain ideas from seeds, then check availability",
|
|
171
118
|
" --max-suggestions <n> Max brainstormed candidates (default: 120)",
|
|
119
|
+
" --table Render results in a readable terminal table",
|
|
120
|
+
" --plain Emit machine-friendly TSV output",
|
|
172
121
|
" --only-available Print only likely available domains",
|
|
173
122
|
" --raw Include WHOIS snippet in console output",
|
|
174
123
|
" --help Show this help",
|
|
175
124
|
"",
|
|
176
125
|
"Notes:",
|
|
177
126
|
" - Labels without a dot are auto-converted to <label>.<tld>",
|
|
127
|
+
" - Brainstorm mode uses your seed words only (no hardcoded keyword packs)",
|
|
128
|
+
" - No seed ideas? Ask your agent (Codex, Claude, etc.) and pass them to --brainstorm",
|
|
178
129
|
" - WHOIS-based checks are heuristic; always confirm at registrar checkout",
|
|
179
130
|
].join("\n"),
|
|
180
131
|
);
|
|
@@ -190,6 +141,8 @@ function parseArgs(argv) {
|
|
|
190
141
|
server: null,
|
|
191
142
|
brainstorm: false,
|
|
192
143
|
maxSuggestions: DEFAULT_MAX_SUGGESTIONS,
|
|
144
|
+
table: false,
|
|
145
|
+
plain: false,
|
|
193
146
|
onlyAvailable: false,
|
|
194
147
|
raw: false,
|
|
195
148
|
labels: [],
|
|
@@ -226,6 +179,12 @@ function parseArgs(argv) {
|
|
|
226
179
|
case "--max-suggestions":
|
|
227
180
|
options.maxSuggestions = Number.parseInt(argv[++i], 10);
|
|
228
181
|
break;
|
|
182
|
+
case "--table":
|
|
183
|
+
options.table = true;
|
|
184
|
+
break;
|
|
185
|
+
case "--plain":
|
|
186
|
+
options.plain = true;
|
|
187
|
+
break;
|
|
229
188
|
case "--only-available":
|
|
230
189
|
options.onlyAvailable = true;
|
|
231
190
|
break;
|
|
@@ -249,7 +208,9 @@ function parseArgs(argv) {
|
|
|
249
208
|
}
|
|
250
209
|
|
|
251
210
|
if (!options.input && options.labels.length === 0 && !options.brainstorm) {
|
|
252
|
-
throw new Error(
|
|
211
|
+
throw new Error(
|
|
212
|
+
"Provide --input <file>, one/more labels/domains, or use --brainstorm. Tip: ask your agent (Codex, Claude, etc.) for seed phrases.",
|
|
213
|
+
);
|
|
253
214
|
}
|
|
254
215
|
if (!Number.isInteger(options.concurrency) || options.concurrency < 1 || options.concurrency > 50) {
|
|
255
216
|
throw new Error("--concurrency must be an integer between 1 and 50.");
|
|
@@ -349,7 +310,12 @@ function tokenizeSeeds(rawValues) {
|
|
|
349
310
|
|
|
350
311
|
function brainstormCandidates(rawSeeds, maxSuggestions) {
|
|
351
312
|
const seedTokens = tokenizeSeeds(rawSeeds);
|
|
352
|
-
|
|
313
|
+
if (seedTokens.length === 0) {
|
|
314
|
+
throw new Error(
|
|
315
|
+
"Brainstorm mode needs seed words. Ask your agent of choice (Codex, Claude, etc.) for naming seeds, then run: --brainstorm \"seed one\" \"seed two\"",
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
const seedPool = Array.from(new Set(seedTokens)).slice(0, 24);
|
|
353
319
|
const scored = new Map();
|
|
354
320
|
|
|
355
321
|
function add(label, story, bonus = 0) {
|
|
@@ -368,24 +334,67 @@ function brainstormCandidates(rawSeeds, maxSuggestions) {
|
|
|
368
334
|
}
|
|
369
335
|
}
|
|
370
336
|
|
|
337
|
+
function shortForm(token) {
|
|
338
|
+
if (token.length < 5) {
|
|
339
|
+
return token;
|
|
340
|
+
}
|
|
341
|
+
const withoutVowels = token[0] + token.slice(1).replace(/[aeiou]/g, "");
|
|
342
|
+
if (withoutVowels.length >= 3 && withoutVowels.length < token.length) {
|
|
343
|
+
return withoutVowels;
|
|
344
|
+
}
|
|
345
|
+
return token;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const formsBySeed = new Map();
|
|
371
349
|
for (const seed of seedPool) {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
add(
|
|
350
|
+
const forms = new Set([seed, shortForm(seed)]);
|
|
351
|
+
if (seed.length > 8) {
|
|
352
|
+
forms.add(seed.slice(0, 7));
|
|
375
353
|
}
|
|
376
|
-
|
|
377
|
-
|
|
354
|
+
formsBySeed.set(seed, Array.from(forms));
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
for (const seed of seedPool) {
|
|
358
|
+
for (const form of formsBySeed.get(seed)) {
|
|
359
|
+
add(form, form === seed ? "Seed keyword" : "Compressed variant", form === seed ? 18 : 10);
|
|
378
360
|
}
|
|
379
361
|
}
|
|
380
362
|
|
|
381
363
|
for (let i = 0; i < seedPool.length; i += 1) {
|
|
382
|
-
for (let j =
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
364
|
+
for (let j = 0; j < seedPool.length; j += 1) {
|
|
365
|
+
if (i === j) {
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
const aForms = formsBySeed.get(seedPool[i]);
|
|
369
|
+
const bForms = formsBySeed.get(seedPool[j]);
|
|
370
|
+
for (const a of aForms) {
|
|
371
|
+
for (const b of bForms) {
|
|
372
|
+
add(`${a}${b}`, "Two-seed compound", 20);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
for (let i = 0; i < seedPool.length; i += 1) {
|
|
379
|
+
for (let j = 0; j < seedPool.length; j += 1) {
|
|
380
|
+
for (let k = 0; k < seedPool.length; k += 1) {
|
|
381
|
+
if (i === j || j === k || i === k) {
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
const a = formsBySeed.get(seedPool[i])[0];
|
|
385
|
+
const b = formsBySeed.get(seedPool[j])[0];
|
|
386
|
+
const c = formsBySeed.get(seedPool[k])[0];
|
|
387
|
+
add(`${a}${b}${c}`, "Three-seed compound", 10);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const acronym = seedPool.map((token) => token[0]).join("");
|
|
393
|
+
if (acronym.length >= 3) {
|
|
394
|
+
add(acronym, "Seed acronym", 16);
|
|
395
|
+
for (const seed of seedPool) {
|
|
396
|
+
add(`${acronym}${seed}`, "Acronym + seed", 12);
|
|
397
|
+
add(`${seed}${acronym}`, "Seed + acronym", 12);
|
|
389
398
|
}
|
|
390
399
|
}
|
|
391
400
|
|
|
@@ -396,7 +405,7 @@ function brainstormCandidates(rawSeeds, maxSuggestions) {
|
|
|
396
405
|
return {
|
|
397
406
|
labels: sorted.map((item) => item.label),
|
|
398
407
|
storyByLabel: new Map(sorted.map((item) => [item.label, item.story])),
|
|
399
|
-
usedSeeds:
|
|
408
|
+
usedSeeds: seedPool,
|
|
400
409
|
};
|
|
401
410
|
}
|
|
402
411
|
|
|
@@ -558,6 +567,125 @@ function snippet(raw) {
|
|
|
558
567
|
return raw.replace(/\s+/g, " ").slice(0, 120);
|
|
559
568
|
}
|
|
560
569
|
|
|
570
|
+
function verdictForStatus(status) {
|
|
571
|
+
switch (status) {
|
|
572
|
+
case "likely_available":
|
|
573
|
+
return "AVAILABLE";
|
|
574
|
+
case "registered":
|
|
575
|
+
return "TAKEN";
|
|
576
|
+
default:
|
|
577
|
+
return "UNKNOWN";
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
function sentenceForRow(row) {
|
|
582
|
+
if (row.status === "likely_available") {
|
|
583
|
+
return `${row.domain} is available.`;
|
|
584
|
+
}
|
|
585
|
+
if (row.status === "registered") {
|
|
586
|
+
return `${row.domain} is not available (already registered).`;
|
|
587
|
+
}
|
|
588
|
+
return `${row.domain} availability is unknown (whois could not confirm).`;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
function toDisplayRow(row, options) {
|
|
592
|
+
return {
|
|
593
|
+
verdict: verdictForStatus(row.status),
|
|
594
|
+
domain: row.domain,
|
|
595
|
+
whois: row.status,
|
|
596
|
+
reason: row.reason,
|
|
597
|
+
risk: row.legalRisk,
|
|
598
|
+
story: row.story ?? "",
|
|
599
|
+
error: row.error ?? "",
|
|
600
|
+
raw: options.raw ? snippet(row.raw) : "",
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
function truncateCell(value, maxWidth) {
|
|
605
|
+
const text = String(value ?? "");
|
|
606
|
+
if (text.length <= maxWidth) {
|
|
607
|
+
return text;
|
|
608
|
+
}
|
|
609
|
+
if (maxWidth <= 1) {
|
|
610
|
+
return text.slice(0, maxWidth);
|
|
611
|
+
}
|
|
612
|
+
return `${text.slice(0, maxWidth - 1)}…`;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function printTable(rows, options) {
|
|
616
|
+
const displayRows = rows.map((row) => toDisplayRow(row, options));
|
|
617
|
+
const hasStory = displayRows.some((row) => row.story);
|
|
618
|
+
const hasError = displayRows.some((row) => row.error);
|
|
619
|
+
const columns = [
|
|
620
|
+
{ key: "verdict", label: "Verdict", maxWidth: 10 },
|
|
621
|
+
{ key: "domain", label: "Domain", maxWidth: 44 },
|
|
622
|
+
{ key: "whois", label: "Whois", maxWidth: 18 },
|
|
623
|
+
{ key: "reason", label: "Reason", maxWidth: 34 },
|
|
624
|
+
{ key: "risk", label: "Risk", maxWidth: 14 },
|
|
625
|
+
...(hasStory ? [{ key: "story", label: "Story", maxWidth: 36 }] : []),
|
|
626
|
+
...(hasError ? [{ key: "error", label: "Error", maxWidth: 44 }] : []),
|
|
627
|
+
...(options.raw ? [{ key: "raw", label: "Raw", maxWidth: 44 }] : []),
|
|
628
|
+
];
|
|
629
|
+
|
|
630
|
+
const widths = new Map();
|
|
631
|
+
for (const col of columns) {
|
|
632
|
+
const maxValueLen = displayRows.reduce((max, row) => Math.max(max, String(row[col.key] ?? "").length), 0);
|
|
633
|
+
widths.set(col.key, Math.min(Math.max(col.label.length, maxValueLen), col.maxWidth));
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
const formatCell = (value, width) => truncateCell(value, width).padEnd(width, " ");
|
|
637
|
+
const header = columns.map((col) => formatCell(col.label, widths.get(col.key))).join(" | ");
|
|
638
|
+
const separator = columns.map((col) => "-".repeat(widths.get(col.key))).join("-+-");
|
|
639
|
+
console.log(header);
|
|
640
|
+
console.log(separator);
|
|
641
|
+
for (const row of displayRows) {
|
|
642
|
+
const line = columns.map((col) => formatCell(row[col.key], widths.get(col.key))).join(" | ");
|
|
643
|
+
console.log(line);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
function printDecoratedResults(rows, options) {
|
|
648
|
+
const title = " DOMAINSTORM RESULTS ";
|
|
649
|
+
const border = "=".repeat(Math.max(68, title.length + 8));
|
|
650
|
+
console.log(border);
|
|
651
|
+
console.log(title);
|
|
652
|
+
console.log(border);
|
|
653
|
+
if (rows.length === 0) {
|
|
654
|
+
console.log("No domains matched your filters.");
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
printTable(rows, options);
|
|
658
|
+
if (rows.length === 1) {
|
|
659
|
+
const row = rows[0];
|
|
660
|
+
console.log("");
|
|
661
|
+
console.log(`Result: ${row.domain} is ${verdictForStatus(row.status)}.`);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function printNarrativeResults(rows, options) {
|
|
666
|
+
if (rows.length === 0) {
|
|
667
|
+
console.log("No domains matched your filters.");
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
for (const row of rows) {
|
|
672
|
+
console.log(sentenceForRow(row));
|
|
673
|
+
if (row.status === "unknown" || row.error) {
|
|
674
|
+
let detail = ` reason: ${row.reason}`;
|
|
675
|
+
if (row.error) {
|
|
676
|
+
detail += ` | error: ${row.error}`;
|
|
677
|
+
}
|
|
678
|
+
console.log(detail);
|
|
679
|
+
}
|
|
680
|
+
if (row.story) {
|
|
681
|
+
console.log(` story: ${row.story}`);
|
|
682
|
+
}
|
|
683
|
+
if (options.raw) {
|
|
684
|
+
console.log(` raw: ${snippet(row.raw)}`);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
561
689
|
async function main() {
|
|
562
690
|
const options = parseArgs(process.argv.slice(2));
|
|
563
691
|
let rawLabels = await loadLabels(options);
|
|
@@ -609,26 +737,30 @@ async function main() {
|
|
|
609
737
|
? checked.filter((row) => row.status === "likely_available")
|
|
610
738
|
: checked;
|
|
611
739
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
740
|
+
if (options.plain) {
|
|
741
|
+
for (const row of filtered) {
|
|
742
|
+
let line = `${row.domain}\t${row.status}\t${row.reason}\t${row.legalRisk}`;
|
|
743
|
+
if (row.story) {
|
|
744
|
+
line += `\tstory=${row.story}`;
|
|
745
|
+
}
|
|
746
|
+
if (row.error) {
|
|
747
|
+
line += `\terror=${row.error}`;
|
|
748
|
+
}
|
|
749
|
+
if (options.raw) {
|
|
750
|
+
line += `\t${snippet(row.raw)}`;
|
|
751
|
+
}
|
|
752
|
+
console.log(line);
|
|
622
753
|
}
|
|
623
|
-
|
|
754
|
+
} else if (options.table) {
|
|
755
|
+
printDecoratedResults(filtered, options);
|
|
756
|
+
} else {
|
|
757
|
+
printNarrativeResults(filtered, options);
|
|
624
758
|
}
|
|
625
759
|
|
|
626
760
|
await maybeWriteCsv(checked, options.output);
|
|
627
761
|
|
|
628
762
|
const counts = summarize(checked);
|
|
629
|
-
console.error(
|
|
630
|
-
`Summary: likely_available=${counts.likelyAvailable}, registered=${counts.registered}, unknown=${counts.unknown}`,
|
|
631
|
-
);
|
|
763
|
+
console.error(`Summary: ${counts.likelyAvailable} available, ${counts.registered} taken, ${counts.unknown} unknown.`);
|
|
632
764
|
if (options.output) {
|
|
633
765
|
console.error(`CSV written: ${path.resolve(options.output)}`);
|
|
634
766
|
}
|