sproutkit-code 0.11.0 → 0.13.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 +154 -28
- package/dist/skill/SKILL.md +7 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -80731,29 +80731,39 @@ var require_places = __commonJS({
|
|
|
80731
80731
|
}
|
|
80732
80732
|
var COST_PER_REQUEST_GBP = 0.025;
|
|
80733
80733
|
async function searchOneType(args) {
|
|
80734
|
-
const
|
|
80735
|
-
|
|
80736
|
-
|
|
80737
|
-
|
|
80738
|
-
|
|
80739
|
-
|
|
80740
|
-
circle: {
|
|
80741
|
-
center: { latitude: args.center.lat, longitude: args.center.lng },
|
|
80742
|
-
radius: args.radiusMeters
|
|
80743
|
-
}
|
|
80734
|
+
const all = [];
|
|
80735
|
+
let pageToken;
|
|
80736
|
+
for (let page = 0; page < 3; page++) {
|
|
80737
|
+
const body = {
|
|
80738
|
+
textQuery: `${args.type} in ${args.city}`,
|
|
80739
|
+
maxResultCount: 20
|
|
80744
80740
|
};
|
|
80741
|
+
if (args.center && args.radiusMeters) {
|
|
80742
|
+
body.locationBias = {
|
|
80743
|
+
circle: {
|
|
80744
|
+
center: { latitude: args.center.lat, longitude: args.center.lng },
|
|
80745
|
+
radius: args.radiusMeters
|
|
80746
|
+
}
|
|
80747
|
+
};
|
|
80748
|
+
}
|
|
80749
|
+
if (pageToken)
|
|
80750
|
+
body.pageToken = pageToken;
|
|
80751
|
+
try {
|
|
80752
|
+
const r = await axios_1.default.post("https://places.googleapis.com/v1/places:searchText", body, {
|
|
80753
|
+
headers: {
|
|
80754
|
+
"X-Goog-Api-Key": args.apiKey,
|
|
80755
|
+
"X-Goog-FieldMask": "nextPageToken,places.id,places.displayName,places.formattedAddress,places.nationalPhoneNumber,places.websiteUri,places.rating,places.userRatingCount,places.location,places.priceLevel,places.businessStatus,places.regularOpeningHours"
|
|
80756
|
+
}
|
|
80757
|
+
});
|
|
80758
|
+
all.push(...r.data.places ?? []);
|
|
80759
|
+
pageToken = r.data.nextPageToken;
|
|
80760
|
+
if (!pageToken)
|
|
80761
|
+
break;
|
|
80762
|
+
} catch {
|
|
80763
|
+
break;
|
|
80764
|
+
}
|
|
80745
80765
|
}
|
|
80746
|
-
|
|
80747
|
-
const r = await axios_1.default.post("https://places.googleapis.com/v1/places:searchText", body, {
|
|
80748
|
-
headers: {
|
|
80749
|
-
"X-Goog-Api-Key": args.apiKey,
|
|
80750
|
-
"X-Goog-FieldMask": "places.id,places.displayName,places.formattedAddress,places.nationalPhoneNumber,places.websiteUri,places.rating,places.userRatingCount,places.location,places.priceLevel,places.businessStatus,places.regularOpeningHours"
|
|
80751
|
-
}
|
|
80752
|
-
});
|
|
80753
|
-
return { type: args.type, places: r.data.places ?? [] };
|
|
80754
|
-
} catch {
|
|
80755
|
-
return { type: args.type, places: [] };
|
|
80756
|
-
}
|
|
80766
|
+
return { type: args.type, places: all };
|
|
80757
80767
|
}
|
|
80758
80768
|
function processRawPlaces(type, places, center, radiusMeters) {
|
|
80759
80769
|
const out = [];
|
|
@@ -81022,7 +81032,7 @@ var require_chat_tools = __commonJS({
|
|
|
81022
81032
|
},
|
|
81023
81033
|
radiusMeters: {
|
|
81024
81034
|
type: "number",
|
|
81025
|
-
description: "Search radius around the location in metres (default
|
|
81035
|
+
description: "Search radius around the location in metres (default 10000 = 10km). Use a larger radius for big cities where central businesses mostly have websites; smaller (e.g. 3000) for a tight local search."
|
|
81026
81036
|
}
|
|
81027
81037
|
},
|
|
81028
81038
|
required: ["location", "categories"]
|
|
@@ -81327,7 +81337,11 @@ Interested?`;
|
|
|
81327
81337
|
types: categories,
|
|
81328
81338
|
maxResults: Math.min(Number(input.maxResults ?? 25), 100),
|
|
81329
81339
|
center: place ? { lat: place.lat, lng: place.lng } : void 0,
|
|
81330
|
-
|
|
81340
|
+
// Default widened from 3km → 10km. A 3km circle in a website-
|
|
81341
|
+
// saturated city centre (e.g. central London) yields almost no
|
|
81342
|
+
// no-website leads; 10km covers a realistic catchment. Caller/model
|
|
81343
|
+
// can still override for a tighter or wider search.
|
|
81344
|
+
radiusMeters: Number(input.radiusMeters ?? 1e4)
|
|
81331
81345
|
},
|
|
81332
81346
|
// Emit each lead the instant it's saved, so the UI/CLI streams results
|
|
81333
81347
|
// in rather than waiting for every category to finish.
|
|
@@ -82551,7 +82565,7 @@ var require_package = __commonJS({
|
|
|
82551
82565
|
"package.json"(exports2, module2) {
|
|
82552
82566
|
module2.exports = {
|
|
82553
82567
|
name: "sproutkit-code",
|
|
82554
|
-
version: "0.
|
|
82568
|
+
version: "0.13.0",
|
|
82555
82569
|
description: "Sproutkit in your terminal \u2014 find leads, build sites, run outreach, all via chat.",
|
|
82556
82570
|
bin: {
|
|
82557
82571
|
sproutkit: "./dist/index.js"
|
|
@@ -83748,6 +83762,69 @@ async function cmdOutreach(argv) {
|
|
|
83748
83762
|
if (result.noPhone) say(c.dim(` ${result.noPhone} had no phone number and were skipped for messaging.`));
|
|
83749
83763
|
say();
|
|
83750
83764
|
}
|
|
83765
|
+
async function cmdRegenerate(argv) {
|
|
83766
|
+
const { flags, rest } = parseArgs(argv);
|
|
83767
|
+
const json = !!flags.json;
|
|
83768
|
+
const id = Number(flags.id ?? rest[0]);
|
|
83769
|
+
const instructions = flags.instructions ? String(flags.instructions) : "";
|
|
83770
|
+
if (!id || Number.isNaN(id)) {
|
|
83771
|
+
const err = 'Usage: sproutkit regenerate <leadId> --instructions "what to change"';
|
|
83772
|
+
json ? emitJson({ ok: false, error: err }) : say(c.red(" " + err));
|
|
83773
|
+
process.exitCode = 1;
|
|
83774
|
+
return;
|
|
83775
|
+
}
|
|
83776
|
+
if (!instructions.trim()) {
|
|
83777
|
+
const err = "regenerate needs --instructions describing what to change.";
|
|
83778
|
+
json ? emitJson({ ok: false, error: err }) : say(c.red(" " + err));
|
|
83779
|
+
process.exitCode = 1;
|
|
83780
|
+
return;
|
|
83781
|
+
}
|
|
83782
|
+
const status = json ? void 0 : new Status();
|
|
83783
|
+
if (status) status.start(`Rebuilding website for lead #${id}`);
|
|
83784
|
+
try {
|
|
83785
|
+
const result = await runTool(
|
|
83786
|
+
"regenerate_site",
|
|
83787
|
+
{ id, instructions },
|
|
83788
|
+
makeCtx({ json, status })
|
|
83789
|
+
);
|
|
83790
|
+
if (result && result.ok === false) {
|
|
83791
|
+
if (status) status.done(false, result.error ?? "Regenerate failed");
|
|
83792
|
+
if (json) emitJson(result);
|
|
83793
|
+
process.exitCode = 1;
|
|
83794
|
+
return;
|
|
83795
|
+
}
|
|
83796
|
+
if (status) status.done(true, result.url ? `Live at ${result.url}` : `Rebuilt lead #${id}`);
|
|
83797
|
+
autoSync();
|
|
83798
|
+
if (json) emitJson(result);
|
|
83799
|
+
} catch (e) {
|
|
83800
|
+
if (status) status.done(false, "Regenerate failed");
|
|
83801
|
+
json ? emitJson({ ok: false, error: e.message }) : say(c.red(" " + e.message));
|
|
83802
|
+
process.exitCode = 1;
|
|
83803
|
+
}
|
|
83804
|
+
}
|
|
83805
|
+
async function cmdCms(argv) {
|
|
83806
|
+
const { flags, rest } = parseArgs(argv);
|
|
83807
|
+
const json = !!flags.json;
|
|
83808
|
+
const id = Number(flags.id ?? rest[0]);
|
|
83809
|
+
if (!id || Number.isNaN(id)) {
|
|
83810
|
+
const err = "Usage: sproutkit cms <leadId>";
|
|
83811
|
+
json ? emitJson({ ok: false, error: err }) : say(c.red(" " + err));
|
|
83812
|
+
process.exitCode = 1;
|
|
83813
|
+
return;
|
|
83814
|
+
}
|
|
83815
|
+
const status = json ? void 0 : new Status();
|
|
83816
|
+
if (status) status.start(`Setting up CMS for lead #${id}`);
|
|
83817
|
+
try {
|
|
83818
|
+
const result = await runTool("provision_cms", { id }, makeCtx({ json, status }));
|
|
83819
|
+
if (status) status.done(true, result.publicUrl ? `CMS live at ${result.publicUrl}` : `CMS set up for #${id}`);
|
|
83820
|
+
autoSync();
|
|
83821
|
+
if (json) emitJson(result);
|
|
83822
|
+
} catch (e) {
|
|
83823
|
+
if (status) status.done(false, "CMS setup failed");
|
|
83824
|
+
json ? emitJson({ ok: false, error: e.message }) : say(c.red(" " + e.message));
|
|
83825
|
+
process.exitCode = 1;
|
|
83826
|
+
}
|
|
83827
|
+
}
|
|
83751
83828
|
async function cmdBook(argv) {
|
|
83752
83829
|
const { flags, rest } = parseArgs(argv);
|
|
83753
83830
|
const json = !!flags.json;
|
|
@@ -83862,8 +83939,14 @@ var TASK_SYSTEM_PROMPT = `You operate the "sproutkit" CLI on the user's behalf.
|
|
|
83862
83939
|
HOW TO ACT \u2014 this is strict:
|
|
83863
83940
|
- Fulfil each request by running the matching sproutkit command(s) via Bash. Nothing else.
|
|
83864
83941
|
- Do NOT read, list, or explore the repo. Do NOT narrate a plan, think out loud, or look for a skill. No preamble, no "let me\u2026". Either ask a crisp question (below) or run the command.
|
|
83865
|
-
- Always pass --json and parse it.
|
|
83866
|
-
|
|
83942
|
+
- Always pass --json and parse it. Get lead ids from \`sproutkit list --json\` \u2014 never guess an id.
|
|
83943
|
+
|
|
83944
|
+
OUTPUT FORMAT \u2014 terse, scannable, NO editorialising:
|
|
83945
|
+
- Never write rambling sentences or commentary about the results ("well short of 20, so the source is returning few matches" \u2014 DON'T). Just present the data.
|
|
83946
|
+
- For find: one line "Found N (M new)", then one line per NEW lead: "#<id> <name> \xB7 <type>".
|
|
83947
|
+
- For list: one line per lead "#<id> <name> \xB7 <type> \xB7 <city> \xB7 <status>" (cap at ~15, then "\u2026and K more").
|
|
83948
|
+
- For build/book: one line "\u2713 <name> \u2014 <url>".
|
|
83949
|
+
- Use a plain "#<id> <name>" style. No tables, no fluff, no "Here's what I found". If a result count is low, state the number plainly \u2014 do NOT speculate about why unless asked.
|
|
83867
83950
|
|
|
83868
83951
|
GATHER REQUIRED CHOICES FIRST \u2014 ask, don't guess, don't chatter:
|
|
83869
83952
|
When a request is missing information the command needs, ask a SHORT, STRUCTURED question that lists the options \u2014 then wait. Present choices as a tight numbered/bulleted list, no rambling. Specifically:
|
|
@@ -83876,10 +83959,12 @@ When a request is missing information the command needs, ask a SHORT, STRUCTURED
|
|
|
83876
83959
|
- Once you have the needed choices, act immediately \u2014 one command, one line of result. Don't re-confirm what they already told you.
|
|
83877
83960
|
|
|
83878
83961
|
COMMANDS:
|
|
83879
|
-
- sproutkit find "<area>" --categories "<type>,<type>" --json \u2014 find leads
|
|
83962
|
+
- sproutkit find "<area>" --categories "<type>,<type>" --json \u2014 find website-less leads
|
|
83880
83963
|
- sproutkit list --json [--status <s>] [--city <c>] \u2014 list leads (get ids)
|
|
83881
83964
|
- sproutkit build <id> --json [--instructions "..."] [--cms] \u2014 build a demo site
|
|
83882
83965
|
- sproutkit build-all --json [--ids 3,5] [--concurrency 3] \u2014 build many at once
|
|
83966
|
+
- sproutkit regenerate <id> --instructions "..." --json \u2014 rebuild an existing site with changes
|
|
83967
|
+
- sproutkit cms <id> --json \u2014 add the editable CMS on top of the site we built them
|
|
83883
83968
|
- 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)
|
|
83884
83969
|
- sproutkit outreach --json [--ids 3,5] \u2014 prepare outreach links
|
|
83885
83970
|
|
|
@@ -84022,7 +84107,7 @@ async function main() {
|
|
|
84022
84107
|
closeTty();
|
|
84023
84108
|
return;
|
|
84024
84109
|
}
|
|
84025
|
-
if (arg === "find" || arg === "list" || arg === "build" || arg === "build-all" || arg === "book" || arg === "outreach") {
|
|
84110
|
+
if (arg === "find" || arg === "list" || arg === "build" || arg === "build-all" || arg === "regenerate" || arg === "cms" || arg === "book" || arg === "outreach") {
|
|
84026
84111
|
const subArgs = process.argv.slice(3);
|
|
84027
84112
|
const lic2 = await check();
|
|
84028
84113
|
if (lic2.status !== "active") {
|
|
@@ -84037,6 +84122,8 @@ async function main() {
|
|
|
84037
84122
|
else if (arg === "list") await cmdList(subArgs);
|
|
84038
84123
|
else if (arg === "build") await cmdBuild(subArgs);
|
|
84039
84124
|
else if (arg === "build-all") await cmdBuildAll(subArgs);
|
|
84125
|
+
else if (arg === "regenerate") await cmdRegenerate(subArgs);
|
|
84126
|
+
else if (arg === "cms") await cmdCms(subArgs);
|
|
84040
84127
|
else if (arg === "book") await cmdBook(subArgs);
|
|
84041
84128
|
else await cmdOutreach(subArgs);
|
|
84042
84129
|
closeTty();
|
|
@@ -84052,6 +84139,8 @@ async function main() {
|
|
|
84052
84139
|
say(` ${c.bold("sproutkit list")} list leads (--status, --city, --json)`);
|
|
84053
84140
|
say(` ${c.bold("sproutkit build <id>")} build a website (--instructions, --cms)`);
|
|
84054
84141
|
say(` ${c.bold("sproutkit build-all")} build many at once (--ids, --concurrency 3)`);
|
|
84142
|
+
say(` ${c.bold("sproutkit regenerate <id>")} rebuild a site (--instructions "\u2026")`);
|
|
84143
|
+
say(` ${c.bold("sproutkit cms <id>")} add the editable CMS to a built site`);
|
|
84055
84144
|
say(` ${c.bold("sproutkit book <id>")} booking site (--standalone for own-URL)`);
|
|
84056
84145
|
say(` ${c.bold("sproutkit outreach")} prep outreach links (--ids 3,5)`);
|
|
84057
84146
|
say(` ${c.bold("sproutkit setup")} set or update keys, licence, backend`);
|
|
@@ -84123,6 +84212,43 @@ async function main() {
|
|
|
84123
84212
|
say();
|
|
84124
84213
|
continue;
|
|
84125
84214
|
}
|
|
84215
|
+
const direct = line.replace(/^\//, "");
|
|
84216
|
+
const verb = direct.split(/\s+/)[0]?.toLowerCase();
|
|
84217
|
+
const DIRECT_VERBS = ["find", "list", "build", "build-all", "regenerate", "cms", "book", "outreach"];
|
|
84218
|
+
if (verb === "commands" || verb === "help" || line === "/help" || line === "/?") {
|
|
84219
|
+
say();
|
|
84220
|
+
say(c.bold(" Commands (type with or without a leading /):"));
|
|
84221
|
+
say(` ${c.greenBold("find")} "<area>" --categories "cafe,barber" find website-less leads`);
|
|
84222
|
+
say(` ${c.greenBold("list")} [--status <s>] [--city <c>] list leads (get ids)`);
|
|
84223
|
+
say(` ${c.greenBold("build")} <id> [--instructions "\u2026"] [--cms] build a demo site`);
|
|
84224
|
+
say(` ${c.greenBold("build-all")} [--ids 3,5] [--concurrency 3] build many at once`);
|
|
84225
|
+
say(` ${c.greenBold("regenerate")} <id> --instructions "\u2026" rebuild a site with changes`);
|
|
84226
|
+
say(` ${c.greenBold("cms")} <id> add the editable CMS to a built site`);
|
|
84227
|
+
say(` ${c.greenBold("book")} <id> [--standalone] [--archetype slots|reservation|enquiry] [--email \u2026]`);
|
|
84228
|
+
say(` ${c.dim(" ")} booking on the site, or --standalone for its own URL`);
|
|
84229
|
+
say(` ${c.greenBold("outreach")} [--ids 3,5] prepare outreach links`);
|
|
84230
|
+
say(c.dim(" /freemode \u2014 open conversation & custom tweaks \xB7 /taskmode \u2014 precise \xB7 exit \u2014 quit"));
|
|
84231
|
+
say(c.dim(" Or just talk: \u201Cfind cafes in Leeds and build the best two\u201D."));
|
|
84232
|
+
say();
|
|
84233
|
+
continue;
|
|
84234
|
+
}
|
|
84235
|
+
const isSlash = line.startsWith("/");
|
|
84236
|
+
if (DIRECT_VERBS.includes(verb) && (isSlash || !freeMode)) {
|
|
84237
|
+
const argv = (direct.match(/"[^"]*"|'[^']*'|\S+/g) ?? []).slice(1).map((a) => a.replace(/^['"]|['"]$/g, ""));
|
|
84238
|
+
try {
|
|
84239
|
+
if (verb === "find") await cmdFind(argv);
|
|
84240
|
+
else if (verb === "list") await cmdList(argv);
|
|
84241
|
+
else if (verb === "build") await cmdBuild(argv);
|
|
84242
|
+
else if (verb === "build-all") await cmdBuildAll(argv);
|
|
84243
|
+
else if (verb === "regenerate") await cmdRegenerate(argv);
|
|
84244
|
+
else if (verb === "cms") await cmdCms(argv);
|
|
84245
|
+
else if (verb === "book") await cmdBook(argv);
|
|
84246
|
+
else if (verb === "outreach") await cmdOutreach(argv);
|
|
84247
|
+
} catch (e) {
|
|
84248
|
+
say(c.red(` Error: ${e.message}`));
|
|
84249
|
+
}
|
|
84250
|
+
continue;
|
|
84251
|
+
}
|
|
84126
84252
|
try {
|
|
84127
84253
|
if (bridged) {
|
|
84128
84254
|
const r = await runClaudeTurn({
|
package/dist/skill/SKILL.md
CHANGED
|
@@ -48,6 +48,13 @@ you can parse results reliably, then summarise them for the user in prose.
|
|
|
48
48
|
renders the progress. One `sproutkit build-all` call is correct; N parallel
|
|
49
49
|
`sproutkit build` calls via subagents is not.
|
|
50
50
|
|
|
51
|
+
- **Rebuild a site** — `sproutkit regenerate <leadId> --instructions "..." --json`
|
|
52
|
+
- Rebuilds an existing site with the requested changes. Instructions required.
|
|
53
|
+
|
|
54
|
+
- **Add the CMS** — `sproutkit cms <leadId> --json`
|
|
55
|
+
- Adds the editable CMS on top of a site Sproutkit already built, so the
|
|
56
|
+
business owner can edit their own content (the claim/on-domain flow).
|
|
57
|
+
|
|
51
58
|
- **Add a booking site** — `sproutkit book <leadId> --json`
|
|
52
59
|
- Default: adds an online booking/reservation form onto a site Sproutkit
|
|
53
60
|
already built for this lead (same domain, at /book).
|