sproutkit-code 0.17.0 → 0.19.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 +151 -6
- package/dist/skill/SKILL.md +6 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -93841,7 +93841,7 @@ var require_auto_mode = __commonJS({
|
|
|
93841
93841
|
exports2.startAutoJob = startAutoJob2;
|
|
93842
93842
|
exports2.pauseAutoJob = pauseAutoJob;
|
|
93843
93843
|
exports2.resumeAutoJob = resumeAutoJob;
|
|
93844
|
-
exports2.cancelAutoJob =
|
|
93844
|
+
exports2.cancelAutoJob = cancelAutoJob2;
|
|
93845
93845
|
exports2.getAutoJobStatus = getAutoJobStatus;
|
|
93846
93846
|
var db_1 = require_db();
|
|
93847
93847
|
var builder_1 = require_builder();
|
|
@@ -94045,7 +94045,7 @@ var require_auto_mode = __commonJS({
|
|
|
94045
94045
|
activeJob.resume();
|
|
94046
94046
|
return activeJob.snapshot();
|
|
94047
94047
|
}
|
|
94048
|
-
function
|
|
94048
|
+
function cancelAutoJob2() {
|
|
94049
94049
|
if (!activeJob)
|
|
94050
94050
|
return null;
|
|
94051
94051
|
activeJob.cancel();
|
|
@@ -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.19.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"
|
|
@@ -94652,6 +94652,7 @@ var getDb = db.getDb;
|
|
|
94652
94652
|
var chatWithTools = chat.chatWithTools;
|
|
94653
94653
|
var runTool = chatTools.runTool;
|
|
94654
94654
|
var startAutoJob = autoMode.startAutoJob;
|
|
94655
|
+
var cancelAutoJob = autoMode.cancelAutoJob;
|
|
94655
94656
|
var provisionCmsForLead = cms.provisionCmsForLead;
|
|
94656
94657
|
var generatePreset = bookingPreset.generatePreset;
|
|
94657
94658
|
var deployBookingOnly = builder.deployBookingOnly;
|
|
@@ -94814,6 +94815,40 @@ function closeTty() {
|
|
|
94814
94815
|
rl?.close();
|
|
94815
94816
|
rl = null;
|
|
94816
94817
|
}
|
|
94818
|
+
function onStopRequested(onStop) {
|
|
94819
|
+
let stopped = false;
|
|
94820
|
+
const stdin = process.stdin;
|
|
94821
|
+
const wasRaw = !!stdin.isTTY && stdin.isRaw;
|
|
94822
|
+
const handler = (buf) => {
|
|
94823
|
+
const s = buf.toString();
|
|
94824
|
+
if (s === "q" || s === "Q" || s === "\x1B" || s === "") {
|
|
94825
|
+
if (!stopped) {
|
|
94826
|
+
stopped = true;
|
|
94827
|
+
onStop?.();
|
|
94828
|
+
}
|
|
94829
|
+
}
|
|
94830
|
+
};
|
|
94831
|
+
if (stdin.isTTY) {
|
|
94832
|
+
try {
|
|
94833
|
+
stdin.setRawMode(true);
|
|
94834
|
+
stdin.resume();
|
|
94835
|
+
stdin.on("data", handler);
|
|
94836
|
+
} catch {
|
|
94837
|
+
}
|
|
94838
|
+
}
|
|
94839
|
+
return {
|
|
94840
|
+
stopped: () => stopped,
|
|
94841
|
+
release: () => {
|
|
94842
|
+
if (!stdin.isTTY) return;
|
|
94843
|
+
try {
|
|
94844
|
+
stdin.removeListener("data", handler);
|
|
94845
|
+
stdin.setRawMode(wasRaw);
|
|
94846
|
+
stdin.pause();
|
|
94847
|
+
} catch {
|
|
94848
|
+
}
|
|
94849
|
+
}
|
|
94850
|
+
};
|
|
94851
|
+
}
|
|
94817
94852
|
function say(msg = "") {
|
|
94818
94853
|
process.stdout.write(msg + "\n");
|
|
94819
94854
|
}
|
|
@@ -95818,6 +95853,103 @@ async function cmdOutreach(argv) {
|
|
|
95818
95853
|
if (result.note) say(c.dim(" " + result.note));
|
|
95819
95854
|
say();
|
|
95820
95855
|
}
|
|
95856
|
+
async function cmdPipeline(argv) {
|
|
95857
|
+
const { flags, rest } = parseArgs(argv);
|
|
95858
|
+
const json = !!flags.json;
|
|
95859
|
+
const xArg = rest.find((r) => /^x\d+$/i.test(r));
|
|
95860
|
+
const concurrency = Math.min(
|
|
95861
|
+
Math.max(Number(flags.concurrency ?? (xArg ? xArg.slice(1) : 2)), 1),
|
|
95862
|
+
4
|
|
95863
|
+
);
|
|
95864
|
+
const withBooking = !!flags.book;
|
|
95865
|
+
const limit = Math.min(Math.max(Number(flags.limit ?? 10), 1), 50);
|
|
95866
|
+
const explicitIds = String(flags.ids ?? "").split(",").map((s) => Number(s.trim())).filter((n) => !Number.isNaN(n) && n > 0);
|
|
95867
|
+
const all = await runTool("list_leads", { limit: 100 }, makeCtx({ json }));
|
|
95868
|
+
const targets = (explicitIds.length ? all.filter((l) => explicitIds.includes(l.id)) : all.filter((l) => !l.site_url)).slice(0, limit);
|
|
95869
|
+
if (targets.length === 0) {
|
|
95870
|
+
const msg = "No leads to run. Find some first, or pass --ids.";
|
|
95871
|
+
json ? emitJson({ ok: false, error: msg }) : say(c.amber(" " + msg));
|
|
95872
|
+
return;
|
|
95873
|
+
}
|
|
95874
|
+
const board = json ? null : new StatusBoard();
|
|
95875
|
+
const rowFor = (t, state, detail) => ({ key: t.id, label: `#${t.id} ${t.name}`, state, detail });
|
|
95876
|
+
if (board) {
|
|
95877
|
+
board.start(
|
|
95878
|
+
`Pipeline for ${targets.length} client${targets.length === 1 ? "" : "s"} (${concurrency} at a time)`,
|
|
95879
|
+
targets.map((t) => rowFor(t, "queued"))
|
|
95880
|
+
);
|
|
95881
|
+
say(c.dim(" Press q to stop after the current clients finish."));
|
|
95882
|
+
}
|
|
95883
|
+
const stop = json ? null : onStopRequested(() => {
|
|
95884
|
+
say(c.amber("\n Stopping \u2014 finishing current clients, not starting new ones\u2026"));
|
|
95885
|
+
});
|
|
95886
|
+
let cancelled = 0;
|
|
95887
|
+
const results = [];
|
|
95888
|
+
const queue = [...targets];
|
|
95889
|
+
async function runOne(t) {
|
|
95890
|
+
const setRow = (state, detail) => board?.update(rowFor(t, state, detail));
|
|
95891
|
+
setRow("building", "building site\u2026");
|
|
95892
|
+
try {
|
|
95893
|
+
const built = await runTool(
|
|
95894
|
+
"build_site",
|
|
95895
|
+
{ id: t.id, skipQa: !!flags["skip-qa"], multipage: !!flags.multipage },
|
|
95896
|
+
makeCtx({ json: true })
|
|
95897
|
+
// suppress per-tool spinners; the board is the UI
|
|
95898
|
+
);
|
|
95899
|
+
if (built && built.ok === false) {
|
|
95900
|
+
setRow("failed", built.error ?? "build failed");
|
|
95901
|
+
results.push({ id: t.id, name: t.name, ok: false, error: built.error });
|
|
95902
|
+
return;
|
|
95903
|
+
}
|
|
95904
|
+
if (withBooking) {
|
|
95905
|
+
setRow("building", "adding booking\u2026");
|
|
95906
|
+
try {
|
|
95907
|
+
await runTool("add_booking_site", { id: t.id }, makeCtx({ json: true }));
|
|
95908
|
+
} catch (e) {
|
|
95909
|
+
setRow("building", `booking skipped (${e.message.slice(0, 40)})`);
|
|
95910
|
+
}
|
|
95911
|
+
}
|
|
95912
|
+
setRow("building", "preparing outreach\u2026");
|
|
95913
|
+
await runTool("prepare_outreach", { ids: [t.id] }, makeCtx({ json: true }));
|
|
95914
|
+
setRow("built", built.url ?? "done");
|
|
95915
|
+
results.push({ id: t.id, name: t.name, ok: true, url: built.url });
|
|
95916
|
+
} catch (e) {
|
|
95917
|
+
setRow("failed", e.message.slice(0, 60));
|
|
95918
|
+
results.push({ id: t.id, name: t.name, ok: false, error: e.message });
|
|
95919
|
+
}
|
|
95920
|
+
}
|
|
95921
|
+
const workers = Array.from({ length: Math.min(concurrency, queue.length) }, async () => {
|
|
95922
|
+
for (; ; ) {
|
|
95923
|
+
if (stop?.stopped()) {
|
|
95924
|
+
for (const t of queue.splice(0)) {
|
|
95925
|
+
board?.update(rowFor(t, "skipped", "stopped"));
|
|
95926
|
+
cancelled++;
|
|
95927
|
+
}
|
|
95928
|
+
return;
|
|
95929
|
+
}
|
|
95930
|
+
const next = queue.shift();
|
|
95931
|
+
if (!next) return;
|
|
95932
|
+
await runOne(next);
|
|
95933
|
+
}
|
|
95934
|
+
});
|
|
95935
|
+
await Promise.all(workers);
|
|
95936
|
+
stop?.release();
|
|
95937
|
+
board?.stop();
|
|
95938
|
+
autoSync();
|
|
95939
|
+
const ok = results.filter((r) => r.ok).length;
|
|
95940
|
+
const failed = results.length - ok;
|
|
95941
|
+
if (json) {
|
|
95942
|
+
emitJson({ ok: failed === 0, ran: results.length, succeeded: ok, failed, concurrency, results });
|
|
95943
|
+
if (failed > 0) process.exitCode = 1;
|
|
95944
|
+
return;
|
|
95945
|
+
}
|
|
95946
|
+
say();
|
|
95947
|
+
say(
|
|
95948
|
+
c.green(` Pipeline ${cancelled ? "stopped" : "done"} \u2014 ${ok} client${ok === 1 ? "" : "s"} built + outreach prepared`) + (failed ? c.red(`, ${failed} failed`) : "") + (cancelled ? c.amber(`, ${cancelled} not started`) : "") + c.dim(` (concurrency ${concurrency}).`)
|
|
95949
|
+
);
|
|
95950
|
+
say(c.dim(" Open the message links above, or check emailed leads in your Contacted column."));
|
|
95951
|
+
say();
|
|
95952
|
+
}
|
|
95821
95953
|
async function cmdRegenerate(argv) {
|
|
95822
95954
|
const { flags, rest } = parseArgs(argv);
|
|
95823
95955
|
const json = !!flags.json;
|
|
@@ -95949,6 +96081,10 @@ async function cmdBuildAll(argv) {
|
|
|
95949
96081
|
}
|
|
95950
96082
|
const concurrency = Math.min(Math.max(Number(flags.concurrency ?? 3), 1), 4);
|
|
95951
96083
|
const board = json ? null : new StatusBoard();
|
|
96084
|
+
const stop = json ? null : onStopRequested(() => {
|
|
96085
|
+
say(c.amber("\n Stopping \u2014 finishing in-flight builds, not starting new ones\u2026"));
|
|
96086
|
+
cancelAutoJob();
|
|
96087
|
+
});
|
|
95952
96088
|
const stateToRow = (l) => ({
|
|
95953
96089
|
key: l.leadId,
|
|
95954
96090
|
label: `#${l.leadId} ${l.name}`,
|
|
@@ -95964,6 +96100,7 @@ async function cmdBuildAll(argv) {
|
|
|
95964
96100
|
board.set(snap2.leads.map(stateToRow));
|
|
95965
96101
|
board.stop();
|
|
95966
96102
|
}
|
|
96103
|
+
stop?.release();
|
|
95967
96104
|
if (json) emitJson(snap2);
|
|
95968
96105
|
else {
|
|
95969
96106
|
say();
|
|
@@ -95984,7 +96121,10 @@ async function cmdBuildAll(argv) {
|
|
|
95984
96121
|
placesKey: getSetting("places_key"),
|
|
95985
96122
|
emit
|
|
95986
96123
|
});
|
|
95987
|
-
if (board)
|
|
96124
|
+
if (board) {
|
|
96125
|
+
board.start(`Building ${leadIds.length} sites (up to ${concurrency} at once)`, snap.leads.map(stateToRow));
|
|
96126
|
+
say(c.dim(" Press q to stop after the current builds finish."));
|
|
96127
|
+
}
|
|
95988
96128
|
});
|
|
95989
96129
|
}
|
|
95990
96130
|
|
|
@@ -96023,6 +96163,7 @@ COMMANDS:
|
|
|
96023
96163
|
- sproutkit cms <id> --json \u2014 add the editable CMS on top of the site we built them
|
|
96024
96164
|
- 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
96165
|
- sproutkit outreach --json [--ids 3,5] \u2014 prepare outreach links
|
|
96166
|
+
- sproutkit pipeline x3 --json [--book] [--limit 10] \u2014 full pipeline (build\u2192booking\u2192outreach) for many clients concurrently
|
|
96026
96167
|
|
|
96027
96168
|
If no command fits the request, say so in one line and name the closest thing you can do. Never invent results.`;
|
|
96028
96169
|
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 +96432,7 @@ async function main() {
|
|
|
96291
96432
|
closeTty();
|
|
96292
96433
|
return;
|
|
96293
96434
|
}
|
|
96294
|
-
if (arg === "find" || arg === "list" || arg === "build" || arg === "build-all" || arg === "regenerate" || arg === "cms" || arg === "book" || arg === "outreach") {
|
|
96435
|
+
if (arg === "find" || arg === "list" || arg === "build" || arg === "build-all" || arg === "regenerate" || arg === "cms" || arg === "book" || arg === "outreach" || arg === "pipeline") {
|
|
96295
96436
|
const subArgs = process.argv.slice(3);
|
|
96296
96437
|
const lic2 = await check();
|
|
96297
96438
|
if (lic2.status !== "active") {
|
|
@@ -96309,6 +96450,7 @@ async function main() {
|
|
|
96309
96450
|
else if (arg === "regenerate") await cmdRegenerate(subArgs);
|
|
96310
96451
|
else if (arg === "cms") await cmdCms(subArgs);
|
|
96311
96452
|
else if (arg === "book") await cmdBook(subArgs);
|
|
96453
|
+
else if (arg === "pipeline") await cmdPipeline(subArgs);
|
|
96312
96454
|
else await cmdOutreach(subArgs);
|
|
96313
96455
|
closeTty();
|
|
96314
96456
|
return;
|
|
@@ -96327,6 +96469,7 @@ async function main() {
|
|
|
96327
96469
|
say(` ${c.bold("sproutkit cms <id>")} add the editable CMS to a built site`);
|
|
96328
96470
|
say(` ${c.bold("sproutkit book <id>")} booking site (--standalone for own-URL)`);
|
|
96329
96471
|
say(` ${c.bold("sproutkit outreach")} prep outreach links (--ids 3,5)`);
|
|
96472
|
+
say(` ${c.bold("sproutkit pipeline x3")} build+outreach many clients concurrently`);
|
|
96330
96473
|
say(` ${c.bold("sproutkit setup")} set or update keys, licence, backend`);
|
|
96331
96474
|
say(` ${c.bold("sproutkit doctor")} check everything is wired up`);
|
|
96332
96475
|
say(` ${c.bold("sproutkit models")} list Claude models and pick one`);
|
|
@@ -96399,7 +96542,7 @@ async function main() {
|
|
|
96399
96542
|
}
|
|
96400
96543
|
const direct = line.replace(/^\//, "");
|
|
96401
96544
|
const verb = direct.split(/\s+/)[0]?.toLowerCase();
|
|
96402
|
-
const DIRECT_VERBS = ["find", "list", "build", "build-all", "regenerate", "cms", "book", "outreach", "doctor"];
|
|
96545
|
+
const DIRECT_VERBS = ["find", "list", "build", "build-all", "regenerate", "cms", "book", "outreach", "pipeline", "doctor"];
|
|
96403
96546
|
if (verb === "commands" || verb === "help" || line === "/help" || line === "/?") {
|
|
96404
96547
|
say();
|
|
96405
96548
|
say(c.bold(" Commands (type with or without a leading /):"));
|
|
@@ -96412,6 +96555,7 @@ async function main() {
|
|
|
96412
96555
|
say(` ${c.greenBold("book")} <id> [--standalone] [--archetype slots|reservation|enquiry] [--email \u2026]`);
|
|
96413
96556
|
say(` ${c.dim(" ")} booking on the site, or --standalone for its own URL`);
|
|
96414
96557
|
say(` ${c.greenBold("outreach")} [--ids 3,5] prepare outreach links`);
|
|
96558
|
+
say(` ${c.greenBold("pipeline")} x3 [--book] [--limit 10] build + outreach many, concurrently`);
|
|
96415
96559
|
say(c.dim(" doctor \u2014 check your setup \xB7 /freemode \u2014 open chat \xB7 /taskmode \u2014 precise \xB7 exit"));
|
|
96416
96560
|
say(c.dim(" Or just talk: \u201Cfind cafes in Leeds and build the best two\u201D."));
|
|
96417
96561
|
say();
|
|
@@ -96429,6 +96573,7 @@ async function main() {
|
|
|
96429
96573
|
else if (verb === "cms") await cmdCms(argv);
|
|
96430
96574
|
else if (verb === "book") await cmdBook(argv);
|
|
96431
96575
|
else if (verb === "outreach") await cmdOutreach(argv);
|
|
96576
|
+
else if (verb === "pipeline") await cmdPipeline(argv);
|
|
96432
96577
|
else if (verb === "doctor") await runDoctor();
|
|
96433
96578
|
} catch (e) {
|
|
96434
96579
|
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),
|