@mestreyoda/fabrica 0.1.9 → 0.1.11

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.9") {
111333
- return "0.1.9";
111332
+ if ("0.1.11") {
111333
+ return "0.1.11";
111334
111334
  }
111335
111335
  try {
111336
111336
  const pkgPath = path5.join(THIS_DIR, "..", "..", "package.json");
@@ -142042,6 +142042,7 @@ import { createHash as createHash6 } from "node:crypto";
142042
142042
  import fs38 from "node:fs/promises";
142043
142043
  import path40 from "node:path";
142044
142044
  var SESSION_TTL_MS = 10 * 6e4;
142045
+ var CLASSIFYING_TTL_MS = 15e3;
142045
142046
  function sessionsDir(workspaceDir) {
142046
142047
  return path40.join(workspaceDir, DATA_DIR, "bootstrap-sessions");
142047
142048
  }
@@ -142066,8 +142067,9 @@ function buildBootstrapRequestFingerprint(input) {
142066
142067
  function buildBootstrapRequestHash(input) {
142067
142068
  return buildBootstrapRequestFingerprint(input);
142068
142069
  }
142069
- function nextSuppressUntil() {
142070
- return new Date(Date.now() + SESSION_TTL_MS).toISOString();
142070
+ function nextSuppressUntil(status) {
142071
+ const ttl = status === "classifying" ? CLASSIFYING_TTL_MS : SESSION_TTL_MS;
142072
+ return new Date(Date.now() + ttl).toISOString();
142071
142073
  }
142072
142074
  async function readTelegramBootstrapSession(workspaceDir, conversationId) {
142073
142075
  try {
@@ -142124,12 +142126,13 @@ async function upsertTelegramBootstrapSession(workspaceDir, input) {
142124
142126
  issueId: input.issueId ?? existing?.issueId ?? null,
142125
142127
  messageThreadId: input.messageThreadId ?? existing?.messageThreadId ?? null,
142126
142128
  projectChannelId: input.projectChannelId ?? existing?.projectChannelId ?? null,
142129
+ language: input.language ?? existing?.language,
142127
142130
  status: input.status,
142128
142131
  pendingClarification: input.pendingClarification !== void 0 ? input.pendingClarification : existing?.pendingClarification ?? null,
142129
142132
  orphanedArtifacts: input.orphanedArtifacts !== void 0 ? input.orphanedArtifacts : existing?.orphanedArtifacts ?? null,
142130
142133
  createdAt: existing?.createdAt ?? now2,
142131
142134
  updatedAt: now2,
142132
- suppressUntil: nextSuppressUntil(),
142135
+ suppressUntil: nextSuppressUntil(input.status),
142133
142136
  error: input.error ?? null
142134
142137
  };
142135
142138
  await writeTelegramBootstrapSession(workspaceDir, session);
@@ -142144,6 +142147,30 @@ function shouldSuppressTelegramBootstrapReply(session, request2) {
142144
142147
  }
142145
142148
 
142146
142149
  // lib/dispatch/telegram-bootstrap-hook.ts
142150
+ var BOOTSTRAP_MESSAGES = {
142151
+ ack: {
142152
+ pt: "Recebi! Vou analisar e come\xE7ar a montar o projeto...",
142153
+ en: "Got it! I'll analyze your request and start setting up the project..."
142154
+ },
142155
+ clarifyStack: {
142156
+ pt: "Qual stack voc\xEA quer usar? (Python, Node.js, Go, Java...)",
142157
+ en: "Which stack do you want to use? (Python, Node.js, Go, Java...)"
142158
+ },
142159
+ clarifyBoth: {
142160
+ pt: "Beleza! S\xF3 preciso de duas coisas pra criar:\n\n1. Qual stack? (Python, Node.js, Go, Java...)\n2. Quer dar um nome pro projeto? Se n\xE3o, eu invento um.",
142161
+ en: "Great! I just need two things:\n\n1. Which stack? (Python, Node.js, Go, Java...)\n2. Want to name the project? If not, I'll pick one."
142162
+ },
142163
+ clarifyStackFollowUp: {
142164
+ pt: "N\xE3o consegui identificar a stack. Pode me dizer qual linguagem/framework voc\xEA quer usar? Ex: Python, Node.js, Go, Java...",
142165
+ 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..."
142166
+ },
142167
+ registered: {
142168
+ pt: (name, link) => `Projeto "${name}" registrado.
142169
+ Vou continuar o fluxo em ${link}`,
142170
+ en: (name, link) => `Project "${name}" registered.
142171
+ I'll continue the flow at ${link}`
142172
+ }
142173
+ };
142147
142174
  function inferProjectSlug(text) {
142148
142175
  const slug = text.toLowerCase().normalize("NFKD").replace(/[^\w\s-]/g, "").trim().replace(/\s+/g, "-").replace(/-+/g, "-").slice(0, 64);
142149
142176
  return slug || void 0;
@@ -142202,21 +142229,22 @@ var DmIntentSchema = external_exports.object({
142202
142229
  intent: external_exports.enum(["create_project", "other"]),
142203
142230
  confidence: external_exports.number().min(0).max(1),
142204
142231
  stackHint: external_exports.string().nullable().optional(),
142205
- projectSlug: external_exports.string().nullable().optional()
142232
+ projectSlug: external_exports.string().nullable().optional(),
142233
+ language: external_exports.enum(["pt", "en"]).optional().default("pt")
142206
142234
  });
142207
142235
  var CLASSIFY_PROMPT_TEMPLATE = `Classify this Telegram DM. Is the user asking to create/build a new software project, or is it something else (question, greeting, status check)?
142208
142236
 
142209
142237
  Message: "$CONTENT"
142210
142238
 
142211
142239
  Return ONLY valid JSON:
142212
- {"intent": "create_project" | "other", "confidence": 0.0-1.0, "stackHint": "<detected stack or null>", "projectSlug": "<suggested slug or null>"}
142240
+ {"intent": "create_project" | "other", "confidence": 0.0-1.0, "stackHint": "<detected stack or null>", "projectSlug": "<suggested slug or null>", "language": "pt" | "en"}
142213
142241
 
142214
142242
  Examples:
142215
- - "Cria uma CLI Python que valida CPF" \u2192 {"intent":"create_project","confidence":0.95,"stackHint":"python-cli","projectSlug":"validador-cpf-cli"}
142216
- - "Build me a REST API for tasks" \u2192 {"intent":"create_project","confidence":0.9,"stackHint":"fastapi","projectSlug":"task-api"}
142217
- - "How's the project going?" \u2192 {"intent":"other","confidence":0.95,"stackHint":null,"projectSlug":null}
142218
- - "Oi, tudo bem?" \u2192 {"intent":"other","confidence":0.99,"stackHint":null,"projectSlug":null}
142219
- - "Me faz um app que converte temperaturas" \u2192 {"intent":"create_project","confidence":0.9,"stackHint":null,"projectSlug":"conversor-temperaturas"}`;
142243
+ - "Cria uma CLI Python que valida CPF" \u2192 {"intent":"create_project","confidence":0.95,"stackHint":"python-cli","projectSlug":"validador-cpf-cli","language":"pt"}
142244
+ - "Build me a REST API for tasks" \u2192 {"intent":"create_project","confidence":0.9,"stackHint":"fastapi","projectSlug":"task-api","language":"en"}
142245
+ - "How's the project going?" \u2192 {"intent":"other","confidence":0.95,"stackHint":null,"projectSlug":null,"language":"en"}
142246
+ - "Oi, tudo bem?" \u2192 {"intent":"other","confidence":0.99,"stackHint":null,"projectSlug":null,"language":"pt"}
142247
+ - "Me faz um app que converte temperaturas" \u2192 {"intent":"create_project","confidence":0.9,"stackHint":null,"projectSlug":"conversor-temperaturas","language":"pt"}`;
142220
142248
  async function classifyDmIntent(ctx, content, workspaceDir) {
142221
142249
  try {
142222
142250
  const truncated = content.slice(0, MAX_CLASSIFY_LENGTH);
@@ -142283,26 +142311,23 @@ function parseClarificationResponse(text, session) {
142283
142311
  }
142284
142312
  return { recognized: false };
142285
142313
  }
142286
- function buildClarificationMessage(parsed, pendingClarification) {
142314
+ function buildClarificationMessage(parsed, pendingClarification, language = "pt") {
142287
142315
  if (pendingClarification === "stack_and_name" || !parsed.stackHint && !parsed.projectName) {
142288
- return `Beleza! S\xF3 preciso de duas coisas pra criar:
142289
-
142290
- 1. Qual stack? (Python, Node.js, Go, Java...)
142291
- 2. Quer dar um nome pro projeto? Se n\xE3o, eu invento um.`;
142316
+ return BOOTSTRAP_MESSAGES.clarifyBoth[language];
142292
142317
  }
142293
- return `Qual stack voc\xEA quer usar? (Python, Node.js, Go, Java...)`;
142318
+ return BOOTSTRAP_MESSAGES.clarifyStack[language];
142294
142319
  }
142295
142320
  function buildFollowUpClarification(session) {
142296
- if (!session.stackHint) {
142297
- return `N\xE3o consegui identificar a stack. Pode me dizer qual linguagem/framework voc\xEA quer usar? Ex: Python, Node.js, Go, Java...`;
142298
- }
142299
- return `Pode me dar mais detalhes sobre o que voc\xEA quer construir?`;
142321
+ const lang = session.language ?? "pt";
142322
+ if (!session.stackHint) return BOOTSTRAP_MESSAGES.clarifyStackFollowUp[lang];
142323
+ return lang === "en" ? "Can you give me more details about what you want to build?" : "Pode me dar mais detalhes sobre o que voc\xEA quer construir?";
142300
142324
  }
142301
- function buildDmAck(projectName, topicName) {
142302
- return [
142303
- `Projeto "${projectName}" registrado.`,
142304
- `Vou continuar o fluxo no t\xF3pico "${topicName}" do grupo de projetos.`
142305
- ].join("\n");
142325
+ function buildTopicDeepLink(chatId, topicId) {
142326
+ const stripped = chatId.replace(/^-100/, "");
142327
+ return `https://t.me/c/${stripped}/${topicId}`;
142328
+ }
142329
+ function buildDmAck(projectName, topicLink, language = "pt") {
142330
+ return BOOTSTRAP_MESSAGES.registered[language](projectName, topicLink);
142306
142331
  }
142307
142332
  function buildTopicKickoff(projectName, idea) {
142308
142333
  return [
@@ -142340,7 +142365,8 @@ async function classifyAndBootstrap(ctx, workspaceDir, conversationId, content)
142340
142365
  await deleteTelegramBootstrapSession(workspaceDir, conversationId);
142341
142366
  return;
142342
142367
  }
142343
- await sendTelegramText(ctx, conversationId, "Recebi! Vou analisar e come\xE7ar a montar o projeto...");
142368
+ const language = classification.language ?? "pt";
142369
+ await sendTelegramText(ctx, conversationId, BOOTSTRAP_MESSAGES.ack[language]);
142344
142370
  const parsed = parseBootstrapRequest(content);
142345
142371
  if (classification.stackHint && !parsed.stackHint) {
142346
142372
  parsed.stackHint = classification.stackHint;
@@ -142374,7 +142400,8 @@ async function classifyAndBootstrap(ctx, workspaceDir, conversationId, content)
142374
142400
  ...incomingRequest,
142375
142401
  sourceRoute,
142376
142402
  sourceChannel: "telegram",
142377
- status: "received"
142403
+ status: "received",
142404
+ language
142378
142405
  });
142379
142406
  if (!parsed.stackHint) {
142380
142407
  const pendingClarification = !parsed.projectName ? "stack_and_name" : "stack";
@@ -142383,9 +142410,10 @@ async function classifyAndBootstrap(ctx, workspaceDir, conversationId, content)
142383
142410
  ...incomingRequest,
142384
142411
  sourceRoute: session.sourceRoute,
142385
142412
  status: "clarifying",
142386
- pendingClarification
142413
+ pendingClarification,
142414
+ language
142387
142415
  });
142388
- await sendTelegramText(ctx, conversationId, buildClarificationMessage(parsed, pendingClarification));
142416
+ await sendTelegramText(ctx, conversationId, buildClarificationMessage(parsed, pendingClarification, language));
142389
142417
  return;
142390
142418
  }
142391
142419
  continueBootstrap(ctx, conversationId, workspaceDir, incomingRequest, sourceRoute).catch((err) => {
@@ -142547,7 +142575,8 @@ Erro: ${result.error ?? "erro desconhecido"}`
142547
142575
  logBootstrapWarning(ctx, `[telegram-bootstrap] immediate projectTick failed: ${error48 instanceof Error ? error48.message : String(error48)}`);
142548
142576
  });
142549
142577
  }
142550
- await sendTelegramText(ctx, conversationId, buildDmAck(resolvedProjectName, `${projectChannelId}:${messageThreadId}`));
142578
+ const sessionLang = currentSession?.language ?? "pt";
142579
+ await sendTelegramText(ctx, conversationId, buildDmAck(resolvedProjectName, buildTopicDeepLink(String(projectChannelId), messageThreadId), sessionLang));
142551
142580
  await upsertTelegramBootstrapSession(workspaceDir, {
142552
142581
  conversationId,
142553
142582
  ...incomingRequest,
@@ -142582,8 +142611,12 @@ function registerTelegramBootstrapHook(api, ctx) {
142582
142611
  api.on("before_prompt_build", async (_event, eventCtx) => {
142583
142612
  const hookCtx = eventCtx;
142584
142613
  if (hookCtx.channelId !== "telegram") return {};
142585
- const conversationId = String(hookCtx.conversationId ?? "").trim();
142586
- if (!conversationId || conversationId.includes(":topic:") || conversationId.startsWith("-")) return {};
142614
+ const sessionKey = hookCtx.sessionKey ?? "";
142615
+ if (sessionKey.includes(":topic:") || sessionKey.includes(":group:")) return {};
142616
+ const sessionKeyParts = sessionKey.split(":");
142617
+ const chatId = sessionKeyParts.length >= 5 ? sessionKeyParts[sessionKeyParts.length - 1] : "";
142618
+ const conversationId = chatId ? `telegram:${chatId}` : "";
142619
+ if (!conversationId) return {};
142587
142620
  const workspaceDir = resolveWorkspaceDir(ctx.config);
142588
142621
  if (!workspaceDir) return {};
142589
142622
  const session = await readTelegramBootstrapSession(workspaceDir, conversationId);
@@ -142596,6 +142629,21 @@ function registerTelegramBootstrapHook(api, ctx) {
142596
142629
  ].join("\n")
142597
142630
  };
142598
142631
  });
142632
+ api.on("message_sending", async (event, eventCtx) => {
142633
+ const hookCtx = eventCtx;
142634
+ if (hookCtx.channelId !== "telegram") return;
142635
+ const sendEvent = event;
142636
+ const rawTo = String(sendEvent.to ?? "").trim();
142637
+ if (!rawTo || rawTo.startsWith("-")) return;
142638
+ const conversationId = rawTo.includes(":") ? rawTo : `telegram:${rawTo}`;
142639
+ if (conversationId.includes(":topic:")) return;
142640
+ const workspaceDir = resolveWorkspaceDir(ctx.config);
142641
+ if (!workspaceDir) return;
142642
+ const session = await readTelegramBootstrapSession(workspaceDir, conversationId);
142643
+ if (session && session.status !== "completed" && session.status !== "failed" && shouldSuppressTelegramBootstrapReply(session)) {
142644
+ return { cancel: true };
142645
+ }
142646
+ });
142599
142647
  api.on("message_received", async (event, eventCtx) => {
142600
142648
  if (eventCtx.channelId !== "telegram") return;
142601
142649
  const telegramConfig = readFabricaTelegramConfig(ctx.pluginConfig);
@@ -142613,6 +142661,10 @@ function registerTelegramBootstrapHook(api, ctx) {
142613
142661
  }
142614
142662
  const existingSession = await readTelegramBootstrapSession(workspaceDir, conversationId);
142615
142663
  const sessionIsExpired = existingSession != null && Date.parse(existingSession.suppressUntil) < Date.now();
142664
+ if (existingSession && !sessionIsExpired && existingSession.status === "classifying") {
142665
+ ctx.logger.info(`[telegram-bootstrap] LLM classification in progress for ${conversationId}, ignoring concurrent message`);
142666
+ return;
142667
+ }
142616
142668
  if (existingSession?.status === "clarifying" && !sessionIsExpired) {
142617
142669
  const clarResult = parseClarificationResponse(content, existingSession);
142618
142670
  if (!clarResult.recognized) {
@@ -142674,7 +142726,8 @@ function registerTelegramBootstrapHook(api, ctx) {
142674
142726
  ctx.logger.info(`[telegram-bootstrap] stale received session (expired) \u2014 restarting pipeline for conversation ${conversationId}`);
142675
142727
  }
142676
142728
  }
142677
- await sendTelegramText(ctx, conversationId, "Recebi! Vou analisar e come\xE7ar a montar o projeto...");
142729
+ const language = /\b(cria|crie|criar|construa|desenvolva|registre|novo projeto)\b/i.test(content) ? "pt" : "en";
142730
+ await sendTelegramText(ctx, conversationId, BOOTSTRAP_MESSAGES.ack[language]);
142678
142731
  const session = await upsertTelegramBootstrapSession(workspaceDir, {
142679
142732
  conversationId,
142680
142733
  ...incomingRequest,
@@ -142683,7 +142736,8 @@ function registerTelegramBootstrapHook(api, ctx) {
142683
142736
  channelId: conversationId
142684
142737
  },
142685
142738
  sourceChannel: "telegram",
142686
- status: "received"
142739
+ status: "received",
142740
+ language
142687
142741
  });
142688
142742
  if (!parsed.stackHint) {
142689
142743
  const pendingClarification = !parsed.projectName ? "stack_and_name" : "stack";
@@ -142692,9 +142746,10 @@ function registerTelegramBootstrapHook(api, ctx) {
142692
142746
  ...incomingRequest,
142693
142747
  sourceRoute: session.sourceRoute,
142694
142748
  status: "clarifying",
142695
- pendingClarification
142749
+ pendingClarification,
142750
+ language
142696
142751
  });
142697
- await sendTelegramText(ctx, conversationId, buildClarificationMessage(parsed, pendingClarification));
142752
+ await sendTelegramText(ctx, conversationId, buildClarificationMessage(parsed, pendingClarification, language));
142698
142753
  return;
142699
142754
  }
142700
142755
  continueBootstrap(ctx, conversationId, workspaceDir, incomingRequest, {