up-cc 0.2.3 → 0.3.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/agents/up-analista-codigo.md +446 -0
- package/agents/up-auditor-modernidade.md +378 -0
- package/agents/up-auditor-performance.md +426 -0
- package/agents/up-auditor-ux.md +396 -0
- package/agents/up-consolidador-ideias.md +493 -0
- package/agents/up-pesquisador-mercado.md +350 -0
- package/agents/up-sintetizador-melhorias.md +407 -0
- package/bin/lib/core.cjs +3 -3
- package/bin/up-tools.cjs +490 -23
- package/commands/ajuda.md +19 -0
- package/commands/ideias.md +49 -0
- package/commands/melhorias.md +45 -0
- package/package.json +1 -1
- package/references/audit-modernidade.md +1617 -0
- package/references/audit-performance.md +478 -0
- package/references/audit-ux.md +1544 -0
- package/templates/report.md +177 -0
- package/templates/suggestion.md +152 -0
- package/workflows/ideias.md +381 -0
- package/workflows/melhorias.md +409 -0
package/bin/up-tools.cjs
CHANGED
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
* Usage: node up-tools.cjs <command> [args] [--raw] [--cwd <path>]
|
|
9
9
|
*
|
|
10
10
|
* Commands:
|
|
11
|
-
* init planejar-fase|executar-fase|novo-projeto|rapido|retomar|operacao-fase|progresso|verificar-trabalho
|
|
11
|
+
* init planejar-fase|executar-fase|novo-projeto|rapido|retomar|operacao-fase|progresso|verificar-trabalho|melhorias|ideias
|
|
12
12
|
* state load|get|update|advance-plan|update-progress|add-decision|record-session|record-metric|snapshot
|
|
13
13
|
* roadmap get-phase|analyze|update-plan-progress
|
|
14
|
-
* phase add|remove|find|complete
|
|
14
|
+
* phase add|remove|find|complete|generate-from-report
|
|
15
15
|
* config get|set
|
|
16
16
|
* requirements mark-complete
|
|
17
17
|
* commit <msg> --files
|
|
@@ -200,8 +200,14 @@ function main() {
|
|
|
200
200
|
case 'verificar-trabalho':
|
|
201
201
|
cmdInitVerificarTrabalho(cwd, args[2], raw);
|
|
202
202
|
break;
|
|
203
|
+
case 'melhorias':
|
|
204
|
+
cmdInitMelhorias(cwd, raw);
|
|
205
|
+
break;
|
|
206
|
+
case 'ideias':
|
|
207
|
+
cmdInitIdeias(cwd, raw);
|
|
208
|
+
break;
|
|
203
209
|
default:
|
|
204
|
-
error(`Unknown init workflow: ${workflow}\nAvailable: planejar-fase, executar-fase, novo-projeto, rapido, retomar, operacao-fase, progresso, verificar-trabalho`);
|
|
210
|
+
error(`Unknown init workflow: ${workflow}\nAvailable: planejar-fase, executar-fase, novo-projeto, rapido, retomar, operacao-fase, progresso, verificar-trabalho, melhorias, ideias`);
|
|
205
211
|
}
|
|
206
212
|
break;
|
|
207
213
|
}
|
|
@@ -279,8 +285,10 @@ function main() {
|
|
|
279
285
|
cmdPhaseRemove(cwd, args[2], { force: forceFlag }, raw);
|
|
280
286
|
} else if (sub === 'complete') {
|
|
281
287
|
cmdPhaseComplete(cwd, args[2], raw);
|
|
288
|
+
} else if (sub === 'generate-from-report') {
|
|
289
|
+
cmdPhaseGenerateFromReport(cwd, args.slice(2), raw);
|
|
282
290
|
} else {
|
|
283
|
-
error('Unknown phase subcommand. Available: find, add, remove, complete');
|
|
291
|
+
error('Unknown phase subcommand. Available: find, add, remove, complete, generate-from-report');
|
|
284
292
|
}
|
|
285
293
|
break;
|
|
286
294
|
}
|
|
@@ -693,6 +701,82 @@ function cmdInitVerificarTrabalho(cwd, phase, raw) {
|
|
|
693
701
|
output(result, raw);
|
|
694
702
|
}
|
|
695
703
|
|
|
704
|
+
function cmdInitMelhorias(cwd, raw) {
|
|
705
|
+
const config = loadConfig(cwd);
|
|
706
|
+
const now = new Date();
|
|
707
|
+
|
|
708
|
+
// Detectar stack hints do projeto para ajustar auditoria
|
|
709
|
+
const pkgPath = path.join(cwd, 'package.json');
|
|
710
|
+
let stackHints = {};
|
|
711
|
+
try {
|
|
712
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
713
|
+
const allDeps = Object.assign({}, pkg.dependencies || {}, pkg.devDependencies || {});
|
|
714
|
+
stackHints = {
|
|
715
|
+
has_react: !!allDeps.react,
|
|
716
|
+
has_next: !!allDeps.next,
|
|
717
|
+
has_vue: !!allDeps.vue,
|
|
718
|
+
has_nuxt: !!allDeps.nuxt,
|
|
719
|
+
has_svelte: !!allDeps.svelte,
|
|
720
|
+
has_tailwind: !!allDeps.tailwindcss,
|
|
721
|
+
has_prisma: !!(allDeps['@prisma/client'] || allDeps.prisma),
|
|
722
|
+
has_typescript: !!(allDeps.typescript || pathExistsInternal(cwd, 'tsconfig.json')),
|
|
723
|
+
type_module: pkg.type === 'module',
|
|
724
|
+
};
|
|
725
|
+
} catch {}
|
|
726
|
+
|
|
727
|
+
const result = {
|
|
728
|
+
planning_exists: pathExistsInternal(cwd, '.plano'),
|
|
729
|
+
melhorias_dir: '.plano/melhorias',
|
|
730
|
+
melhorias_exists: pathExistsInternal(cwd, '.plano/melhorias'),
|
|
731
|
+
has_claude_md: pathExistsInternal(cwd, 'CLAUDE.md'),
|
|
732
|
+
has_package_json: pathExistsInternal(cwd, 'package.json'),
|
|
733
|
+
date: now.toISOString().split('T')[0],
|
|
734
|
+
timestamp: now.toISOString(),
|
|
735
|
+
commit_docs: config.commit_docs,
|
|
736
|
+
stack_hints: stackHints,
|
|
737
|
+
};
|
|
738
|
+
|
|
739
|
+
output(result, raw);
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
function cmdInitIdeias(cwd, raw) {
|
|
743
|
+
const config = loadConfig(cwd);
|
|
744
|
+
const now = new Date();
|
|
745
|
+
|
|
746
|
+
// Detectar stack hints do projeto para contextualizar analise
|
|
747
|
+
const pkgPath = path.join(cwd, 'package.json');
|
|
748
|
+
let stackHints = {};
|
|
749
|
+
try {
|
|
750
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
751
|
+
const allDeps = Object.assign({}, pkg.dependencies || {}, pkg.devDependencies || {});
|
|
752
|
+
stackHints = {
|
|
753
|
+
has_react: !!allDeps.react,
|
|
754
|
+
has_next: !!allDeps.next,
|
|
755
|
+
has_vue: !!allDeps.vue,
|
|
756
|
+
has_nuxt: !!allDeps.nuxt,
|
|
757
|
+
has_svelte: !!allDeps.svelte,
|
|
758
|
+
has_tailwind: !!allDeps.tailwindcss,
|
|
759
|
+
has_prisma: !!(allDeps['@prisma/client'] || allDeps.prisma),
|
|
760
|
+
has_typescript: !!(allDeps.typescript || pathExistsInternal(cwd, 'tsconfig.json')),
|
|
761
|
+
type_module: pkg.type === 'module',
|
|
762
|
+
};
|
|
763
|
+
} catch {}
|
|
764
|
+
|
|
765
|
+
const result = {
|
|
766
|
+
planning_exists: pathExistsInternal(cwd, '.plano'),
|
|
767
|
+
ideias_dir: '.plano/ideias',
|
|
768
|
+
ideias_exists: pathExistsInternal(cwd, '.plano/ideias'),
|
|
769
|
+
has_claude_md: pathExistsInternal(cwd, 'CLAUDE.md'),
|
|
770
|
+
has_package_json: pathExistsInternal(cwd, 'package.json'),
|
|
771
|
+
date: now.toISOString().split('T')[0],
|
|
772
|
+
timestamp: now.toISOString(),
|
|
773
|
+
commit_docs: config.commit_docs,
|
|
774
|
+
stack_hints: stackHints,
|
|
775
|
+
};
|
|
776
|
+
|
|
777
|
+
output(result, raw);
|
|
778
|
+
}
|
|
779
|
+
|
|
696
780
|
// =====================================================================
|
|
697
781
|
// STATE COMMANDS
|
|
698
782
|
// =====================================================================
|
|
@@ -1019,7 +1103,7 @@ function cmdRoadmapGetPhase(cwd, phaseNum, raw) {
|
|
|
1019
1103
|
const content = fs.readFileSync(roadmapPath, 'utf-8');
|
|
1020
1104
|
const escapedPhase = escapeRegex(phaseNum);
|
|
1021
1105
|
|
|
1022
|
-
const phasePattern = new RegExp(`#{2,4}\\s*Phase\\s+${escapedPhase}:\\s*([^\\n]+)`, 'i');
|
|
1106
|
+
const phasePattern = new RegExp(`#{2,4}\\s*(?:Phase|Fase)\\s+${escapedPhase}:\\s*([^\\n]+)`, 'i');
|
|
1023
1107
|
const headerMatch = content.match(phasePattern);
|
|
1024
1108
|
|
|
1025
1109
|
if (!headerMatch) {
|
|
@@ -1030,11 +1114,11 @@ function cmdRoadmapGetPhase(cwd, phaseNum, raw) {
|
|
|
1030
1114
|
const phaseName = headerMatch[1].trim();
|
|
1031
1115
|
const headerIndex = headerMatch.index;
|
|
1032
1116
|
const restOfContent = content.slice(headerIndex);
|
|
1033
|
-
const nextHeaderMatch = restOfContent.match(/\n#{2,4}\s+Phase\s+\d/i);
|
|
1117
|
+
const nextHeaderMatch = restOfContent.match(/\n#{2,4}\s+(?:Phase|Fase)\s+\d/i);
|
|
1034
1118
|
const sectionEnd = nextHeaderMatch ? headerIndex + nextHeaderMatch.index : content.length;
|
|
1035
1119
|
const section = content.slice(headerIndex, sectionEnd).trim();
|
|
1036
1120
|
|
|
1037
|
-
const goalMatch = section.match(/\*\*Goal:\*\*\s*([^\n]+)/i);
|
|
1121
|
+
const goalMatch = section.match(/\*\*(?:Goal|Objetivo):\*\*\s*([^\n]+)/i);
|
|
1038
1122
|
const goal = goalMatch ? goalMatch[1].trim() : null;
|
|
1039
1123
|
|
|
1040
1124
|
output({ found: true, phase_number: phaseNum, phase_name: phaseName, goal, section }, raw, section);
|
|
@@ -1054,7 +1138,7 @@ function cmdRoadmapAnalyze(cwd, raw) {
|
|
|
1054
1138
|
const content = fs.readFileSync(roadmapPath, 'utf-8');
|
|
1055
1139
|
const phasesDir = path.join(cwd, '.plano', 'fases');
|
|
1056
1140
|
|
|
1057
|
-
const phasePattern = /#{2,4}\s*Phase\s+(\d+[A-Z]?(?:\.\d+)*)\s*:\s*([^\n]+)/gi;
|
|
1141
|
+
const phasePattern = /#{2,4}\s*(?:Phase|Fase)\s+(\d+[A-Z]?(?:\.\d+)*)\s*:\s*([^\n]+)/gi;
|
|
1058
1142
|
const phases = [];
|
|
1059
1143
|
let match;
|
|
1060
1144
|
|
|
@@ -1064,11 +1148,11 @@ function cmdRoadmapAnalyze(cwd, raw) {
|
|
|
1064
1148
|
|
|
1065
1149
|
const sectionStart = match.index;
|
|
1066
1150
|
const restOfContent = content.slice(sectionStart);
|
|
1067
|
-
const nextHeader = restOfContent.match(/\n#{2,4}\s+Phase\s+\d/i);
|
|
1151
|
+
const nextHeader = restOfContent.match(/\n#{2,4}\s+(?:Phase|Fase)\s+\d/i);
|
|
1068
1152
|
const sectionEnd = nextHeader ? sectionStart + nextHeader.index : content.length;
|
|
1069
1153
|
const section = content.slice(sectionStart, sectionEnd);
|
|
1070
1154
|
|
|
1071
|
-
const goalMatch = section.match(/\*\*Goal:\*\*\s*([^\n]+)/i);
|
|
1155
|
+
const goalMatch = section.match(/\*\*(?:Goal|Objetivo):\*\*\s*([^\n]+)/i);
|
|
1072
1156
|
const goal = goalMatch ? goalMatch[1].trim() : null;
|
|
1073
1157
|
|
|
1074
1158
|
const normalized = normalizePhaseName(phaseNum);
|
|
@@ -1093,7 +1177,7 @@ function cmdRoadmapAnalyze(cwd, raw) {
|
|
|
1093
1177
|
}
|
|
1094
1178
|
} catch {}
|
|
1095
1179
|
|
|
1096
|
-
const checkboxPattern = new RegExp(`-\\s*\\[(x| )\\]\\s*.*Phase\\s+${escapeRegex(phaseNum)}`, 'i');
|
|
1180
|
+
const checkboxPattern = new RegExp(`-\\s*\\[(x| )\\]\\s*.*(?:Phase|Fase)\\s+${escapeRegex(phaseNum)}`, 'i');
|
|
1097
1181
|
const checkboxMatch = content.match(checkboxPattern);
|
|
1098
1182
|
const roadmapComplete = checkboxMatch ? checkboxMatch[1] === 'x' : false;
|
|
1099
1183
|
|
|
@@ -1167,7 +1251,7 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
|
|
1167
1251
|
|
|
1168
1252
|
// Update plan count in phase detail section
|
|
1169
1253
|
const planCountPattern = new RegExp(
|
|
1170
|
-
`(#{2,4}\\s*Phase\\s+${phaseEscaped}[\\s\\S]*?\\*\\*Plans:\\*\\*\\s*)[^\\n]+`,
|
|
1254
|
+
`(#{2,4}\\s*(?:Phase|Fase)\\s+${phaseEscaped}[\\s\\S]*?\\*\\*(?:Plans|Planos):\\*\\*\\s*)[^\\n]+`,
|
|
1171
1255
|
'i'
|
|
1172
1256
|
);
|
|
1173
1257
|
const planCountText = isComplete
|
|
@@ -1177,7 +1261,7 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
|
|
1177
1261
|
|
|
1178
1262
|
if (isComplete) {
|
|
1179
1263
|
const checkboxPattern = new RegExp(
|
|
1180
|
-
`(-\\s*\\[)[ ](\\]\\s*.*Phase\\s+${phaseEscaped}[:\\s][^\\n]*)`,
|
|
1264
|
+
`(-\\s*\\[)[ ](\\]\\s*.*(?:Phase|Fase)\\s+${phaseEscaped}[:\\s][^\\n]*)`,
|
|
1181
1265
|
'i'
|
|
1182
1266
|
);
|
|
1183
1267
|
roadmapContent = roadmapContent.replace(checkboxPattern, `$1x$2 (completed ${today})`);
|
|
@@ -1243,7 +1327,7 @@ function cmdPhaseAdd(cwd, description, raw) {
|
|
|
1243
1327
|
const content = fs.readFileSync(roadmapPath, 'utf-8');
|
|
1244
1328
|
const slug = generateSlugInternal(description);
|
|
1245
1329
|
|
|
1246
|
-
const phasePattern = /#{2,4}\s*Phase\s+(\d+)[A-Z]?(?:\.\d+)*:/gi;
|
|
1330
|
+
const phasePattern = /#{2,4}\s*(?:Phase|Fase)\s+(\d+)[A-Z]?(?:\.\d+)*:/gi;
|
|
1247
1331
|
let maxPhase = 0;
|
|
1248
1332
|
let m;
|
|
1249
1333
|
while ((m = phasePattern.exec(content)) !== null) {
|
|
@@ -1259,7 +1343,11 @@ function cmdPhaseAdd(cwd, description, raw) {
|
|
|
1259
1343
|
fs.mkdirSync(dirPath, { recursive: true });
|
|
1260
1344
|
fs.writeFileSync(path.join(dirPath, '.gitkeep'), '');
|
|
1261
1345
|
|
|
1262
|
-
|
|
1346
|
+
// Detect ROADMAP language: if it contains '### Fase ' use Portuguese, otherwise English
|
|
1347
|
+
const usePt = /###\s*Fase\s+\d/.test(content);
|
|
1348
|
+
const phaseEntry = usePt
|
|
1349
|
+
? `\n### Fase ${newPhaseNum}: ${description}\n\n**Objetivo:** [A ser planejado]\n**Requisitos**: TBD\n**Depende de:** Fase ${maxPhase}\n**Planos:** 0 planos\n`
|
|
1350
|
+
: `\n### Phase ${newPhaseNum}: ${description}\n\n**Goal:** [To be planned]\n**Requirements**: TBD\n**Depends on:** Phase ${maxPhase}\n**Plans:** 0 plans\n`;
|
|
1263
1351
|
|
|
1264
1352
|
let updatedContent;
|
|
1265
1353
|
const lastSeparator = content.lastIndexOf('\n---');
|
|
@@ -1357,12 +1445,12 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
|
|
|
1357
1445
|
|
|
1358
1446
|
const targetEscaped = escapeRegex(targetPhase);
|
|
1359
1447
|
const sectionPattern = new RegExp(
|
|
1360
|
-
`\\n?#{2,4}\\s*Phase\\s+${targetEscaped}\\s*:[\\s\\S]*?(?=\\n#{2,4}\\s+Phase\\s+\\d|$)`,
|
|
1448
|
+
`\\n?#{2,4}\\s*(?:Phase|Fase)\\s+${targetEscaped}\\s*:[\\s\\S]*?(?=\\n#{2,4}\\s+(?:Phase|Fase)\\s+\\d|$)`,
|
|
1361
1449
|
'i'
|
|
1362
1450
|
);
|
|
1363
1451
|
roadmapContent = roadmapContent.replace(sectionPattern, '');
|
|
1364
1452
|
|
|
1365
|
-
const checkboxPattern = new RegExp(`\\n?-\\s*\\[[ x]\\]\\s*.*Phase\\s+${targetEscaped}[:\\s][^\\n]*`, 'gi');
|
|
1453
|
+
const checkboxPattern = new RegExp(`\\n?-\\s*\\[[ x]\\]\\s*.*(?:Phase|Fase)\\s+${targetEscaped}[:\\s][^\\n]*`, 'gi');
|
|
1366
1454
|
roadmapContent = roadmapContent.replace(checkboxPattern, '');
|
|
1367
1455
|
|
|
1368
1456
|
if (!isDecimal) {
|
|
@@ -1373,11 +1461,11 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
|
|
|
1373
1461
|
const newStr = String(newNum);
|
|
1374
1462
|
|
|
1375
1463
|
roadmapContent = roadmapContent.replace(
|
|
1376
|
-
new RegExp(`(#{2,4}\\s*Phase\\s+)${oldStr}(\\s*:)`, 'gi'),
|
|
1464
|
+
new RegExp(`(#{2,4}\\s*(?:Phase|Fase)\\s+)${oldStr}(\\s*:)`, 'gi'),
|
|
1377
1465
|
`$1${newStr}$2`
|
|
1378
1466
|
);
|
|
1379
1467
|
roadmapContent = roadmapContent.replace(
|
|
1380
|
-
new RegExp(`(Phase\\s+)${oldStr}([:\\s])`, 'g'),
|
|
1468
|
+
new RegExp(`((?:Phase|Fase)\\s+)${oldStr}([:\\s])`, 'g'),
|
|
1381
1469
|
`$1${newStr}$2`
|
|
1382
1470
|
);
|
|
1383
1471
|
}
|
|
@@ -1426,13 +1514,13 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
1426
1514
|
const phaseEscaped = escapeRegex(phaseNum);
|
|
1427
1515
|
|
|
1428
1516
|
const checkboxPattern = new RegExp(
|
|
1429
|
-
`(-\\s*\\[)[ ](\\]\\s*.*Phase\\s+${phaseEscaped}[:\\s][^\\n]*)`,
|
|
1517
|
+
`(-\\s*\\[)[ ](\\]\\s*.*(?:Phase|Fase)\\s+${phaseEscaped}[:\\s][^\\n]*)`,
|
|
1430
1518
|
'i'
|
|
1431
1519
|
);
|
|
1432
1520
|
roadmapContent = roadmapContent.replace(checkboxPattern, `$1x$2 (completed ${today})`);
|
|
1433
1521
|
|
|
1434
1522
|
const planCountPattern = new RegExp(
|
|
1435
|
-
`(#{2,4}\\s*Phase\\s+${phaseEscaped}[\\s\\S]*?\\*\\*Plans:\\*\\*\\s*)[^\\n]+`,
|
|
1523
|
+
`(#{2,4}\\s*(?:Phase|Fase)\\s+${phaseEscaped}[\\s\\S]*?\\*\\*(?:Plans|Planos):\\*\\*\\s*)[^\\n]+`,
|
|
1436
1524
|
'i'
|
|
1437
1525
|
);
|
|
1438
1526
|
roadmapContent = roadmapContent.replace(planCountPattern, `$1${summaryCount}/${planCount} plans complete`);
|
|
@@ -1443,7 +1531,7 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
1443
1531
|
const reqPath = path.join(cwd, '.plano', 'REQUIREMENTS.md');
|
|
1444
1532
|
if (fs.existsSync(reqPath)) {
|
|
1445
1533
|
const reqMatch = roadmapContent.match(
|
|
1446
|
-
new RegExp(`Phase\\s+${escapeRegex(phaseNum)}[\\s\\S]*?\\*\\*Requirements:\\*\\*\\s*([^\\n]+)`, 'i')
|
|
1534
|
+
new RegExp(`(?:Phase|Fase)\\s+${escapeRegex(phaseNum)}[\\s\\S]*?\\*\\*(?:Requirements|Requisitos):\\*\\*\\s*([^\\n]+)`, 'i')
|
|
1447
1535
|
);
|
|
1448
1536
|
if (reqMatch) {
|
|
1449
1537
|
const reqIds = reqMatch[1].replace(/[\[\]]/g, '').split(/[,\s]+/).map(r => r.trim()).filter(Boolean);
|
|
@@ -1485,7 +1573,7 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
1485
1573
|
if (isLastPhase && fs.existsSync(roadmapPath)) {
|
|
1486
1574
|
try {
|
|
1487
1575
|
const roadmapForPhases = fs.readFileSync(roadmapPath, 'utf-8');
|
|
1488
|
-
const phasePattern = /#{2,4}\s*Phase\s+(\d+[A-Z]?(?:\.\d+)*)\s*:\s*([^\n]+)/gi;
|
|
1576
|
+
const phasePattern = /#{2,4}\s*(?:Phase|Fase)\s+(\d+[A-Z]?(?:\.\d+)*)\s*:\s*([^\n]+)/gi;
|
|
1489
1577
|
let pm;
|
|
1490
1578
|
while ((pm = phasePattern.exec(roadmapForPhases)) !== null) {
|
|
1491
1579
|
if (comparePhaseNum(pm[1], phaseNum) > 0) {
|
|
@@ -1526,6 +1614,385 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
1526
1614
|
}, raw);
|
|
1527
1615
|
}
|
|
1528
1616
|
|
|
1617
|
+
// --- Phase Generate From Report ---
|
|
1618
|
+
|
|
1619
|
+
function cmdPhaseGenerateFromReport(cwd, args, raw) {
|
|
1620
|
+
// Parse input: try stdin JSON first, fall back to args
|
|
1621
|
+
let source = null;
|
|
1622
|
+
let reportPath = null;
|
|
1623
|
+
let approvedIds = [];
|
|
1624
|
+
let grouping = 'auto';
|
|
1625
|
+
|
|
1626
|
+
let stdinData = null;
|
|
1627
|
+
try {
|
|
1628
|
+
const stdinRaw = fs.readFileSync('/dev/stdin', 'utf-8').trim();
|
|
1629
|
+
if (stdinRaw) {
|
|
1630
|
+
stdinData = JSON.parse(stdinRaw);
|
|
1631
|
+
}
|
|
1632
|
+
} catch {
|
|
1633
|
+
// stdin not available or not JSON -- use args
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
if (stdinData) {
|
|
1637
|
+
source = stdinData.source || null;
|
|
1638
|
+
reportPath = stdinData.report_path || null;
|
|
1639
|
+
approvedIds = stdinData.approved_ids || [];
|
|
1640
|
+
grouping = stdinData.grouping || 'auto';
|
|
1641
|
+
} else {
|
|
1642
|
+
source = args[0] || null;
|
|
1643
|
+
reportPath = args[1] || null;
|
|
1644
|
+
const idsArg = args.slice(2).join(',');
|
|
1645
|
+
approvedIds = idsArg ? idsArg.split(',').map(s => s.trim()).filter(Boolean) : [];
|
|
1646
|
+
// Check for --grouping flag
|
|
1647
|
+
const groupIdx = args.indexOf('--grouping');
|
|
1648
|
+
if (groupIdx !== -1 && args[groupIdx + 1]) {
|
|
1649
|
+
grouping = args[groupIdx + 1];
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
if (!source) error('source required (melhorias or ideias)');
|
|
1654
|
+
if (!reportPath) error('report_path required');
|
|
1655
|
+
if (approvedIds.length === 0) error('approved_ids required (at least one ID)');
|
|
1656
|
+
|
|
1657
|
+
// Read the report file
|
|
1658
|
+
const fullReportPath = path.join(cwd, reportPath);
|
|
1659
|
+
if (!fs.existsSync(fullReportPath)) {
|
|
1660
|
+
error(`Report file not found: ${reportPath}`);
|
|
1661
|
+
}
|
|
1662
|
+
const reportContent = fs.readFileSync(fullReportPath, 'utf-8');
|
|
1663
|
+
|
|
1664
|
+
// Parse suggestions from report
|
|
1665
|
+
const suggestions = parseSuggestionsFromReport(reportContent, approvedIds);
|
|
1666
|
+
|
|
1667
|
+
if (suggestions.length === 0) {
|
|
1668
|
+
error(`No approved suggestions found in report. IDs requested: ${approvedIds.join(', ')}`);
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
// Group suggestions into phases
|
|
1672
|
+
const groups = grouping === 'single'
|
|
1673
|
+
? [{ name: buildGroupName(source, suggestions), suggestions }]
|
|
1674
|
+
: groupSuggestionsByDimension(suggestions, source);
|
|
1675
|
+
|
|
1676
|
+
// Read ROADMAP to detect language and max phase
|
|
1677
|
+
const roadmapPath = path.join(cwd, '.plano', 'ROADMAP.md');
|
|
1678
|
+
if (!fs.existsSync(roadmapPath)) error('ROADMAP.md not found');
|
|
1679
|
+
let roadmapContent = fs.readFileSync(roadmapPath, 'utf-8');
|
|
1680
|
+
|
|
1681
|
+
const usePt = /###\s*Fase\s+\d/.test(roadmapContent);
|
|
1682
|
+
|
|
1683
|
+
// Find max phase number
|
|
1684
|
+
const phaseNumPattern = /#{2,4}\s*(?:Phase|Fase)\s+(\d+)[A-Z]?(?:\.\d+)*:/gi;
|
|
1685
|
+
let maxPhase = 0;
|
|
1686
|
+
let pm;
|
|
1687
|
+
while ((pm = phaseNumPattern.exec(roadmapContent)) !== null) {
|
|
1688
|
+
const num = parseInt(pm[1], 10);
|
|
1689
|
+
if (num > maxPhase) maxPhase = num;
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
const phasesCreated = [];
|
|
1693
|
+
|
|
1694
|
+
for (const group of groups) {
|
|
1695
|
+
const newPhaseNum = maxPhase + 1;
|
|
1696
|
+
maxPhase = newPhaseNum;
|
|
1697
|
+
const paddedNum = String(newPhaseNum).padStart(2, '0');
|
|
1698
|
+
const slug = generateSlugInternal(group.name).substring(0, 50);
|
|
1699
|
+
const dirName = `${paddedNum}-${slug}`;
|
|
1700
|
+
const dirPath = path.join(cwd, '.plano', 'fases', dirName);
|
|
1701
|
+
|
|
1702
|
+
// Create phase directory
|
|
1703
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
1704
|
+
fs.writeFileSync(path.join(dirPath, '.gitkeep'), '');
|
|
1705
|
+
|
|
1706
|
+
// Build criteria
|
|
1707
|
+
const criteria = buildCriteria(group.suggestions, source, usePt);
|
|
1708
|
+
|
|
1709
|
+
// Build suggestion list
|
|
1710
|
+
const suggestionList = group.suggestions.map(s => {
|
|
1711
|
+
const effortLabel = usePt ? 'Esforco' : 'Effort';
|
|
1712
|
+
const impactLabel = usePt ? 'Impacto' : 'Impact';
|
|
1713
|
+
return `- ${s.id}: ${s.title} (${effortLabel}: ${s.effort}, ${impactLabel}: ${s.impact})`;
|
|
1714
|
+
}).join('\n');
|
|
1715
|
+
|
|
1716
|
+
// Build the phase entry
|
|
1717
|
+
let phaseEntry;
|
|
1718
|
+
if (usePt) {
|
|
1719
|
+
const criteriaText = criteria.map((c, i) => ` ${i + 1}. ${c}`).join('\n');
|
|
1720
|
+
phaseEntry = `\n### Fase ${newPhaseNum}: ${group.name}\n` +
|
|
1721
|
+
`**Objetivo**: Implementar ${group.suggestions.length} ${source === 'ideias' ? 'ideias' : 'melhorias'} de ${group.dimension || 'multiplas dimensoes'} identificadas pela auditoria\n` +
|
|
1722
|
+
`**Depende de**: Fase ${newPhaseNum - 1}\n` +
|
|
1723
|
+
`**Criterios de Sucesso** (o que deve ser VERDADE):\n${criteriaText}\n` +
|
|
1724
|
+
`**Planos**: TBD\n\n` +
|
|
1725
|
+
`Sugestoes incluidas:\n${suggestionList}\n`;
|
|
1726
|
+
} else {
|
|
1727
|
+
const criteriaText = criteria.map((c, i) => ` ${i + 1}. ${c}`).join('\n');
|
|
1728
|
+
phaseEntry = `\n### Phase ${newPhaseNum}: ${group.name}\n` +
|
|
1729
|
+
`**Goal**: Implement ${group.suggestions.length} ${source === 'ideias' ? 'ideas' : 'improvements'} for ${group.dimension || 'multiple dimensions'} identified by audit\n` +
|
|
1730
|
+
`**Depends on**: Phase ${newPhaseNum - 1}\n` +
|
|
1731
|
+
`**Success Criteria** (what must be TRUE):\n${criteriaText}\n` +
|
|
1732
|
+
`**Plans**: TBD\n\n` +
|
|
1733
|
+
`Included suggestions:\n${suggestionList}\n`;
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
// Insert phase entry before progress table or at end
|
|
1737
|
+
const tableHeaderPattern = /\n##\s*(?:Tabela de Progresso|Progress Table)/i;
|
|
1738
|
+
const tableMatch = roadmapContent.match(tableHeaderPattern);
|
|
1739
|
+
if (tableMatch) {
|
|
1740
|
+
roadmapContent = roadmapContent.slice(0, tableMatch.index) + phaseEntry + roadmapContent.slice(tableMatch.index);
|
|
1741
|
+
} else {
|
|
1742
|
+
roadmapContent += phaseEntry;
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
// Add checkbox in Fases/Phases section
|
|
1746
|
+
const checkboxSectionPattern = /\n(##\s*(?:Fases|Phases)\s*\n)/i;
|
|
1747
|
+
const checkboxSection = roadmapContent.match(checkboxSectionPattern);
|
|
1748
|
+
if (checkboxSection) {
|
|
1749
|
+
// Find the last checkbox line in the section
|
|
1750
|
+
const sectionStart = checkboxSection.index + checkboxSection[0].length;
|
|
1751
|
+
const sectionRest = roadmapContent.slice(sectionStart);
|
|
1752
|
+
const lastCheckboxEnd = findLastCheckboxEnd(sectionRest);
|
|
1753
|
+
const insertPos = sectionStart + lastCheckboxEnd;
|
|
1754
|
+
const label = usePt ? 'Fase' : 'Phase';
|
|
1755
|
+
const shortDesc = group.suggestions.length + (usePt ? ' sugestoes de ' : ' suggestions for ') + (group.dimension || source);
|
|
1756
|
+
const checkboxLine = `\n- [ ] **${label} ${newPhaseNum}: ${group.name}** - ${shortDesc}`;
|
|
1757
|
+
roadmapContent = roadmapContent.slice(0, insertPos) + checkboxLine + roadmapContent.slice(insertPos);
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
// Add row in progress table
|
|
1761
|
+
const progressTablePattern = /(\|\s*(?:Fase|Phase)\s*\|[^\n]*\n\|[-|\s]+\n)([\s\S]*?)(?=\n##|\n$|$)/i;
|
|
1762
|
+
const progressMatch = roadmapContent.match(progressTablePattern);
|
|
1763
|
+
if (progressMatch) {
|
|
1764
|
+
const tableBody = progressMatch[2].trimEnd();
|
|
1765
|
+
const statusLabel = usePt ? 'Nao iniciado' : 'Not started';
|
|
1766
|
+
const newRow = `| ${newPhaseNum}. ${group.name} | 0/? | ${statusLabel} | - |`;
|
|
1767
|
+
const newTableBody = tableBody + '\n' + newRow;
|
|
1768
|
+
roadmapContent = roadmapContent.replace(progressTablePattern,
|
|
1769
|
+
(_match, header) => `${header}${newTableBody}\n`
|
|
1770
|
+
);
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
phasesCreated.push({
|
|
1774
|
+
phase_number: newPhaseNum,
|
|
1775
|
+
name: group.name,
|
|
1776
|
+
suggestion_count: group.suggestions.length,
|
|
1777
|
+
suggestion_ids: group.suggestions.map(s => s.id),
|
|
1778
|
+
directory: `.plano/fases/${dirName}/`,
|
|
1779
|
+
});
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
// Write updated ROADMAP
|
|
1783
|
+
fs.writeFileSync(roadmapPath, roadmapContent, 'utf-8');
|
|
1784
|
+
|
|
1785
|
+
output({
|
|
1786
|
+
phases_created: phasesCreated,
|
|
1787
|
+
total_phases: phasesCreated.length,
|
|
1788
|
+
total_suggestions: suggestions.length,
|
|
1789
|
+
roadmap_updated: true,
|
|
1790
|
+
}, raw);
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
// --- Helper: Parse suggestions from RELATORIO.md ---
|
|
1794
|
+
|
|
1795
|
+
function parseSuggestionsFromReport(content, approvedIds) {
|
|
1796
|
+
const suggestions = [];
|
|
1797
|
+
const idSet = new Set(approvedIds.map(id => id.toUpperCase()));
|
|
1798
|
+
|
|
1799
|
+
// Match suggestion blocks: ### ID: title
|
|
1800
|
+
const suggestionPattern = /###\s+([\w-]+):\s*([^\n]+)/g;
|
|
1801
|
+
let match;
|
|
1802
|
+
|
|
1803
|
+
while ((match = suggestionPattern.exec(content)) !== null) {
|
|
1804
|
+
const id = match[1].trim().toUpperCase();
|
|
1805
|
+
if (!idSet.has(id)) continue;
|
|
1806
|
+
|
|
1807
|
+
const title = match[2].trim();
|
|
1808
|
+
const blockStart = match.index;
|
|
1809
|
+
|
|
1810
|
+
// Find the end of this suggestion block (next ### or end)
|
|
1811
|
+
const restContent = content.slice(blockStart + match[0].length);
|
|
1812
|
+
const nextSuggestion = restContent.match(/\n###\s+[\w-]+:/);
|
|
1813
|
+
const blockEnd = nextSuggestion
|
|
1814
|
+
? blockStart + match[0].length + nextSuggestion.index
|
|
1815
|
+
: content.length;
|
|
1816
|
+
const block = content.slice(blockStart, blockEnd);
|
|
1817
|
+
|
|
1818
|
+
// Parse table fields
|
|
1819
|
+
const arquivo = extractTableField(block, 'Arquivo');
|
|
1820
|
+
const dimensao = extractTableField(block, 'Dimensao') || extractTableField(block, 'Dimension');
|
|
1821
|
+
const esforco = extractTableField(block, 'Esforco') || extractTableField(block, 'Effort');
|
|
1822
|
+
const impacto = extractTableField(block, 'Impacto') || extractTableField(block, 'Impact');
|
|
1823
|
+
|
|
1824
|
+
// Parse Problema/Sugestao
|
|
1825
|
+
const problemaMatch = block.match(/\*\*(?:Problema|Problem):\*\*\s*([\s\S]*?)(?=\*\*(?:Sugestao|Suggestion|Referencia|Reference):\*\*|$)/i);
|
|
1826
|
+
const sugestaoMatch = block.match(/\*\*(?:Sugestao|Suggestion):\*\*\s*([\s\S]*?)(?=\*\*(?:Referencia|Reference):\*\*|$)/i);
|
|
1827
|
+
|
|
1828
|
+
suggestions.push({
|
|
1829
|
+
id: match[1].trim(), // preserve original case
|
|
1830
|
+
title,
|
|
1831
|
+
file: arquivo ? arquivo.replace(/`/g, '') : null,
|
|
1832
|
+
dimension: dimensao ? dimensao.split(/\s*\(/)[0].trim() : null,
|
|
1833
|
+
effort: esforco || '?',
|
|
1834
|
+
impact: impacto || '?',
|
|
1835
|
+
problem: problemaMatch ? problemaMatch[1].trim() : null,
|
|
1836
|
+
suggestion: sugestaoMatch ? sugestaoMatch[1].trim() : null,
|
|
1837
|
+
});
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
return suggestions;
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
function extractTableField(block, fieldName) {
|
|
1844
|
+
const pattern = new RegExp(`\\|\\s*${fieldName}\\s*\\|\\s*([^|]+)\\|`, 'i');
|
|
1845
|
+
const match = block.match(pattern);
|
|
1846
|
+
return match ? match[1].trim() : null;
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
// --- Helper: Group suggestions by dimension ---
|
|
1850
|
+
|
|
1851
|
+
function groupSuggestionsByDimension(suggestions, source) {
|
|
1852
|
+
// Group by primary dimension
|
|
1853
|
+
const dimensionMap = {};
|
|
1854
|
+
for (const s of suggestions) {
|
|
1855
|
+
const dim = s.dimension || 'Geral';
|
|
1856
|
+
if (!dimensionMap[dim]) dimensionMap[dim] = [];
|
|
1857
|
+
dimensionMap[dim].push(s);
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
const groups = [];
|
|
1861
|
+
|
|
1862
|
+
for (const [dim, items] of Object.entries(dimensionMap)) {
|
|
1863
|
+
// If 5+ suggestions in a dimension, try to subdivide by directory
|
|
1864
|
+
if (items.length >= 5) {
|
|
1865
|
+
const dirMap = {};
|
|
1866
|
+
for (const item of items) {
|
|
1867
|
+
const dir = item.file ? path.dirname(item.file) : '_root';
|
|
1868
|
+
if (!dirMap[dir]) dirMap[dir] = [];
|
|
1869
|
+
dirMap[dir].push(item);
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
const dirKeys = Object.keys(dirMap);
|
|
1873
|
+
if (dirKeys.length > 1) {
|
|
1874
|
+
for (const [dir, dirItems] of Object.entries(dirMap)) {
|
|
1875
|
+
const dirLabel = dir === '_root' ? 'raiz' : dir.replace(/\//g, '/');
|
|
1876
|
+
groups.push({
|
|
1877
|
+
name: `${dim}: ${buildSubgroupName(dirItems, source)} (${dirLabel})`,
|
|
1878
|
+
dimension: dim,
|
|
1879
|
+
suggestions: dirItems,
|
|
1880
|
+
});
|
|
1881
|
+
}
|
|
1882
|
+
continue;
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
groups.push({
|
|
1887
|
+
name: `${dim}: ${buildSubgroupName(items, source)}`,
|
|
1888
|
+
dimension: dim,
|
|
1889
|
+
suggestions: items,
|
|
1890
|
+
});
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
// Merge small groups: if a group has only 1 suggestion with small effort, try to merge
|
|
1894
|
+
const mergedGroups = [];
|
|
1895
|
+
const pendingMerge = [];
|
|
1896
|
+
|
|
1897
|
+
for (const group of groups) {
|
|
1898
|
+
if (group.suggestions.length === 1 && group.suggestions[0].effort === 'P') {
|
|
1899
|
+
pendingMerge.push(group);
|
|
1900
|
+
} else {
|
|
1901
|
+
mergedGroups.push(group);
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
// Try to merge pending into adjacent groups of same dimension
|
|
1906
|
+
for (const pending of pendingMerge) {
|
|
1907
|
+
const target = mergedGroups.find(g => g.dimension === pending.dimension);
|
|
1908
|
+
if (target) {
|
|
1909
|
+
target.suggestions.push(...pending.suggestions);
|
|
1910
|
+
// Update name if needed
|
|
1911
|
+
target.name = `${target.dimension}: ${buildSubgroupName(target.suggestions, source)}`;
|
|
1912
|
+
} else {
|
|
1913
|
+
mergedGroups.push(pending);
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
return mergedGroups;
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1920
|
+
function buildSubgroupName(suggestions, source) {
|
|
1921
|
+
if (suggestions.length === 1) {
|
|
1922
|
+
return suggestions[0].title;
|
|
1923
|
+
}
|
|
1924
|
+
// Synthesize a short name from titles
|
|
1925
|
+
const uniqueWords = new Set();
|
|
1926
|
+
for (const s of suggestions) {
|
|
1927
|
+
const words = s.title.split(/\s+/).slice(0, 3);
|
|
1928
|
+
for (const w of words) {
|
|
1929
|
+
if (w.length > 3) uniqueWords.add(w.toLowerCase());
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
const wordList = [...uniqueWords].slice(0, 4).join(', ');
|
|
1933
|
+
const prefix = source === 'ideias' ? 'Ideias sobre' : 'Melhorias em';
|
|
1934
|
+
return `${prefix} ${wordList}`;
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
function buildGroupName(source, suggestions) {
|
|
1938
|
+
const prefix = source === 'ideias' ? 'Ideias' : 'Melhorias';
|
|
1939
|
+
return `${prefix}: ${suggestions.length} sugestoes aprovadas`;
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
// --- Helper: Build success criteria ---
|
|
1943
|
+
|
|
1944
|
+
function buildCriteria(suggestions, source, usePt) {
|
|
1945
|
+
if (suggestions.length <= 5) {
|
|
1946
|
+
return suggestions.map(s => {
|
|
1947
|
+
if (usePt) {
|
|
1948
|
+
const prefix = source === 'ideias' ? 'Feature' : 'Sugestao';
|
|
1949
|
+
return `${prefix} ${s.id} implementada: ${s.title}`;
|
|
1950
|
+
} else {
|
|
1951
|
+
const prefix = source === 'ideias' ? 'Feature' : 'Suggestion';
|
|
1952
|
+
return `${prefix} ${s.id} implemented: ${s.title}`;
|
|
1953
|
+
}
|
|
1954
|
+
});
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
// More than 5 suggestions: summarize
|
|
1958
|
+
const dimCounts = {};
|
|
1959
|
+
for (const s of suggestions) {
|
|
1960
|
+
const dim = s.dimension || 'Geral';
|
|
1961
|
+
dimCounts[dim] = (dimCounts[dim] || 0) + 1;
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
const criteria = [];
|
|
1965
|
+
for (const [dim, count] of Object.entries(dimCounts)) {
|
|
1966
|
+
if (usePt) {
|
|
1967
|
+
criteria.push(`${count} sugestoes de ${dim} implementadas conforme RELATORIO.md`);
|
|
1968
|
+
} else {
|
|
1969
|
+
criteria.push(`${count} ${dim} suggestions implemented per RELATORIO.md`);
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
if (criteria.length > 5) {
|
|
1974
|
+
if (usePt) {
|
|
1975
|
+
return [`${suggestions.length} sugestoes implementadas conforme RELATORIO.md`];
|
|
1976
|
+
} else {
|
|
1977
|
+
return [`${suggestions.length} suggestions implemented per RELATORIO.md`];
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
return criteria;
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
// --- Helper: Find end of last checkbox line ---
|
|
1985
|
+
|
|
1986
|
+
function findLastCheckboxEnd(text) {
|
|
1987
|
+
let lastEnd = 0;
|
|
1988
|
+
const checkboxRegex = /^-\s*\[[ x]\]\s*\*\*.*\n/gm;
|
|
1989
|
+
let m;
|
|
1990
|
+
while ((m = checkboxRegex.exec(text)) !== null) {
|
|
1991
|
+
lastEnd = m.index + m[0].length;
|
|
1992
|
+
}
|
|
1993
|
+
return lastEnd;
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1529
1996
|
function cmdPhasePlanIndex(cwd, phase, raw) {
|
|
1530
1997
|
if (!phase) error('phase required for phase-plan-index');
|
|
1531
1998
|
|
package/commands/ajuda.md
CHANGED
|
@@ -50,6 +50,13 @@ Sistema de desenvolvimento orientado a fases para projetos de software.
|
|
|
50
50
|
| `/up:remover-fase` | Remover fase futura e renumerar | `/up:remover-fase 5` |
|
|
51
51
|
| `/up:resetar` | Resetar projeto (limpar .plano/) | `/up:resetar` |
|
|
52
52
|
|
|
53
|
+
### Auditoria
|
|
54
|
+
|
|
55
|
+
| Comando | Descricao | Uso |
|
|
56
|
+
|---------|-----------|-----|
|
|
57
|
+
| `/up:melhorias` | Auditoria completa do codebase (UX, performance, modernidade) | `/up:melhorias` |
|
|
58
|
+
| `/up:ideias` | Sugestoes de features novas com pesquisa de mercado | `/up:ideias` |
|
|
59
|
+
|
|
53
60
|
### Utilitarios
|
|
54
61
|
|
|
55
62
|
| Comando | Descricao | Uso |
|
|
@@ -116,6 +123,18 @@ O mapeamento do codebase alimenta todo o pipeline automaticamente.
|
|
|
116
123
|
/up:progresso
|
|
117
124
|
```
|
|
118
125
|
|
|
126
|
+
### Auditoria de Codebase
|
|
127
|
+
```
|
|
128
|
+
/up:melhorias # Auditoria completa (standalone, nao requer /up:novo-projeto)
|
|
129
|
+
```
|
|
130
|
+
Resultado em .plano/melhorias/RELATORIO.md com sugestoes priorizadas.
|
|
131
|
+
|
|
132
|
+
### Ideacao de Features
|
|
133
|
+
```
|
|
134
|
+
/up:ideias # Sugestoes de features novas (standalone, nao requer /up:novo-projeto)
|
|
135
|
+
```
|
|
136
|
+
Resultado em .plano/ideias/RELATORIO.md com ranking ICE e anti-features.
|
|
137
|
+
|
|
119
138
|
### Correcao Rapida
|
|
120
139
|
```
|
|
121
140
|
/up:rapido "Descricao da tarefa"
|