@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.
- package/configs/interview-templates.json +12 -20
- package/dist/index.js +30 -19
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
|
@@ -1,43 +1,35 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.
|
|
3
|
-
"max_rounds":
|
|
2
|
+
"version": "1.1",
|
|
3
|
+
"max_rounds": 1,
|
|
4
4
|
"types": {
|
|
5
5
|
"feature": {
|
|
6
6
|
"round1": [
|
|
7
|
-
{ "id": "f1", "question": "Qual
|
|
8
|
-
{ "id": "f2", "question": "
|
|
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
|
|
17
|
-
{ "id": "b2", "question": "
|
|
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
|
|
25
|
-
{ "id": "r2", "question": "Qual o
|
|
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
|
|
32
|
-
{ "id": "s2", "question": "Qual
|
|
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
|
|
39
|
-
{ "id": "i2", "question": "
|
|
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.
|
|
111333
|
-
return "0.1.
|
|
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
|
|
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
|
-
|
|
116090
|
-
|
|
116089
|
+
User request: "${payload.raw_idea}"
|
|
116090
|
+
Type: ${type}
|
|
116091
116091
|
|
|
116092
|
-
|
|
116093
|
-
${
|
|
116092
|
+
Context from earlier steps:
|
|
116093
|
+
${answersText || "(none)"}
|
|
116094
116094
|
|
|
116095
|
-
|
|
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": "<
|
|
116101
|
-
"objective": "<
|
|
116102
|
-
"scope_v1": ["<
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|