@mestreyoda/fabrica 0.1.3 → 0.1.5

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.
@@ -1,43 +1,35 @@
1
1
  {
2
- "version": "1.0",
3
- "max_rounds": 2,
2
+ "version": "1.1",
3
+ "max_rounds": 1,
4
4
  "types": {
5
5
  "feature": {
6
6
  "round1": [
7
- { "id": "f1", "question": "Qual problema específico essa feature resolve para o usuário?", "required": true, "follow_up_if_vague": "Pode dar um exemplo concreto de quando o usuário sentiria falta dessa funcionalidade?" },
8
- { "id": "f2", "question": "Quem vai usar isso? Descreva o perfil do usuário principal.", "required": true, "follow_up_if_vague": um usuário final, admin, desenvolvedor, ou outro perfil?" },
9
- { "id": "f3", "question": "Qual é o fluxo principal? O que o usuário faz passo a passo?", "required": true, "follow_up_if_vague": "Tente descrever: 'O usuário clica em X, preenche Y, vê Z'." },
10
- { "id": "f4", "question": "Existe alguma restrição de tecnologia, prazo ou compatibilidade?", "required": false, "follow_up_if_vague": null },
11
- { "id": "f5", "question": "Esse fluxo exige login? Se sim, quais perfis existem e quais permissões cada perfil deve ter?", "required": false, "follow_up_if_vague": "Descreva pelo menos um exemplo de ação permitida e uma ação bloqueada por perfil." }
7
+ { "id": "f1", "question": "Qual o fluxo principal? O que o usuario faz e o que recebe de volta?", "required": true, "follow_up_if_vague": "Descreva o uso mais basico: entrada saida." },
8
+ { "id": "f2", "question": "Alguma restricao de tecnologia ou compatibilidade?", "required": false, "follow_up_if_vague": null }
12
9
  ]
13
10
  },
14
11
  "bugfix": {
15
12
  "round1": [
16
- { "id": "b1", "question": "O que está acontecendo de errado? Descreva o comportamento atual.", "required": true, "follow_up_if_vague": "Você alguma mensagem de erro? O que aparece na tela?" },
17
- { "id": "b2", "question": "O que deveria acontecer? Qual o comportamento esperado?", "required": true, "follow_up_if_vague": "Antes funcionava corretamente? Quando parou?" },
18
- { "id": "b3", "question": "Como reproduzir o bug? Passos exatos.", "required": true, "follow_up_if_vague": "Em qual página/tela? Com qual tipo de dado?" },
19
- { "id": "b4", "question": "Com que frequência acontece? Sempre, às vezes, em condições específicas?", "required": false, "follow_up_if_vague": null }
13
+ { "id": "b1", "question": "O que esta acontecendo vs. o que deveria acontecer?", "required": true, "follow_up_if_vague": "Voce ve alguma mensagem de erro?" },
14
+ { "id": "b2", "question": "Como reproduzir?", "required": true, "follow_up_if_vague": null }
20
15
  ]
21
16
  },
22
17
  "refactor": {
23
18
  "round1": [
24
- { "id": "r1", "question": "Qual parte do código precisa ser refatorada? Qual módulo/arquivo?", "required": true, "follow_up_if_vague": "Qual funcionalidade está nesse código?" },
25
- { "id": "r2", "question": "Qual o problema atual? (duplicação, complexidade, acoplamento, performance)", "required": true, "follow_up_if_vague": "O que dificulta trabalhar nesse código hoje?" },
26
- { "id": "r3", "question": "Qual o resultado esperado da refatoração?", "required": true, "follow_up_if_vague": "Quer que fique mais legível, mais rápido, ou mais testável?" }
19
+ { "id": "r1", "question": "Qual parte do codigo e qual o problema atual?", "required": true, "follow_up_if_vague": null },
20
+ { "id": "r2", "question": "Qual o resultado esperado da refatoracao?", "required": true, "follow_up_if_vague": null }
27
21
  ]
28
22
  },
29
23
  "research": {
30
24
  "round1": [
31
- { "id": "s1", "question": "O que precisa ser investigado ou avaliado?", "required": true, "follow_up_if_vague": uma tecnologia, abordagem, ou viabilidade de algo?" },
32
- { "id": "s2", "question": "Qual decisão essa pesquisa vai informar?", "required": true, "follow_up_if_vague": "O que muda se a conclusão for positiva vs negativa?" },
33
- { "id": "s3", "question": "Quais critérios definem sucesso da pesquisa?", "required": true, "follow_up_if_vague": "Qual entregável esperado? Documento, PoC, comparativo?" }
25
+ { "id": "s1", "question": "O que precisa ser investigado e qual decisao isso informa?", "required": true, "follow_up_if_vague": null },
26
+ { "id": "s2", "question": "Qual o entregavel? Documento, PoC, comparativo?", "required": true, "follow_up_if_vague": null }
34
27
  ]
35
28
  },
36
29
  "infra": {
37
30
  "round1": [
38
- { "id": "i1", "question": "O que precisa ser configurado/implantado/migrado?", "required": true, "follow_up_if_vague": CI/CD, deploy, banco de dados, monitoramento?" },
39
- { "id": "i2", "question": "Qual o ambiente alvo? (produção, staging, dev, local)", "required": true, "follow_up_if_vague": null },
40
- { "id": "i3", "question": "Há requisitos de downtime, rollback ou compatibilidade?", "required": false, "follow_up_if_vague": null }
31
+ { "id": "i1", "question": "O que precisa ser configurado e em qual ambiente?", "required": true, "follow_up_if_vague": null },
32
+ { "id": "i2", "question": "Ha requisitos de downtime ou rollback?", "required": false, "follow_up_if_vague": null }
41
33
  ]
42
34
  }
43
35
  }
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.3") {
111333
- return "0.1.3";
111332
+ if ("0.1.5") {
111333
+ return "0.1.5";
111334
111334
  }
111335
111335
  try {
111336
111336
  const pkgPath = path5.join(THIS_DIR, "..", "..", "package.json");
@@ -116084,30 +116084,33 @@ var init_conduct_interview = __esm({
116084
116084
  try {
116085
116085
  const questionsText = (payload.interview?.questions ?? []).map((q, i2) => `${i2 + 1}. ${q.question}`).join("\n");
116086
116086
  const answersText = Object.entries(payload.answers ?? {}).map(([key, value]) => `- ${key}: ${value}`).join("\n");
116087
- const prompt = `You are a software specification expert. Based on this project idea, answer the interview questions to produce a structured specification.
116087
+ const prompt = `You are a pragmatic senior engineer scoping a small project from a brief user request. Your job is to derive a tight, actionable specification \u2014 NOT to ask more questions.
116088
116088
 
116089
- Idea: ${payload.raw_idea}
116090
- Classification: ${type}
116089
+ User request: "${payload.raw_idea}"
116090
+ Type: ${type}
116091
116091
 
116092
- Questions:
116093
- ${questionsText}
116092
+ Context from earlier steps:
116093
+ ${answersText || "(none)"}
116094
116094
 
116095
- Existing answers:
116096
- ${answersText || "(none provided yet)"}
116095
+ Produce a concise JSON spec. The title should be a SHORT name (3-5 words, suitable as a repo name). Keep scope minimal \u2014 only what the user explicitly asked for.
116097
116096
 
116098
116097
  Return ONLY valid JSON (no markdown fences):
116099
116098
  {
116100
- "title": "<concise project title, max 120 chars>",
116101
- "objective": "<clear objective statement>",
116102
- "scope_v1": ["<scope item 1>", "<scope item 2>"],
116099
+ "title": "<short project name, 3-5 words, max 60 chars>",
116100
+ "objective": "<1-2 sentence objective>",
116101
+ "scope_v1": ["<concrete deliverable 1>", "<concrete deliverable 2>"],
116103
116102
  "out_of_scope": ["<item>"],
116104
- "acceptance_criteria": ["<specific, domain-aware AC 1>", "<AC 2>"],
116103
+ "acceptance_criteria": ["<specific, testable AC 1>", "<AC 2>"],
116105
116104
  "definition_of_done": ["Code reviewed and merged", "Tests pass", "QA contract passes"],
116106
116105
  "constraints": "<constraints or 'None specified'>",
116107
116106
  "risks": ["<risk 1>"]
116108
116107
  }
116109
116108
 
116110
- IMPORTANT: Acceptance criteria must be domain-specific, not generic.`;
116109
+ Rules:
116110
+ - Title must be a short name like "email-validator-cli" or "task-tracker-api", NOT a full sentence.
116111
+ - Acceptance criteria must be domain-specific and testable.
116112
+ - Do NOT invent features the user didn't ask for.
116113
+ - Keep it lean \u2014 a CLI that validates emails doesn't need auth, profiles, or config files.`;
116111
116114
  const result = await withLlmRetry(() => ctx.runCommand(resolveOpenClawCli({
116112
116115
  homeDir: ctx.homeDir,
116113
116116
  workspaceDir: ctx.workspaceDir
@@ -117982,7 +117985,7 @@ function normalizeIntakeText(value) {
117982
117985
  return trimmed || null;
117983
117986
  }
117984
117987
  function sanitizeRepoName(raw) {
117985
- return raw.normalize("NFKD").replace(/[^\x00-\x7F]/g, "").toLowerCase().replace(/[\s_]+/g, "-").replace(/[^a-z0-9._-]+/g, "-").replace(/[._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80).replace(/-+$/g, "");
117988
+ return raw.normalize("NFKD").replace(/[^\x00-\x7F]/g, "").toLowerCase().replace(/[\s_]+/g, "-").replace(/[^a-z0-9._-]+/g, "-").replace(/[._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40).replace(/-+$/g, "");
117986
117989
  }
117987
117990
  function isValidRepoName(name) {
117988
117991
  return name.length >= 3 && !REPO_NAME_BLOCKLIST.has(name);
@@ -142071,7 +142074,7 @@ init_migrate_layout();
142071
142074
  import { createHash as createHash6 } from "node:crypto";
142072
142075
  import fs38 from "node:fs/promises";
142073
142076
  import path39 from "node:path";
142074
- var SESSION_TTL_MS = 2 * 6e4;
142077
+ var SESSION_TTL_MS = 10 * 6e4;
142075
142078
  function sessionsDir(workspaceDir) {
142076
142079
  return path39.join(workspaceDir, DATA_DIR, "bootstrap-sessions");
142077
142080
  }
@@ -142540,9 +142543,11 @@ function registerTelegramBootstrapHook(api, ctx) {
142540
142543
  repoPath: existingSession.repoPath ?? null
142541
142544
  };
142542
142545
  ctx.logger.info(`[telegram-bootstrap] clarification resolved: stack=${mergedRequest.stackHint}, idea="${mergedRequest.rawIdea}" (conversation: ${conversationId})`);
142543
- await continueBootstrap(ctx, conversationId, workspaceDir, mergedRequest, existingSession.sourceRoute ?? {
142546
+ continueBootstrap(ctx, conversationId, workspaceDir, mergedRequest, existingSession.sourceRoute ?? {
142544
142547
  channel: "telegram",
142545
142548
  channelId: conversationId
142549
+ }).catch((err) => {
142550
+ logBootstrapWarning(ctx, `[telegram-bootstrap] unhandled pipeline error: ${err instanceof Error ? err.message : String(err)}`);
142546
142551
  });
142547
142552
  return;
142548
142553
  }
@@ -142562,10 +142567,14 @@ function registerTelegramBootstrapHook(api, ctx) {
142562
142567
  ctx.logger.info(`[telegram-bootstrap] duplicate completed DM ignored for conversation ${conversationId}`);
142563
142568
  return;
142564
142569
  }
142565
- if (sessionForHash.status !== "failed") {
142570
+ const isExpiredReceived = sessionForHash.status === "received" && Date.parse(sessionForHash.suppressUntil) < Date.now();
142571
+ if (sessionForHash.status !== "failed" && !isExpiredReceived) {
142566
142572
  ctx.logger.info(`[telegram-bootstrap] duplicate in-flight DM ignored for conversation ${conversationId}`);
142567
142573
  return;
142568
142574
  }
142575
+ if (isExpiredReceived) {
142576
+ ctx.logger.info(`[telegram-bootstrap] stale received session (expired) \u2014 restarting pipeline for conversation ${conversationId}`);
142577
+ }
142569
142578
  }
142570
142579
  const session = await upsertTelegramBootstrapSession(workspaceDir, {
142571
142580
  conversationId,
@@ -142589,9 +142598,11 @@ function registerTelegramBootstrapHook(api, ctx) {
142589
142598
  await sendTelegramText(ctx, conversationId, buildClarificationMessage(parsed, pendingClarification));
142590
142599
  return;
142591
142600
  }
142592
- await continueBootstrap(ctx, conversationId, workspaceDir, incomingRequest, {
142601
+ continueBootstrap(ctx, conversationId, workspaceDir, incomingRequest, {
142593
142602
  channel: "telegram",
142594
142603
  channelId: conversationId
142604
+ }).catch((err) => {
142605
+ logBootstrapWarning(ctx, `[telegram-bootstrap] unhandled pipeline error: ${err instanceof Error ? err.message : String(err)}`);
142595
142606
  });
142596
142607
  });
142597
142608
  }