@mestreyoda/fabrica 0.1.13 → 0.1.14

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 CHANGED
@@ -111329,8 +111329,8 @@ import fsSync from "node:fs";
111329
111329
  import path5 from "node:path";
111330
111330
  import { fileURLToPath as fileURLToPath3 } from "node:url";
111331
111331
  function getCurrentVersion() {
111332
- if ("0.1.13") {
111333
- return "0.1.13";
111332
+ if ("0.1.14") {
111333
+ return "0.1.14";
111334
111334
  }
111335
111335
  try {
111336
111336
  const pkgPath = path5.join(THIS_DIR, "..", "..", "package.json");
@@ -142208,8 +142208,6 @@ function registerAttachmentHook(api, ctx) {
142208
142208
 
142209
142209
  // lib/dispatch/telegram-bootstrap-hook.ts
142210
142210
  import { homedir as homedir3 } from "node:os";
142211
- init_runtime_paths();
142212
- init_extract_json();
142213
142211
  init_zod();
142214
142212
 
142215
142213
  // lib/dispatch/telegram-bootstrap-session.ts
@@ -142244,14 +142242,14 @@ function buildBootstrapRequestHash(input) {
142244
142242
  return buildBootstrapRequestFingerprint(input);
142245
142243
  }
142246
142244
  function nextSuppressUntil(status) {
142247
- const ttl = status === "classifying" ? CLASSIFYING_TTL_MS : SESSION_TTL_MS;
142245
+ const ttl = status === "classifying" || status === "pending_classify" ? CLASSIFYING_TTL_MS : SESSION_TTL_MS;
142248
142246
  return new Date(Date.now() + ttl).toISOString();
142249
142247
  }
142250
142248
  async function readTelegramBootstrapSession(workspaceDir, conversationId) {
142251
142249
  try {
142252
142250
  const raw = await fs38.readFile(sessionPath(workspaceDir, conversationId), "utf-8");
142253
142251
  const session = JSON.parse(raw);
142254
- if ((session.status === "clarifying" || session.status === "classifying") && Date.parse(session.suppressUntil) < Date.now()) {
142252
+ if ((session.status === "clarifying" || session.status === "classifying" || session.status === "pending_classify") && Date.parse(session.suppressUntil) < Date.now()) {
142255
142253
  await fs38.unlink(sessionPath(workspaceDir, conversationId)).catch(() => {
142256
142254
  });
142257
142255
  return null;
@@ -142341,6 +142339,10 @@ var BOOTSTRAP_MESSAGES = {
142341
142339
  pt: "N\xE3o consegui identificar a stack. Pode me dizer qual linguagem/framework voc\xEA quer usar? Ex: Python, Node.js, Go, Java...",
142342
142340
  en: "Couldn't identify the stack. Can you tell me which language/framework you'd like to use? e.g., Python, Node.js, Go, Java..."
142343
142341
  },
142342
+ clarifyName: {
142343
+ pt: "Como voc\xEA quer chamar o projeto? Se preferir, posso escolher um nome.",
142344
+ en: "What do you want to name the project? If you prefer, I can pick one."
142345
+ },
142344
142346
  registered: {
142345
142347
  pt: (name, link) => `Projeto "${name}" registrado.
142346
142348
  Vou continuar o fluxo em ${link}`,
@@ -142349,7 +142351,9 @@ I'll continue the flow at ${link}`
142349
142351
  }
142350
142352
  };
142351
142353
  function inferProjectSlug(text) {
142352
- const slug = text.toLowerCase().normalize("NFKD").replace(/[^\w\s-]/g, "").trim().replace(/\s+/g, "-").replace(/-+/g, "-").slice(0, 64);
142354
+ let cleaned = text.replace(/^(create|build|crie|cria|criar|fazer?|quero|i need|i want)\s+(uma|um|me\s+a?|an|a)?\s*/i, "").replace(/\s+(that|which|que|para|for|pra)\s+.*/i, "").trim();
142355
+ if (!cleaned) cleaned = text;
142356
+ const slug = cleaned.toLowerCase().normalize("NFKD").replace(/[^\w\s-]/g, "").trim().replace(/\s+/g, "-").replace(/-+/g, "-").slice(0, 64);
142353
142357
  return slug || void 0;
142354
142358
  }
142355
142359
  function normalizeText3(value) {
@@ -142422,26 +142426,31 @@ Examples:
142422
142426
  - "How's the project going?" \u2192 {"intent":"other","confidence":0.95,"stackHint":null,"projectSlug":null,"language":"en"}
142423
142427
  - "Oi, tudo bem?" \u2192 {"intent":"other","confidence":0.99,"stackHint":null,"projectSlug":null,"language":"pt"}
142424
142428
  - "Me faz um app que converte temperaturas" \u2192 {"intent":"create_project","confidence":0.9,"stackHint":null,"projectSlug":"conversor-temperaturas","language":"pt"}`;
142425
- async function classifyDmIntent(ctx, content, workspaceDir) {
142429
+ async function classifyDmIntent(ctx, content, _workspaceDir) {
142426
142430
  try {
142431
+ const runtime = ctx.runtime;
142432
+ if (!runtime?.subagent?.run) return null;
142427
142433
  const truncated = content.slice(0, MAX_CLASSIFY_LENGTH);
142428
142434
  const prompt = CLASSIFY_PROMPT_TEMPLATE.replace("$CONTENT", truncated.replace(/"/g, '\\"'));
142429
- const cliPath = resolveOpenClawCli({ homeDir: homedir3(), workspaceDir });
142430
- const sessionId = `dm-classify-${Date.now()}`;
142431
- const result = await ctx.runCommand(
142432
- [cliPath, "agent", "--local", "-m", prompt, "--session-id", sessionId, "--json"],
142433
- { timeoutMs: 15e3 }
142434
- );
142435
- const stdout = result.stdout ?? "";
142436
- if (!stdout.trim()) return null;
142437
- const parsed = extractJsonFromStdout(stdout);
142438
- if (!parsed) return null;
142439
- const text = parsed?.payloads?.[0]?.text;
142440
- const jsonStr = text ? text.replace(/^```(json)?/gm, "").replace(/```$/gm, "").trim() : JSON.stringify(parsed);
142435
+ const sessionKey = `dm-classify-${Date.now()}`;
142436
+ const { runId } = await runtime.subagent.run({
142437
+ sessionKey,
142438
+ message: prompt,
142439
+ extraSystemPrompt: "You are a JSON classifier. Return ONLY valid JSON, no markdown fences.",
142440
+ lane: "subagent",
142441
+ deliver: false,
142442
+ idempotencyKey: sessionKey
142443
+ });
142444
+ const waitResult = await runtime.subagent.waitForRun({ runId, timeoutMs: 15e3 });
142445
+ if (waitResult.status !== "ok") return null;
142446
+ const messages = await runtime.subagent.getSessionMessages({ sessionKey });
142447
+ const lastAssistant = messages?.filter((m2) => m2.role === "assistant").pop();
142448
+ const text = lastAssistant?.content ?? "";
142449
+ if (!text.trim()) return null;
142450
+ const jsonStr = text.replace(/^```(json)?/gm, "").replace(/```$/gm, "").trim();
142441
142451
  const intentData = JSON.parse(jsonStr);
142442
142452
  const validated = DmIntentSchema.safeParse(intentData);
142443
- if (!validated.success) return null;
142444
- return validated.data;
142453
+ return validated.success ? validated.data : null;
142445
142454
  } catch {
142446
142455
  return null;
142447
142456
  }
@@ -142454,6 +142463,21 @@ function isBootstrapCandidate(text) {
142454
142463
  return createCue && softwareCue;
142455
142464
  }
142456
142465
  function parseClarificationResponse(text, session) {
142466
+ if (session.pendingClarification === "name") {
142467
+ const trimmed = text.trim();
142468
+ const autoPatterns = /^(escolha|pick one|tanto faz|you choose|pode escolher|auto|skip)$/i;
142469
+ if (autoPatterns.test(trimmed)) {
142470
+ return { recognized: true, projectName: inferProjectSlug(session.rawIdea) ?? void 0, stackHint: session.stackHint ?? void 0 };
142471
+ }
142472
+ const nameField = parseField(text, ["project name", "nome do projeto", "nome", "name"]);
142473
+ if (nameField) {
142474
+ return { recognized: true, projectName: nameField, stackHint: session.stackHint ?? void 0 };
142475
+ }
142476
+ if (trimmed.length > 0 && trimmed.length <= 64) {
142477
+ return { recognized: true, projectName: trimmed, stackHint: session.stackHint ?? void 0 };
142478
+ }
142479
+ return { recognized: false };
142480
+ }
142457
142481
  const stackField = parseField(text, ["stack", "framework", "linguagem", "language"]);
142458
142482
  if (stackField) {
142459
142483
  return { recognized: true, stackHint: detectStackHint(stackField) ?? stackField };
@@ -142489,6 +142513,9 @@ function parseClarificationResponse(text, session) {
142489
142513
  return { recognized: false };
142490
142514
  }
142491
142515
  function buildClarificationMessage(parsed, pendingClarification, language = "pt") {
142516
+ if (pendingClarification === "name") {
142517
+ return BOOTSTRAP_MESSAGES.clarifyName[language];
142518
+ }
142492
142519
  if (pendingClarification === "stack_and_name" || !parsed.stackHint && !parsed.projectName) {
142493
142520
  return BOOTSTRAP_MESSAGES.clarifyBoth[language];
142494
142521
  }
@@ -142534,6 +142561,12 @@ function logBootstrapWarning(ctx, message) {
142534
142561
  }
142535
142562
  }
142536
142563
  async function classifyAndBootstrap(ctx, workspaceDir, conversationId, content) {
142564
+ await upsertTelegramBootstrapSession(workspaceDir, {
142565
+ conversationId,
142566
+ rawIdea: content,
142567
+ sourceRoute: { channel: "telegram", channelId: conversationId },
142568
+ status: "classifying"
142569
+ });
142537
142570
  const classification = await classifyDmIntent(ctx, content, workspaceDir);
142538
142571
  if (!classification || classification.intent !== "create_project" || classification.confidence < 0.7) {
142539
142572
  if (!classification) {
@@ -142643,6 +142676,28 @@ async function continueBootstrap(ctx, conversationId, workspaceDir, request2, so
142643
142676
  ));
142644
142677
  return;
142645
142678
  }
142679
+ if (!projectName) {
142680
+ const existingSession = await readTelegramBootstrapSession(workspaceDir, conversationId);
142681
+ const lang = existingSession?.language ?? "pt";
142682
+ await upsertTelegramBootstrapSession(workspaceDir, {
142683
+ conversationId,
142684
+ rawIdea: request2.rawIdea,
142685
+ stackHint: request2.stackHint ?? void 0,
142686
+ status: "clarifying",
142687
+ pendingClarification: "name",
142688
+ language: lang
142689
+ });
142690
+ await sendTelegramText(
142691
+ ctx,
142692
+ conversationId,
142693
+ buildClarificationMessage(
142694
+ { rawIdea: request2.rawIdea, projectName: void 0, stackHint: request2.stackHint ?? void 0 },
142695
+ "name",
142696
+ lang
142697
+ )
142698
+ );
142699
+ return;
142700
+ }
142646
142701
  const candidateSlug = inferProjectSlug(projectName ?? request2.rawIdea);
142647
142702
  if (candidateSlug) {
142648
142703
  const projects = await readProjects(workspaceDir).catch(() => null);
@@ -142872,7 +142927,7 @@ function registerTelegramBootstrapHook(api, ctx) {
142872
142927
  }
142873
142928
  const existingSession = await readTelegramBootstrapSession(workspaceDir, conversationId);
142874
142929
  const sessionIsExpired = existingSession != null && Date.parse(existingSession.suppressUntil) < Date.now();
142875
- if (existingSession && !sessionIsExpired && existingSession.status === "classifying") {
142930
+ if (existingSession && !sessionIsExpired && (existingSession.status === "classifying" || existingSession.status === "pending_classify")) {
142876
142931
  ctx.logger.info(`[telegram-bootstrap] LLM classification in progress for ${conversationId}, ignoring concurrent message`);
142877
142932
  return;
142878
142933
  }
@@ -142903,7 +142958,7 @@ function registerTelegramBootstrapHook(api, ctx) {
142903
142958
  conversationId,
142904
142959
  rawIdea: content,
142905
142960
  sourceRoute: { channel: "telegram", channelId: conversationId },
142906
- status: "classifying"
142961
+ status: "pending_classify"
142907
142962
  });
142908
142963
  classifyAndBootstrap(ctx, workspaceDir, conversationId, content).catch((err) => {
142909
142964
  logBootstrapWarning(ctx, `[telegram-bootstrap] LLM classify error: ${err instanceof Error ? err.message : String(err)}`);
@@ -142912,6 +142967,12 @@ function registerTelegramBootstrapHook(api, ctx) {
142912
142967
  return;
142913
142968
  }
142914
142969
  const parsed = parseBootstrapRequest(content);
142970
+ if (!parsed.projectName && ctx.runtime?.subagent?.run) {
142971
+ const classification = await classifyDmIntent(ctx, content, workspaceDir);
142972
+ if (classification?.projectSlug) {
142973
+ parsed.projectName = classification.projectSlug;
142974
+ }
142975
+ }
142915
142976
  const incomingRequest = {
142916
142977
  rawIdea: parsed.rawIdea,
142917
142978
  projectName: parsed.projectName ?? null,