sproutkit-code 0.17.0 → 0.18.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 +92 -3
- package/dist/skill/SKILL.md +6 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -94597,7 +94597,7 @@ var require_package2 = __commonJS({
|
|
|
94597
94597
|
"package.json"(exports2, module2) {
|
|
94598
94598
|
module2.exports = {
|
|
94599
94599
|
name: "sproutkit-code",
|
|
94600
|
-
version: "0.
|
|
94600
|
+
version: "0.18.0",
|
|
94601
94601
|
description: "Sproutkit in your terminal \u2014 find leads, build sites, run outreach, all via chat.",
|
|
94602
94602
|
bin: {
|
|
94603
94603
|
sproutkit: "./dist/index.js"
|
|
@@ -95818,6 +95818,90 @@ async function cmdOutreach(argv) {
|
|
|
95818
95818
|
if (result.note) say(c.dim(" " + result.note));
|
|
95819
95819
|
say();
|
|
95820
95820
|
}
|
|
95821
|
+
async function cmdPipeline(argv) {
|
|
95822
|
+
const { flags, rest } = parseArgs(argv);
|
|
95823
|
+
const json = !!flags.json;
|
|
95824
|
+
const xArg = rest.find((r) => /^x\d+$/i.test(r));
|
|
95825
|
+
const concurrency = Math.min(
|
|
95826
|
+
Math.max(Number(flags.concurrency ?? (xArg ? xArg.slice(1) : 2)), 1),
|
|
95827
|
+
4
|
|
95828
|
+
);
|
|
95829
|
+
const withBooking = !!flags.book;
|
|
95830
|
+
const limit = Math.min(Math.max(Number(flags.limit ?? 10), 1), 50);
|
|
95831
|
+
const explicitIds = String(flags.ids ?? "").split(",").map((s) => Number(s.trim())).filter((n) => !Number.isNaN(n) && n > 0);
|
|
95832
|
+
const all = await runTool("list_leads", { limit: 100 }, makeCtx({ json }));
|
|
95833
|
+
const targets = (explicitIds.length ? all.filter((l) => explicitIds.includes(l.id)) : all.filter((l) => !l.site_url)).slice(0, limit);
|
|
95834
|
+
if (targets.length === 0) {
|
|
95835
|
+
const msg = "No leads to run. Find some first, or pass --ids.";
|
|
95836
|
+
json ? emitJson({ ok: false, error: msg }) : say(c.amber(" " + msg));
|
|
95837
|
+
return;
|
|
95838
|
+
}
|
|
95839
|
+
const board = json ? null : new StatusBoard();
|
|
95840
|
+
const rowFor = (t, state, detail) => ({ key: t.id, label: `#${t.id} ${t.name}`, state, detail });
|
|
95841
|
+
if (board) {
|
|
95842
|
+
board.start(
|
|
95843
|
+
`Pipeline for ${targets.length} client${targets.length === 1 ? "" : "s"} (${concurrency} at a time)`,
|
|
95844
|
+
targets.map((t) => rowFor(t, "queued"))
|
|
95845
|
+
);
|
|
95846
|
+
}
|
|
95847
|
+
const results = [];
|
|
95848
|
+
const queue = [...targets];
|
|
95849
|
+
async function runOne(t) {
|
|
95850
|
+
const setRow = (state, detail) => board?.update(rowFor(t, state, detail));
|
|
95851
|
+
setRow("building", "building site\u2026");
|
|
95852
|
+
try {
|
|
95853
|
+
const built = await runTool(
|
|
95854
|
+
"build_site",
|
|
95855
|
+
{ id: t.id, skipQa: !!flags["skip-qa"], multipage: !!flags.multipage },
|
|
95856
|
+
makeCtx({ json: true })
|
|
95857
|
+
// suppress per-tool spinners; the board is the UI
|
|
95858
|
+
);
|
|
95859
|
+
if (built && built.ok === false) {
|
|
95860
|
+
setRow("failed", built.error ?? "build failed");
|
|
95861
|
+
results.push({ id: t.id, name: t.name, ok: false, error: built.error });
|
|
95862
|
+
return;
|
|
95863
|
+
}
|
|
95864
|
+
if (withBooking) {
|
|
95865
|
+
setRow("building", "adding booking\u2026");
|
|
95866
|
+
try {
|
|
95867
|
+
await runTool("add_booking_site", { id: t.id }, makeCtx({ json: true }));
|
|
95868
|
+
} catch (e) {
|
|
95869
|
+
setRow("building", `booking skipped (${e.message.slice(0, 40)})`);
|
|
95870
|
+
}
|
|
95871
|
+
}
|
|
95872
|
+
setRow("building", "preparing outreach\u2026");
|
|
95873
|
+
await runTool("prepare_outreach", { ids: [t.id] }, makeCtx({ json: true }));
|
|
95874
|
+
setRow("built", built.url ?? "done");
|
|
95875
|
+
results.push({ id: t.id, name: t.name, ok: true, url: built.url });
|
|
95876
|
+
} catch (e) {
|
|
95877
|
+
setRow("failed", e.message.slice(0, 60));
|
|
95878
|
+
results.push({ id: t.id, name: t.name, ok: false, error: e.message });
|
|
95879
|
+
}
|
|
95880
|
+
}
|
|
95881
|
+
const workers = Array.from({ length: Math.min(concurrency, queue.length) }, async () => {
|
|
95882
|
+
for (; ; ) {
|
|
95883
|
+
const next = queue.shift();
|
|
95884
|
+
if (!next) return;
|
|
95885
|
+
await runOne(next);
|
|
95886
|
+
}
|
|
95887
|
+
});
|
|
95888
|
+
await Promise.all(workers);
|
|
95889
|
+
board?.stop();
|
|
95890
|
+
autoSync();
|
|
95891
|
+
const ok = results.filter((r) => r.ok).length;
|
|
95892
|
+
const failed = results.length - ok;
|
|
95893
|
+
if (json) {
|
|
95894
|
+
emitJson({ ok: failed === 0, ran: results.length, succeeded: ok, failed, concurrency, results });
|
|
95895
|
+
if (failed > 0) process.exitCode = 1;
|
|
95896
|
+
return;
|
|
95897
|
+
}
|
|
95898
|
+
say();
|
|
95899
|
+
say(
|
|
95900
|
+
c.green(` Pipeline done \u2014 ${ok} client${ok === 1 ? "" : "s"} built + outreach prepared`) + (failed ? c.red(`, ${failed} failed`) : "") + c.dim(` (concurrency ${concurrency}).`)
|
|
95901
|
+
);
|
|
95902
|
+
say(c.dim(" Open the message links above, or check emailed leads in your Contacted column."));
|
|
95903
|
+
say();
|
|
95904
|
+
}
|
|
95821
95905
|
async function cmdRegenerate(argv) {
|
|
95822
95906
|
const { flags, rest } = parseArgs(argv);
|
|
95823
95907
|
const json = !!flags.json;
|
|
@@ -96023,6 +96107,7 @@ COMMANDS:
|
|
|
96023
96107
|
- sproutkit cms <id> --json \u2014 add the editable CMS on top of the site we built them
|
|
96024
96108
|
- sproutkit book <id> --json [--standalone] [--instructions "..."] [--archetype slots|reservation|enquiry] [--email x@y.com] \u2014 booking site (--standalone = its own URL for clients who already have a website)
|
|
96025
96109
|
- sproutkit outreach --json [--ids 3,5] \u2014 prepare outreach links
|
|
96110
|
+
- sproutkit pipeline x3 --json [--book] [--limit 10] \u2014 full pipeline (build\u2192booking\u2192outreach) for many clients concurrently
|
|
96026
96111
|
|
|
96027
96112
|
If no command fits the request, say so in one line and name the closest thing you can do. Never invent results.`;
|
|
96028
96113
|
var FREE_SYSTEM_PROMPT = `You are Sproutkit's assistant, talking with the user about their lead-generation and website work. You can run the sproutkit CLI (find, list, build, build-all, book, outreach \u2014 all support --json) via Bash, and you can help with small custom changes and questions. Be conversational and helpful, but stay within Sproutkit's domain (leads, demo sites, booking, outreach). You don't need to collapse everything to a single command here \u2014 it's fine to discuss, then act.`;
|
|
@@ -96291,7 +96376,7 @@ async function main() {
|
|
|
96291
96376
|
closeTty();
|
|
96292
96377
|
return;
|
|
96293
96378
|
}
|
|
96294
|
-
if (arg === "find" || arg === "list" || arg === "build" || arg === "build-all" || arg === "regenerate" || arg === "cms" || arg === "book" || arg === "outreach") {
|
|
96379
|
+
if (arg === "find" || arg === "list" || arg === "build" || arg === "build-all" || arg === "regenerate" || arg === "cms" || arg === "book" || arg === "outreach" || arg === "pipeline") {
|
|
96295
96380
|
const subArgs = process.argv.slice(3);
|
|
96296
96381
|
const lic2 = await check();
|
|
96297
96382
|
if (lic2.status !== "active") {
|
|
@@ -96309,6 +96394,7 @@ async function main() {
|
|
|
96309
96394
|
else if (arg === "regenerate") await cmdRegenerate(subArgs);
|
|
96310
96395
|
else if (arg === "cms") await cmdCms(subArgs);
|
|
96311
96396
|
else if (arg === "book") await cmdBook(subArgs);
|
|
96397
|
+
else if (arg === "pipeline") await cmdPipeline(subArgs);
|
|
96312
96398
|
else await cmdOutreach(subArgs);
|
|
96313
96399
|
closeTty();
|
|
96314
96400
|
return;
|
|
@@ -96327,6 +96413,7 @@ async function main() {
|
|
|
96327
96413
|
say(` ${c.bold("sproutkit cms <id>")} add the editable CMS to a built site`);
|
|
96328
96414
|
say(` ${c.bold("sproutkit book <id>")} booking site (--standalone for own-URL)`);
|
|
96329
96415
|
say(` ${c.bold("sproutkit outreach")} prep outreach links (--ids 3,5)`);
|
|
96416
|
+
say(` ${c.bold("sproutkit pipeline x3")} build+outreach many clients concurrently`);
|
|
96330
96417
|
say(` ${c.bold("sproutkit setup")} set or update keys, licence, backend`);
|
|
96331
96418
|
say(` ${c.bold("sproutkit doctor")} check everything is wired up`);
|
|
96332
96419
|
say(` ${c.bold("sproutkit models")} list Claude models and pick one`);
|
|
@@ -96399,7 +96486,7 @@ async function main() {
|
|
|
96399
96486
|
}
|
|
96400
96487
|
const direct = line.replace(/^\//, "");
|
|
96401
96488
|
const verb = direct.split(/\s+/)[0]?.toLowerCase();
|
|
96402
|
-
const DIRECT_VERBS = ["find", "list", "build", "build-all", "regenerate", "cms", "book", "outreach", "doctor"];
|
|
96489
|
+
const DIRECT_VERBS = ["find", "list", "build", "build-all", "regenerate", "cms", "book", "outreach", "pipeline", "doctor"];
|
|
96403
96490
|
if (verb === "commands" || verb === "help" || line === "/help" || line === "/?") {
|
|
96404
96491
|
say();
|
|
96405
96492
|
say(c.bold(" Commands (type with or without a leading /):"));
|
|
@@ -96412,6 +96499,7 @@ async function main() {
|
|
|
96412
96499
|
say(` ${c.greenBold("book")} <id> [--standalone] [--archetype slots|reservation|enquiry] [--email \u2026]`);
|
|
96413
96500
|
say(` ${c.dim(" ")} booking on the site, or --standalone for its own URL`);
|
|
96414
96501
|
say(` ${c.greenBold("outreach")} [--ids 3,5] prepare outreach links`);
|
|
96502
|
+
say(` ${c.greenBold("pipeline")} x3 [--book] [--limit 10] build + outreach many, concurrently`);
|
|
96415
96503
|
say(c.dim(" doctor \u2014 check your setup \xB7 /freemode \u2014 open chat \xB7 /taskmode \u2014 precise \xB7 exit"));
|
|
96416
96504
|
say(c.dim(" Or just talk: \u201Cfind cafes in Leeds and build the best two\u201D."));
|
|
96417
96505
|
say();
|
|
@@ -96429,6 +96517,7 @@ async function main() {
|
|
|
96429
96517
|
else if (verb === "cms") await cmdCms(argv);
|
|
96430
96518
|
else if (verb === "book") await cmdBook(argv);
|
|
96431
96519
|
else if (verb === "outreach") await cmdOutreach(argv);
|
|
96520
|
+
else if (verb === "pipeline") await cmdPipeline(argv);
|
|
96432
96521
|
else if (verb === "doctor") await runDoctor();
|
|
96433
96522
|
} catch (e) {
|
|
96434
96523
|
say(c.red(` Error: ${e.message}`));
|
package/dist/skill/SKILL.md
CHANGED
|
@@ -66,6 +66,12 @@ you can parse results reliably, then summarise them for the user in prose.
|
|
|
66
66
|
- Use `book --standalone` when the user says the client "already has a
|
|
67
67
|
website" or "just needs a booking page".
|
|
68
68
|
|
|
69
|
+
- **Run the full pipeline for many clients** — `sproutkit pipeline x3 --json` (optional `--book`, `--limit 10`, `--ids 3,5`)
|
|
70
|
+
- Builds → (optionally adds booking) → prepares outreach for several clients
|
|
71
|
+
CONCURRENTLY (capped at 4), refilling as each finishes. Use for "run the
|
|
72
|
+
pipeline", "do the whole thing for my leads", "build and message them all".
|
|
73
|
+
- Run `sproutkit find` FIRST — pipeline works through leads you already have.
|
|
74
|
+
|
|
69
75
|
- **Prepare outreach** — `sproutkit outreach --json` (optional `--ids 3,5,9`)
|
|
70
76
|
- Per lead: if it has a PHONE, returns a WhatsApp/SMS link for the user to
|
|
71
77
|
open (not auto-sent); if it has NO phone but an EMAIL (and SMTP is set up),
|