@mestreyoda/fabrica 0.2.19 → 0.2.20
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 +120 -9
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -113905,8 +113905,8 @@ import fsSync from "node:fs";
|
|
|
113905
113905
|
import path5 from "node:path";
|
|
113906
113906
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
113907
113907
|
function getCurrentVersion() {
|
|
113908
|
-
if ("0.2.
|
|
113909
|
-
return "0.2.
|
|
113908
|
+
if ("0.2.20") {
|
|
113909
|
+
return "0.2.20";
|
|
113910
113910
|
}
|
|
113911
113911
|
try {
|
|
113912
113912
|
const pkgPath = path5.join(THIS_DIR, "..", "..", "package.json");
|
|
@@ -126282,6 +126282,25 @@ async function validatePrExistsForDeveloper(issueId, repoPath, provider, runComm
|
|
|
126282
126282
|
const branchPrIsReviewable = !!branchPr?.url && branchPr.state !== PrState.MERGED && branchPr.state !== PrState.CLOSED;
|
|
126283
126283
|
const prStatus = preferIssuePr ? issuePrIsReviewable ? issuePr : branchPr ?? issuePr : branchPrIsReviewable ? branchPr : issuePr;
|
|
126284
126284
|
if (!prStatus.url || prStatus.state === PrState.MERGED || prStatus.state === PrState.CLOSED) {
|
|
126285
|
+
if (preferIssuePr && isCurrentProjectBaseBranch(branchName, baseBranch)) {
|
|
126286
|
+
const currentBase = branchName || baseBranch || "main";
|
|
126287
|
+
const suggestedBranch = `feature/${issueId}-${projectSlug.replace(/[^a-z0-9]+/g, "-").slice(0, 40)}`;
|
|
126288
|
+
throw new Error(
|
|
126289
|
+
`Cannot mark work_finish(done) while on the base branch ("${currentBase}") without an open PR.
|
|
126290
|
+
|
|
126291
|
+
You must implement changes on a feature branch and open a PR before calling work_finish.
|
|
126292
|
+
|
|
126293
|
+
Steps to fix:
|
|
126294
|
+
1. git worktree add ../${projectSlug}.worktrees/${suggestedBranch} -b ${suggestedBranch}
|
|
126295
|
+
2. cd ../${projectSlug}.worktrees/${suggestedBranch}
|
|
126296
|
+
3. Implement the changes there, commit, push, and create a PR:
|
|
126297
|
+
git push -u origin ${suggestedBranch}
|
|
126298
|
+
gh pr create --base ${baseBranch ?? "main"} --head ${suggestedBranch} --title "feat: ..." --body "Closes #${issueId}"
|
|
126299
|
+
4. Then call work_finish again.
|
|
126300
|
+
|
|
126301
|
+
If the worktree already exists, cd into it and continue from there.`
|
|
126302
|
+
);
|
|
126303
|
+
}
|
|
126285
126304
|
const currentBranch = branchName || "current-branch";
|
|
126286
126305
|
const reason = !prStatus.url ? `\u2717 No PR found for branch: ${currentBranch}` : prStatus.state === PrState.MERGED ? `\u2717 Last linked PR is already merged: ${prStatus.url}` : `\u2717 Last linked PR is closed and not reviewable: ${prStatus.url}`;
|
|
126287
126306
|
throw new Error(
|
|
@@ -126290,7 +126309,7 @@ async function validatePrExistsForDeveloper(issueId, repoPath, provider, runComm
|
|
|
126290
126309
|
${reason}
|
|
126291
126310
|
|
|
126292
126311
|
Please create a PR first:
|
|
126293
|
-
gh pr create --base main --head ${currentBranch} --title "..." --body "
|
|
126312
|
+
gh pr create --base ${baseBranch ?? "main"} --head ${currentBranch} --title "..." --body "Closes #${issueId}"
|
|
126294
126313
|
|
|
126295
126314
|
Then call work_finish again.`
|
|
126296
126315
|
);
|
|
@@ -139671,11 +139690,44 @@ var createTaskStep = {
|
|
|
139671
139690
|
};
|
|
139672
139691
|
|
|
139673
139692
|
// lib/intake/lib/triage-logic.ts
|
|
139674
|
-
function
|
|
139675
|
-
|
|
139676
|
-
|
|
139677
|
-
|
|
139678
|
-
|
|
139693
|
+
function detectRawIdeaComplexity(rawIdea) {
|
|
139694
|
+
const text = rawIdea.toLowerCase();
|
|
139695
|
+
const signals = [];
|
|
139696
|
+
const subsystemPatterns = [
|
|
139697
|
+
[/\b(worker|background.?job|queue|celery|bull|sidekiq|task.?runner)\b/i, "background-worker"],
|
|
139698
|
+
[/\b(websocket|server.?sent|sse|real.?time|realtime|socket\.io|push.?notif)\b/i, "realtime"],
|
|
139699
|
+
[/\b(auth|oauth|jwt|login|register|session|user.?account|signup)\b/i, "auth"],
|
|
139700
|
+
[/\b(notif|alert|email|sms|webhook|subscription|subscribe)\b/i, "notifications"],
|
|
139701
|
+
[/\b(database|banco|db|postgres|mysql|mongodb|redis|sqlite|orm)\b/i, "database"],
|
|
139702
|
+
[/\b(api\s+rest|rest\s+api|endpoint|rota|route|graphql|grpc)\b/i, "api-layer"],
|
|
139703
|
+
[/\b(docker|kubernetes|k8s|deploy|ci|cd|pipeline)\b/i, "infra"],
|
|
139704
|
+
[/\b(dashboard|frontend|interface|ui|tela|p[aá]gina)\b/i, "frontend"]
|
|
139705
|
+
];
|
|
139706
|
+
for (const [regex, label] of subsystemPatterns) {
|
|
139707
|
+
if (regex.test(text)) signals.push(label);
|
|
139708
|
+
}
|
|
139709
|
+
let floor = null;
|
|
139710
|
+
if (signals.length >= 4) floor = "large";
|
|
139711
|
+
else if (signals.length >= 3) floor = "medium";
|
|
139712
|
+
else if (signals.length >= 2) floor = "medium";
|
|
139713
|
+
return { floor, signals };
|
|
139714
|
+
}
|
|
139715
|
+
function calculateEffort(filesChanged, acCount, rawIdea) {
|
|
139716
|
+
let effort;
|
|
139717
|
+
if (filesChanged <= 3 && acCount <= 3) effort = "small";
|
|
139718
|
+
else if (filesChanged <= 10 && acCount <= 7) effort = "medium";
|
|
139719
|
+
else if (filesChanged <= 25 && acCount <= 15) effort = "large";
|
|
139720
|
+
else effort = "xlarge";
|
|
139721
|
+
if (rawIdea) {
|
|
139722
|
+
const { floor } = detectRawIdeaComplexity(rawIdea);
|
|
139723
|
+
if (floor) {
|
|
139724
|
+
const ORDER = ["small", "medium", "large", "xlarge"];
|
|
139725
|
+
if (ORDER.indexOf(floor) > ORDER.indexOf(effort)) {
|
|
139726
|
+
effort = floor;
|
|
139727
|
+
}
|
|
139728
|
+
}
|
|
139729
|
+
}
|
|
139730
|
+
return effort;
|
|
139679
139731
|
}
|
|
139680
139732
|
function calculatePriority(type, effort, totalRisks, matrix) {
|
|
139681
139733
|
for (const rule of matrix.priority_rules_v2) {
|
|
@@ -139742,7 +139794,7 @@ function determineLevel(effort, targetState) {
|
|
|
139742
139794
|
return level;
|
|
139743
139795
|
}
|
|
139744
139796
|
function runTriageLogic(input, matrix) {
|
|
139745
|
-
const effort = calculateEffort(input.filesChanged, input.acCount);
|
|
139797
|
+
const effort = calculateEffort(input.filesChanged, input.acCount, input.rawIdea);
|
|
139746
139798
|
const { priority, label: priorityLabel } = calculatePriority(input.type, effort, input.totalRisks, matrix);
|
|
139747
139799
|
const effortLabel = matrix.effort_rules[effort]?.label ?? `effort:${effort}`;
|
|
139748
139800
|
const typeLabel = matrix.auto_labels[input.type] ?? "";
|
|
@@ -140996,6 +141048,22 @@ var BOOTSTRAP_MESSAGES = {
|
|
|
140996
141048
|
pt: "Como voc\xEA quer chamar o projeto? Se preferir, posso escolher um nome.",
|
|
140997
141049
|
en: "What do you want to name the project? If you prefer, I can pick one."
|
|
140998
141050
|
},
|
|
141051
|
+
clarifyScope: {
|
|
141052
|
+
pt: (idea) => `Recebi! Seu pedido envolve v\xE1rios subsistemas (autentica\xE7\xE3o, notifica\xE7\xF5es, worker, banco de dados...). Para montar uma spec de qualidade, preciso de algumas defini\xE7\xF5es:
|
|
141053
|
+
|
|
141054
|
+
1. **Stack/linguagem**: qual prefere? (Python/FastAPI, Node.js/Express, Go...)
|
|
141055
|
+
2. **Banco de dados**: PostgreSQL, MongoDB, Redis, outro?
|
|
141056
|
+
3. **Autentica\xE7\xE3o**: JWT, OAuth2, sess\xE3o?
|
|
141057
|
+
|
|
141058
|
+
Se preferir deixar a escolha comigo, responda "livre" e eu decido.`,
|
|
141059
|
+
en: (idea) => `Got it! Your request involves multiple subsystems (auth, notifications, background worker, DB...). To produce a quality spec, I need a few decisions:
|
|
141060
|
+
|
|
141061
|
+
1. **Stack/language**: which do you prefer? (Python/FastAPI, Node.js/Express, Go...)
|
|
141062
|
+
2. **Database**: PostgreSQL, MongoDB, Redis, other?
|
|
141063
|
+
3. **Auth**: JWT, OAuth2, session?
|
|
141064
|
+
|
|
141065
|
+
If you want me to choose, reply "your call" and I'll decide.`
|
|
141066
|
+
},
|
|
140999
141067
|
registered: {
|
|
141000
141068
|
pt: (name, link) => `Projeto "${name}" registrado.
|
|
141001
141069
|
Vou continuar o fluxo em ${link}`,
|
|
@@ -141003,6 +141071,28 @@ Vou continuar o fluxo em ${link}`,
|
|
|
141003
141071
|
I'll continue the flow at ${link}`
|
|
141004
141072
|
}
|
|
141005
141073
|
};
|
|
141074
|
+
function detectScopeAmbiguity(rawIdea, stackHint) {
|
|
141075
|
+
const text = rawIdea.toLowerCase();
|
|
141076
|
+
if (/\b(livre|free.?choice|your.?call|pode.?escolher|voc[eê].?decide|qualquer)\b/i.test(text)) {
|
|
141077
|
+
return false;
|
|
141078
|
+
}
|
|
141079
|
+
const subsystems = [
|
|
141080
|
+
/\b(worker|background.?job|queue|task.?runner|celery|bull)\b/i,
|
|
141081
|
+
/\b(websocket|sse|real.?time|realtime|push.?notif)\b/i,
|
|
141082
|
+
/\b(auth|oauth|jwt|login|register|signup|autenticac)\b/i,
|
|
141083
|
+
/\b(notif|alert|assinatura|subscription|subscribe|email|sms)\b/i,
|
|
141084
|
+
/\b(banco|database|db|postgres|mysql|mongodb|redis|sqlite)\b/i,
|
|
141085
|
+
/\b(api\s+rest|rest\s+api|endpoint|graphql|grpc)\b/i,
|
|
141086
|
+
/\b(dashboard|frontend|interface|ui|tela)\b/i
|
|
141087
|
+
];
|
|
141088
|
+
const matchedSubsystems = subsystems.filter((r2) => r2.test(text)).length;
|
|
141089
|
+
if (matchedSubsystems < 3) return false;
|
|
141090
|
+
const hasExplicitDB = /\b(postgres(ql)?|mysql|mongodb|mongo|redis|sqlite|supabase|dynamodb|cockroach)\b/i.test(text);
|
|
141091
|
+
const hasExplicitAuth = /\b(jwt|oauth2?|basic.?auth|api.?key|session.?based|cookie.?auth)\b/i.test(text);
|
|
141092
|
+
const hasExplicitStack = !!stackHint && !["api", "rest-api", "backend"].includes(stackHint);
|
|
141093
|
+
const unspecifiedDimensions = [!hasExplicitDB, !hasExplicitAuth, !hasExplicitStack].filter(Boolean).length;
|
|
141094
|
+
return unspecifiedDimensions >= 2;
|
|
141095
|
+
}
|
|
141006
141096
|
function inferProjectSlug(text) {
|
|
141007
141097
|
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();
|
|
141008
141098
|
if (!cleaned) cleaned = text;
|
|
@@ -141471,6 +141561,27 @@ async function handleTelegramBootstrapDmMessage(ctx, rawConversationId, content)
|
|
|
141471
141561
|
));
|
|
141472
141562
|
return;
|
|
141473
141563
|
}
|
|
141564
|
+
if (detectScopeAmbiguity(content, parsed.stackHint)) {
|
|
141565
|
+
const existingSession2 = await readTelegramBootstrapSession(workspaceDir, conversationId);
|
|
141566
|
+
const alreadyAskedScope = existingSession2?.pendingClarification === "scope";
|
|
141567
|
+
if (!alreadyAskedScope) {
|
|
141568
|
+
await upsertTelegramBootstrapSession(workspaceDir, {
|
|
141569
|
+
conversationId,
|
|
141570
|
+
rawIdea: content,
|
|
141571
|
+
stackHint: parsed.stackHint ?? void 0,
|
|
141572
|
+
projectName: parsed.projectSlug ?? void 0,
|
|
141573
|
+
status: "clarifying",
|
|
141574
|
+
pendingClarification: "scope",
|
|
141575
|
+
language
|
|
141576
|
+
});
|
|
141577
|
+
const clarifyMsg = BOOTSTRAP_MESSAGES.clarifyScope[language](content);
|
|
141578
|
+
await sendTelegramText(ctx, rawConversationId, clarifyMsg);
|
|
141579
|
+
return;
|
|
141580
|
+
}
|
|
141581
|
+
if (existingSession2?.stackHint) {
|
|
141582
|
+
incomingRequest.stackHint = existingSession2.stackHint;
|
|
141583
|
+
}
|
|
141584
|
+
}
|
|
141474
141585
|
const handled = await runBootstrapPreflightOrFail(
|
|
141475
141586
|
ctx,
|
|
141476
141587
|
conversationId,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mestreyoda/fabrica",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.20",
|
|
4
4
|
"description": "Autonomous software engineering pipeline for OpenClaw. Turns ideas into deployed code via intake, dispatch, review, test, and merge.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|