sproutkit-code 0.1.3 → 0.2.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 +145 -21
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -30351,11 +30351,11 @@ var require_models = __commonJS({
30351
30351
  };
30352
30352
  Object.defineProperty(exports2, "__esModule", { value: true });
30353
30353
  exports2.fetchModelsFromAnthropic = fetchModelsFromAnthropic;
30354
- exports2.refreshModels = refreshModels;
30355
- exports2.listCachedModels = listCachedModels;
30354
+ exports2.refreshModels = refreshModels2;
30355
+ exports2.listCachedModels = listCachedModels2;
30356
30356
  exports2.getCacheFetchedAt = getCacheFetchedAt;
30357
- exports2.refreshIfStale = refreshIfStale;
30358
- exports2.getPreferredModel = getPreferredModel;
30357
+ exports2.refreshIfStale = refreshIfStale2;
30358
+ exports2.getPreferredModel = getPreferredModel2;
30359
30359
  var axios_1 = __importDefault(require_axios());
30360
30360
  var db_1 = require_db();
30361
30361
  var CACHE_KEY = "claude_models_cache";
@@ -30398,36 +30398,36 @@ var require_models = __commonJS({
30398
30398
  created_at: m.created_at
30399
30399
  }));
30400
30400
  }
30401
- async function refreshModels(apiKey) {
30402
- const models = await fetchModelsFromAnthropic(apiKey);
30403
- if (models.length === 0)
30401
+ async function refreshModels2(apiKey) {
30402
+ const models2 = await fetchModelsFromAnthropic(apiKey);
30403
+ if (models2.length === 0)
30404
30404
  throw new Error("Anthropic returned no models");
30405
- writeCache({ fetched_at: Date.now(), models });
30406
- return models;
30405
+ writeCache({ fetched_at: Date.now(), models: models2 });
30406
+ return models2;
30407
30407
  }
30408
- function listCachedModels() {
30408
+ function listCachedModels2() {
30409
30409
  const cache = readCache();
30410
30410
  return cache?.models ?? FALLBACK_MODELS;
30411
30411
  }
30412
30412
  function getCacheFetchedAt() {
30413
30413
  return readCache()?.fetched_at ?? null;
30414
30414
  }
30415
- async function refreshIfStale(apiKey) {
30415
+ async function refreshIfStale2(apiKey) {
30416
30416
  if (!apiKey)
30417
30417
  return;
30418
30418
  const cache = readCache();
30419
30419
  if (cache && Date.now() - cache.fetched_at < REFRESH_INTERVAL_MS)
30420
30420
  return;
30421
30421
  try {
30422
- await refreshModels(apiKey);
30422
+ await refreshModels2(apiKey);
30423
30423
  } catch {
30424
30424
  }
30425
30425
  }
30426
- function getPreferredModel() {
30426
+ function getPreferredModel2() {
30427
30427
  const db2 = (0, db_1.getDb)();
30428
30428
  const row = db2.get("SELECT value FROM settings WHERE key = ?", ["claude_model"]);
30429
30429
  const chosen = row?.value;
30430
- const available = listCachedModels();
30430
+ const available = listCachedModels2();
30431
30431
  if (chosen && available.some((m) => m.id === chosen))
30432
30432
  return chosen;
30433
30433
  const sortedByRecency = [...available].sort((a, b) => {
@@ -79756,7 +79756,13 @@ ${prompt}` : prompt;
79756
79756
  }
79757
79757
  let appCode = await generateOnce();
79758
79758
  if (!appCode.includes("export default App")) {
79759
- throw new Error("Generated code is incomplete (no export default)");
79759
+ onProgress("First attempt came back truncated \u2014 retrying with a leaner build\u2026");
79760
+ const retry = await generateOnce("Your previous attempt was cut off before finishing. Produce a COMPLETE, self-contained App component that ends with `export default App;`. Keep it tighter \u2014 fewer sections, less inline copy \u2014 so it fits comfortably. Do not exceed what you can finish.");
79761
+ if (retry.includes("export default App")) {
79762
+ appCode = retry;
79763
+ } else {
79764
+ throw new Error("The site was too large to generate in one pass (Claude's output was cut off twice). Try again with instructions to keep it simple \u2014 e.g. 'minimal, few sections'.");
79765
+ }
79760
79766
  }
79761
79767
  const stockHosts = findStockImages(appCode);
79762
79768
  if (stockHosts.length > 0) {
@@ -82047,7 +82053,7 @@ var require_package = __commonJS({
82047
82053
  "package.json"(exports2, module2) {
82048
82054
  module2.exports = {
82049
82055
  name: "sproutkit-code",
82050
- version: "0.1.3",
82056
+ version: "0.2.0",
82051
82057
  description: "Sproutkit in your terminal \u2014 find leads, build sites, run outreach, all via chat.",
82052
82058
  bin: {
82053
82059
  sproutkit: "./dist/index.js"
@@ -82093,6 +82099,7 @@ var autoMode = require_auto_mode();
82093
82099
  var cms = require_cms_provision();
82094
82100
  var bookingPreset = require_booking_preset();
82095
82101
  var builder = require_builder();
82102
+ var models = require_models();
82096
82103
  var initDatabase = db.initDatabase;
82097
82104
  var getDb = db.getDb;
82098
82105
  var chatWithTools = chat.chatWithTools;
@@ -82100,6 +82107,10 @@ var startAutoJob = autoMode.startAutoJob;
82100
82107
  var provisionCmsForLead = cms.provisionCmsForLead;
82101
82108
  var generatePreset = bookingPreset.generatePreset;
82102
82109
  var deployBookingOnly = builder.deployBookingOnly;
82110
+ var refreshModels = models.refreshModels;
82111
+ var refreshIfStale = models.refreshIfStale;
82112
+ var listCachedModels = models.listCachedModels;
82113
+ var getPreferredModel = models.getPreferredModel;
82103
82114
 
82104
82115
  // src/config.ts
82105
82116
  var REQUIRED_KEYS = [
@@ -82236,6 +82247,53 @@ function closeTty() {
82236
82247
  function say(msg = "") {
82237
82248
  process.stdout.write(msg + "\n");
82238
82249
  }
82250
+ var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
82251
+ var Status = class {
82252
+ timer = null;
82253
+ frame = 0;
82254
+ label = "";
82255
+ stage = "";
82256
+ tty = process.stdout.isTTY && useColor;
82257
+ start(label) {
82258
+ this.label = label;
82259
+ this.stage = "";
82260
+ if (!this.tty) {
82261
+ say(c.cyan(` \u27F3 ${label}`));
82262
+ return;
82263
+ }
82264
+ this.render();
82265
+ this.timer = setInterval(() => {
82266
+ this.frame = (this.frame + 1) % FRAMES.length;
82267
+ this.render();
82268
+ }, 80);
82269
+ }
82270
+ // Update the current stage (e.g. "Generating site with Claude…").
82271
+ update(stage) {
82272
+ this.stage = stage;
82273
+ if (!this.tty) {
82274
+ say(c.dim(` \u2026 ${stage}`));
82275
+ return;
82276
+ }
82277
+ this.render();
82278
+ }
82279
+ render() {
82280
+ const spin = c.cyan(FRAMES[this.frame]);
82281
+ const stage = this.stage ? c.dim(` \xB7 ${this.stage}`) : "";
82282
+ process.stdout.write(`\r\x1B[2K ${spin} ${this.label}${stage}`);
82283
+ }
82284
+ // Stop with a terminal state, leaving one clean line behind.
82285
+ done(ok, finalLabel) {
82286
+ if (this.timer) {
82287
+ clearInterval(this.timer);
82288
+ this.timer = null;
82289
+ }
82290
+ const label = finalLabel ?? this.label;
82291
+ const mark = ok ? c.green("\u2713") : c.red("\u2717");
82292
+ if (this.tty) process.stdout.write(`\r\x1B[2K ${mark} ${label}
82293
+ `);
82294
+ else say(` ${mark} ${label}`);
82295
+ }
82296
+ };
82239
82297
 
82240
82298
  // src/onboarding.ts
82241
82299
  async function onboardLicense(force) {
@@ -82286,6 +82344,13 @@ async function runOnboarding(force = false) {
82286
82344
  }
82287
82345
  say();
82288
82346
  }
82347
+ const anthropicKey = getSetting("anthropic_key");
82348
+ if (anthropicKey) {
82349
+ try {
82350
+ await refreshModels(anthropicKey);
82351
+ } catch {
82352
+ }
82353
+ }
82289
82354
  const stillMissing = missingRequiredKeys();
82290
82355
  if (stillMissing.length > 0) {
82291
82356
  say(c.amber(` Still missing: ${stillMissing.map((m) => m.label).join(", ")}.`));
@@ -82529,12 +82594,14 @@ async function runTurn(userMessage) {
82529
82594
  const history = loadHistory().slice(0, -1);
82530
82595
  const confirmations = {};
82531
82596
  const completedResults = {};
82597
+ const status = new Status();
82598
+ let statusActive = false;
82532
82599
  let lastProgress = "";
82533
- const emit = (channel, payload) => {
82600
+ const emit = (_channel, payload) => {
82534
82601
  const p = payload;
82535
82602
  if (p?.msg && p.msg !== lastProgress) {
82536
82603
  lastProgress = p.msg;
82537
- say(c.dim(` \u2026 ${p.msg}`));
82604
+ if (statusActive) status.update(p.msg);
82538
82605
  }
82539
82606
  };
82540
82607
  const invokeIpc = makeInvokeIpc(emit);
@@ -82553,11 +82620,15 @@ async function runTurn(userMessage) {
82553
82620
  const t = e.data;
82554
82621
  if (t.status === "pending" && !seenRunning.has(t.id)) {
82555
82622
  seenRunning.add(t.id);
82556
- say(c.cyan(` \u27F3 ${labelFor(t)}`));
82623
+ lastProgress = "";
82624
+ status.start(labelFor(t));
82625
+ statusActive = true;
82557
82626
  } else if (t.status === "completed") {
82558
- say(c.green(` \u2713 ${labelFor(t)}`));
82627
+ status.done(true, labelFor(t));
82628
+ statusActive = false;
82559
82629
  } else if (t.status === "error") {
82560
- say(c.red(` \u2717 ${labelFor(t)} \u2014 ${t.error ?? "failed"}`));
82630
+ status.done(false, `${labelFor(t)} \u2014 ${t.error ?? "failed"}`);
82631
+ statusActive = false;
82561
82632
  }
82562
82633
  }
82563
82634
  },
@@ -82576,6 +82647,10 @@ async function runTurn(userMessage) {
82576
82647
  if (ranSomething) autoSync();
82577
82648
  return;
82578
82649
  }
82650
+ if (statusActive) {
82651
+ status.done(false, "paused for confirmation");
82652
+ statusActive = false;
82653
+ }
82579
82654
  say();
82580
82655
  say(c.amber(` ${pending.length} action${pending.length === 1 ? "" : "s"} need confirmation:`));
82581
82656
  for (const t of pending) {
@@ -82651,6 +82726,47 @@ async function notifyIfOutdated() {
82651
82726
  }
82652
82727
  }
82653
82728
 
82729
+ // src/models-cmd.ts
82730
+ async function runModels() {
82731
+ const apiKey = getSetting("anthropic_key");
82732
+ if (!apiKey) {
82733
+ say(c.red(" No Anthropic key set. Run 'sproutkit setup' first."));
82734
+ return;
82735
+ }
82736
+ say(c.dim(" Fetching your available models from Anthropic\u2026"));
82737
+ let models2 = listCachedModels();
82738
+ try {
82739
+ models2 = await refreshModels(apiKey);
82740
+ } catch (e) {
82741
+ say(c.amber(` Couldn't refresh from the API (${e.message}).`));
82742
+ say(c.dim(" Showing the last known list instead."));
82743
+ }
82744
+ if (models2.length === 0) {
82745
+ say(c.red(" No models returned. Check your Anthropic key and connection."));
82746
+ return;
82747
+ }
82748
+ const current = getPreferredModel();
82749
+ say();
82750
+ say(c.bold(" Available models:"));
82751
+ models2.forEach((m, i) => {
82752
+ const active = m.id === current ? c.green(" \u2190 current") : "";
82753
+ say(` ${c.greenBold(String(i + 1).padStart(2))} ${m.display_name ?? m.id} ${c.dim(m.id)}${active}`);
82754
+ });
82755
+ say();
82756
+ const pick = await ask(c.green(" Pick a number to switch (or Enter to keep current): "));
82757
+ if (!pick) {
82758
+ say(c.dim(" Kept current model."));
82759
+ return;
82760
+ }
82761
+ const idx = Number(pick) - 1;
82762
+ if (Number.isNaN(idx) || idx < 0 || idx >= models2.length) {
82763
+ say(c.amber(" Not a valid choice \u2014 no change made."));
82764
+ return;
82765
+ }
82766
+ setSetting("claude_model", models2[idx].id);
82767
+ say(c.green(` \u2713 Now using ${models2[idx].display_name ?? models2[idx].id}.`));
82768
+ }
82769
+
82654
82770
  // src/index.ts
82655
82771
  process.env.SPROUTKIT_HEADLESS = "1";
82656
82772
  async function main() {
@@ -82666,6 +82782,11 @@ async function main() {
82666
82782
  closeTty();
82667
82783
  return;
82668
82784
  }
82785
+ if (arg === "models") {
82786
+ await runModels();
82787
+ closeTty();
82788
+ return;
82789
+ }
82669
82790
  if (arg === "sync") {
82670
82791
  const r = syncToDesktop();
82671
82792
  if (!r.ok && r.reason === "no_desktop_db") {
@@ -82687,6 +82808,7 @@ async function main() {
82687
82808
  say();
82688
82809
  say(` ${c.bold("sproutkit")} start the chat`);
82689
82810
  say(` ${c.bold("sproutkit setup")} set or update your keys and licence`);
82811
+ say(` ${c.bold("sproutkit models")} list Claude models and pick one`);
82690
82812
  say(` ${c.bold("sproutkit sync")} push your leads into the desktop app`);
82691
82813
  say(` ${c.bold("sproutkit update")} update to the latest version`);
82692
82814
  say(` ${c.bold("sproutkit version")} show the installed version`);
@@ -82715,6 +82837,8 @@ async function main() {
82715
82837
  closeTty();
82716
82838
  return;
82717
82839
  }
82840
+ await refreshIfStale(getSetting("anthropic_key")).catch(() => {
82841
+ });
82718
82842
  await notifyIfOutdated();
82719
82843
  say(c.dim(" Chat with Sproutkit. Type a request, or 'exit' to quit."));
82720
82844
  say();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sproutkit-code",
3
- "version": "0.1.3",
3
+ "version": "0.2.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"