@mestreyoda/fabrica 0.1.0
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/ARCHITECTURE.md +87 -0
- package/LICENSE +21 -0
- package/README.md +289 -0
- package/defaults/AGENTS.md +150 -0
- package/defaults/HEARTBEAT.md +3 -0
- package/defaults/IDENTITY.md +6 -0
- package/defaults/SOUL.md +39 -0
- package/defaults/TOOLS.md +15 -0
- package/defaults/fabrica/prompts/architect.md +147 -0
- package/defaults/fabrica/prompts/developer.md +211 -0
- package/defaults/fabrica/prompts/reviewer.md +114 -0
- package/defaults/fabrica/prompts/security-checklist.md +58 -0
- package/defaults/fabrica/prompts/tester.md +150 -0
- package/defaults/fabrica/workflow.yaml +184 -0
- package/dist/index.js +143075 -0
- package/dist/index.js.map +7 -0
- package/dist/lib/worker.cjs +214 -0
- package/dist/worker.cjs +4754 -0
- package/fabrica.manifest.json +24 -0
- package/genesis/configs/classification-rules.json +32 -0
- package/genesis/configs/interview-templates.json +73 -0
- package/genesis/configs/labels.json +202 -0
- package/genesis/configs/triage-matrix.json +39 -0
- package/genesis/scripts/classify-idea.sh +161 -0
- package/genesis/scripts/conduct-interview.sh +199 -0
- package/genesis/scripts/create-task.sh +797 -0
- package/genesis/scripts/delivery-target-lib.sh +88 -0
- package/genesis/scripts/generate-qa-contract.sh +188 -0
- package/genesis/scripts/generate-spec.sh +171 -0
- package/genesis/scripts/genesis-telemetry.sh +97 -0
- package/genesis/scripts/genesis-utils.sh +617 -0
- package/genesis/scripts/impact-analysis.sh +135 -0
- package/genesis/scripts/interview.sh +98 -0
- package/genesis/scripts/map-project.sh +309 -0
- package/genesis/scripts/receive-idea.sh +69 -0
- package/genesis/scripts/register-project.sh +520 -0
- package/genesis/scripts/research-idea.sh +84 -0
- package/genesis/scripts/scaffold-project.sh +1396 -0
- package/genesis/scripts/security-review.sh +141 -0
- package/genesis/scripts/sideband-lib.sh +243 -0
- package/genesis/scripts/stack-detection-lib.sh +130 -0
- package/genesis/scripts/triage.sh +598 -0
- package/genesis/scripts/validate-step.sh +81 -0
- package/openclaw.plugin.json +45 -0
- package/package.json +60 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"layoutVersion": "fabrica-v1",
|
|
3
|
+
"assets": {
|
|
4
|
+
"defaultsDir": "defaults",
|
|
5
|
+
"promptsDir": "defaults/fabrica/prompts",
|
|
6
|
+
"securityChecklistPath": "defaults/fabrica/prompts/security-checklist.md",
|
|
7
|
+
"workflowPath": "defaults/fabrica/workflow.yaml",
|
|
8
|
+
"templatesDir": "defaults",
|
|
9
|
+
"genesis": {
|
|
10
|
+
"root": "genesis",
|
|
11
|
+
"scriptsDir": "genesis/scripts",
|
|
12
|
+
"configsDir": "genesis/configs"
|
|
13
|
+
},
|
|
14
|
+
"migrations": [
|
|
15
|
+
"layout",
|
|
16
|
+
"workflow"
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
"layout": {
|
|
20
|
+
"primaryDataDir": "fabrica",
|
|
21
|
+
"legacyDataDir": "devclaw",
|
|
22
|
+
"versionFile": ".layout-version"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.0",
|
|
3
|
+
"default_type": "feature",
|
|
4
|
+
"confidence_threshold": 0.6,
|
|
5
|
+
"types": {
|
|
6
|
+
"bugfix": {
|
|
7
|
+
"keywords": ["bug", "fix", "erro", "error", "crash", "broken", "quebrado", "falha", "fail", "exception", "not working", "nao funciona", "não funciona", "regression", "regressão"],
|
|
8
|
+
"patterns": ["(?i)\\b(fix|corrig|resolv|repair|patch)\\b", "(?i)\\b(bug|erro|falha|crash|quebr)\\b"],
|
|
9
|
+
"weight": 1.2
|
|
10
|
+
},
|
|
11
|
+
"feature": {
|
|
12
|
+
"keywords": ["add", "create", "new", "implement", "build", "fazer", "criar", "adicionar", "novo", "nova", "implementar", "construir", "develop", "desenvolver", "want", "quero", "need", "preciso"],
|
|
13
|
+
"patterns": ["(?i)\\b(add|creat|implement|build|desenvolv|faz)\\b", "(?i)\\b(nov[oa]|new)\\b"],
|
|
14
|
+
"weight": 1.0
|
|
15
|
+
},
|
|
16
|
+
"refactor": {
|
|
17
|
+
"keywords": ["refactor", "refatorar", "cleanup", "clean up", "reorganize", "reorganizar", "simplify", "simplificar", "extract", "extrair", "rename", "renomear", "move", "mover", "split", "dividir", "merge", "consolidar"],
|
|
18
|
+
"patterns": ["(?i)\\b(refactor|refator|cleanup|reorganiz|simplif)\\b", "(?i)\\b(extract|split|merg|consolid)\\b"],
|
|
19
|
+
"weight": 0.9
|
|
20
|
+
},
|
|
21
|
+
"research": {
|
|
22
|
+
"keywords": ["research", "pesquisar", "investigate", "investigar", "explore", "explorar", "evaluate", "avaliar", "compare", "comparar", "spike", "poc", "proof of concept", "prova de conceito", "prototype", "protótipo", "study", "estudar"],
|
|
23
|
+
"patterns": ["(?i)\\b(research|pesquis|investigat|explor|avaliar|compar)\\b", "(?i)\\b(spike|poc|proof.of.concept|prova.de.conceito|protot)\\b"],
|
|
24
|
+
"weight": 0.8
|
|
25
|
+
},
|
|
26
|
+
"infra": {
|
|
27
|
+
"keywords": ["deploy", "ci", "cd", "pipeline", "docker", "kubernetes", "k8s", "terraform", "ansible", "monitoring", "monitoramento", "logging", "infrastructure", "infraestrutura", "devops", "server", "servidor", "database", "banco de dados", "migration", "migração", "config", "configuração", "env", "environment", "ambiente"],
|
|
28
|
+
"patterns": ["(?i)\\b(deploy|docker|k8s|kubernetes|terraform|ansible|ci.?cd)\\b", "(?i)\\b(infra|devops|pipeline|migration|migra[çc])\\b"],
|
|
29
|
+
"weight": 0.9
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.0",
|
|
3
|
+
"max_rounds": 2,
|
|
4
|
+
"types": {
|
|
5
|
+
"feature": {
|
|
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." }
|
|
12
|
+
],
|
|
13
|
+
"technical_additions": [
|
|
14
|
+
{ "id": "ft1", "question": "Quais APIs ou serviços externos essa feature precisa consumir?", "required": false },
|
|
15
|
+
{ "id": "ft2", "question": "Há requisitos de performance (latência, throughput)?", "required": false },
|
|
16
|
+
{ "id": "ft3", "question": "Qual o modelo de dados? Precisa de novas tabelas/coleções?", "required": false }
|
|
17
|
+
],
|
|
18
|
+
"non_technical_additions": [
|
|
19
|
+
{ "id": "fn1", "question": "Como você saberia que essa feature está funcionando bem? O que muda na sua rotina?", "required": false }
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
"bugfix": {
|
|
23
|
+
"round1": [
|
|
24
|
+
{ "id": "b1", "question": "O que está acontecendo de errado? Descreva o comportamento atual.", "required": true, "follow_up_if_vague": "Você vê alguma mensagem de erro? O que aparece na tela?" },
|
|
25
|
+
{ "id": "b2", "question": "O que deveria acontecer? Qual o comportamento esperado?", "required": true, "follow_up_if_vague": "Antes funcionava corretamente? Quando parou?" },
|
|
26
|
+
{ "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?" },
|
|
27
|
+
{ "id": "b4", "question": "Com que frequência acontece? Sempre, às vezes, em condições específicas?", "required": false, "follow_up_if_vague": null }
|
|
28
|
+
],
|
|
29
|
+
"technical_additions": [
|
|
30
|
+
{ "id": "bt1", "question": "Em qual ambiente ocorre? (produção, staging, local)", "required": false },
|
|
31
|
+
{ "id": "bt2", "question": "Tem logs ou stack trace disponível?", "required": false }
|
|
32
|
+
],
|
|
33
|
+
"non_technical_additions": [
|
|
34
|
+
{ "id": "bn1", "question": "Quantas pessoas são afetadas por esse problema?", "required": false }
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
"refactor": {
|
|
38
|
+
"round1": [
|
|
39
|
+
{ "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?" },
|
|
40
|
+
{ "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?" },
|
|
41
|
+
{ "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?" }
|
|
42
|
+
],
|
|
43
|
+
"technical_additions": [
|
|
44
|
+
{ "id": "rt1", "question": "A refatoração pode mudar interfaces públicas/APIs?", "required": false },
|
|
45
|
+
{ "id": "rt2", "question": "Há testes existentes cobrindo esse código?", "required": false }
|
|
46
|
+
],
|
|
47
|
+
"non_technical_additions": []
|
|
48
|
+
},
|
|
49
|
+
"research": {
|
|
50
|
+
"round1": [
|
|
51
|
+
{ "id": "s1", "question": "O que precisa ser investigado ou avaliado?", "required": true, "follow_up_if_vague": "É uma tecnologia, abordagem, ou viabilidade de algo?" },
|
|
52
|
+
{ "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?" },
|
|
53
|
+
{ "id": "s3", "question": "Quais critérios definem sucesso da pesquisa?", "required": true, "follow_up_if_vague": "Qual entregável esperado? Documento, PoC, comparativo?" }
|
|
54
|
+
],
|
|
55
|
+
"technical_additions": [
|
|
56
|
+
{ "id": "st1", "question": "Há restrições de stack ou compatibilidade a considerar?", "required": false }
|
|
57
|
+
],
|
|
58
|
+
"non_technical_additions": []
|
|
59
|
+
},
|
|
60
|
+
"infra": {
|
|
61
|
+
"round1": [
|
|
62
|
+
{ "id": "i1", "question": "O que precisa ser configurado/implantado/migrado?", "required": true, "follow_up_if_vague": "É CI/CD, deploy, banco de dados, monitoramento?" },
|
|
63
|
+
{ "id": "i2", "question": "Qual o ambiente alvo? (produção, staging, dev, local)", "required": true, "follow_up_if_vague": null },
|
|
64
|
+
{ "id": "i3", "question": "Há requisitos de downtime, rollback ou compatibilidade?", "required": false, "follow_up_if_vague": null }
|
|
65
|
+
],
|
|
66
|
+
"technical_additions": [
|
|
67
|
+
{ "id": "it1", "question": "Quais serviços/ferramentas estão envolvidos?", "required": false },
|
|
68
|
+
{ "id": "it2", "question": "Há secrets ou credenciais que precisam ser provisionadas?", "required": false }
|
|
69
|
+
],
|
|
70
|
+
"non_technical_additions": []
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"name": "Planning",
|
|
4
|
+
"color": "95a5a6",
|
|
5
|
+
"description": "Issue in planning phase"
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"name": "To Do",
|
|
9
|
+
"color": "428bca",
|
|
10
|
+
"description": "Ready for development"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"name": "Doing",
|
|
14
|
+
"color": "f0ad4e",
|
|
15
|
+
"description": "In progress"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "To Review",
|
|
19
|
+
"color": "7057ff",
|
|
20
|
+
"description": "Awaiting code review"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"name": "Reviewing",
|
|
24
|
+
"color": "c5def5",
|
|
25
|
+
"description": "Review in progress"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"name": "To Test",
|
|
29
|
+
"color": "5bc0de",
|
|
30
|
+
"description": "Awaiting QA testing"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "Testing",
|
|
34
|
+
"color": "9b59b6",
|
|
35
|
+
"description": "Testing in progress"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "Done",
|
|
39
|
+
"color": "5cb85c",
|
|
40
|
+
"description": "Completed"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"name": "Rejected",
|
|
44
|
+
"color": "e11d48",
|
|
45
|
+
"description": "Rejected"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"name": "To Improve",
|
|
49
|
+
"color": "d9534f",
|
|
50
|
+
"description": "Changes requested"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"name": "Refining",
|
|
54
|
+
"color": "f39c12",
|
|
55
|
+
"description": "Needs refinement"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"name": "To Research",
|
|
59
|
+
"color": "0075ca",
|
|
60
|
+
"description": "Needs investigation"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"name": "Researching",
|
|
64
|
+
"color": "4a90e2",
|
|
65
|
+
"description": "Research in progress"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"name": "approved",
|
|
69
|
+
"color": "0e8a16",
|
|
70
|
+
"description": "Approved for dispatch"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"name": "review:agent",
|
|
74
|
+
"color": "d93f0b",
|
|
75
|
+
"description": "Review by agent"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"name": "review:human",
|
|
79
|
+
"color": "d93f0b",
|
|
80
|
+
"description": "Review by human"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"name": "review:skip",
|
|
84
|
+
"color": "d93f0b",
|
|
85
|
+
"description": "Skip review"
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"name": "test:skip",
|
|
89
|
+
"color": "d93f0b",
|
|
90
|
+
"description": "Skip testing"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"name": "type:feature",
|
|
94
|
+
"color": "0E8A16",
|
|
95
|
+
"description": "Feature request"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"name": "type:bugfix",
|
|
99
|
+
"color": "D93F0B",
|
|
100
|
+
"description": "Bug fix"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"name": "type:refactor",
|
|
104
|
+
"color": "FBCA04",
|
|
105
|
+
"description": "Code refactoring"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"name": "type:research",
|
|
109
|
+
"color": "0075CA",
|
|
110
|
+
"description": "Research/investigation"
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"name": "type:infra",
|
|
114
|
+
"color": "5319E7",
|
|
115
|
+
"description": "Infrastructure/DevOps"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"name": "priority:critical",
|
|
119
|
+
"color": "B60205",
|
|
120
|
+
"description": "P0 - Critical"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"name": "priority:high",
|
|
124
|
+
"color": "D93F0B",
|
|
125
|
+
"description": "P1 - High"
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"name": "priority:medium",
|
|
129
|
+
"color": "FBCA04",
|
|
130
|
+
"description": "P2 - Medium"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"name": "priority:normal",
|
|
134
|
+
"color": "0E8A16",
|
|
135
|
+
"description": "P3 - Normal"
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"name": "effort:small",
|
|
139
|
+
"color": "C2E0C6",
|
|
140
|
+
"description": "≤3 files, ≤3 ACs"
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"name": "effort:medium",
|
|
144
|
+
"color": "FEF2C0",
|
|
145
|
+
"description": "≤10 files, ≤7 ACs"
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"name": "effort:large",
|
|
149
|
+
"color": "F9D0C4",
|
|
150
|
+
"description": "≤25 files, ≤15 ACs"
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"name": "developer:junior",
|
|
154
|
+
"color": "0e8a16",
|
|
155
|
+
"description": ""
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"name": "developer:medior",
|
|
159
|
+
"color": "0e8a16",
|
|
160
|
+
"description": ""
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"name": "developer:senior",
|
|
164
|
+
"color": "0e8a16",
|
|
165
|
+
"description": ""
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"name": "reviewer:junior",
|
|
169
|
+
"color": "d93f0b",
|
|
170
|
+
"description": ""
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
"name": "reviewer:senior",
|
|
174
|
+
"color": "d93f0b",
|
|
175
|
+
"description": ""
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"name": "tester:junior",
|
|
179
|
+
"color": "5319e7",
|
|
180
|
+
"description": ""
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"name": "tester:medior",
|
|
184
|
+
"color": "5319e7",
|
|
185
|
+
"description": ""
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
"name": "tester:senior",
|
|
189
|
+
"color": "5319e7",
|
|
190
|
+
"description": ""
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
"name": "needs-human",
|
|
194
|
+
"color": "d73a4a",
|
|
195
|
+
"description": "Requires human intervention"
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
"name": "effort:xlarge",
|
|
199
|
+
"color": "BFD4F2",
|
|
200
|
+
"description": ">25 files or >15 ACs"
|
|
201
|
+
}
|
|
202
|
+
]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.0",
|
|
3
|
+
"priority_rules": [
|
|
4
|
+
{ "condition": "type == 'bugfix' && risk_count > 2", "priority": "P0", "label": "priority:critical" },
|
|
5
|
+
{ "condition": "type == 'bugfix'", "priority": "P1", "label": "priority:high" },
|
|
6
|
+
{ "condition": "type == 'feature' && effort == 'small'", "priority": "P2", "label": "priority:medium" },
|
|
7
|
+
{ "condition": "type == 'feature'", "priority": "P3", "label": "priority:normal" },
|
|
8
|
+
{ "condition": "type == 'refactor'", "priority": "P3", "label": "priority:normal" },
|
|
9
|
+
{ "condition": "type == 'infra'", "priority": "P2", "label": "priority:medium" },
|
|
10
|
+
{ "condition": "type == 'research'", "priority": "P3", "label": "priority:normal" }
|
|
11
|
+
],
|
|
12
|
+
"priority_rules_v2": [
|
|
13
|
+
{ "when": { "type": "bugfix", "min_risk_count": 3 }, "priority": "P0", "label": "priority:critical" },
|
|
14
|
+
{ "when": { "type": "bugfix" }, "priority": "P1", "label": "priority:high" },
|
|
15
|
+
{ "when": { "type": "feature", "effort": "small" }, "priority": "P2", "label": "priority:medium" },
|
|
16
|
+
{ "when": { "type": "feature" }, "priority": "P3", "label": "priority:normal" },
|
|
17
|
+
{ "when": { "type": "refactor" }, "priority": "P3", "label": "priority:normal" },
|
|
18
|
+
{ "when": { "type": "infra" }, "priority": "P2", "label": "priority:medium" },
|
|
19
|
+
{ "when": { "type": "research" }, "priority": "P3", "label": "priority:normal" }
|
|
20
|
+
],
|
|
21
|
+
"effort_rules": {
|
|
22
|
+
"small": { "max_files": 3, "max_acs": 3, "label": "effort:small" },
|
|
23
|
+
"medium": { "max_files": 10, "max_acs": 7, "label": "effort:medium" },
|
|
24
|
+
"large": { "max_files": 25, "max_acs": 15, "label": "effort:large" },
|
|
25
|
+
"xlarge": { "max_files": 999, "max_acs": 999, "label": "effort:xlarge" }
|
|
26
|
+
},
|
|
27
|
+
"auto_labels": {
|
|
28
|
+
"feature": "type:feature",
|
|
29
|
+
"bugfix": "type:bugfix",
|
|
30
|
+
"refactor": "type:refactor",
|
|
31
|
+
"research": "type:research",
|
|
32
|
+
"infra": "type:infra"
|
|
33
|
+
},
|
|
34
|
+
"target_state_by_type": {
|
|
35
|
+
"default": "To Do",
|
|
36
|
+
"research": "To Research"
|
|
37
|
+
},
|
|
38
|
+
"dispatch_label": "approved"
|
|
39
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Step 2: Classify idea type
|
|
5
|
+
# Input: stdin JSON (from receive-idea.sh)
|
|
6
|
+
# Output: JSON with classification to stdout
|
|
7
|
+
|
|
8
|
+
GENESIS_LOG="${GENESIS_LOG:-$HOME/.openclaw/workspace/logs/genesis.log}"
|
|
9
|
+
mkdir -p "$(dirname "$GENESIS_LOG")"
|
|
10
|
+
exec 2> >(tee -a "$GENESIS_LOG" >&2)
|
|
11
|
+
|
|
12
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13
|
+
RULES="$SCRIPT_DIR/../configs/classification-rules.json"
|
|
14
|
+
source "$SCRIPT_DIR/delivery-target-lib.sh"
|
|
15
|
+
|
|
16
|
+
score_gt_zero() {
|
|
17
|
+
local value="${1:-0}"
|
|
18
|
+
[[ "$(echo "$value > 0" | bc 2>/dev/null || echo 0)" == "1" ]]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if [[ -n "${1:-}" && -f "${1:-}" ]]; then
|
|
22
|
+
INPUT="$(cat "$1")"
|
|
23
|
+
else
|
|
24
|
+
INPUT="$(cat)"
|
|
25
|
+
fi
|
|
26
|
+
SESSION_ID="$(echo "$INPUT" | jq -r '.session_id')"
|
|
27
|
+
echo "=== $(date -Iseconds) | classify-idea.sh | session=$SESSION_ID ===" >&2
|
|
28
|
+
RAW_IDEA="$(echo "$INPUT" | jq -r '.raw_idea')"
|
|
29
|
+
METADATA="$(echo "$INPUT" | jq '.metadata // {}')"
|
|
30
|
+
THRESHOLD="$(jq -r '.confidence_threshold' "$RULES")"
|
|
31
|
+
DEFAULT_TYPE="$(jq -r '.default_type' "$RULES")"
|
|
32
|
+
|
|
33
|
+
echo "Classifying idea for session $SESSION_ID..." >&2
|
|
34
|
+
|
|
35
|
+
# === Try LLM-based classification first (via openclaw agent --local) ===
|
|
36
|
+
LLM_CLASSIFICATION=""
|
|
37
|
+
if command -v openclaw &>/dev/null; then
|
|
38
|
+
echo "Trying LLM-based classification..." >&2
|
|
39
|
+
VALID_TYPES="$(jq -r '.types | keys | join(", ")' "$RULES")"
|
|
40
|
+
LLM_PROMPT="Classify this software project idea into exactly one type.
|
|
41
|
+
|
|
42
|
+
Valid types: $VALID_TYPES
|
|
43
|
+
|
|
44
|
+
Idea: $RAW_IDEA
|
|
45
|
+
|
|
46
|
+
Return ONLY valid JSON (no markdown fences, no explanation):
|
|
47
|
+
{\"type\": \"<one of: $VALID_TYPES>\", \"confidence\": <0.0-1.0>, \"reasoning\": \"<1 sentence>\"}"
|
|
48
|
+
|
|
49
|
+
LLM_RAW="$(timeout 60 openclaw agent --local \
|
|
50
|
+
-m "$LLM_PROMPT" \
|
|
51
|
+
--session-id "genesis-classify-${SESSION_ID}" \
|
|
52
|
+
--json 2>&1)" || echo "[classify-idea] LLM call failed (exit $?)" >&2
|
|
53
|
+
|
|
54
|
+
if [[ -n "$LLM_RAW" ]]; then
|
|
55
|
+
LLM_TEXT="$(echo "$LLM_RAW" | jq -r '.payloads[0].text // empty' 2>/dev/null || true)"
|
|
56
|
+
LLM_TEXT="$(echo "$LLM_TEXT" | sed '/^```/d; /^json$/d')"
|
|
57
|
+
# Validate: must have type field with a known type
|
|
58
|
+
LLM_TYPE="$(echo "$LLM_TEXT" | jq -r '.type // empty' 2>/dev/null || true)"
|
|
59
|
+
if [[ -n "$LLM_TYPE" ]] && jq -e --arg t "$LLM_TYPE" '.types[$t]' "$RULES" &>/dev/null; then
|
|
60
|
+
LLM_CLASSIFICATION="$LLM_TEXT"
|
|
61
|
+
echo "LLM classification: $LLM_TYPE" >&2
|
|
62
|
+
fi
|
|
63
|
+
fi
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
# === LLM classification succeeded ===
|
|
67
|
+
if [[ -n "$LLM_CLASSIFICATION" ]]; then
|
|
68
|
+
best_type="$(echo "$LLM_CLASSIFICATION" | jq -r '.type')"
|
|
69
|
+
confidence="$(echo "$LLM_CLASSIFICATION" | jq -r '.confidence // 0.85')"
|
|
70
|
+
reasoning="$(echo "$LLM_CLASSIFICATION" | jq -r '.reasoning // "LLM-based classification"')"
|
|
71
|
+
reasoning="[LLM] $reasoning"
|
|
72
|
+
alternatives="[]"
|
|
73
|
+
else
|
|
74
|
+
# === Fallback: keyword/pattern matching ===
|
|
75
|
+
echo "LLM unavailable, using keyword-based classification..." >&2
|
|
76
|
+
|
|
77
|
+
IDEA_LOWER="$(echo "$RAW_IDEA" | tr '[:upper:]' '[:lower:]')"
|
|
78
|
+
|
|
79
|
+
best_type="$DEFAULT_TYPE"
|
|
80
|
+
best_score=0
|
|
81
|
+
results="[]"
|
|
82
|
+
|
|
83
|
+
for type in $(jq -r '.types | keys[]' "$RULES"); do
|
|
84
|
+
score=0
|
|
85
|
+
weight="$(jq -r ".types.\"$type\".weight" "$RULES")"
|
|
86
|
+
|
|
87
|
+
# Keyword matching
|
|
88
|
+
while IFS= read -r kw; do
|
|
89
|
+
if [[ "$IDEA_LOWER" == *"$kw"* ]]; then
|
|
90
|
+
score=$((score + 1))
|
|
91
|
+
fi
|
|
92
|
+
done < <(jq -r ".types.\"$type\".keywords[]" "$RULES")
|
|
93
|
+
|
|
94
|
+
# Pattern matching
|
|
95
|
+
while IFS= read -r pat; do
|
|
96
|
+
if echo "$RAW_IDEA" | grep -qP "$pat" 2>/dev/null; then
|
|
97
|
+
score=$((score + 2))
|
|
98
|
+
fi
|
|
99
|
+
done < <(jq -r ".types.\"$type\".patterns[]" "$RULES")
|
|
100
|
+
|
|
101
|
+
# Apply weight
|
|
102
|
+
weighted="$(echo "$score * $weight" | bc 2>/dev/null || echo "$score")"
|
|
103
|
+
|
|
104
|
+
results="$(echo "$results" | jq --arg t "$type" --argjson s "$score" '. + [{"type": $t, "raw_score": $s}]')"
|
|
105
|
+
|
|
106
|
+
if [[ "$(echo "$weighted > $best_score" | bc 2>/dev/null || echo 0)" == "1" ]] || \
|
|
107
|
+
{ [[ "$score" -gt 0 ]] && [[ "$best_score" == "0" ]]; }; then
|
|
108
|
+
best_score="$weighted"
|
|
109
|
+
best_type="$type"
|
|
110
|
+
fi
|
|
111
|
+
done
|
|
112
|
+
|
|
113
|
+
# Calculate confidence
|
|
114
|
+
if score_gt_zero "$best_score"; then
|
|
115
|
+
confidence="$(echo "scale=2; $best_score / ($best_score + 2)" | bc 2>/dev/null || echo "0.50")"
|
|
116
|
+
else
|
|
117
|
+
confidence="0.30"
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
# Build alternatives
|
|
121
|
+
alternatives="$(echo "$results" | jq --arg best "$best_type" '[.[] | select(.type != $best and .raw_score > 0) | {type: .type, confidence: ((.raw_score / (.raw_score + 3)) * 100 | floor / 100)}]')"
|
|
122
|
+
|
|
123
|
+
if score_gt_zero "$best_score"; then
|
|
124
|
+
reasoning="[Keywords] Classified as '$best_type' based on $best_score keyword/pattern matches."
|
|
125
|
+
else
|
|
126
|
+
reasoning="[Keywords] No strong signals found. Defaulting to '$DEFAULT_TYPE'."
|
|
127
|
+
fi
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
DELIVERY_TARGET_RAW="$(echo "$INPUT" | jq -r '.metadata.delivery_target // empty')"
|
|
131
|
+
if [[ -z "$DELIVERY_TARGET_RAW" || "$DELIVERY_TARGET_RAW" == "null" ]]; then
|
|
132
|
+
DELIVERY_TARGET="$(genesis_detect_delivery_target_from_text "$RAW_IDEA")"
|
|
133
|
+
else
|
|
134
|
+
DELIVERY_TARGET="$(genesis_normalize_delivery_target "$DELIVERY_TARGET_RAW")"
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
echo "Result: $best_type (confidence: $confidence)" >&2
|
|
138
|
+
echo "Delivery target: $DELIVERY_TARGET" >&2
|
|
139
|
+
|
|
140
|
+
jq -n \
|
|
141
|
+
--arg sid "$SESSION_ID" \
|
|
142
|
+
--arg idea "$RAW_IDEA" \
|
|
143
|
+
--arg type "$best_type" \
|
|
144
|
+
--argjson conf "$confidence" \
|
|
145
|
+
--argjson alts "$alternatives" \
|
|
146
|
+
--arg reasoning "$reasoning" \
|
|
147
|
+
--arg delivery_target "$DELIVERY_TARGET" \
|
|
148
|
+
--argjson meta "$METADATA" \
|
|
149
|
+
'{
|
|
150
|
+
session_id: $sid,
|
|
151
|
+
step: "classify",
|
|
152
|
+
raw_idea: $idea,
|
|
153
|
+
classification: {
|
|
154
|
+
type: $type,
|
|
155
|
+
confidence: $conf,
|
|
156
|
+
alternatives: $alts,
|
|
157
|
+
reasoning: $reasoning,
|
|
158
|
+
delivery_target: $delivery_target
|
|
159
|
+
},
|
|
160
|
+
metadata: $meta
|
|
161
|
+
}'
|