legacy-squad 1.2.0 → 1.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/dist/cli.mjs +314 -22
- package/dist/templates/claude-commands/legacy-squad.md +38 -0
- package/package.json +3 -3
package/dist/cli.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// apps/cli/src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import
|
|
5
|
+
import path10 from "node:path";
|
|
6
6
|
import { existsSync } from "node:fs";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
|
|
@@ -1318,7 +1318,7 @@ var Installer = class {
|
|
|
1318
1318
|
mode: { execution: "read_only" },
|
|
1319
1319
|
ide: { primary: "claude-code" },
|
|
1320
1320
|
installed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1321
|
-
framework_version: "1.
|
|
1321
|
+
framework_version: "1.3.0"
|
|
1322
1322
|
};
|
|
1323
1323
|
await writeFile2(
|
|
1324
1324
|
path7.join(configDir, "project.yaml"),
|
|
@@ -1328,9 +1328,11 @@ var Installer = class {
|
|
|
1328
1328
|
await mkdir2(path7.join(effectiveRoot, ".legacy-squad", "outputs", "reports"), { recursive: true });
|
|
1329
1329
|
await mkdir2(path7.join(effectiveRoot, ".legacy-squad", "outputs", "assessments"), { recursive: true });
|
|
1330
1330
|
await mkdir2(path7.join(effectiveRoot, ".legacy-squad", "logs"), { recursive: true });
|
|
1331
|
-
const
|
|
1331
|
+
const claudeCommandsRoot = path7.join(effectiveRoot, ".claude", "commands");
|
|
1332
|
+
const claudeCommandsDir = path7.join(claudeCommandsRoot, "legacy-squad");
|
|
1332
1333
|
await mkdir2(claudeCommandsDir, { recursive: true });
|
|
1333
1334
|
await this.copySlashCommands(templateDir, claudeCommandsDir);
|
|
1335
|
+
await this.copyOrchestrator(templateDir, claudeCommandsRoot);
|
|
1334
1336
|
await this.generateAgentsMd(effectiveRoot, repoIndex.project.name);
|
|
1335
1337
|
const logContent = [
|
|
1336
1338
|
`Legacy Squad Framework \u2014 Install Log`,
|
|
@@ -1390,6 +1392,21 @@ var Installer = class {
|
|
|
1390
1392
|
}
|
|
1391
1393
|
}
|
|
1392
1394
|
}
|
|
1395
|
+
/**
|
|
1396
|
+
* DA-012: copia o template do orchestrator para `.claude/commands/legacy-squad.md`
|
|
1397
|
+
* (raiz de commands, não subdir). No Claude Code, arquivos na raiz de commands viram
|
|
1398
|
+
* o comando puro `/<filename>` — então este caminho expressa `/legacy-squad` (validado
|
|
1399
|
+
* emp.). Falha silenciosamente se o template não existir, no mesmo estilo de copySlashCommands.
|
|
1400
|
+
*/
|
|
1401
|
+
async copyOrchestrator(templateDir, commandsRoot) {
|
|
1402
|
+
const sourcePath = path7.join(templateDir, "legacy-squad.md");
|
|
1403
|
+
const targetPath = path7.join(commandsRoot, "legacy-squad.md");
|
|
1404
|
+
try {
|
|
1405
|
+
const content = await readFile2(sourcePath, "utf-8");
|
|
1406
|
+
await writeFile2(targetPath, content, "utf-8");
|
|
1407
|
+
} catch {
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1393
1410
|
async generateAgentsMd(projectRoot, projectName) {
|
|
1394
1411
|
const lines = [
|
|
1395
1412
|
`# Legacy Squad Agents \u2014 ${projectName}`,
|
|
@@ -1442,6 +1459,7 @@ var Doctor = class {
|
|
|
1442
1459
|
checks.push(await this.checkFile(projectRoot, ".legacy-squad/memory/context-packs.json", "Context Packs"));
|
|
1443
1460
|
checks.push(await this.checkFile(projectRoot, ".legacy-squad/config/project.yaml", "Project Config"));
|
|
1444
1461
|
checks.push(await this.checkDir(projectRoot, ".claude/commands/legacy-squad", "Claude Code Agents"));
|
|
1462
|
+
checks.push(await this.checkFile(projectRoot, ".claude/commands/legacy-squad.md", "Orchestrator /legacy-squad"));
|
|
1445
1463
|
checks.push(await this.checkFile(projectRoot, "AGENTS.md", "Codex AGENTS.md"));
|
|
1446
1464
|
checks.push(await this.checkDir(projectRoot, ".legacy-squad/outputs/assessments", "Assessments Dir"));
|
|
1447
1465
|
checks.push(await this.checkDir(projectRoot, ".legacy-squad/outputs/reports", "Reports Dir"));
|
|
@@ -1507,19 +1525,281 @@ var Doctor = class {
|
|
|
1507
1525
|
}
|
|
1508
1526
|
};
|
|
1509
1527
|
|
|
1528
|
+
// packages/agents/src/lifecycle-detector.ts
|
|
1529
|
+
import path9 from "node:path";
|
|
1530
|
+
var REPO_INDEX_PATH = ".legacy-squad/memory/repo-index.json";
|
|
1531
|
+
var FINDINGS_INDEX_PATH = ".legacy-squad/memory/findings/index.json";
|
|
1532
|
+
var assessmentPath = (slug) => `.legacy-squad/outputs/assessments/${slug}-assessment.md`;
|
|
1533
|
+
var PRS_PATH = ".legacy-squad/outputs/reports/PRS.md";
|
|
1534
|
+
var SDD_PATH = ".legacy-squad/outputs/sdd/SDD.md";
|
|
1535
|
+
var MMP_PATH = ".legacy-squad/outputs/mmp/MMP.md";
|
|
1536
|
+
var SPECS_PATH = ".legacy-squad/outputs/specs/INDEX.md";
|
|
1537
|
+
var MATURITY_LABELS = {
|
|
1538
|
+
1: "Unknown",
|
|
1539
|
+
2: "Understood",
|
|
1540
|
+
3: "Assessed",
|
|
1541
|
+
4: "Planned",
|
|
1542
|
+
5: "Modernization Ready",
|
|
1543
|
+
6: "Continuously Modernized"
|
|
1544
|
+
};
|
|
1545
|
+
var STEPS = [
|
|
1546
|
+
{
|
|
1547
|
+
id: "scan",
|
|
1548
|
+
label: "Scan",
|
|
1549
|
+
command: null,
|
|
1550
|
+
phase: "discovery",
|
|
1551
|
+
target: REPO_INDEX_PATH,
|
|
1552
|
+
reason: "Rode `npx legacy-squad install` para escanear o projeto e gerar o repo-index."
|
|
1553
|
+
},
|
|
1554
|
+
{
|
|
1555
|
+
id: "security",
|
|
1556
|
+
label: "Security assessment",
|
|
1557
|
+
command: "/legacy-squad:security",
|
|
1558
|
+
phase: "assessment",
|
|
1559
|
+
target: assessmentPath("security"),
|
|
1560
|
+
reason: "Inicie os assessments rodando `/legacy-squad:security`."
|
|
1561
|
+
},
|
|
1562
|
+
{
|
|
1563
|
+
id: "architecture",
|
|
1564
|
+
label: "Architecture assessment",
|
|
1565
|
+
command: "/legacy-squad:architecture",
|
|
1566
|
+
phase: "assessment",
|
|
1567
|
+
target: assessmentPath("architecture"),
|
|
1568
|
+
reason: "Rode `/legacy-squad:architecture`."
|
|
1569
|
+
},
|
|
1570
|
+
{
|
|
1571
|
+
id: "legacy-code",
|
|
1572
|
+
label: "Legacy code assessment",
|
|
1573
|
+
command: "/legacy-squad:legacy-code",
|
|
1574
|
+
phase: "assessment",
|
|
1575
|
+
target: assessmentPath("legacy-code"),
|
|
1576
|
+
reason: "Rode `/legacy-squad:legacy-code`."
|
|
1577
|
+
},
|
|
1578
|
+
{
|
|
1579
|
+
id: "business-rules",
|
|
1580
|
+
label: "Business rules assessment",
|
|
1581
|
+
command: "/legacy-squad:business-rules",
|
|
1582
|
+
phase: "assessment",
|
|
1583
|
+
target: assessmentPath("business-rules"),
|
|
1584
|
+
reason: "Rode `/legacy-squad:business-rules`."
|
|
1585
|
+
},
|
|
1586
|
+
{
|
|
1587
|
+
id: "modernization",
|
|
1588
|
+
label: "Modernization assessment",
|
|
1589
|
+
command: "/legacy-squad:modernization",
|
|
1590
|
+
phase: "assessment",
|
|
1591
|
+
target: assessmentPath("modernization"),
|
|
1592
|
+
reason: "Rode `/legacy-squad:modernization`."
|
|
1593
|
+
},
|
|
1594
|
+
{
|
|
1595
|
+
id: "generate-prs",
|
|
1596
|
+
label: "PRS",
|
|
1597
|
+
command: "/legacy-squad:generate-prs",
|
|
1598
|
+
phase: "assessment",
|
|
1599
|
+
target: PRS_PATH,
|
|
1600
|
+
reason: "Consolide o diagn\xF3stico com `/legacy-squad:generate-prs`."
|
|
1601
|
+
},
|
|
1602
|
+
{
|
|
1603
|
+
id: "generate-sdd",
|
|
1604
|
+
label: "SDD",
|
|
1605
|
+
command: "/legacy-squad:generate-sdd",
|
|
1606
|
+
phase: "design",
|
|
1607
|
+
target: SDD_PATH,
|
|
1608
|
+
reason: "Gere o desenho t\xE9cnico com `/legacy-squad:generate-sdd`."
|
|
1609
|
+
},
|
|
1610
|
+
{
|
|
1611
|
+
id: "generate-mmp",
|
|
1612
|
+
label: "MMP",
|
|
1613
|
+
command: "/legacy-squad:generate-mmp",
|
|
1614
|
+
phase: "planning",
|
|
1615
|
+
target: MMP_PATH,
|
|
1616
|
+
reason: "Gere o plano mestre com `/legacy-squad:generate-mmp`."
|
|
1617
|
+
},
|
|
1618
|
+
{
|
|
1619
|
+
id: "generate-specs",
|
|
1620
|
+
label: "Execution Specs",
|
|
1621
|
+
command: "/legacy-squad:generate-specs",
|
|
1622
|
+
phase: "execution",
|
|
1623
|
+
target: SPECS_PATH,
|
|
1624
|
+
reason: "Decomponha em specs com `/legacy-squad:generate-specs`."
|
|
1625
|
+
}
|
|
1626
|
+
];
|
|
1627
|
+
var PHASE_ORDER = [
|
|
1628
|
+
{ id: "discovery", label: "Discovery" },
|
|
1629
|
+
{ id: "assessment", label: "Assessment" },
|
|
1630
|
+
{ id: "design", label: "Design" },
|
|
1631
|
+
{ id: "planning", label: "Planning" },
|
|
1632
|
+
{ id: "execution", label: "Execution" }
|
|
1633
|
+
];
|
|
1634
|
+
var ASSESSMENT_STEP_IDS = [
|
|
1635
|
+
"security",
|
|
1636
|
+
"architecture",
|
|
1637
|
+
"legacy-code",
|
|
1638
|
+
"business-rules",
|
|
1639
|
+
"modernization"
|
|
1640
|
+
];
|
|
1641
|
+
var LifecycleDetector = class {
|
|
1642
|
+
constructor(fs) {
|
|
1643
|
+
this.fs = fs;
|
|
1644
|
+
}
|
|
1645
|
+
fs;
|
|
1646
|
+
/** Detecta e retorna o snapshot determinístico do lifecycle em `projectRoot`. */
|
|
1647
|
+
async detect(projectRoot) {
|
|
1648
|
+
const done = /* @__PURE__ */ new Map();
|
|
1649
|
+
for (const step of STEPS) {
|
|
1650
|
+
done.set(step.id, await this.fs.exists(path9.join(projectRoot, step.target)));
|
|
1651
|
+
}
|
|
1652
|
+
const installed = done.get("scan") === true;
|
|
1653
|
+
const project = installed ? await this.readProject(projectRoot) : null;
|
|
1654
|
+
const findingCount = installed ? await this.readFindingCount(projectRoot) : 0;
|
|
1655
|
+
const maturityLevel = this.computeMaturity(done);
|
|
1656
|
+
return {
|
|
1657
|
+
project,
|
|
1658
|
+
installed,
|
|
1659
|
+
findingCount,
|
|
1660
|
+
maturityLevel,
|
|
1661
|
+
maturityLabel: MATURITY_LABELS[maturityLevel],
|
|
1662
|
+
phases: this.buildPhases(done),
|
|
1663
|
+
nextStep: this.computeNextStep(done),
|
|
1664
|
+
complete: done.get("generate-specs") === true
|
|
1665
|
+
};
|
|
1666
|
+
}
|
|
1667
|
+
/** Agrupa os passos por fase, calculando a contagem de concluídos. */
|
|
1668
|
+
buildPhases(done) {
|
|
1669
|
+
return PHASE_ORDER.map(({ id, label }) => {
|
|
1670
|
+
const steps = STEPS.filter((s) => s.phase === id).map((s) => ({
|
|
1671
|
+
id: s.id,
|
|
1672
|
+
label: s.label,
|
|
1673
|
+
command: s.command,
|
|
1674
|
+
done: done.get(s.id) === true
|
|
1675
|
+
}));
|
|
1676
|
+
return {
|
|
1677
|
+
id,
|
|
1678
|
+
label,
|
|
1679
|
+
steps,
|
|
1680
|
+
doneCount: steps.filter((s) => s.done).length,
|
|
1681
|
+
totalCount: steps.length
|
|
1682
|
+
};
|
|
1683
|
+
});
|
|
1684
|
+
}
|
|
1685
|
+
/** Deriva o Maturity Level (§10) a partir dos artefatos presentes. */
|
|
1686
|
+
computeMaturity(done) {
|
|
1687
|
+
if (done.get("generate-specs")) return 5;
|
|
1688
|
+
if (done.get("generate-mmp")) return 4;
|
|
1689
|
+
if (ASSESSMENT_STEP_IDS.every((id) => done.get(id))) return 3;
|
|
1690
|
+
if (done.get("scan")) return 2;
|
|
1691
|
+
return 1;
|
|
1692
|
+
}
|
|
1693
|
+
/** Próximo passo = primeiro `target` ausente na ordem canônica; null se tudo concluído. */
|
|
1694
|
+
computeNextStep(done) {
|
|
1695
|
+
const [scanStep] = STEPS;
|
|
1696
|
+
if (!done.get("scan")) {
|
|
1697
|
+
return { id: "install", command: null, reason: scanStep.reason };
|
|
1698
|
+
}
|
|
1699
|
+
for (const step of STEPS) {
|
|
1700
|
+
if (!done.get(step.id)) {
|
|
1701
|
+
return { id: step.id, command: step.command, reason: step.reason };
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
return null;
|
|
1705
|
+
}
|
|
1706
|
+
/** Lê nome, tipo e stack do repo-index; null se ausente ou ilegível. */
|
|
1707
|
+
async readProject(root) {
|
|
1708
|
+
try {
|
|
1709
|
+
const raw = await this.fs.readFile(path9.join(root, REPO_INDEX_PATH));
|
|
1710
|
+
const idx = JSON.parse(raw);
|
|
1711
|
+
return {
|
|
1712
|
+
name: idx.project?.name ?? "unknown",
|
|
1713
|
+
type: idx.project?.type ?? "unknown",
|
|
1714
|
+
stack: (idx.stack ?? []).map((s) => s.name ?? "").filter((n) => n.length > 0)
|
|
1715
|
+
};
|
|
1716
|
+
} catch {
|
|
1717
|
+
return null;
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
/** Conta os achados no index slim (DA-011); 0 se ausente ou ilegível. */
|
|
1721
|
+
async readFindingCount(root) {
|
|
1722
|
+
try {
|
|
1723
|
+
const raw = await this.fs.readFile(path9.join(root, FINDINGS_INDEX_PATH));
|
|
1724
|
+
const parsed = JSON.parse(raw);
|
|
1725
|
+
return Array.isArray(parsed) ? parsed.length : 0;
|
|
1726
|
+
} catch {
|
|
1727
|
+
return 0;
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
};
|
|
1731
|
+
|
|
1732
|
+
// packages/output/src/dashboard-renderer.ts
|
|
1733
|
+
var RULE = "\u2501".repeat(40);
|
|
1734
|
+
var STEP_DISPLAY = {
|
|
1735
|
+
scan: "scan",
|
|
1736
|
+
security: "security",
|
|
1737
|
+
architecture: "architecture",
|
|
1738
|
+
"legacy-code": "legacy-code",
|
|
1739
|
+
"business-rules": "business-rules",
|
|
1740
|
+
modernization: "modernization",
|
|
1741
|
+
"generate-prs": "PRS",
|
|
1742
|
+
"generate-sdd": "SDD",
|
|
1743
|
+
"generate-mmp": "MMP",
|
|
1744
|
+
"generate-specs": "Specs"
|
|
1745
|
+
};
|
|
1746
|
+
var display = (id) => STEP_DISPLAY[id] ?? id;
|
|
1747
|
+
function phaseIcon(phase) {
|
|
1748
|
+
if (phase.totalCount > 0 && phase.doneCount === phase.totalCount) return "\u2713";
|
|
1749
|
+
if (phase.doneCount > 0) return "\u25D0";
|
|
1750
|
+
return "\xB7";
|
|
1751
|
+
}
|
|
1752
|
+
function phaseDetail(phase) {
|
|
1753
|
+
if (phase.steps.length === 1) {
|
|
1754
|
+
return display(phase.steps[0].id);
|
|
1755
|
+
}
|
|
1756
|
+
const names = phase.steps.map((s) => display(s.id)).join(" \xB7 ");
|
|
1757
|
+
return `${phase.doneCount}/${phase.totalCount} ${names}`;
|
|
1758
|
+
}
|
|
1759
|
+
var DashboardRenderer = class {
|
|
1760
|
+
render(snapshot) {
|
|
1761
|
+
const lines = [RULE];
|
|
1762
|
+
if (snapshot.project) {
|
|
1763
|
+
const stack = snapshot.project.stack.join(", ");
|
|
1764
|
+
lines.push(` Legacy Squad \u2014 ${snapshot.project.name}`);
|
|
1765
|
+
lines.push(` ${snapshot.project.type}${stack ? ` \xB7 ${stack}` : ""}`);
|
|
1766
|
+
} else {
|
|
1767
|
+
lines.push(" Legacy Squad");
|
|
1768
|
+
lines.push(" (framework n\xE3o instalado neste diret\xF3rio)");
|
|
1769
|
+
}
|
|
1770
|
+
lines.push(RULE, "");
|
|
1771
|
+
lines.push(` Maturity Level ${snapshot.maturityLevel} \u2014 ${snapshot.maturityLabel}`);
|
|
1772
|
+
lines.push(` Findings ${snapshot.findingCount}`, "");
|
|
1773
|
+
const labelWidth = Math.max(...snapshot.phases.map((p) => p.label.length));
|
|
1774
|
+
for (const phase of snapshot.phases) {
|
|
1775
|
+
lines.push(` ${phaseIcon(phase)} ${phase.label.padEnd(labelWidth)} ${phaseDetail(phase)}`);
|
|
1776
|
+
}
|
|
1777
|
+
lines.push("");
|
|
1778
|
+
if (snapshot.nextStep) {
|
|
1779
|
+
const target = snapshot.nextStep.command ?? "npx legacy-squad install";
|
|
1780
|
+
lines.push(` \u2192 Pr\xF3ximo passo: ${target}`);
|
|
1781
|
+
lines.push(` ${snapshot.nextStep.reason}`);
|
|
1782
|
+
} else {
|
|
1783
|
+
lines.push(" \u2713 Lifecycle V1 completo \u2014 specs prontas para execu\xE7\xE3o.");
|
|
1784
|
+
}
|
|
1785
|
+
lines.push(RULE);
|
|
1786
|
+
return lines.join("\n");
|
|
1787
|
+
}
|
|
1788
|
+
};
|
|
1789
|
+
|
|
1510
1790
|
// apps/cli/src/index.ts
|
|
1511
1791
|
var program = new Command();
|
|
1512
1792
|
function getTemplateDir() {
|
|
1513
|
-
const cliDir =
|
|
1514
|
-
const bundledPath =
|
|
1793
|
+
const cliDir = path10.dirname(fileURLToPath(import.meta.url));
|
|
1794
|
+
const bundledPath = path10.resolve(cliDir, "templates", "claude-commands");
|
|
1515
1795
|
if (existsSync(bundledPath)) return bundledPath;
|
|
1516
|
-
const devPath =
|
|
1796
|
+
const devPath = path10.resolve(cliDir, "..", "..", "..", "templates", "claude-commands");
|
|
1517
1797
|
if (existsSync(devPath)) return devPath;
|
|
1518
1798
|
throw new Error("Templates not found. Run from the framework root or use the published package.");
|
|
1519
1799
|
}
|
|
1520
|
-
program.name("legacy-squad").description("AI-Powered Legacy Modernization Platform \u2014 Understand. Plan. Modernize.").version("1.
|
|
1800
|
+
program.name("legacy-squad").description("AI-Powered Legacy Modernization Platform \u2014 Understand. Plan. Modernize.").version("1.3.0");
|
|
1521
1801
|
program.command("install").description("Install Legacy Squad Framework inside the current project").option("-p, --path <dir>", "Project root directory", ".").action(async (opts) => {
|
|
1522
|
-
const projectRoot =
|
|
1802
|
+
const projectRoot = path10.resolve(opts.path);
|
|
1523
1803
|
const templateDir = getTemplateDir();
|
|
1524
1804
|
console.log("\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
1525
1805
|
console.log(" Legacy Squad Framework V1 \u2014 Install");
|
|
@@ -1561,21 +1841,21 @@ program.command("install").description("Install Legacy Squad Framework inside th
|
|
|
1561
1841
|
console.log(" 1. Open Claude Code: claude");
|
|
1562
1842
|
console.log("");
|
|
1563
1843
|
console.log(" Analysis (run in order):");
|
|
1564
|
-
console.log(" /legacy-squad
|
|
1565
|
-
console.log(" /legacy-squad
|
|
1566
|
-
console.log(" /legacy-squad
|
|
1567
|
-
console.log(" /legacy-squad
|
|
1568
|
-
console.log(" /legacy-squad
|
|
1844
|
+
console.log(" /legacy-squad:security");
|
|
1845
|
+
console.log(" /legacy-squad:architecture");
|
|
1846
|
+
console.log(" /legacy-squad:legacy-code");
|
|
1847
|
+
console.log(" /legacy-squad:business-rules");
|
|
1848
|
+
console.log(" /legacy-squad:modernization");
|
|
1569
1849
|
console.log("");
|
|
1570
1850
|
console.log(" Consolidated artifacts (run after analysis):");
|
|
1571
|
-
console.log(" /legacy-squad
|
|
1572
|
-
console.log(" /legacy-squad
|
|
1573
|
-
console.log(" /legacy-squad
|
|
1574
|
-
console.log(" /legacy-squad
|
|
1851
|
+
console.log(" /legacy-squad:generate-prs (Product Refactor Specification)");
|
|
1852
|
+
console.log(" /legacy-squad:generate-sdd (Software Design Document)");
|
|
1853
|
+
console.log(" /legacy-squad:generate-mmp (Modernization Master Plan)");
|
|
1854
|
+
console.log(" /legacy-squad:generate-specs (Execution Specs for V2)");
|
|
1575
1855
|
console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n");
|
|
1576
1856
|
});
|
|
1577
1857
|
program.command("scan").description("Re-scan the project and update .legacy-squad/memory/").option("-p, --path <dir>", "Project root directory", ".").action(async (opts) => {
|
|
1578
|
-
const projectRoot =
|
|
1858
|
+
const projectRoot = path10.resolve(opts.path);
|
|
1579
1859
|
console.log(`
|
|
1580
1860
|
\u{1F50D} Re-scanning: ${projectRoot}
|
|
1581
1861
|
`);
|
|
@@ -1590,18 +1870,18 @@ program.command("scan").description("Re-scan the project and update .legacy-squa
|
|
|
1590
1870
|
const compliance = new ComplianceEngine(fs);
|
|
1591
1871
|
const findings = await compliance.evaluate(effectiveRoot, repoIndex);
|
|
1592
1872
|
const { mkdir: mkdir3, writeFile: writeFile3 } = await import("node:fs/promises");
|
|
1593
|
-
const memoryDir =
|
|
1873
|
+
const memoryDir = path10.join(effectiveRoot, ".legacy-squad", "memory");
|
|
1594
1874
|
await mkdir3(memoryDir, { recursive: true });
|
|
1595
|
-
await writeFile3(
|
|
1875
|
+
await writeFile3(path10.join(memoryDir, "repo-index.json"), JSON.stringify(repoIndex, null, 2), "utf-8");
|
|
1596
1876
|
await new FindingsWriter(fs).write(findings, memoryDir);
|
|
1597
1877
|
console.log(`\u2705 Stack: ${repoIndex.stack.map((s) => s.name).join(", ")}`);
|
|
1598
1878
|
console.log(`\u{1F4E6} Modules: ${repoIndex.modules.length}`);
|
|
1599
1879
|
console.log(`\u{1F512} Findings: ${findings.length}`);
|
|
1600
|
-
console.log(`\u{1F4C4} Updated: ${
|
|
1880
|
+
console.log(`\u{1F4C4} Updated: ${path10.join(effectiveRoot, ".legacy-squad", "memory")}
|
|
1601
1881
|
`);
|
|
1602
1882
|
});
|
|
1603
1883
|
program.command("doctor").description("Verify Legacy Squad installation health").option("-p, --path <dir>", "Project root directory", ".").action(async (opts) => {
|
|
1604
|
-
const projectRoot =
|
|
1884
|
+
const projectRoot = path10.resolve(opts.path);
|
|
1605
1885
|
console.log("\n\u{1FA7A} Legacy Squad Doctor\n");
|
|
1606
1886
|
const doctor = new Doctor();
|
|
1607
1887
|
const checks = await doctor.check(projectRoot);
|
|
@@ -1618,4 +1898,16 @@ program.command("doctor").description("Verify Legacy Squad installation health")
|
|
|
1618
1898
|
console.log("\n\u2705 All checks passed.\n");
|
|
1619
1899
|
}
|
|
1620
1900
|
});
|
|
1901
|
+
program.command("status").description("Show the project lifecycle dashboard and the recommended next step").option("-p, --path <dir>", "Project root directory", ".").option("--json", "Output the raw lifecycle snapshot as JSON").action(async (opts) => {
|
|
1902
|
+
const projectRoot = path10.resolve(opts.path);
|
|
1903
|
+
const fs = new NodeFileSystem();
|
|
1904
|
+
const snapshot = await new LifecycleDetector(fs).detect(projectRoot);
|
|
1905
|
+
if (opts.json) {
|
|
1906
|
+
console.log(JSON.stringify(snapshot, null, 2));
|
|
1907
|
+
return;
|
|
1908
|
+
}
|
|
1909
|
+
console.log("");
|
|
1910
|
+
console.log(new DashboardRenderer().render(snapshot));
|
|
1911
|
+
console.log("");
|
|
1912
|
+
});
|
|
1621
1913
|
program.parse();
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
Você é o **Orchestrator** do Legacy Squad Framework — o ponto de entrada que mostra onde o projeto está no lifecycle (Discovery → Assessment → Design → Planning → Execution) e qual é o próximo passo recomendado, respeitando dependências.
|
|
2
|
+
|
|
3
|
+
## Passos
|
|
4
|
+
|
|
5
|
+
1. **Obtenha o estado do lifecycle.** Tente primeiro:
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
npx legacy-squad status --json
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
O comando retorna um snapshot determinístico (`LifecycleSnapshot`) com `project`, `installed`, `findingCount`, `maturityLevel`, `phases` e `nextStep`.
|
|
12
|
+
|
|
13
|
+
2. **Fallback** — se o comando não estiver disponível (ex.: o framework não foi instalado ainda neste projeto), inspecione `.legacy-squad/` diretamente:
|
|
14
|
+
- `memory/repo-index.json` — projeto instalado (Discovery concluído).
|
|
15
|
+
- `memory/findings/index.json` — contagem de achados (DA-011).
|
|
16
|
+
- `outputs/assessments/{security,architecture,legacy-code,business-rules,modernization}-assessment.md` — assessments por pilar.
|
|
17
|
+
- `outputs/reports/PRS.md`, `outputs/sdd/SDD.md`, `outputs/mmp/MMP.md`, `outputs/specs/INDEX.md` — artefatos.
|
|
18
|
+
|
|
19
|
+
Se nada existir, oriente o usuário a rodar `npx legacy-squad install`.
|
|
20
|
+
|
|
21
|
+
3. **Renderize o dashboard** a partir do JSON. O `status` (sem `--json`) já imprime um dashboard formatado — você pode rodar `npx legacy-squad status` e mostrar a saída ao usuário. Se preferir reorganizar, use os campos do snapshot.
|
|
22
|
+
|
|
23
|
+
4. **Ofereça o próximo passo.** Leia `snapshot.nextStep`:
|
|
24
|
+
- Se `nextStep.command` estiver definido, ofereça executá-lo (ex.: "Quer que eu rode `/legacy-squad:security` agora?").
|
|
25
|
+
- Se `nextStep` for `null`, o lifecycle V1 está completo (Level 5 — Modernization Ready) e as specs estão prontas para execução assistida (V2).
|
|
26
|
+
- Se `nextStep.id` for `install`, oriente o usuário a rodar `npx legacy-squad install` (você não roda comandos da CLI sem confirmação).
|
|
27
|
+
|
|
28
|
+
5. **Respeite a ordem canônica** do lifecycle (FRAMEWORK_SPECIFICATION §3): scan → 5 assessments (security, architecture, legacy-code, business-rules, modernization) → PRS → SDD → MMP → Specs. Nunca pule passos: as dependências duras (SDD←architecture-assessment, MMP←modernization-assessment, Specs←MMP) já estão refletidas nessa ordem.
|
|
29
|
+
|
|
30
|
+
## Output
|
|
31
|
+
|
|
32
|
+
Não escreva em `.legacy-squad/outputs/`. Este comando é apenas o orchestrator — quem produz artefatos são `/legacy-squad:security`, `/legacy-squad:generate-prs`, etc. Sua função é mostrar o estado e sugerir o próximo passo.
|
|
33
|
+
|
|
34
|
+
## Restrições
|
|
35
|
+
|
|
36
|
+
- Não invente progresso: o snapshot é determinístico (calculado em TypeScript a partir da existência de arquivos canônicos). Se um arquivo não existe, o passo não está concluído — não tente "interpretar".
|
|
37
|
+
- Não execute o próximo passo sem confirmação explícita do usuário.
|
|
38
|
+
- O comando `npx legacy-squad status --json` lê o filesystem; ele não modifica nada.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "legacy-squad",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "AI-Powered Legacy Modernization Platform — Install-first, IDE-native, evidence-driven framework that transforms legacy systems into modernization-ready assets.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"legacy",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"homepage": "https://github.com/hrpimenta/legacy-squad",
|
|
24
24
|
"repository": {
|
|
25
25
|
"type": "git",
|
|
26
|
-
"url": "https://github.com/hrpimenta/legacy-squad.git"
|
|
26
|
+
"url": "git+https://github.com/hrpimenta/legacy-squad.git"
|
|
27
27
|
},
|
|
28
28
|
"license": "MIT",
|
|
29
29
|
"author": "Legacy Squad Team",
|
|
@@ -56,4 +56,4 @@
|
|
|
56
56
|
"engines": {
|
|
57
57
|
"node": ">=18"
|
|
58
58
|
}
|
|
59
|
-
}
|
|
59
|
+
}
|