sproutkit-code 0.15.0 → 0.16.0
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/dist/index.js +237 -15
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -35390,7 +35390,7 @@ var require_cross_spawn = __commonJS({
|
|
|
35390
35390
|
enoent.hookChildProcess(spawned, parsed);
|
|
35391
35391
|
return spawned;
|
|
35392
35392
|
}
|
|
35393
|
-
function
|
|
35393
|
+
function spawnSync4(command, args, options) {
|
|
35394
35394
|
const parsed = parse(command, args, options);
|
|
35395
35395
|
const result = cp.spawnSync(parsed.command, parsed.args, parsed.options);
|
|
35396
35396
|
result.error = result.error || enoent.verifyENOENTSync(result.status, parsed);
|
|
@@ -35398,7 +35398,7 @@ var require_cross_spawn = __commonJS({
|
|
|
35398
35398
|
}
|
|
35399
35399
|
module2.exports = spawn2;
|
|
35400
35400
|
module2.exports.spawn = spawn2;
|
|
35401
|
-
module2.exports.sync =
|
|
35401
|
+
module2.exports.sync = spawnSync4;
|
|
35402
35402
|
module2.exports._parse = parse;
|
|
35403
35403
|
module2.exports._enoent = enoent;
|
|
35404
35404
|
}
|
|
@@ -92713,6 +92713,77 @@ var require_email = __commonJS({
|
|
|
92713
92713
|
}
|
|
92714
92714
|
});
|
|
92715
92715
|
|
|
92716
|
+
// ../desktop/dist/main/lib/email-harvest.js
|
|
92717
|
+
var require_email_harvest = __commonJS({
|
|
92718
|
+
"../desktop/dist/main/lib/email-harvest.js"(exports2) {
|
|
92719
|
+
"use strict";
|
|
92720
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
92721
|
+
exports2.harvestEmail = harvestEmail;
|
|
92722
|
+
var llm_1 = require_llm();
|
|
92723
|
+
var BAD_EMAIL_DOMAINS = [
|
|
92724
|
+
"yelp.com",
|
|
92725
|
+
"yell.com",
|
|
92726
|
+
"tripadvisor",
|
|
92727
|
+
"facebook.com",
|
|
92728
|
+
"instagram.com",
|
|
92729
|
+
"google.com",
|
|
92730
|
+
"justeat",
|
|
92731
|
+
"deliveroo",
|
|
92732
|
+
"ubereats",
|
|
92733
|
+
"treatwell",
|
|
92734
|
+
"fresha.com",
|
|
92735
|
+
"booking.com",
|
|
92736
|
+
"example.com",
|
|
92737
|
+
"sentry.io",
|
|
92738
|
+
"wixpress.com",
|
|
92739
|
+
"squarespace.com"
|
|
92740
|
+
];
|
|
92741
|
+
function plausibleEmail(raw) {
|
|
92742
|
+
if (typeof raw !== "string")
|
|
92743
|
+
return null;
|
|
92744
|
+
const email = raw.trim().toLowerCase();
|
|
92745
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(email))
|
|
92746
|
+
return null;
|
|
92747
|
+
const domain = email.split("@")[1] ?? "";
|
|
92748
|
+
if (BAD_EMAIL_DOMAINS.some((d) => domain.includes(d)))
|
|
92749
|
+
return null;
|
|
92750
|
+
return email;
|
|
92751
|
+
}
|
|
92752
|
+
async function harvestEmail(args) {
|
|
92753
|
+
const prompt = `Search the web for the contact email address of this specific local business.
|
|
92754
|
+
|
|
92755
|
+
Business: "${args.name}"
|
|
92756
|
+
Address: ${args.address}${args.city ? `, ${args.city}` : ""}
|
|
92757
|
+
|
|
92758
|
+
Rules \u2014 be strict:
|
|
92759
|
+
- The email must clearly belong to THIS business at THIS address/city. A business with the same or similar name in a different town or country is NOT a match \u2014 return null.
|
|
92760
|
+
- Prefer an email on the business's own website or its official social profile.
|
|
92761
|
+
- Do NOT return an email belonging to a directory, aggregator or booking platform (Yelp, Yell, TripAdvisor, JustEat, Deliveroo, Treatwell, Fresha, etc.).
|
|
92762
|
+
- Do NOT guess, construct, or infer an address (e.g. never invent info@<name>.com).
|
|
92763
|
+
- If you cannot confirm a real email for this exact business, return null.
|
|
92764
|
+
|
|
92765
|
+
Respond with ONLY compact JSON, no prose: {"email": string|null}`;
|
|
92766
|
+
try {
|
|
92767
|
+
const res = await (0, llm_1.generateText)({
|
|
92768
|
+
apiKey: args.apiKey,
|
|
92769
|
+
prompt,
|
|
92770
|
+
allowWebSearch: true,
|
|
92771
|
+
maxTokens: 512,
|
|
92772
|
+
timeoutMs: 9e4
|
|
92773
|
+
});
|
|
92774
|
+
const raw = res.text.replace(/^```(?:json)?\s*/i, "").replace(/\s*```\s*$/, "").trim();
|
|
92775
|
+
const match = raw.match(/\{[\s\S]*\}/);
|
|
92776
|
+
if (!match)
|
|
92777
|
+
return null;
|
|
92778
|
+
const parsed = JSON.parse(match[0]);
|
|
92779
|
+
return plausibleEmail(parsed.email);
|
|
92780
|
+
} catch {
|
|
92781
|
+
return null;
|
|
92782
|
+
}
|
|
92783
|
+
}
|
|
92784
|
+
}
|
|
92785
|
+
});
|
|
92786
|
+
|
|
92716
92787
|
// ../desktop/dist/main/lib/chat-tools.js
|
|
92717
92788
|
var require_chat_tools = __commonJS({
|
|
92718
92789
|
"../desktop/dist/main/lib/chat-tools.js"(exports2) {
|
|
@@ -92726,6 +92797,7 @@ var require_chat_tools = __commonJS({
|
|
|
92726
92797
|
var lead_search_1 = require_lead_search();
|
|
92727
92798
|
var places_1 = require_places();
|
|
92728
92799
|
var email_1 = require_email();
|
|
92800
|
+
var email_harvest_1 = require_email_harvest();
|
|
92729
92801
|
exports2.TOOLS = [
|
|
92730
92802
|
{
|
|
92731
92803
|
name: "find_leads",
|
|
@@ -93160,7 +93232,7 @@ Interested?`;
|
|
|
93160
93232
|
}
|
|
93161
93233
|
case "prepare_outreach": {
|
|
93162
93234
|
const explicit = input.ids;
|
|
93163
|
-
const rows = explicit && explicit.length > 0 ? db2.all(`SELECT id, name, phone, email, site_url FROM leads WHERE id IN (${explicit.map(() => "?").join(",")})`, explicit) : db2.all("SELECT id, name, phone, email, site_url FROM leads WHERE status = 'site_built' AND site_url IS NOT NULL AND site_url != '' ORDER BY score DESC NULLS LAST, id DESC", []);
|
|
93235
|
+
const rows = explicit && explicit.length > 0 ? db2.all(`SELECT id, name, phone, email, address, city, site_url FROM leads WHERE id IN (${explicit.map(() => "?").join(",")})`, explicit) : db2.all("SELECT id, name, phone, email, address, city, site_url FROM leads WHERE status = 'site_built' AND site_url IS NOT NULL AND site_url != '' ORDER BY score DESC NULLS LAST, id DESC", []);
|
|
93164
93236
|
const eligible = rows.filter((r) => r.site_url);
|
|
93165
93237
|
if (eligible.length === 0) {
|
|
93166
93238
|
return {
|
|
@@ -93174,9 +93246,27 @@ Interested?`;
|
|
|
93174
93246
|
const smtp = (0, email_1.smtpFromSettings)((k) => getDecryptedSetting(k));
|
|
93175
93247
|
const emailSubject = getDecryptedSetting("outreach_email_subject") || "A free demo website for {name}";
|
|
93176
93248
|
const prepared = [];
|
|
93249
|
+
let harvested = 0;
|
|
93177
93250
|
for (const r of eligible) {
|
|
93178
93251
|
const message = template.replace(/\{name\}/g, r.name).replace(/\{site_url\}/g, r.site_url ?? "");
|
|
93179
93252
|
const e164 = toE164(r.phone ?? "");
|
|
93253
|
+
if (!e164 && !r.email && smtp) {
|
|
93254
|
+
ctx.emitProgress?.({
|
|
93255
|
+
channel: "outreach:progress",
|
|
93256
|
+
payload: { msg: `Looking up an email for ${r.name}\u2026` }
|
|
93257
|
+
});
|
|
93258
|
+
const found = await (0, email_harvest_1.harvestEmail)({
|
|
93259
|
+
name: r.name,
|
|
93260
|
+
address: r.address ?? "",
|
|
93261
|
+
city: r.city ?? "",
|
|
93262
|
+
apiKey: getDecryptedSetting("anthropic_key")
|
|
93263
|
+
});
|
|
93264
|
+
if (found) {
|
|
93265
|
+
db2.run("UPDATE leads SET email = ? WHERE id = ?", [found, r.id]);
|
|
93266
|
+
r.email = found;
|
|
93267
|
+
harvested++;
|
|
93268
|
+
}
|
|
93269
|
+
}
|
|
93180
93270
|
if (e164) {
|
|
93181
93271
|
prepared.push({
|
|
93182
93272
|
id: r.id,
|
|
@@ -93227,6 +93317,7 @@ Interested?`;
|
|
|
93227
93317
|
phoneLinks: prepared.filter((p) => p.via === "phone").length,
|
|
93228
93318
|
emailsSent,
|
|
93229
93319
|
emailsFailed,
|
|
93320
|
+
emailsFound: harvested,
|
|
93230
93321
|
unreachable,
|
|
93231
93322
|
// Guide the user if email-only leads were skipped for want of SMTP.
|
|
93232
93323
|
note: smtpMissing ? "Some leads have only an email but SMTP isn't set up \u2014 run 'sproutkit setup' to add SMTP and email them." : void 0,
|
|
@@ -94328,7 +94419,7 @@ var require_package2 = __commonJS({
|
|
|
94328
94419
|
"package.json"(exports2, module2) {
|
|
94329
94420
|
module2.exports = {
|
|
94330
94421
|
name: "sproutkit-code",
|
|
94331
|
-
version: "0.
|
|
94422
|
+
version: "0.16.0",
|
|
94332
94423
|
description: "Sproutkit in your terminal \u2014 find leads, build sites, run outreach, all via chat.",
|
|
94333
94424
|
bin: {
|
|
94334
94425
|
sproutkit: "./dist/index.js"
|
|
@@ -94589,10 +94680,10 @@ var Status = class {
|
|
|
94589
94680
|
this.timer = null;
|
|
94590
94681
|
}
|
|
94591
94682
|
const label = finalLabel ?? this.label;
|
|
94592
|
-
const
|
|
94593
|
-
if (this.tty) process.stdout.write(`\r\x1B[2K ${
|
|
94683
|
+
const mark2 = ok ? c.green("\u2713") : c.red("\u2717");
|
|
94684
|
+
if (this.tty) process.stdout.write(`\r\x1B[2K ${mark2} ${label}
|
|
94594
94685
|
`);
|
|
94595
|
-
else say(` ${
|
|
94686
|
+
else say(` ${mark2} ${label}`);
|
|
94596
94687
|
}
|
|
94597
94688
|
};
|
|
94598
94689
|
var STATE_MARK = {
|
|
@@ -94641,16 +94732,16 @@ var StatusBoard = class {
|
|
|
94641
94732
|
else this.render();
|
|
94642
94733
|
}
|
|
94643
94734
|
line(r) {
|
|
94644
|
-
const
|
|
94735
|
+
const mark2 = r.state === "building" ? c.cyan(FRAMES[this.frame]) : STATE_MARK[r.state]();
|
|
94645
94736
|
const detail = r.detail ? c.dim(` \xB7 ${r.detail}`) : "";
|
|
94646
|
-
return ` ${
|
|
94737
|
+
return ` ${mark2} ${r.label}${detail}`;
|
|
94647
94738
|
}
|
|
94648
94739
|
logRowIfChanged(r) {
|
|
94649
94740
|
const sig = `${r.state}:${r.detail ?? ""}`;
|
|
94650
94741
|
if (this.lastSig.get(r.key) === sig) return;
|
|
94651
94742
|
this.lastSig.set(r.key, sig);
|
|
94652
|
-
const
|
|
94653
|
-
say(` ${
|
|
94743
|
+
const mark2 = r.state === "built" ? "\u2713" : r.state === "failed" ? "\u2717" : r.state === "skipped" ? "\u2013" : "\u2026";
|
|
94744
|
+
say(` ${mark2} ${r.label}${r.detail ? ` \xB7 ${r.detail}` : ""}`);
|
|
94654
94745
|
}
|
|
94655
94746
|
render() {
|
|
94656
94747
|
if (this.lastLineCount > 0) process.stdout.write(`\x1B[${this.lastLineCount}A`);
|
|
@@ -95540,6 +95631,7 @@ async function cmdOutreach(argv) {
|
|
|
95540
95631
|
say();
|
|
95541
95632
|
const parts = [];
|
|
95542
95633
|
if (result.phoneLinks) parts.push(`${result.phoneLinks} message link${result.phoneLinks === 1 ? "" : "s"} to open`);
|
|
95634
|
+
if (result.emailsFound) parts.push(c.dim(`${result.emailsFound} email${result.emailsFound === 1 ? "" : "s"} found`));
|
|
95543
95635
|
if (result.emailsSent) parts.push(`${result.emailsSent} emailed`);
|
|
95544
95636
|
if (result.emailsFailed) parts.push(c.red(`${result.emailsFailed} email failed`));
|
|
95545
95637
|
if (result.unreachable) parts.push(c.amber(`${result.unreachable} unreachable`));
|
|
@@ -95845,13 +95937,136 @@ function runClaudeTurn(args) {
|
|
|
95845
95937
|
});
|
|
95846
95938
|
}
|
|
95847
95939
|
|
|
95848
|
-
// src/
|
|
95940
|
+
// src/doctor.ts
|
|
95849
95941
|
var import_child_process4 = require("child_process");
|
|
95942
|
+
function mark(l) {
|
|
95943
|
+
return l === "ok" ? c.green("\u2713") : l === "warn" ? c.amber("!") : c.red("\u2717");
|
|
95944
|
+
}
|
|
95945
|
+
async function runDoctor() {
|
|
95946
|
+
const rows = [];
|
|
95947
|
+
const backend = getBackend();
|
|
95948
|
+
const lic = await check();
|
|
95949
|
+
rows.push(
|
|
95950
|
+
lic.status === "active" ? { level: "ok", label: "Licence", detail: "active" } : {
|
|
95951
|
+
level: "fail",
|
|
95952
|
+
label: "Licence",
|
|
95953
|
+
detail: lic.status,
|
|
95954
|
+
fix: "Run 'sproutkit setup' and enter your key, or buy one at https://sproutkit.app"
|
|
95955
|
+
}
|
|
95956
|
+
);
|
|
95957
|
+
if (backend === "claude_code") {
|
|
95958
|
+
const present = (() => {
|
|
95959
|
+
try {
|
|
95960
|
+
return (0, import_child_process4.spawnSync)(claudeBin(), ["--version"], { stdio: "ignore", timeout: 1e4 }).status === 0;
|
|
95961
|
+
} catch {
|
|
95962
|
+
return false;
|
|
95963
|
+
}
|
|
95964
|
+
})();
|
|
95965
|
+
if (!present) {
|
|
95966
|
+
rows.push({
|
|
95967
|
+
level: "fail",
|
|
95968
|
+
label: "Claude Code",
|
|
95969
|
+
detail: "CLI not found",
|
|
95970
|
+
fix: "Install Claude Code, run 'claude /login', then 'sproutkit setup'."
|
|
95971
|
+
});
|
|
95972
|
+
} else {
|
|
95973
|
+
const auth = await checkClaudeCodeAuth();
|
|
95974
|
+
rows.push(
|
|
95975
|
+
auth.ok ? {
|
|
95976
|
+
level: "ok",
|
|
95977
|
+
label: "Claude (subscription)",
|
|
95978
|
+
detail: `logged in \xB7 model: ${getClaudeCodeModel() ?? "default"}`
|
|
95979
|
+
} : {
|
|
95980
|
+
level: "fail",
|
|
95981
|
+
label: "Claude (subscription)",
|
|
95982
|
+
detail: auth.reason,
|
|
95983
|
+
fix: "Run 'claude /login' in another terminal, then re-run doctor."
|
|
95984
|
+
}
|
|
95985
|
+
);
|
|
95986
|
+
}
|
|
95987
|
+
} else {
|
|
95988
|
+
rows.push(
|
|
95989
|
+
getSetting("anthropic_key") ? { level: "ok", label: "Anthropic API key", detail: "set" } : {
|
|
95990
|
+
level: "fail",
|
|
95991
|
+
label: "Anthropic API key",
|
|
95992
|
+
detail: "missing",
|
|
95993
|
+
fix: "Run 'sproutkit setup' (or switch to the Claude subscription backend)."
|
|
95994
|
+
}
|
|
95995
|
+
);
|
|
95996
|
+
}
|
|
95997
|
+
rows.push(
|
|
95998
|
+
getSetting("places_key") ? { level: "ok", label: "Google Places key", detail: "set \u2014 lead search works" } : {
|
|
95999
|
+
level: "fail",
|
|
96000
|
+
label: "Google Places key",
|
|
96001
|
+
detail: "missing",
|
|
96002
|
+
fix: "Run 'sproutkit setup'. Without it, 'find' can't search for leads."
|
|
96003
|
+
}
|
|
96004
|
+
);
|
|
96005
|
+
rows.push(
|
|
96006
|
+
getSetting("vercel_token") ? { level: "ok", label: "Vercel token", detail: "set \u2014 deploys work" } : {
|
|
96007
|
+
level: "fail",
|
|
96008
|
+
label: "Vercel token",
|
|
96009
|
+
detail: "missing",
|
|
96010
|
+
fix: "Run 'sproutkit setup'. Without it, sites can't deploy."
|
|
96011
|
+
}
|
|
96012
|
+
);
|
|
96013
|
+
rows.push(
|
|
96014
|
+
getSetting("blob_token") ? { level: "ok", label: "Vercel Blob token", detail: "set \u2014 booking & CMS work" } : {
|
|
96015
|
+
level: "warn",
|
|
96016
|
+
label: "Vercel Blob token",
|
|
96017
|
+
detail: "missing \u2014 booking sites & CMS will fail",
|
|
96018
|
+
fix: "Create a Blob store at https://vercel.com/dashboard/stores, copy BLOB_READ_WRITE_TOKEN, then 'sproutkit setup'. One token covers all your sites."
|
|
96019
|
+
}
|
|
96020
|
+
);
|
|
96021
|
+
const smtpReady = !!(getSetting("smtp_host") && getSetting("smtp_user") && getSetting("smtp_pass"));
|
|
96022
|
+
rows.push(
|
|
96023
|
+
smtpReady ? { level: "ok", label: "SMTP (email outreach)", detail: `via ${getSetting("smtp_host")}` } : {
|
|
96024
|
+
level: "warn",
|
|
96025
|
+
label: "SMTP (email outreach)",
|
|
96026
|
+
detail: "not set \u2014 leads without a phone can't be emailed",
|
|
96027
|
+
fix: "Run 'sproutkit setup' and add SMTP host/user/password."
|
|
96028
|
+
}
|
|
96029
|
+
);
|
|
96030
|
+
rows.push(
|
|
96031
|
+
getSetting("resend_key") ? { level: "ok", label: "Resend key", detail: "set \u2014 booking emails notify the owner" } : {
|
|
96032
|
+
level: "warn",
|
|
96033
|
+
label: "Resend key",
|
|
96034
|
+
detail: "not set \u2014 bookings are stored but the owner isn't emailed",
|
|
96035
|
+
fix: "Optional. Add it in 'sproutkit setup' if you want booking notifications."
|
|
96036
|
+
}
|
|
96037
|
+
);
|
|
96038
|
+
rows.push(
|
|
96039
|
+
skillInstalled() ? { level: "ok", label: "Claude Code skill", detail: skillTargetDir() } : {
|
|
96040
|
+
level: "warn",
|
|
96041
|
+
label: "Claude Code skill",
|
|
96042
|
+
detail: "not installed",
|
|
96043
|
+
fix: "Run 'sproutkit setup' and say yes to installing the skill, so `claude` can drive Sproutkit."
|
|
96044
|
+
}
|
|
96045
|
+
);
|
|
96046
|
+
say();
|
|
96047
|
+
say(c.greenBold(" Sproutkit doctor"));
|
|
96048
|
+
say(c.dim(` Backend: ${backend === "claude_code" ? "Claude subscription" : "Anthropic API key"}`));
|
|
96049
|
+
say();
|
|
96050
|
+
for (const r of rows) {
|
|
96051
|
+
say(` ${mark(r.level)} ${c.bold(r.label)}${r.detail ? c.dim(` \u2014 ${r.detail}`) : ""}`);
|
|
96052
|
+
if (r.fix && r.level !== "ok") say(c.dim(` \u2192 ${r.fix}`));
|
|
96053
|
+
}
|
|
96054
|
+
say();
|
|
96055
|
+
const fails = rows.filter((r) => r.level === "fail").length;
|
|
96056
|
+
const warns = rows.filter((r) => r.level === "warn").length;
|
|
96057
|
+
if (fails === 0 && warns === 0) say(c.green(" All good \u2014 you're ready to find leads and build sites."));
|
|
96058
|
+
else if (fails === 0) say(c.amber(` Ready to build, with ${warns} optional thing${warns === 1 ? "" : "s"} unset (see above).`));
|
|
96059
|
+
else say(c.red(` ${fails} blocking issue${fails === 1 ? "" : "s"} \u2014 fix those first.`));
|
|
96060
|
+
say();
|
|
96061
|
+
}
|
|
96062
|
+
|
|
96063
|
+
// src/index.ts
|
|
96064
|
+
var import_child_process5 = require("child_process");
|
|
95850
96065
|
process.env.SPROUTKIT_HEADLESS = "1";
|
|
95851
96066
|
function claudePresent() {
|
|
95852
96067
|
try {
|
|
95853
96068
|
const bin = process.env.SPROUTKIT_CLAUDE_BIN || "claude";
|
|
95854
|
-
return (0,
|
|
96069
|
+
return (0, import_child_process5.spawnSync)(bin, ["--version"], { stdio: "ignore", timeout: 1e4 }).status === 0;
|
|
95855
96070
|
} catch {
|
|
95856
96071
|
return false;
|
|
95857
96072
|
}
|
|
@@ -95892,6 +96107,11 @@ async function main() {
|
|
|
95892
96107
|
closeTty();
|
|
95893
96108
|
return;
|
|
95894
96109
|
}
|
|
96110
|
+
if (arg === "doctor") {
|
|
96111
|
+
await runDoctor();
|
|
96112
|
+
closeTty();
|
|
96113
|
+
return;
|
|
96114
|
+
}
|
|
95895
96115
|
if (arg === "find" || arg === "list" || arg === "build" || arg === "build-all" || arg === "regenerate" || arg === "cms" || arg === "book" || arg === "outreach") {
|
|
95896
96116
|
const subArgs = process.argv.slice(3);
|
|
95897
96117
|
const lic2 = await check();
|
|
@@ -95929,6 +96149,7 @@ async function main() {
|
|
|
95929
96149
|
say(` ${c.bold("sproutkit book <id>")} booking site (--standalone for own-URL)`);
|
|
95930
96150
|
say(` ${c.bold("sproutkit outreach")} prep outreach links (--ids 3,5)`);
|
|
95931
96151
|
say(` ${c.bold("sproutkit setup")} set or update keys, licence, backend`);
|
|
96152
|
+
say(` ${c.bold("sproutkit doctor")} check everything is wired up`);
|
|
95932
96153
|
say(` ${c.bold("sproutkit models")} list Claude models and pick one`);
|
|
95933
96154
|
say(` ${c.bold("sproutkit sync")} push your leads into the desktop app`);
|
|
95934
96155
|
say(` ${c.bold("sproutkit update")} update to the latest version`);
|
|
@@ -95999,7 +96220,7 @@ async function main() {
|
|
|
95999
96220
|
}
|
|
96000
96221
|
const direct = line.replace(/^\//, "");
|
|
96001
96222
|
const verb = direct.split(/\s+/)[0]?.toLowerCase();
|
|
96002
|
-
const DIRECT_VERBS = ["find", "list", "build", "build-all", "regenerate", "cms", "book", "outreach"];
|
|
96223
|
+
const DIRECT_VERBS = ["find", "list", "build", "build-all", "regenerate", "cms", "book", "outreach", "doctor"];
|
|
96003
96224
|
if (verb === "commands" || verb === "help" || line === "/help" || line === "/?") {
|
|
96004
96225
|
say();
|
|
96005
96226
|
say(c.bold(" Commands (type with or without a leading /):"));
|
|
@@ -96012,7 +96233,7 @@ async function main() {
|
|
|
96012
96233
|
say(` ${c.greenBold("book")} <id> [--standalone] [--archetype slots|reservation|enquiry] [--email \u2026]`);
|
|
96013
96234
|
say(` ${c.dim(" ")} booking on the site, or --standalone for its own URL`);
|
|
96014
96235
|
say(` ${c.greenBold("outreach")} [--ids 3,5] prepare outreach links`);
|
|
96015
|
-
say(c.dim("
|
|
96236
|
+
say(c.dim(" doctor \u2014 check your setup \xB7 /freemode \u2014 open chat \xB7 /taskmode \u2014 precise \xB7 exit"));
|
|
96016
96237
|
say(c.dim(" Or just talk: \u201Cfind cafes in Leeds and build the best two\u201D."));
|
|
96017
96238
|
say();
|
|
96018
96239
|
continue;
|
|
@@ -96029,6 +96250,7 @@ async function main() {
|
|
|
96029
96250
|
else if (verb === "cms") await cmdCms(argv);
|
|
96030
96251
|
else if (verb === "book") await cmdBook(argv);
|
|
96031
96252
|
else if (verb === "outreach") await cmdOutreach(argv);
|
|
96253
|
+
else if (verb === "doctor") await runDoctor();
|
|
96032
96254
|
} catch (e) {
|
|
96033
96255
|
say(c.red(` Error: ${e.message}`));
|
|
96034
96256
|
}
|