sproutkit-code 0.8.0 → 0.9.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.
Files changed (2) hide show
  1. package/dist/index.js +59 -8
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -82456,7 +82456,7 @@ var require_package = __commonJS({
82456
82456
  "package.json"(exports2, module2) {
82457
82457
  module2.exports = {
82458
82458
  name: "sproutkit-code",
82459
- version: "0.8.0",
82459
+ version: "0.9.0",
82460
82460
  description: "Sproutkit in your terminal \u2014 find leads, build sites, run outreach, all via chat.",
82461
82461
  bin: {
82462
82462
  sproutkit: "./dist/index.js"
@@ -82529,7 +82529,8 @@ var REQUIRED_KEYS = [
82529
82529
  { key: "anthropic_key", label: "Anthropic API key", hint: "sk-ant-\u2026 \u2014 powers the chat + site generation", required: true, apiBackendOnly: true },
82530
82530
  { key: "places_key", label: "Google Places API key", hint: "finds businesses, photos, reviews", required: true },
82531
82531
  { key: "vercel_token", label: "Vercel token", hint: "deploys the generated sites", required: true },
82532
- { key: "blob_token", label: "Vercel Blob token", hint: "optional \u2014 only needed for CMS/booking hand-off", required: false }
82532
+ { key: "blob_token", label: "Vercel Blob token", hint: "required for booking sites & CMS (stores form config/submissions)", required: false },
82533
+ { key: "resend_key", label: "Resend API key", hint: "optional \u2014 emails the business owner when a booking comes in", required: false }
82533
82534
  ];
82534
82535
  function getSetting(key) {
82535
82536
  const row = getDb().get("SELECT value FROM settings WHERE key = ?", [key]);
@@ -83029,7 +83030,7 @@ function makeInvokeIpc(emit) {
83029
83030
  const resendKey = getSetting("resend_key");
83030
83031
  if (!anthropicKey) throw new Error("Anthropic API key not set");
83031
83032
  if (!vercelToken) throw new Error("Vercel token not set");
83032
- if (!blobToken) throw new Error("Vercel Blob token not set");
83033
+ if (!blobToken) throw new Error("Booking sites need a Vercel Blob token (stores the form config & submissions). Add one with 'sproutkit setup' \u2014 create a Blob store at vercel.com, copy its read-write token.");
83033
83034
  emit("bookings:progress", { leadId: p.leadId, msg: "Designing the booking form\u2026" });
83034
83035
  const leadInfo = lead;
83035
83036
  const config = await generatePreset({
@@ -83070,7 +83071,7 @@ function makeInvokeIpc(emit) {
83070
83071
  const blobToken = getSetting("blob_token");
83071
83072
  const resendKey = getSetting("resend_key");
83072
83073
  if (!vercelToken) throw new Error("Vercel token not set");
83073
- if (!blobToken) throw new Error("Vercel Blob token not set");
83074
+ if (!blobToken) throw new Error("Booking sites need a Vercel Blob token (stores the form config & submissions). Add one with 'sproutkit setup' \u2014 create a Blob store at vercel.com, copy its read-write token.");
83074
83075
  emit("bookings:progress", { leadId: p.leadId, msg: "Designing the booking form\u2026" });
83075
83076
  const leadInfo = lead;
83076
83077
  const config = await generatePreset({
@@ -83742,8 +83743,36 @@ async function cmdBuildAll(argv) {
83742
83743
 
83743
83744
  // src/claude-bridge.ts
83744
83745
  var import_child_process3 = require("child_process");
83746
+ var TASK_SYSTEM_PROMPT = `You operate the "sproutkit" CLI on the user's behalf. Sproutkit finds local businesses without websites, builds them demo sites, adds booking, and prepares outreach.
83747
+
83748
+ HOW TO ACT \u2014 this is strict:
83749
+ - Fulfil each request by running the matching sproutkit command(s) via Bash. Nothing else.
83750
+ - 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.
83751
+ - Always pass --json and parse it. When an action completes, report the outcome in ONE short line (what happened + the URL/result).
83752
+ - Get lead ids from \`sproutkit list --json\` \u2014 never guess an id.
83753
+
83754
+ GATHER REQUIRED CHOICES FIRST \u2014 ask, don't guess, don't chatter:
83755
+ 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:
83756
+ - Which business/lead is not clear \u2192 run \`sproutkit list --json\`, then ask: "Which business?" and list a few (id + name).
83757
+ - "make/build a booking site" \u2192 you MUST establish the booking TYPE before running. Ask: "What type of booking site?" and present exactly:
83758
+ 1. Slot-based \u2014 timed appointments (barbers, salons, clinics)
83759
+ 2. Reservation \u2014 table / party booking (restaurants, cafes)
83760
+ 3. Enquiry \u2014 a simple request/contact form
83761
+ Also ask whether it should be standalone (its own URL, for a client who already has a website) or added to a Sproutkit-built site. Then run \`sproutkit book <id> --archetype <slots|reservation|enquiry> [--standalone] --json\`.
83762
+ - Once you have the needed choices, act immediately \u2014 one command, one line of result. Don't re-confirm what they already told you.
83763
+
83764
+ COMMANDS:
83765
+ - sproutkit find "<area>" --categories "<type>,<type>" --json \u2014 find leads
83766
+ - sproutkit list --json [--status <s>] [--city <c>] \u2014 list leads (get ids)
83767
+ - sproutkit build <id> --json [--instructions "..."] [--cms] \u2014 build a demo site
83768
+ - sproutkit build-all --json [--ids 3,5] [--concurrency 3] \u2014 build many at once
83769
+ - 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)
83770
+ - sproutkit outreach --json [--ids 3,5] \u2014 prepare outreach links
83771
+
83772
+ If no command fits the request, say so in one line and name the closest thing you can do. Never invent results.`;
83773
+ 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.`;
83745
83774
  function runClaudeTurn(args) {
83746
- const { message, sessionId, write } = args;
83775
+ const { message, sessionId, write, freeMode } = args;
83747
83776
  const timeoutMs = args.timeoutMs ?? 6e5;
83748
83777
  const cliArgs = [
83749
83778
  "-p",
@@ -83752,6 +83781,11 @@ function runClaudeTurn(args) {
83752
83781
  "--include-partial-messages",
83753
83782
  "--verbose",
83754
83783
  // required for stream_event emission
83784
+ // Inject the operating instructions directly so behaviour is precise and
83785
+ // doesn't depend on flaky skill discovery. Task mode pins exact commands;
83786
+ // free mode allows open conversation.
83787
+ "--append-system-prompt",
83788
+ freeMode ? FREE_SYSTEM_PROMPT : TASK_SYSTEM_PROMPT,
83755
83789
  // Let Claude drive the sproutkit CLI without hanging on a permission
83756
83790
  // prompt, but ONLY the sproutkit command — nothing else runs unattended.
83757
83791
  "--allowedTools",
@@ -83950,20 +83984,37 @@ async function main() {
83950
83984
  if (bridged && !skillInstalled()) {
83951
83985
  say(c.amber(" Tip: run 'sproutkit setup' to install the Sproutkit skill so Claude can find/build/book for you."));
83952
83986
  }
83953
- say(
83954
- bridged ? c.dim(" Talk to Sproutkit (powered by your Claude subscription). Type a request, or 'exit'.") : c.dim(" Chat with Sproutkit. Type a request, or 'exit' to quit.")
83955
- );
83987
+ if (bridged) {
83988
+ say(c.dim(" Talk to Sproutkit (powered by your Claude subscription). Type a request, or 'exit'."));
83989
+ say(c.dim(" It runs the right action precisely. Type '/freemode' for open conversation + custom tweaks."));
83990
+ } else {
83991
+ say(c.dim(" Chat with Sproutkit. Type a request, or 'exit' to quit."));
83992
+ }
83956
83993
  say();
83957
83994
  let claudeSession = null;
83995
+ let freeMode = false;
83958
83996
  while (true) {
83959
83997
  const line = await ask(c.greenBold("sproutkit \u203A "));
83960
83998
  if (!line) continue;
83961
83999
  if (line === "exit" || line === "quit") break;
84000
+ if (bridged && (line === "/freemode" || line === "/free")) {
84001
+ freeMode = true;
84002
+ say(c.amber(" Free mode ON \u2014 open conversation. You can ask for small custom changes. '/taskmode' to switch back."));
84003
+ say();
84004
+ continue;
84005
+ }
84006
+ if (bridged && (line === "/taskmode" || line === "/task")) {
84007
+ freeMode = false;
84008
+ say(c.green(" Task mode ON \u2014 precise actions."));
84009
+ say();
84010
+ continue;
84011
+ }
83962
84012
  try {
83963
84013
  if (bridged) {
83964
84014
  const r = await runClaudeTurn({
83965
84015
  message: line,
83966
84016
  sessionId: claudeSession,
84017
+ freeMode,
83967
84018
  write: (t) => process.stdout.write(t)
83968
84019
  });
83969
84020
  say();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sproutkit-code",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "Sproutkit in your terminal — find leads, build sites, run outreach, all via chat.",
5
5
  "bin": {
6
6
  "sproutkit": "./dist/index.js"