sproutkit-code 0.1.3 → 0.2.1
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 +158 -24
- 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 =
|
|
30355
|
-
exports2.listCachedModels =
|
|
30354
|
+
exports2.refreshModels = refreshModels2;
|
|
30355
|
+
exports2.listCachedModels = listCachedModels2;
|
|
30356
30356
|
exports2.getCacheFetchedAt = getCacheFetchedAt;
|
|
30357
|
-
exports2.refreshIfStale =
|
|
30358
|
-
exports2.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
|
|
30402
|
-
const
|
|
30403
|
-
if (
|
|
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
|
|
30405
|
+
writeCache({ fetched_at: Date.now(), models: models2 });
|
|
30406
|
+
return models2;
|
|
30407
30407
|
}
|
|
30408
|
-
function
|
|
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
|
|
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
|
|
30422
|
+
await refreshModels2(apiKey);
|
|
30423
30423
|
} catch {
|
|
30424
30424
|
}
|
|
30425
30425
|
}
|
|
30426
|
-
function
|
|
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 =
|
|
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) => {
|
|
@@ -79735,28 +79735,43 @@ content state.
|
|
|
79735
79735
|
OUTPUT
|
|
79736
79736
|
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
79737
79737
|
Return ONLY the complete App.jsx code. No markdown fences, no explanation, no preamble. Just code.`;
|
|
79738
|
+
let lastStop = null;
|
|
79738
79739
|
async function generateOnce(extraSystemNote) {
|
|
79739
79740
|
const finalPrompt = extraSystemNote ? `${extraSystemNote}
|
|
79740
79741
|
|
|
79741
79742
|
${prompt}` : prompt;
|
|
79742
79743
|
const res2 = await anthropic.messages.create({
|
|
79743
79744
|
model: (0, models_1.getPreferredModel)(),
|
|
79744
|
-
max_tokens
|
|
79745
|
+
// The API requires a max_tokens, so we set it to the model's ceiling
|
|
79746
|
+
// rather than an arbitrary low cap — truncation was the main build
|
|
79747
|
+
// failure. 64000 covers current Claude output limits; the model stops
|
|
79748
|
+
// naturally at end_turn long before this for a normal site.
|
|
79749
|
+
max_tokens: 64e3,
|
|
79745
79750
|
messages: [{ role: "user", content: finalPrompt }]
|
|
79746
79751
|
});
|
|
79752
|
+
lastStop = res2.stop_reason ?? null;
|
|
79747
79753
|
const textBlock = res2.content.find((b) => b.type === "text");
|
|
79748
79754
|
if (!textBlock) {
|
|
79749
79755
|
const types = res2.content.map((b) => b.type).join(", ") || "empty";
|
|
79750
79756
|
throw new Error(`Claude returned no text (stop_reason=${res2.stop_reason}, blocks=[${types}], model=${res2.model})`);
|
|
79751
79757
|
}
|
|
79752
79758
|
if (res2.stop_reason === "max_tokens") {
|
|
79753
|
-
onProgress(`\u26A0\uFE0E Claude hit
|
|
79759
|
+
onProgress(`\u26A0\uFE0E Claude hit the length limit \u2014 output was truncated.`);
|
|
79754
79760
|
}
|
|
79755
79761
|
return textBlock.text.replace(/^```[a-z]*\s*/i, "").replace(/\s*```$/, "").trim();
|
|
79756
79762
|
}
|
|
79757
79763
|
let appCode = await generateOnce();
|
|
79758
79764
|
if (!appCode.includes("export default App")) {
|
|
79759
|
-
|
|
79765
|
+
const wasTruncated = lastStop === "max_tokens";
|
|
79766
|
+
onProgress(wasTruncated ? "Output was truncated \u2014 retrying\u2026" : "First attempt came back incomplete \u2014 retrying\u2026");
|
|
79767
|
+
const retry = await generateOnce("Your previous attempt did not finish with `export default App;`. Produce the COMPLETE, self-contained App component ending in that exact line. Do not stop early.");
|
|
79768
|
+
if (retry.includes("export default App")) {
|
|
79769
|
+
appCode = retry;
|
|
79770
|
+
} else if (lastStop === "max_tokens") {
|
|
79771
|
+
throw new Error("This site's content is unusually large and Claude's output was cut off. Try regenerating with 'keep it minimal \u2014 few sections'.");
|
|
79772
|
+
} else {
|
|
79773
|
+
throw new Error(`Claude returned incomplete code twice (stop reason: ${lastStop ?? "unknown"}). This usually clears on another try, or check the model with 'sproutkit models'.`);
|
|
79774
|
+
}
|
|
79760
79775
|
}
|
|
79761
79776
|
const stockHosts = findStockImages(appCode);
|
|
79762
79777
|
if (stockHosts.length > 0) {
|
|
@@ -81172,8 +81187,9 @@ Formatting \u2014 you're writing into a narrow chat bubble, not a document:
|
|
|
81172
81187
|
- Skip empty fields entirely rather than writing "\u2014" or "N/A" in a column.
|
|
81173
81188
|
- Lead with the answer. If the user asked "how many", give the number first, then the breakdown.
|
|
81174
81189
|
- For more than about 8 leads, show the most relevant handful and say how many others there are instead of listing everything.`;
|
|
81190
|
+
var VOLATILE_INPUT_FIELDS = /* @__PURE__ */ new Set(["instructions", "message", "concurrency"]);
|
|
81175
81191
|
function confirmKey(name, input) {
|
|
81176
|
-
const norm = Object.keys(input ?? {}).sort().reduce((acc, k) => {
|
|
81192
|
+
const norm = Object.keys(input ?? {}).filter((k) => !VOLATILE_INPUT_FIELDS.has(k)).sort().reduce((acc, k) => {
|
|
81177
81193
|
const v = input[k];
|
|
81178
81194
|
if (v === void 0 || v === null || v === false || v === "")
|
|
81179
81195
|
return acc;
|
|
@@ -82047,7 +82063,7 @@ var require_package = __commonJS({
|
|
|
82047
82063
|
"package.json"(exports2, module2) {
|
|
82048
82064
|
module2.exports = {
|
|
82049
82065
|
name: "sproutkit-code",
|
|
82050
|
-
version: "0.1
|
|
82066
|
+
version: "0.2.1",
|
|
82051
82067
|
description: "Sproutkit in your terminal \u2014 find leads, build sites, run outreach, all via chat.",
|
|
82052
82068
|
bin: {
|
|
82053
82069
|
sproutkit: "./dist/index.js"
|
|
@@ -82093,6 +82109,7 @@ var autoMode = require_auto_mode();
|
|
|
82093
82109
|
var cms = require_cms_provision();
|
|
82094
82110
|
var bookingPreset = require_booking_preset();
|
|
82095
82111
|
var builder = require_builder();
|
|
82112
|
+
var models = require_models();
|
|
82096
82113
|
var initDatabase = db.initDatabase;
|
|
82097
82114
|
var getDb = db.getDb;
|
|
82098
82115
|
var chatWithTools = chat.chatWithTools;
|
|
@@ -82100,6 +82117,10 @@ var startAutoJob = autoMode.startAutoJob;
|
|
|
82100
82117
|
var provisionCmsForLead = cms.provisionCmsForLead;
|
|
82101
82118
|
var generatePreset = bookingPreset.generatePreset;
|
|
82102
82119
|
var deployBookingOnly = builder.deployBookingOnly;
|
|
82120
|
+
var refreshModels = models.refreshModels;
|
|
82121
|
+
var refreshIfStale = models.refreshIfStale;
|
|
82122
|
+
var listCachedModels = models.listCachedModels;
|
|
82123
|
+
var getPreferredModel = models.getPreferredModel;
|
|
82103
82124
|
|
|
82104
82125
|
// src/config.ts
|
|
82105
82126
|
var REQUIRED_KEYS = [
|
|
@@ -82236,6 +82257,53 @@ function closeTty() {
|
|
|
82236
82257
|
function say(msg = "") {
|
|
82237
82258
|
process.stdout.write(msg + "\n");
|
|
82238
82259
|
}
|
|
82260
|
+
var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
82261
|
+
var Status = class {
|
|
82262
|
+
timer = null;
|
|
82263
|
+
frame = 0;
|
|
82264
|
+
label = "";
|
|
82265
|
+
stage = "";
|
|
82266
|
+
tty = process.stdout.isTTY && useColor;
|
|
82267
|
+
start(label) {
|
|
82268
|
+
this.label = label;
|
|
82269
|
+
this.stage = "";
|
|
82270
|
+
if (!this.tty) {
|
|
82271
|
+
say(c.cyan(` \u27F3 ${label}`));
|
|
82272
|
+
return;
|
|
82273
|
+
}
|
|
82274
|
+
this.render();
|
|
82275
|
+
this.timer = setInterval(() => {
|
|
82276
|
+
this.frame = (this.frame + 1) % FRAMES.length;
|
|
82277
|
+
this.render();
|
|
82278
|
+
}, 80);
|
|
82279
|
+
}
|
|
82280
|
+
// Update the current stage (e.g. "Generating site with Claude…").
|
|
82281
|
+
update(stage) {
|
|
82282
|
+
this.stage = stage;
|
|
82283
|
+
if (!this.tty) {
|
|
82284
|
+
say(c.dim(` \u2026 ${stage}`));
|
|
82285
|
+
return;
|
|
82286
|
+
}
|
|
82287
|
+
this.render();
|
|
82288
|
+
}
|
|
82289
|
+
render() {
|
|
82290
|
+
const spin = c.cyan(FRAMES[this.frame]);
|
|
82291
|
+
const stage = this.stage ? c.dim(` \xB7 ${this.stage}`) : "";
|
|
82292
|
+
process.stdout.write(`\r\x1B[2K ${spin} ${this.label}${stage}`);
|
|
82293
|
+
}
|
|
82294
|
+
// Stop with a terminal state, leaving one clean line behind.
|
|
82295
|
+
done(ok, finalLabel) {
|
|
82296
|
+
if (this.timer) {
|
|
82297
|
+
clearInterval(this.timer);
|
|
82298
|
+
this.timer = null;
|
|
82299
|
+
}
|
|
82300
|
+
const label = finalLabel ?? this.label;
|
|
82301
|
+
const mark = ok ? c.green("\u2713") : c.red("\u2717");
|
|
82302
|
+
if (this.tty) process.stdout.write(`\r\x1B[2K ${mark} ${label}
|
|
82303
|
+
`);
|
|
82304
|
+
else say(` ${mark} ${label}`);
|
|
82305
|
+
}
|
|
82306
|
+
};
|
|
82239
82307
|
|
|
82240
82308
|
// src/onboarding.ts
|
|
82241
82309
|
async function onboardLicense(force) {
|
|
@@ -82286,6 +82354,13 @@ async function runOnboarding(force = false) {
|
|
|
82286
82354
|
}
|
|
82287
82355
|
say();
|
|
82288
82356
|
}
|
|
82357
|
+
const anthropicKey = getSetting("anthropic_key");
|
|
82358
|
+
if (anthropicKey) {
|
|
82359
|
+
try {
|
|
82360
|
+
await refreshModels(anthropicKey);
|
|
82361
|
+
} catch {
|
|
82362
|
+
}
|
|
82363
|
+
}
|
|
82289
82364
|
const stillMissing = missingRequiredKeys();
|
|
82290
82365
|
if (stillMissing.length > 0) {
|
|
82291
82366
|
say(c.amber(` Still missing: ${stillMissing.map((m) => m.label).join(", ")}.`));
|
|
@@ -82529,12 +82604,14 @@ async function runTurn(userMessage) {
|
|
|
82529
82604
|
const history = loadHistory().slice(0, -1);
|
|
82530
82605
|
const confirmations = {};
|
|
82531
82606
|
const completedResults = {};
|
|
82607
|
+
const status = new Status();
|
|
82608
|
+
let statusActive = false;
|
|
82532
82609
|
let lastProgress = "";
|
|
82533
|
-
const emit = (
|
|
82610
|
+
const emit = (_channel, payload) => {
|
|
82534
82611
|
const p = payload;
|
|
82535
82612
|
if (p?.msg && p.msg !== lastProgress) {
|
|
82536
82613
|
lastProgress = p.msg;
|
|
82537
|
-
|
|
82614
|
+
if (statusActive) status.update(p.msg);
|
|
82538
82615
|
}
|
|
82539
82616
|
};
|
|
82540
82617
|
const invokeIpc = makeInvokeIpc(emit);
|
|
@@ -82553,11 +82630,15 @@ async function runTurn(userMessage) {
|
|
|
82553
82630
|
const t = e.data;
|
|
82554
82631
|
if (t.status === "pending" && !seenRunning.has(t.id)) {
|
|
82555
82632
|
seenRunning.add(t.id);
|
|
82556
|
-
|
|
82633
|
+
lastProgress = "";
|
|
82634
|
+
status.start(labelFor(t));
|
|
82635
|
+
statusActive = true;
|
|
82557
82636
|
} else if (t.status === "completed") {
|
|
82558
|
-
|
|
82637
|
+
status.done(true, labelFor(t));
|
|
82638
|
+
statusActive = false;
|
|
82559
82639
|
} else if (t.status === "error") {
|
|
82560
|
-
|
|
82640
|
+
status.done(false, `${labelFor(t)} \u2014 ${t.error ?? "failed"}`);
|
|
82641
|
+
statusActive = false;
|
|
82561
82642
|
}
|
|
82562
82643
|
}
|
|
82563
82644
|
},
|
|
@@ -82576,6 +82657,10 @@ async function runTurn(userMessage) {
|
|
|
82576
82657
|
if (ranSomething) autoSync();
|
|
82577
82658
|
return;
|
|
82578
82659
|
}
|
|
82660
|
+
if (statusActive) {
|
|
82661
|
+
status.done(false, "paused for confirmation");
|
|
82662
|
+
statusActive = false;
|
|
82663
|
+
}
|
|
82579
82664
|
say();
|
|
82580
82665
|
say(c.amber(` ${pending.length} action${pending.length === 1 ? "" : "s"} need confirmation:`));
|
|
82581
82666
|
for (const t of pending) {
|
|
@@ -82651,6 +82736,47 @@ async function notifyIfOutdated() {
|
|
|
82651
82736
|
}
|
|
82652
82737
|
}
|
|
82653
82738
|
|
|
82739
|
+
// src/models-cmd.ts
|
|
82740
|
+
async function runModels() {
|
|
82741
|
+
const apiKey = getSetting("anthropic_key");
|
|
82742
|
+
if (!apiKey) {
|
|
82743
|
+
say(c.red(" No Anthropic key set. Run 'sproutkit setup' first."));
|
|
82744
|
+
return;
|
|
82745
|
+
}
|
|
82746
|
+
say(c.dim(" Fetching your available models from Anthropic\u2026"));
|
|
82747
|
+
let models2 = listCachedModels();
|
|
82748
|
+
try {
|
|
82749
|
+
models2 = await refreshModels(apiKey);
|
|
82750
|
+
} catch (e) {
|
|
82751
|
+
say(c.amber(` Couldn't refresh from the API (${e.message}).`));
|
|
82752
|
+
say(c.dim(" Showing the last known list instead."));
|
|
82753
|
+
}
|
|
82754
|
+
if (models2.length === 0) {
|
|
82755
|
+
say(c.red(" No models returned. Check your Anthropic key and connection."));
|
|
82756
|
+
return;
|
|
82757
|
+
}
|
|
82758
|
+
const current = getPreferredModel();
|
|
82759
|
+
say();
|
|
82760
|
+
say(c.bold(" Available models:"));
|
|
82761
|
+
models2.forEach((m, i) => {
|
|
82762
|
+
const active = m.id === current ? c.green(" \u2190 current") : "";
|
|
82763
|
+
say(` ${c.greenBold(String(i + 1).padStart(2))} ${m.display_name ?? m.id} ${c.dim(m.id)}${active}`);
|
|
82764
|
+
});
|
|
82765
|
+
say();
|
|
82766
|
+
const pick = await ask(c.green(" Pick a number to switch (or Enter to keep current): "));
|
|
82767
|
+
if (!pick) {
|
|
82768
|
+
say(c.dim(" Kept current model."));
|
|
82769
|
+
return;
|
|
82770
|
+
}
|
|
82771
|
+
const idx = Number(pick) - 1;
|
|
82772
|
+
if (Number.isNaN(idx) || idx < 0 || idx >= models2.length) {
|
|
82773
|
+
say(c.amber(" Not a valid choice \u2014 no change made."));
|
|
82774
|
+
return;
|
|
82775
|
+
}
|
|
82776
|
+
setSetting("claude_model", models2[idx].id);
|
|
82777
|
+
say(c.green(` \u2713 Now using ${models2[idx].display_name ?? models2[idx].id}.`));
|
|
82778
|
+
}
|
|
82779
|
+
|
|
82654
82780
|
// src/index.ts
|
|
82655
82781
|
process.env.SPROUTKIT_HEADLESS = "1";
|
|
82656
82782
|
async function main() {
|
|
@@ -82666,6 +82792,11 @@ async function main() {
|
|
|
82666
82792
|
closeTty();
|
|
82667
82793
|
return;
|
|
82668
82794
|
}
|
|
82795
|
+
if (arg === "models") {
|
|
82796
|
+
await runModels();
|
|
82797
|
+
closeTty();
|
|
82798
|
+
return;
|
|
82799
|
+
}
|
|
82669
82800
|
if (arg === "sync") {
|
|
82670
82801
|
const r = syncToDesktop();
|
|
82671
82802
|
if (!r.ok && r.reason === "no_desktop_db") {
|
|
@@ -82687,6 +82818,7 @@ async function main() {
|
|
|
82687
82818
|
say();
|
|
82688
82819
|
say(` ${c.bold("sproutkit")} start the chat`);
|
|
82689
82820
|
say(` ${c.bold("sproutkit setup")} set or update your keys and licence`);
|
|
82821
|
+
say(` ${c.bold("sproutkit models")} list Claude models and pick one`);
|
|
82690
82822
|
say(` ${c.bold("sproutkit sync")} push your leads into the desktop app`);
|
|
82691
82823
|
say(` ${c.bold("sproutkit update")} update to the latest version`);
|
|
82692
82824
|
say(` ${c.bold("sproutkit version")} show the installed version`);
|
|
@@ -82715,6 +82847,8 @@ async function main() {
|
|
|
82715
82847
|
closeTty();
|
|
82716
82848
|
return;
|
|
82717
82849
|
}
|
|
82850
|
+
await refreshIfStale(getSetting("anthropic_key")).catch(() => {
|
|
82851
|
+
});
|
|
82718
82852
|
await notifyIfOutdated();
|
|
82719
82853
|
say(c.dim(" Chat with Sproutkit. Type a request, or 'exit' to quit."));
|
|
82720
82854
|
say();
|