workflow-agent-cli 2.4.2 → 2.6.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/README.md +48 -15
- package/dist/{chunk-WXXFUPYO.js → chunk-6NWQLGHI.js} +3 -1
- package/dist/{chunk-RDVTKGQV.js → chunk-DEAF7P4L.js} +36 -2
- package/dist/chunk-DEAF7P4L.js.map +1 -0
- package/dist/chunk-LN5OAWEQ.js +92 -0
- package/dist/chunk-LN5OAWEQ.js.map +1 -0
- package/dist/cli/index.js +3199 -457
- package/dist/cli/index.js.map +1 -1
- package/dist/config/index.d.ts +2 -2
- package/dist/config/index.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/{schema-OJg7YAI_.d.ts → schema-D0zTM83x.d.ts} +111 -0
- package/dist/scripts/postinstall.js +37 -20
- package/dist/scripts/postinstall.js.map +1 -1
- package/dist/validators/index.d.ts +1 -1
- package/package.json +36 -3
- package/templates/PATTERN_ANALYSIS_WORKFLOW.md +72 -55
- package/dist/chunk-RDVTKGQV.js.map +0 -1
- /package/dist/{chunk-WXXFUPYO.js.map → chunk-6NWQLGHI.js.map} +0 -0
package/dist/cli/index.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
analyzeProject,
|
|
4
|
+
detectPackageManager,
|
|
3
5
|
generateAuditReport,
|
|
4
6
|
hasUncommittedChanges,
|
|
7
|
+
isMonorepo,
|
|
5
8
|
runAllChecks,
|
|
6
9
|
runAllSetups,
|
|
7
10
|
stageAllChanges
|
|
8
|
-
} from "../chunk-
|
|
11
|
+
} from "../chunk-6NWQLGHI.js";
|
|
9
12
|
import {
|
|
10
13
|
validateBranchName,
|
|
11
14
|
validateCommitMessage,
|
|
@@ -15,7 +18,12 @@ import {
|
|
|
15
18
|
hasConfig,
|
|
16
19
|
loadConfig,
|
|
17
20
|
validateScopeDefinitions
|
|
18
|
-
} from "../chunk-
|
|
21
|
+
} from "../chunk-DEAF7P4L.js";
|
|
22
|
+
import {
|
|
23
|
+
SCRIPT_CATEGORIES,
|
|
24
|
+
TOTAL_SCRIPTS,
|
|
25
|
+
WORKFLOW_SCRIPTS
|
|
26
|
+
} from "../chunk-LN5OAWEQ.js";
|
|
19
27
|
|
|
20
28
|
// src/cli/index.ts
|
|
21
29
|
import { Command } from "commander";
|
|
@@ -302,10 +310,10 @@ async function initCommand(options) {
|
|
|
302
310
|
try {
|
|
303
311
|
const presetModule = await import(`@workflow/scopes-${preset}`);
|
|
304
312
|
scopes = presetModule.scopes || presetModule.default.scopes;
|
|
305
|
-
const
|
|
306
|
-
|
|
313
|
+
const spinner7 = p.spinner();
|
|
314
|
+
spinner7.start(`Loading ${presetModule.default?.name || preset} preset`);
|
|
307
315
|
await new Promise((resolve2) => setTimeout(resolve2, 500));
|
|
308
|
-
|
|
316
|
+
spinner7.stop(`\u2713 Loaded ${scopes.length} scopes from preset`);
|
|
309
317
|
} catch (error) {
|
|
310
318
|
console.log(
|
|
311
319
|
chalk.yellow(
|
|
@@ -352,8 +360,8 @@ async function initCommand(options) {
|
|
|
352
360
|
process.exit(0);
|
|
353
361
|
}
|
|
354
362
|
if (shouldGenerateGuidelines) {
|
|
355
|
-
const
|
|
356
|
-
|
|
363
|
+
const spinner7 = p.spinner();
|
|
364
|
+
spinner7.start("Generating guidelines...");
|
|
357
365
|
try {
|
|
358
366
|
const templatesDir = join(__dirname, "../../templates");
|
|
359
367
|
await validateTemplateDirectory(templatesDir);
|
|
@@ -365,9 +373,9 @@ async function initCommand(options) {
|
|
|
365
373
|
guidelinesDir,
|
|
366
374
|
context
|
|
367
375
|
);
|
|
368
|
-
|
|
376
|
+
spinner7.stop(`\u2713 Generated ${renderedFiles.length} guideline documents`);
|
|
369
377
|
} catch (error) {
|
|
370
|
-
|
|
378
|
+
spinner7.stop("\u26A0\uFE0F Could not generate guidelines");
|
|
371
379
|
console.log(
|
|
372
380
|
chalk.yellow(
|
|
373
381
|
`
|
|
@@ -590,12 +598,6 @@ import * as p3 from "@clack/prompts";
|
|
|
590
598
|
import chalk6 from "chalk";
|
|
591
599
|
import { readFileSync, writeFileSync, existsSync as existsSync2 } from "fs";
|
|
592
600
|
import { join as join2 } from "path";
|
|
593
|
-
var WORKFLOW_SCRIPTS = {
|
|
594
|
-
"workflow:init": "workflow-agent init",
|
|
595
|
-
"workflow:validate": "workflow-agent validate",
|
|
596
|
-
"workflow:suggest": "workflow-agent suggest",
|
|
597
|
-
"workflow:doctor": "workflow-agent doctor"
|
|
598
|
-
};
|
|
599
601
|
async function setupCommand() {
|
|
600
602
|
p3.intro(chalk6.bgBlue(" workflow-agent setup "));
|
|
601
603
|
const cwd = process.cwd();
|
|
@@ -609,48 +611,57 @@ async function setupCommand() {
|
|
|
609
611
|
if (!packageJson.scripts) {
|
|
610
612
|
packageJson.scripts = {};
|
|
611
613
|
}
|
|
612
|
-
const
|
|
613
|
-
const
|
|
614
|
+
const addedScripts = [];
|
|
615
|
+
const updatedScripts = [];
|
|
614
616
|
for (const [scriptName, scriptCommand] of Object.entries(WORKFLOW_SCRIPTS)) {
|
|
615
|
-
if (packageJson.scripts[scriptName]) {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
617
|
+
if (!packageJson.scripts[scriptName]) {
|
|
618
|
+
packageJson.scripts[scriptName] = scriptCommand;
|
|
619
|
+
addedScripts.push(scriptName);
|
|
620
|
+
} else if (packageJson.scripts[scriptName] !== scriptCommand) {
|
|
621
|
+
packageJson.scripts[scriptName] = scriptCommand;
|
|
622
|
+
updatedScripts.push(scriptName);
|
|
619
623
|
}
|
|
620
624
|
}
|
|
621
|
-
|
|
622
|
-
|
|
625
|
+
const totalChanges = addedScripts.length + updatedScripts.length;
|
|
626
|
+
if (totalChanges === 0) {
|
|
627
|
+
p3.outro(
|
|
628
|
+
chalk6.green(
|
|
629
|
+
`\u2713 All ${TOTAL_SCRIPTS} workflow scripts are already configured!`
|
|
630
|
+
)
|
|
631
|
+
);
|
|
623
632
|
return;
|
|
624
633
|
}
|
|
625
|
-
console.log(chalk6.dim("\nScripts to add:"));
|
|
626
|
-
for (const [scriptName, scriptCommand] of Object.entries(scriptsToAdd)) {
|
|
627
|
-
console.log(chalk6.dim(` ${scriptName}: ${scriptCommand}`));
|
|
628
|
-
}
|
|
629
|
-
if (existingScripts.length > 0) {
|
|
630
|
-
console.log(chalk6.yellow("\nExisting scripts (will be skipped):"));
|
|
631
|
-
existingScripts.forEach((name) => {
|
|
632
|
-
console.log(chalk6.yellow(` ${name}`));
|
|
633
|
-
});
|
|
634
|
-
}
|
|
635
|
-
const shouldAdd = await p3.confirm({
|
|
636
|
-
message: "Add these scripts to package.json?",
|
|
637
|
-
initialValue: true
|
|
638
|
-
});
|
|
639
|
-
if (p3.isCancel(shouldAdd) || !shouldAdd) {
|
|
640
|
-
p3.cancel("Setup cancelled");
|
|
641
|
-
process.exit(0);
|
|
642
|
-
}
|
|
643
|
-
for (const [scriptName, scriptCommand] of Object.entries(scriptsToAdd)) {
|
|
644
|
-
packageJson.scripts[scriptName] = scriptCommand;
|
|
645
|
-
}
|
|
646
634
|
writeFileSync(
|
|
647
635
|
packageJsonPath,
|
|
648
636
|
JSON.stringify(packageJson, null, 2) + "\n",
|
|
649
637
|
"utf-8"
|
|
650
638
|
);
|
|
639
|
+
const summaryParts = [];
|
|
640
|
+
if (addedScripts.length > 0) {
|
|
641
|
+
summaryParts.push(`${addedScripts.length} new`);
|
|
642
|
+
}
|
|
643
|
+
if (updatedScripts.length > 0) {
|
|
644
|
+
summaryParts.push(`${updatedScripts.length} updated`);
|
|
645
|
+
}
|
|
646
|
+
console.log(
|
|
647
|
+
chalk6.green(
|
|
648
|
+
`
|
|
649
|
+
\u2713 Workflow scripts configured (${summaryParts.join(", ")}):
|
|
650
|
+
`
|
|
651
|
+
)
|
|
652
|
+
);
|
|
653
|
+
for (const [category, scripts] of Object.entries(SCRIPT_CATEGORIES)) {
|
|
654
|
+
console.log(chalk6.cyan(` ${category}:`));
|
|
655
|
+
for (const script of scripts) {
|
|
656
|
+
const isNew = addedScripts.includes(script);
|
|
657
|
+
const isUpdated = updatedScripts.includes(script);
|
|
658
|
+
const marker = isNew ? chalk6.green(" (new)") : isUpdated ? chalk6.yellow(" (updated)") : "";
|
|
659
|
+
console.log(chalk6.dim(` - ${script}`) + marker);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
651
662
|
p3.outro(
|
|
652
663
|
chalk6.green(
|
|
653
|
-
`\u2713
|
|
664
|
+
`\u2713 ${TOTAL_SCRIPTS} workflow scripts available in package.json!`
|
|
654
665
|
)
|
|
655
666
|
);
|
|
656
667
|
console.log(chalk6.dim("\nRun them with:"));
|
|
@@ -668,8 +679,8 @@ async function scopeCreateCommand(options) {
|
|
|
668
679
|
console.log(chalk7.bold.cyan("\n\u{1F3A8} Create Custom Scope Package\n"));
|
|
669
680
|
const cwd = process.cwd();
|
|
670
681
|
const isNonInteractive = !!(options.name && options.scopes && options.presetName);
|
|
671
|
-
const
|
|
672
|
-
if (
|
|
682
|
+
const isMonorepo2 = existsSync3(join3(cwd, "pnpm-workspace.yaml"));
|
|
683
|
+
if (isMonorepo2) {
|
|
673
684
|
console.log(chalk7.dim("\u2713 Detected monorepo workspace\n"));
|
|
674
685
|
}
|
|
675
686
|
const packageNameInput = isNonInteractive ? options.name : await p4.text({
|
|
@@ -813,7 +824,7 @@ async function scopeCreateCommand(options) {
|
|
|
813
824
|
let outputDir;
|
|
814
825
|
if (options.outputDir) {
|
|
815
826
|
outputDir = options.outputDir;
|
|
816
|
-
} else if (
|
|
827
|
+
} else if (isMonorepo2) {
|
|
817
828
|
outputDir = join3(cwd, "packages", `scopes-${packageName}`);
|
|
818
829
|
} else {
|
|
819
830
|
const customDir = await p4.text({
|
|
@@ -837,8 +848,8 @@ async function scopeCreateCommand(options) {
|
|
|
837
848
|
process.exit(0);
|
|
838
849
|
}
|
|
839
850
|
}
|
|
840
|
-
const
|
|
841
|
-
|
|
851
|
+
const spinner7 = p4.spinner();
|
|
852
|
+
spinner7.start("Creating package structure...");
|
|
842
853
|
try {
|
|
843
854
|
await mkdir2(join3(outputDir, "src"), { recursive: true });
|
|
844
855
|
const packageJson = {
|
|
@@ -969,8 +980,8 @@ describe('${presetName} Scope Preset', () => {
|
|
|
969
980
|
"utf-8"
|
|
970
981
|
);
|
|
971
982
|
}
|
|
972
|
-
|
|
973
|
-
if (
|
|
983
|
+
spinner7.stop("\u2713 Package structure created");
|
|
984
|
+
if (isMonorepo2) {
|
|
974
985
|
const workspaceFile = join3(cwd, "pnpm-workspace.yaml");
|
|
975
986
|
const workspaceContent = await readFile(workspaceFile, "utf-8");
|
|
976
987
|
const packagePath = `packages/scopes-${packageName}`;
|
|
@@ -1021,7 +1032,7 @@ describe('${presetName} Scope Preset', () => {
|
|
|
1021
1032
|
);
|
|
1022
1033
|
}
|
|
1023
1034
|
} catch (error) {
|
|
1024
|
-
|
|
1035
|
+
spinner7.stop("\u2717 Failed to create package");
|
|
1025
1036
|
console.error(chalk7.red("\nError:"), error);
|
|
1026
1037
|
process.exit(1);
|
|
1027
1038
|
}
|
|
@@ -1076,8 +1087,8 @@ async function scopeMigrateCommand(options) {
|
|
|
1076
1087
|
p5.cancel("Migration cancelled");
|
|
1077
1088
|
process.exit(0);
|
|
1078
1089
|
}
|
|
1079
|
-
const
|
|
1080
|
-
if (
|
|
1090
|
+
const isMonorepo2 = existsSync4(join4(cwd, "pnpm-workspace.yaml"));
|
|
1091
|
+
if (isMonorepo2) {
|
|
1081
1092
|
console.log(chalk8.dim("\n\u2713 Detected monorepo workspace\n"));
|
|
1082
1093
|
}
|
|
1083
1094
|
const packageNameInput = options.name || await p5.text({
|
|
@@ -1125,7 +1136,7 @@ async function scopeMigrateCommand(options) {
|
|
|
1125
1136
|
let outputDir;
|
|
1126
1137
|
if (options.outputDir) {
|
|
1127
1138
|
outputDir = options.outputDir;
|
|
1128
|
-
} else if (
|
|
1139
|
+
} else if (isMonorepo2) {
|
|
1129
1140
|
outputDir = join4(cwd, "packages", `scopes-${packageName}`);
|
|
1130
1141
|
} else {
|
|
1131
1142
|
const customDir = await p5.text({
|
|
@@ -1149,8 +1160,8 @@ async function scopeMigrateCommand(options) {
|
|
|
1149
1160
|
process.exit(0);
|
|
1150
1161
|
}
|
|
1151
1162
|
}
|
|
1152
|
-
const
|
|
1153
|
-
|
|
1163
|
+
const spinner7 = p5.spinner();
|
|
1164
|
+
spinner7.start("Migrating scopes to package...");
|
|
1154
1165
|
try {
|
|
1155
1166
|
await mkdir3(join4(outputDir, "src"), { recursive: true });
|
|
1156
1167
|
const packageJson = {
|
|
@@ -1278,8 +1289,8 @@ describe('${presetName} Scope Preset (Migrated)', () => {
|
|
|
1278
1289
|
});
|
|
1279
1290
|
`;
|
|
1280
1291
|
await writeFile3(join4(outputDir, "src", "index.test.ts"), testFile, "utf-8");
|
|
1281
|
-
|
|
1282
|
-
if (
|
|
1292
|
+
spinner7.stop("\u2713 Package created from migrated scopes");
|
|
1293
|
+
if (isMonorepo2) {
|
|
1283
1294
|
const workspaceFile = join4(cwd, "pnpm-workspace.yaml");
|
|
1284
1295
|
const workspaceContent = await readFile2(workspaceFile, "utf-8");
|
|
1285
1296
|
const packagePath = `packages/scopes-${packageName}`;
|
|
@@ -1350,7 +1361,7 @@ describe('${presetName} Scope Preset (Migrated)', () => {
|
|
|
1350
1361
|
)
|
|
1351
1362
|
);
|
|
1352
1363
|
} catch (error) {
|
|
1353
|
-
|
|
1364
|
+
spinner7.stop("\u2717 Migration failed");
|
|
1354
1365
|
console.error(chalk8.red("\nError:"), error);
|
|
1355
1366
|
process.exit(1);
|
|
1356
1367
|
}
|
|
@@ -1379,7 +1390,9 @@ async function verifyCommand(options) {
|
|
|
1379
1390
|
console.log(chalk9.dim(` Max retries: ${maxRetries}`));
|
|
1380
1391
|
console.log(chalk9.dim(` Commit on success: ${shouldCommit ? "yes" : "no"}`));
|
|
1381
1392
|
console.log(chalk9.dim(` Dry-run: ${dryRun ? "yes" : "no"}`));
|
|
1382
|
-
console.log(
|
|
1393
|
+
console.log(
|
|
1394
|
+
chalk9.dim(` Learn from fixes: ${learnFromFixes ? "yes" : "no"}`)
|
|
1395
|
+
);
|
|
1383
1396
|
const startTime = Date.now();
|
|
1384
1397
|
const result = await runAllChecks(cwd, {
|
|
1385
1398
|
maxRetries,
|
|
@@ -1489,7 +1502,9 @@ async function recordSuccessfulFixes(cwd, result) {
|
|
|
1489
1502
|
} catch {
|
|
1490
1503
|
}
|
|
1491
1504
|
if (result.appliedFixes && result.appliedFixes.length > 0) {
|
|
1492
|
-
console.log(
|
|
1505
|
+
console.log(
|
|
1506
|
+
chalk9.cyan("\n\u{1F4DA} Recording successful fixes for learning...\n")
|
|
1507
|
+
);
|
|
1493
1508
|
for (const fix of result.appliedFixes) {
|
|
1494
1509
|
const patternName = `Auto-fix: ${fix.displayName}`;
|
|
1495
1510
|
const patternId = crypto.randomUUID();
|
|
@@ -1572,8 +1587,10 @@ async function recordSuccessfulFixes(cwd, result) {
|
|
|
1572
1587
|
}
|
|
1573
1588
|
} catch (error) {
|
|
1574
1589
|
console.log(
|
|
1575
|
-
chalk9.dim(
|
|
1576
|
-
|
|
1590
|
+
chalk9.dim(
|
|
1591
|
+
`
|
|
1592
|
+
Note: Could not record learning patterns: ${error.message}`
|
|
1593
|
+
)
|
|
1577
1594
|
);
|
|
1578
1595
|
}
|
|
1579
1596
|
}
|
|
@@ -1584,14 +1601,14 @@ import chalk10 from "chalk";
|
|
|
1584
1601
|
async function autoSetupCommand(options) {
|
|
1585
1602
|
console.log(chalk10.bold.cyan("\n\u{1F527} Workflow Agent Auto-Setup\n"));
|
|
1586
1603
|
const cwd = process.cwd();
|
|
1587
|
-
const
|
|
1588
|
-
|
|
1604
|
+
const spinner7 = p6.spinner();
|
|
1605
|
+
spinner7.start("Analyzing project...");
|
|
1589
1606
|
let report;
|
|
1590
1607
|
try {
|
|
1591
1608
|
report = await generateAuditReport(cwd);
|
|
1592
|
-
|
|
1609
|
+
spinner7.stop("\u2713 Project analysis complete");
|
|
1593
1610
|
} catch (error) {
|
|
1594
|
-
|
|
1611
|
+
spinner7.stop("\u2717 Failed to analyze project");
|
|
1595
1612
|
console.error(
|
|
1596
1613
|
chalk10.red(
|
|
1597
1614
|
`Error: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -1724,204 +1741,2852 @@ function formatAuditReportColored(report) {
|
|
|
1724
1741
|
return lines.join("\n");
|
|
1725
1742
|
}
|
|
1726
1743
|
|
|
1727
|
-
// src/cli/commands/
|
|
1728
|
-
import chalk11 from "chalk";
|
|
1744
|
+
// src/cli/commands/advisory.ts
|
|
1729
1745
|
import * as p7 from "@clack/prompts";
|
|
1730
|
-
import
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
}
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
}
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
if (!patternType) {
|
|
1755
|
-
const typeChoice = await p7.select({
|
|
1756
|
-
message: "What type of pattern are you recording?",
|
|
1757
|
-
options: [
|
|
1758
|
-
{
|
|
1759
|
-
value: "fix",
|
|
1760
|
-
label: "\u{1F527} Fix Pattern - A specific solution to a problem"
|
|
1761
|
-
},
|
|
1762
|
-
{
|
|
1763
|
-
value: "blueprint",
|
|
1764
|
-
label: "\u{1F4D0} Blueprint - A project structure template"
|
|
1765
|
-
}
|
|
1766
|
-
]
|
|
1767
|
-
});
|
|
1768
|
-
if (p7.isCancel(typeChoice)) {
|
|
1769
|
-
p7.cancel("Recording cancelled");
|
|
1770
|
-
process.exit(0);
|
|
1746
|
+
import chalk11 from "chalk";
|
|
1747
|
+
import { existsSync as existsSync6 } from "fs";
|
|
1748
|
+
import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
|
|
1749
|
+
import { join as join6 } from "path";
|
|
1750
|
+
|
|
1751
|
+
// src/utils/advisory-analyzer.ts
|
|
1752
|
+
import { existsSync as existsSync5 } from "fs";
|
|
1753
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
1754
|
+
import { join as join5 } from "path";
|
|
1755
|
+
import fg from "fast-glob";
|
|
1756
|
+
var AdvisoryAnalyzer = class {
|
|
1757
|
+
options;
|
|
1758
|
+
constructor(options) {
|
|
1759
|
+
this.options = options;
|
|
1760
|
+
}
|
|
1761
|
+
/**
|
|
1762
|
+
* Run analysis at specified depth level
|
|
1763
|
+
*/
|
|
1764
|
+
async analyze() {
|
|
1765
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1766
|
+
const { depth } = this.options;
|
|
1767
|
+
const project = await this.analyzeProject();
|
|
1768
|
+
if (depth === "executive") {
|
|
1769
|
+
return this.analyzeExecutive(timestamp, project);
|
|
1771
1770
|
}
|
|
1772
|
-
|
|
1771
|
+
const technology = await this.analyzeTechnology();
|
|
1772
|
+
const packages = await this.analyzePackages();
|
|
1773
|
+
const risks = await this.assessRisks(project, technology, packages);
|
|
1774
|
+
const opportunities = await this.assessOpportunities(
|
|
1775
|
+
project,
|
|
1776
|
+
technology,
|
|
1777
|
+
packages
|
|
1778
|
+
);
|
|
1779
|
+
if (depth === "quick") {
|
|
1780
|
+
return {
|
|
1781
|
+
depth,
|
|
1782
|
+
timestamp,
|
|
1783
|
+
project,
|
|
1784
|
+
technology,
|
|
1785
|
+
packages,
|
|
1786
|
+
risks,
|
|
1787
|
+
opportunities
|
|
1788
|
+
};
|
|
1789
|
+
}
|
|
1790
|
+
const architecture = await this.analyzeArchitecture();
|
|
1791
|
+
if (depth === "standard") {
|
|
1792
|
+
const health2 = this.options.includeHealth ? await this.analyzeHealth() : void 0;
|
|
1793
|
+
return {
|
|
1794
|
+
depth,
|
|
1795
|
+
timestamp,
|
|
1796
|
+
project,
|
|
1797
|
+
technology,
|
|
1798
|
+
packages,
|
|
1799
|
+
architecture,
|
|
1800
|
+
risks,
|
|
1801
|
+
opportunities,
|
|
1802
|
+
health: health2
|
|
1803
|
+
};
|
|
1804
|
+
}
|
|
1805
|
+
const codePatterns = await this.analyzeCodePatterns();
|
|
1806
|
+
const health = this.options.includeHealth ? await this.analyzeHealth() : void 0;
|
|
1807
|
+
return {
|
|
1808
|
+
depth,
|
|
1809
|
+
timestamp,
|
|
1810
|
+
project,
|
|
1811
|
+
technology,
|
|
1812
|
+
packages,
|
|
1813
|
+
architecture,
|
|
1814
|
+
codePatterns,
|
|
1815
|
+
risks,
|
|
1816
|
+
opportunities,
|
|
1817
|
+
health
|
|
1818
|
+
};
|
|
1773
1819
|
}
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1820
|
+
/**
|
|
1821
|
+
* Executive depth: High-level business summary only
|
|
1822
|
+
*/
|
|
1823
|
+
async analyzeExecutive(timestamp, project) {
|
|
1824
|
+
const packageJson = await this.readPackageJson();
|
|
1825
|
+
const deps = packageJson.dependencies || {};
|
|
1826
|
+
const devDeps = packageJson.devDependencies || {};
|
|
1827
|
+
const techCategories = this.categorizeTechnologies(deps, devDeps);
|
|
1828
|
+
const risks = this.calculateExecutiveRisks(project, techCategories);
|
|
1829
|
+
const opportunities = this.calculateExecutiveOpportunities(
|
|
1830
|
+
project,
|
|
1831
|
+
techCategories
|
|
1832
|
+
);
|
|
1833
|
+
return {
|
|
1834
|
+
depth: "executive",
|
|
1835
|
+
timestamp,
|
|
1836
|
+
project,
|
|
1837
|
+
technology: {
|
|
1838
|
+
language: this.detectLanguage(deps, devDeps),
|
|
1839
|
+
runtime: this.detectRuntime(deps, devDeps),
|
|
1840
|
+
buildTools: this.detectBuildTools(devDeps),
|
|
1841
|
+
platforms: this.detectPlatforms(deps),
|
|
1842
|
+
infrastructure: this.detectInfrastructure(deps, devDeps)
|
|
1843
|
+
},
|
|
1844
|
+
packages: {
|
|
1845
|
+
total: Object.keys(deps).length + Object.keys(devDeps).length,
|
|
1846
|
+
production: [],
|
|
1847
|
+
development: [],
|
|
1848
|
+
categories: techCategories,
|
|
1849
|
+
outdated: [],
|
|
1850
|
+
security: []
|
|
1851
|
+
},
|
|
1852
|
+
risks,
|
|
1853
|
+
opportunities
|
|
1854
|
+
};
|
|
1855
|
+
}
|
|
1856
|
+
/**
|
|
1857
|
+
* Analyze project overview
|
|
1858
|
+
*/
|
|
1859
|
+
async analyzeProject() {
|
|
1860
|
+
const packageJson = await this.readPackageJson();
|
|
1861
|
+
const isMonorepoProject = await isMonorepo(this.options.cwd);
|
|
1862
|
+
const fileCount = await this.countFiles();
|
|
1863
|
+
const totalLines = await this.countTotalLines();
|
|
1864
|
+
let workspaceCount;
|
|
1865
|
+
if (isMonorepoProject && packageJson.workspaces) {
|
|
1866
|
+
workspaceCount = Array.isArray(packageJson.workspaces) ? packageJson.workspaces.length : 0;
|
|
1867
|
+
}
|
|
1868
|
+
return {
|
|
1869
|
+
name: packageJson.name || "Unknown Project",
|
|
1870
|
+
version: packageJson.version || "0.0.0",
|
|
1871
|
+
description: packageJson.description,
|
|
1872
|
+
isMonorepo: isMonorepoProject,
|
|
1873
|
+
packageManager: await detectPackageManager(this.options.cwd),
|
|
1874
|
+
workspaceCount,
|
|
1875
|
+
fileCount,
|
|
1876
|
+
totalLines
|
|
1877
|
+
};
|
|
1878
|
+
}
|
|
1879
|
+
/**
|
|
1880
|
+
* Analyze technology stack
|
|
1881
|
+
*/
|
|
1882
|
+
async analyzeTechnology() {
|
|
1883
|
+
const packageJson = await this.readPackageJson();
|
|
1884
|
+
const deps = packageJson.dependencies || {};
|
|
1885
|
+
const devDeps = packageJson.devDependencies || {};
|
|
1886
|
+
const projectAnalysis = await analyzeProject(this.options.cwd);
|
|
1887
|
+
return {
|
|
1888
|
+
framework: projectAnalysis.framework,
|
|
1889
|
+
frameworkVersion: deps[projectAnalysis.framework || ""] || devDeps[projectAnalysis.framework || ""],
|
|
1890
|
+
language: this.detectLanguage(deps, devDeps),
|
|
1891
|
+
runtime: this.detectRuntime(deps, devDeps),
|
|
1892
|
+
buildTools: this.detectBuildTools(devDeps),
|
|
1893
|
+
platforms: this.detectPlatforms(deps),
|
|
1894
|
+
infrastructure: this.detectInfrastructure(deps, devDeps)
|
|
1895
|
+
};
|
|
1896
|
+
}
|
|
1897
|
+
/**
|
|
1898
|
+
* Analyze packages in detail
|
|
1899
|
+
*/
|
|
1900
|
+
async analyzePackages() {
|
|
1901
|
+
const packageJson = await this.readPackageJson();
|
|
1902
|
+
const deps = packageJson.dependencies || {};
|
|
1903
|
+
const devDeps = packageJson.devDependencies || {};
|
|
1904
|
+
const production = this.analyzeDependencies(deps, "production");
|
|
1905
|
+
const development = this.analyzeDependencies(devDeps, "development");
|
|
1906
|
+
const categories = this.categorizeTechnologies(deps, devDeps);
|
|
1907
|
+
return {
|
|
1908
|
+
total: Object.keys(deps).length + Object.keys(devDeps).length,
|
|
1909
|
+
production,
|
|
1910
|
+
development,
|
|
1911
|
+
categories,
|
|
1912
|
+
outdated: [],
|
|
1913
|
+
// TODO: Implement outdated check
|
|
1914
|
+
security: []
|
|
1915
|
+
// TODO: Implement security audit
|
|
1916
|
+
};
|
|
1917
|
+
}
|
|
1918
|
+
/**
|
|
1919
|
+
* Analyze dependencies and categorize them
|
|
1920
|
+
*/
|
|
1921
|
+
analyzeDependencies(deps, _type) {
|
|
1922
|
+
return Object.entries(deps).map(([name, version]) => {
|
|
1923
|
+
const category = this.categorizeDependency(name);
|
|
1924
|
+
const info = this.getDependencyInfo(name, category);
|
|
1925
|
+
return {
|
|
1926
|
+
name,
|
|
1927
|
+
version,
|
|
1928
|
+
category,
|
|
1929
|
+
...info
|
|
1930
|
+
};
|
|
1784
1931
|
});
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1932
|
+
}
|
|
1933
|
+
/**
|
|
1934
|
+
* Categorize a dependency
|
|
1935
|
+
*/
|
|
1936
|
+
categorizeDependency(name) {
|
|
1937
|
+
if (["react", "vue", "angular", "svelte", "solid-js"].some(
|
|
1938
|
+
(fw) => name.includes(fw)
|
|
1939
|
+
)) {
|
|
1940
|
+
return "UI Framework";
|
|
1788
1941
|
}
|
|
1789
|
-
|
|
1942
|
+
if (["next", "nuxt", "remix", "sveltekit", "astro"].some(
|
|
1943
|
+
(fw) => name.includes(fw)
|
|
1944
|
+
)) {
|
|
1945
|
+
return "Meta-Framework";
|
|
1946
|
+
}
|
|
1947
|
+
if (["redux", "zustand", "jotai", "recoil", "mobx", "pinia"].includes(name)) {
|
|
1948
|
+
return "State Management";
|
|
1949
|
+
}
|
|
1950
|
+
if ([
|
|
1951
|
+
"axios",
|
|
1952
|
+
"fetch",
|
|
1953
|
+
"swr",
|
|
1954
|
+
"react-query",
|
|
1955
|
+
"@tanstack/react-query",
|
|
1956
|
+
"apollo",
|
|
1957
|
+
"urql"
|
|
1958
|
+
].some((lib) => name.includes(lib))) {
|
|
1959
|
+
return "API/Data Fetching";
|
|
1960
|
+
}
|
|
1961
|
+
if (["react-router", "vue-router", "@tanstack/router"].some(
|
|
1962
|
+
(r) => name.includes(r)
|
|
1963
|
+
)) {
|
|
1964
|
+
return "Routing";
|
|
1965
|
+
}
|
|
1966
|
+
if (["formik", "react-hook-form", "final-form"].some((f) => name.includes(f))) {
|
|
1967
|
+
return "Forms";
|
|
1968
|
+
}
|
|
1969
|
+
if ([
|
|
1970
|
+
"styled-components",
|
|
1971
|
+
"emotion",
|
|
1972
|
+
"tailwind",
|
|
1973
|
+
"sass",
|
|
1974
|
+
"less",
|
|
1975
|
+
"@mui",
|
|
1976
|
+
"antd",
|
|
1977
|
+
"chakra-ui"
|
|
1978
|
+
].some((s) => name.includes(s))) {
|
|
1979
|
+
return "Styling/UI Components";
|
|
1980
|
+
}
|
|
1981
|
+
if ([
|
|
1982
|
+
"vitest",
|
|
1983
|
+
"jest",
|
|
1984
|
+
"mocha",
|
|
1985
|
+
"chai",
|
|
1986
|
+
"testing-library",
|
|
1987
|
+
"playwright",
|
|
1988
|
+
"cypress"
|
|
1989
|
+
].some((t) => name.includes(t))) {
|
|
1990
|
+
return "Testing";
|
|
1991
|
+
}
|
|
1992
|
+
if (["vite", "webpack", "rollup", "esbuild", "turbo", "tsup"].includes(name)) {
|
|
1993
|
+
return "Build Tools";
|
|
1994
|
+
}
|
|
1995
|
+
if (["eslint", "prettier", "stylelint"].some((l) => name.includes(l))) {
|
|
1996
|
+
return "Code Quality";
|
|
1997
|
+
}
|
|
1998
|
+
if (["prisma", "drizzle", "mongoose", "sequelize", "typeorm", "knex"].some(
|
|
1999
|
+
(db) => name.includes(db)
|
|
2000
|
+
)) {
|
|
2001
|
+
return "Database ORM";
|
|
2002
|
+
}
|
|
2003
|
+
if (["next-auth", "auth0", "supabase", "clerk"].some((a) => name.includes(a))) {
|
|
2004
|
+
return "Authentication";
|
|
2005
|
+
}
|
|
2006
|
+
if (["vercel", "netlify", "aws-sdk", "@google-cloud"].some(
|
|
2007
|
+
(d) => name.includes(d)
|
|
2008
|
+
)) {
|
|
2009
|
+
return "Infrastructure";
|
|
2010
|
+
}
|
|
2011
|
+
if (["@analytics", "posthog", "mixpanel", "segment"].some(
|
|
2012
|
+
(a) => name.includes(a)
|
|
2013
|
+
)) {
|
|
2014
|
+
return "Analytics";
|
|
2015
|
+
}
|
|
2016
|
+
return "Other";
|
|
1790
2017
|
}
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
2018
|
+
/**
|
|
2019
|
+
* Get detailed info about a dependency
|
|
2020
|
+
*/
|
|
2021
|
+
getDependencyInfo(name, category) {
|
|
2022
|
+
const defaultInfo = {
|
|
2023
|
+
purpose: `${category} library`,
|
|
2024
|
+
businessValue: "Supports application functionality",
|
|
2025
|
+
usagePatterns: ["Used throughout the application"]
|
|
2026
|
+
};
|
|
2027
|
+
const knownPackages = {
|
|
2028
|
+
react: {
|
|
2029
|
+
purpose: "Core UI library for building component-based interfaces",
|
|
2030
|
+
businessValue: "Enables fast, interactive user experiences with reusable components",
|
|
2031
|
+
usagePatterns: [
|
|
2032
|
+
"Component rendering",
|
|
2033
|
+
"State management",
|
|
2034
|
+
"Event handling"
|
|
2035
|
+
],
|
|
2036
|
+
alternatives: ["Vue", "Svelte", "Solid"]
|
|
2037
|
+
},
|
|
2038
|
+
next: {
|
|
2039
|
+
purpose: "React meta-framework with SSR, routing, and optimization",
|
|
2040
|
+
businessValue: "Improves SEO, performance, and developer productivity",
|
|
2041
|
+
usagePatterns: [
|
|
2042
|
+
"Server-side rendering",
|
|
2043
|
+
"API routes",
|
|
2044
|
+
"File-based routing"
|
|
2045
|
+
],
|
|
2046
|
+
alternatives: ["Remix", "Gatsby"]
|
|
2047
|
+
},
|
|
2048
|
+
typescript: {
|
|
2049
|
+
purpose: "Static type checking for JavaScript",
|
|
2050
|
+
businessValue: "Reduces bugs, improves code quality, and enhances developer experience",
|
|
2051
|
+
usagePatterns: ["Type definitions", "Compile-time checks"]
|
|
2052
|
+
}
|
|
2053
|
+
// Add more as needed
|
|
2054
|
+
};
|
|
2055
|
+
return knownPackages[name] || defaultInfo;
|
|
2056
|
+
}
|
|
2057
|
+
/**
|
|
2058
|
+
* Categorize technologies at high level
|
|
2059
|
+
*/
|
|
2060
|
+
categorizeTechnologies(deps, devDeps) {
|
|
2061
|
+
const allDeps = { ...deps, ...devDeps };
|
|
2062
|
+
const categories = /* @__PURE__ */ new Map();
|
|
2063
|
+
Object.keys(allDeps).forEach((name) => {
|
|
2064
|
+
const category = this.categorizeDependency(name);
|
|
2065
|
+
if (!categories.has(category)) {
|
|
2066
|
+
categories.set(category, []);
|
|
1800
2067
|
}
|
|
2068
|
+
categories.get(category).push(name);
|
|
1801
2069
|
});
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
2070
|
+
return Array.from(categories.entries()).map(([name, packages]) => ({
|
|
2071
|
+
name,
|
|
2072
|
+
count: packages.length,
|
|
2073
|
+
packages,
|
|
2074
|
+
businessImpact: this.getCategoryBusinessImpact(name)
|
|
2075
|
+
}));
|
|
2076
|
+
}
|
|
2077
|
+
/**
|
|
2078
|
+
* Get business impact description for a category
|
|
2079
|
+
*/
|
|
2080
|
+
getCategoryBusinessImpact(category) {
|
|
2081
|
+
const impacts = {
|
|
2082
|
+
"UI Framework": "Core user experience - directly impacts customer satisfaction and engagement",
|
|
2083
|
+
"Meta-Framework": "Application performance and SEO - affects discoverability and user retention",
|
|
2084
|
+
"State Management": "Data consistency - ensures reliable application behavior",
|
|
2085
|
+
"API/Data Fetching": "Backend integration - enables core business functionality",
|
|
2086
|
+
Routing: "Navigation - affects user flow and conversion rates",
|
|
2087
|
+
Forms: "Data collection - critical for user onboarding and conversions",
|
|
2088
|
+
"Styling/UI Components": "Visual design - impacts brand perception and usability",
|
|
2089
|
+
Testing: "Quality assurance - reduces production bugs and support costs",
|
|
2090
|
+
"Build Tools": "Development efficiency - affects time-to-market for new features",
|
|
2091
|
+
"Code Quality": "Maintainability - reduces technical debt and long-term costs",
|
|
2092
|
+
"Database ORM": "Data persistence - ensures business data integrity",
|
|
2093
|
+
Authentication: "Security and user management - protects business and customer data",
|
|
2094
|
+
Infrastructure: "Hosting and scalability - affects uptime and operational costs",
|
|
2095
|
+
Analytics: "Business intelligence - enables data-driven decision making"
|
|
2096
|
+
};
|
|
2097
|
+
return impacts[category] || "Supports various application features";
|
|
2098
|
+
}
|
|
2099
|
+
/**
|
|
2100
|
+
* Analyze architecture patterns
|
|
2101
|
+
*/
|
|
2102
|
+
async analyzeArchitecture() {
|
|
2103
|
+
const files = await this.getProjectFiles();
|
|
2104
|
+
const entryPoints = this.detectEntryPoints(files);
|
|
2105
|
+
const pattern = this.detectArchitecturePattern(files);
|
|
2106
|
+
const layers = this.detectLayers(files);
|
|
2107
|
+
return {
|
|
2108
|
+
pattern,
|
|
2109
|
+
layers,
|
|
2110
|
+
entryPoints,
|
|
2111
|
+
dataFlow: this.analyzeDataFlow(files),
|
|
2112
|
+
keyDecisions: this.extractKeyDecisions(files)
|
|
2113
|
+
};
|
|
2114
|
+
}
|
|
2115
|
+
/**
|
|
2116
|
+
* Analyze code patterns in detail
|
|
2117
|
+
*/
|
|
2118
|
+
async analyzeCodePatterns() {
|
|
2119
|
+
const files = await this.getProjectFiles();
|
|
2120
|
+
return {
|
|
2121
|
+
components: await this.analyzeComponents(files),
|
|
2122
|
+
services: await this.analyzeServices(files),
|
|
2123
|
+
utilities: await this.analyzeUtilities(files),
|
|
2124
|
+
tests: await this.analyzeTests(files),
|
|
2125
|
+
customPatterns: await this.detectCustomPatterns(files)
|
|
2126
|
+
};
|
|
2127
|
+
}
|
|
2128
|
+
/**
|
|
2129
|
+
* Assess risks
|
|
2130
|
+
*/
|
|
2131
|
+
async assessRisks(project, technology, packages) {
|
|
2132
|
+
const categories = [];
|
|
2133
|
+
const critical = [];
|
|
2134
|
+
const high = [];
|
|
2135
|
+
const medium = [];
|
|
2136
|
+
const low = [];
|
|
2137
|
+
const depRisk = this.assessDependencyRisk(packages);
|
|
2138
|
+
categories.push(depRisk);
|
|
2139
|
+
this.categorizeRiskItems(depRisk, critical, high, medium, low);
|
|
2140
|
+
const techRisk = this.assessTechnologyRisk(technology);
|
|
2141
|
+
categories.push(techRisk);
|
|
2142
|
+
this.categorizeRiskItems(techRisk, critical, high, medium, low);
|
|
2143
|
+
const complexityRisk = this.assessComplexityRisk(project);
|
|
2144
|
+
categories.push(complexityRisk);
|
|
2145
|
+
this.categorizeRiskItems(complexityRisk, critical, high, medium, low);
|
|
2146
|
+
const overall = categories.reduce((sum, cat) => sum + cat.score, 0) / categories.length;
|
|
2147
|
+
return { overall, categories, critical, high, medium, low };
|
|
2148
|
+
}
|
|
2149
|
+
/**
|
|
2150
|
+
* Assess opportunities
|
|
2151
|
+
*/
|
|
2152
|
+
async assessOpportunities(project, technology, packages) {
|
|
2153
|
+
const categories = [];
|
|
2154
|
+
const immediate = [];
|
|
2155
|
+
const shortTerm = [];
|
|
2156
|
+
const longTerm = [];
|
|
2157
|
+
const modernization = this.assessModernizationOpportunities(
|
|
2158
|
+
technology,
|
|
2159
|
+
packages
|
|
2160
|
+
);
|
|
2161
|
+
categories.push(modernization);
|
|
2162
|
+
this.categorizeOpportunityItems(
|
|
2163
|
+
modernization,
|
|
2164
|
+
immediate,
|
|
2165
|
+
shortTerm,
|
|
2166
|
+
longTerm
|
|
2167
|
+
);
|
|
2168
|
+
const optimization = this.assessOptimizationOpportunities(project);
|
|
2169
|
+
categories.push(optimization);
|
|
2170
|
+
this.categorizeOpportunityItems(
|
|
2171
|
+
optimization,
|
|
2172
|
+
immediate,
|
|
2173
|
+
shortTerm,
|
|
2174
|
+
longTerm
|
|
2175
|
+
);
|
|
2176
|
+
const growth = this.assessGrowthOpportunities(technology, packages);
|
|
2177
|
+
categories.push(growth);
|
|
2178
|
+
this.categorizeOpportunityItems(growth, immediate, shortTerm, longTerm);
|
|
2179
|
+
const overall = categories.reduce((sum, cat) => sum + cat.potential, 0) / categories.length;
|
|
2180
|
+
return { overall, categories, immediate, shortTerm, longTerm };
|
|
2181
|
+
}
|
|
2182
|
+
/**
|
|
2183
|
+
* Analyze health metrics
|
|
2184
|
+
*/
|
|
2185
|
+
async analyzeHealth() {
|
|
2186
|
+
return {
|
|
2187
|
+
typecheck: false,
|
|
2188
|
+
lint: false,
|
|
2189
|
+
tests: false,
|
|
2190
|
+
build: false,
|
|
2191
|
+
issues: 0
|
|
2192
|
+
};
|
|
2193
|
+
}
|
|
2194
|
+
// ============================================================================
|
|
2195
|
+
// Helper Methods
|
|
2196
|
+
// ============================================================================
|
|
2197
|
+
async readPackageJson() {
|
|
2198
|
+
const pkgPath = join5(this.options.cwd, "package.json");
|
|
2199
|
+
if (!existsSync5(pkgPath)) {
|
|
2200
|
+
throw new Error("package.json not found");
|
|
1805
2201
|
}
|
|
1806
|
-
|
|
2202
|
+
const content = await readFile3(pkgPath, "utf-8");
|
|
2203
|
+
return JSON.parse(content);
|
|
1807
2204
|
}
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
const
|
|
1811
|
-
|
|
1812
|
-
|
|
2205
|
+
async countFiles() {
|
|
2206
|
+
const patterns = ["**/*.{ts,tsx,js,jsx,py,go,rs,java}"];
|
|
2207
|
+
const files = await fg(patterns, {
|
|
2208
|
+
cwd: this.options.cwd,
|
|
2209
|
+
ignore: [
|
|
2210
|
+
"node_modules/**",
|
|
2211
|
+
"dist/**",
|
|
2212
|
+
"build/**",
|
|
2213
|
+
".next/**",
|
|
2214
|
+
"coverage/**"
|
|
2215
|
+
]
|
|
1813
2216
|
});
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
2217
|
+
return files.length;
|
|
2218
|
+
}
|
|
2219
|
+
async countTotalLines() {
|
|
2220
|
+
const fileCount = await this.countFiles();
|
|
2221
|
+
return fileCount * 50;
|
|
2222
|
+
}
|
|
2223
|
+
detectLanguage(deps, devDeps) {
|
|
2224
|
+
if (devDeps.typescript || deps.typescript) return "TypeScript";
|
|
2225
|
+
if (Object.keys(deps).some((d) => d.includes("react") || d.includes("vue"))) {
|
|
2226
|
+
return "JavaScript";
|
|
1817
2227
|
}
|
|
1818
|
-
|
|
2228
|
+
return "JavaScript";
|
|
1819
2229
|
}
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
placeholder: "e.g., >=14.0.0, ^18.0.0",
|
|
1825
|
-
initialValue: ">=1.0.0"
|
|
1826
|
-
});
|
|
1827
|
-
if (p7.isCancel(versionInput)) {
|
|
1828
|
-
p7.cancel("Recording cancelled");
|
|
1829
|
-
process.exit(0);
|
|
2230
|
+
detectRuntime(deps, devDeps) {
|
|
2231
|
+
if (deps.next || devDeps.next) return "Node.js (Next.js)";
|
|
2232
|
+
if (Object.keys(deps).some((d) => d.includes("react"))) {
|
|
2233
|
+
return "Browser + Node.js";
|
|
1830
2234
|
}
|
|
1831
|
-
|
|
2235
|
+
return "Node.js";
|
|
1832
2236
|
}
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
2237
|
+
detectBuildTools(devDeps) {
|
|
2238
|
+
const tools = [];
|
|
2239
|
+
if (devDeps.vite) tools.push("Vite");
|
|
2240
|
+
if (devDeps.webpack) tools.push("Webpack");
|
|
2241
|
+
if (devDeps.rollup) tools.push("Rollup");
|
|
2242
|
+
if (devDeps.esbuild) tools.push("esbuild");
|
|
2243
|
+
if (devDeps.turbo || devDeps.turborepo) tools.push("Turborepo");
|
|
2244
|
+
if (devDeps.tsup) tools.push("tsup");
|
|
2245
|
+
return tools;
|
|
2246
|
+
}
|
|
2247
|
+
detectPlatforms(deps) {
|
|
2248
|
+
const platforms = [];
|
|
2249
|
+
if (deps.react || deps.next) platforms.push("Web");
|
|
2250
|
+
if (deps["react-native"]) platforms.push("Mobile (React Native)");
|
|
2251
|
+
if (deps.electron) platforms.push("Desktop (Electron)");
|
|
2252
|
+
return platforms.length > 0 ? platforms : ["Web"];
|
|
2253
|
+
}
|
|
2254
|
+
detectInfrastructure(deps, devDeps) {
|
|
2255
|
+
const infra = [];
|
|
2256
|
+
const allDeps = { ...deps, ...devDeps };
|
|
2257
|
+
if (allDeps.vercel || allDeps["@vercel/node"]) infra.push("Vercel");
|
|
2258
|
+
if (Object.keys(allDeps).some((d) => d.includes("aws-"))) {
|
|
2259
|
+
infra.push("AWS");
|
|
1853
2260
|
}
|
|
1854
|
-
|
|
2261
|
+
if (Object.keys(allDeps).some((d) => d.includes("@google-cloud"))) {
|
|
2262
|
+
infra.push("Google Cloud");
|
|
2263
|
+
}
|
|
2264
|
+
if (allDeps.netlify) infra.push("Netlify");
|
|
2265
|
+
if (allDeps.supabase || allDeps["@supabase/supabase-js"]) {
|
|
2266
|
+
infra.push("Supabase");
|
|
2267
|
+
}
|
|
2268
|
+
if (allDeps.firebase || allDeps["firebase-admin"]) infra.push("Firebase");
|
|
2269
|
+
return infra;
|
|
1855
2270
|
}
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
2271
|
+
calculateExecutiveRisks(project, categories) {
|
|
2272
|
+
const risks = [];
|
|
2273
|
+
let overallScore = 0;
|
|
2274
|
+
if (project.fileCount > 1e3) {
|
|
2275
|
+
risks.push(
|
|
2276
|
+
"Large codebase may require significant maintenance resources"
|
|
2277
|
+
);
|
|
2278
|
+
overallScore += 0.3;
|
|
2279
|
+
}
|
|
2280
|
+
if (categories.length > 15) {
|
|
2281
|
+
risks.push(
|
|
2282
|
+
"High number of technology categories indicates potential complexity"
|
|
2283
|
+
);
|
|
2284
|
+
overallScore += 0.2;
|
|
1867
2285
|
}
|
|
2286
|
+
const totalPackages = categories.reduce((sum, cat) => sum + cat.count, 0);
|
|
2287
|
+
if (totalPackages > 100) {
|
|
2288
|
+
risks.push(
|
|
2289
|
+
"Large dependency footprint increases security and maintenance burden"
|
|
2290
|
+
);
|
|
2291
|
+
overallScore += 0.3;
|
|
2292
|
+
}
|
|
2293
|
+
overallScore = Math.min(overallScore, 1);
|
|
2294
|
+
return {
|
|
2295
|
+
overall: overallScore,
|
|
2296
|
+
categories: [
|
|
2297
|
+
{
|
|
2298
|
+
name: "Technical Complexity",
|
|
2299
|
+
score: overallScore,
|
|
2300
|
+
issues: risks,
|
|
2301
|
+
impact: "May affect development velocity and operational costs"
|
|
2302
|
+
}
|
|
2303
|
+
],
|
|
2304
|
+
critical: [],
|
|
2305
|
+
high: overallScore > 0.7 ? risks : [],
|
|
2306
|
+
medium: overallScore > 0.4 && overallScore <= 0.7 ? risks : [],
|
|
2307
|
+
low: overallScore <= 0.4 ? risks : []
|
|
2308
|
+
};
|
|
1868
2309
|
}
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
const
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
metrics: {
|
|
1902
|
-
applications: 0,
|
|
1903
|
-
successes: 0,
|
|
1904
|
-
failures: 0,
|
|
1905
|
-
successRate: 0
|
|
1906
|
-
},
|
|
1907
|
-
source: "manual",
|
|
1908
|
-
isPrivate: true,
|
|
1909
|
-
createdAt: now,
|
|
1910
|
-
updatedAt: now
|
|
2310
|
+
calculateExecutiveOpportunities(_project, categories) {
|
|
2311
|
+
const opportunities = [];
|
|
2312
|
+
let overallScore = 0.5;
|
|
2313
|
+
const hasModernFramework = categories.some(
|
|
2314
|
+
(cat) => cat.name.includes("Meta-Framework")
|
|
2315
|
+
);
|
|
2316
|
+
if (hasModernFramework) {
|
|
2317
|
+
opportunities.push(
|
|
2318
|
+
"Modern framework provides foundation for rapid feature development"
|
|
2319
|
+
);
|
|
2320
|
+
overallScore += 0.2;
|
|
2321
|
+
}
|
|
2322
|
+
const hasTestingTools = categories.some((cat) => cat.name === "Testing");
|
|
2323
|
+
if (hasTestingTools) {
|
|
2324
|
+
opportunities.push(
|
|
2325
|
+
"Testing infrastructure enables quality-driven expansion"
|
|
2326
|
+
);
|
|
2327
|
+
overallScore += 0.1;
|
|
2328
|
+
}
|
|
2329
|
+
return {
|
|
2330
|
+
overall: Math.min(overallScore, 1),
|
|
2331
|
+
categories: [
|
|
2332
|
+
{
|
|
2333
|
+
name: "Growth Potential",
|
|
2334
|
+
potential: overallScore,
|
|
2335
|
+
recommendations: opportunities,
|
|
2336
|
+
businessValue: "Strong foundation for scaling features and market reach"
|
|
2337
|
+
}
|
|
2338
|
+
],
|
|
2339
|
+
immediate: [],
|
|
2340
|
+
shortTerm: opportunities,
|
|
2341
|
+
longTerm: []
|
|
1911
2342
|
};
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
} else {
|
|
1920
|
-
console.log(chalk11.red("\n\u274C Failed to record pattern"));
|
|
1921
|
-
console.log(chalk11.dim(` Error: ${result.error}`));
|
|
1922
|
-
process.exit(1);
|
|
2343
|
+
}
|
|
2344
|
+
assessDependencyRisk(packages) {
|
|
2345
|
+
let score = 0;
|
|
2346
|
+
const issues = [];
|
|
2347
|
+
if (packages.total > 100) {
|
|
2348
|
+
score += 0.3;
|
|
2349
|
+
issues.push(`High dependency count (${packages.total} packages)`);
|
|
1923
2350
|
}
|
|
1924
|
-
|
|
2351
|
+
if (packages.outdated.length > 10) {
|
|
2352
|
+
score += 0.3;
|
|
2353
|
+
issues.push(`${packages.outdated.length} outdated packages`);
|
|
2354
|
+
}
|
|
2355
|
+
if (packages.security.length > 0) {
|
|
2356
|
+
score += 0.4;
|
|
2357
|
+
issues.push(`${packages.security.length} security vulnerabilities`);
|
|
2358
|
+
}
|
|
2359
|
+
return {
|
|
2360
|
+
name: "Dependency Management",
|
|
2361
|
+
score: Math.min(score, 1),
|
|
2362
|
+
issues,
|
|
2363
|
+
impact: "Affects security, stability, and maintenance costs"
|
|
2364
|
+
};
|
|
2365
|
+
}
|
|
2366
|
+
assessTechnologyRisk(technology) {
|
|
2367
|
+
let score = 0;
|
|
2368
|
+
const issues = [];
|
|
2369
|
+
const legacyFrameworks = ["angular.js", "backbone", "ember"];
|
|
2370
|
+
if (technology.framework && legacyFrameworks.some((f) => technology.framework?.includes(f))) {
|
|
2371
|
+
score += 0.5;
|
|
2372
|
+
issues.push("Legacy framework may limit future development");
|
|
2373
|
+
}
|
|
2374
|
+
return {
|
|
2375
|
+
name: "Technology Stack",
|
|
2376
|
+
score,
|
|
2377
|
+
issues,
|
|
2378
|
+
impact: "May affect ability to attract talent and adopt new features"
|
|
2379
|
+
};
|
|
2380
|
+
}
|
|
2381
|
+
assessComplexityRisk(project) {
|
|
2382
|
+
let score = 0;
|
|
2383
|
+
const issues = [];
|
|
2384
|
+
if (project.fileCount > 1e3) {
|
|
2385
|
+
score += 0.2;
|
|
2386
|
+
issues.push("Large codebase requires careful management");
|
|
2387
|
+
}
|
|
2388
|
+
if (project.isMonorepo && (project.workspaceCount || 0) > 10) {
|
|
2389
|
+
score += 0.2;
|
|
2390
|
+
issues.push("Complex monorepo structure");
|
|
2391
|
+
}
|
|
2392
|
+
return {
|
|
2393
|
+
name: "Project Complexity",
|
|
2394
|
+
score,
|
|
2395
|
+
issues,
|
|
2396
|
+
impact: "Affects onboarding time and development velocity"
|
|
2397
|
+
};
|
|
2398
|
+
}
|
|
2399
|
+
assessModernizationOpportunities(_technology, packages) {
|
|
2400
|
+
const recommendations = [];
|
|
2401
|
+
let potential = 0.5;
|
|
2402
|
+
if (packages.outdated.length > 0) {
|
|
2403
|
+
recommendations.push(
|
|
2404
|
+
"Upgrade dependencies to access new features and improvements"
|
|
2405
|
+
);
|
|
2406
|
+
potential += 0.2;
|
|
2407
|
+
}
|
|
2408
|
+
return {
|
|
2409
|
+
name: "Modernization",
|
|
2410
|
+
potential: Math.min(potential, 1),
|
|
2411
|
+
recommendations,
|
|
2412
|
+
businessValue: "Improved performance, security, and developer experience"
|
|
2413
|
+
};
|
|
2414
|
+
}
|
|
2415
|
+
assessOptimizationOpportunities(project) {
|
|
2416
|
+
const recommendations = [];
|
|
2417
|
+
if (project.fileCount > 500) {
|
|
2418
|
+
recommendations.push(
|
|
2419
|
+
"Consider code splitting and lazy loading strategies"
|
|
2420
|
+
);
|
|
2421
|
+
}
|
|
2422
|
+
return {
|
|
2423
|
+
name: "Performance Optimization",
|
|
2424
|
+
potential: 0.6,
|
|
2425
|
+
recommendations,
|
|
2426
|
+
businessValue: "Faster load times and better user experience"
|
|
2427
|
+
};
|
|
2428
|
+
}
|
|
2429
|
+
assessGrowthOpportunities(_technology, packages) {
|
|
2430
|
+
const recommendations = [];
|
|
2431
|
+
let potential = 0.5;
|
|
2432
|
+
const hasAnalytics = packages.categories.some(
|
|
2433
|
+
(cat) => cat.name.includes("Analytics")
|
|
2434
|
+
);
|
|
2435
|
+
if (!hasAnalytics) {
|
|
2436
|
+
recommendations.push("Add analytics to enable data-driven decisions");
|
|
2437
|
+
potential += 0.2;
|
|
2438
|
+
}
|
|
2439
|
+
return {
|
|
2440
|
+
name: "Growth & Expansion",
|
|
2441
|
+
potential: Math.min(potential, 1),
|
|
2442
|
+
recommendations,
|
|
2443
|
+
businessValue: "Data-driven growth and improved user insights"
|
|
2444
|
+
};
|
|
2445
|
+
}
|
|
2446
|
+
categorizeRiskItems(risk, critical, high, medium, low) {
|
|
2447
|
+
if (risk.score > 0.7) {
|
|
2448
|
+
critical.push(...risk.issues);
|
|
2449
|
+
} else if (risk.score > 0.5) {
|
|
2450
|
+
high.push(...risk.issues);
|
|
2451
|
+
} else if (risk.score > 0.3) {
|
|
2452
|
+
medium.push(...risk.issues);
|
|
2453
|
+
} else {
|
|
2454
|
+
low.push(...risk.issues);
|
|
2455
|
+
}
|
|
2456
|
+
}
|
|
2457
|
+
categorizeOpportunityItems(opportunity, immediate, shortTerm, longTerm) {
|
|
2458
|
+
if (opportunity.potential > 0.7) {
|
|
2459
|
+
immediate.push(...opportunity.recommendations);
|
|
2460
|
+
} else if (opportunity.potential > 0.5) {
|
|
2461
|
+
shortTerm.push(...opportunity.recommendations);
|
|
2462
|
+
} else {
|
|
2463
|
+
longTerm.push(...opportunity.recommendations);
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
async getProjectFiles() {
|
|
2467
|
+
const patterns = ["**/*.{ts,tsx,js,jsx}"];
|
|
2468
|
+
return await fg(patterns, {
|
|
2469
|
+
cwd: this.options.cwd,
|
|
2470
|
+
ignore: this.options.excludePatterns || [
|
|
2471
|
+
"node_modules/**",
|
|
2472
|
+
"dist/**",
|
|
2473
|
+
"build/**",
|
|
2474
|
+
".next/**",
|
|
2475
|
+
"coverage/**"
|
|
2476
|
+
]
|
|
2477
|
+
});
|
|
2478
|
+
}
|
|
2479
|
+
detectEntryPoints(files) {
|
|
2480
|
+
const entryPoints = files.filter(
|
|
2481
|
+
(f) => f.includes("index.") || f.includes("main.") || f.includes("app.") || f.includes("_app.") || f.includes("layout.")
|
|
2482
|
+
);
|
|
2483
|
+
return entryPoints.slice(0, 5);
|
|
2484
|
+
}
|
|
2485
|
+
detectArchitecturePattern(files) {
|
|
2486
|
+
const hasComponents = files.some((f) => f.includes("components/"));
|
|
2487
|
+
const hasPages = files.some(
|
|
2488
|
+
(f) => f.includes("pages/") || f.includes("app/")
|
|
2489
|
+
);
|
|
2490
|
+
const hasServices = files.some((f) => f.includes("services/"));
|
|
2491
|
+
if (hasComponents && hasPages && hasServices) {
|
|
2492
|
+
return "Layered Architecture (Pages \u2192 Components \u2192 Services)";
|
|
2493
|
+
}
|
|
2494
|
+
if (hasComponents && hasPages) {
|
|
2495
|
+
return "Component-Based Architecture";
|
|
2496
|
+
}
|
|
2497
|
+
return "Standard Application Structure";
|
|
2498
|
+
}
|
|
2499
|
+
detectLayers(files) {
|
|
2500
|
+
const layers = [];
|
|
2501
|
+
if (files.some((f) => f.includes("pages/") || f.includes("app/"))) {
|
|
2502
|
+
layers.push("Presentation (Pages/Routes)");
|
|
2503
|
+
}
|
|
2504
|
+
if (files.some((f) => f.includes("components/"))) {
|
|
2505
|
+
layers.push("UI Components");
|
|
2506
|
+
}
|
|
2507
|
+
if (files.some((f) => f.includes("services/") || f.includes("api/"))) {
|
|
2508
|
+
layers.push("Business Logic/Services");
|
|
2509
|
+
}
|
|
2510
|
+
if (files.some((f) => f.includes("lib/") || f.includes("utils/"))) {
|
|
2511
|
+
layers.push("Utilities/Helpers");
|
|
2512
|
+
}
|
|
2513
|
+
if (files.some((f) => f.includes("models/") || f.includes("types/"))) {
|
|
2514
|
+
layers.push("Data Models");
|
|
2515
|
+
}
|
|
2516
|
+
return layers;
|
|
2517
|
+
}
|
|
2518
|
+
analyzeDataFlow(files) {
|
|
2519
|
+
const hasStateManagement = files.some(
|
|
2520
|
+
(f) => f.includes("store") || f.includes("context")
|
|
2521
|
+
);
|
|
2522
|
+
const hasApi = files.some(
|
|
2523
|
+
(f) => f.includes("api") || f.includes("services")
|
|
2524
|
+
);
|
|
2525
|
+
if (hasStateManagement && hasApi) {
|
|
2526
|
+
return "API \u2192 Services \u2192 State Management \u2192 Components";
|
|
2527
|
+
}
|
|
2528
|
+
if (hasApi) {
|
|
2529
|
+
return "API \u2192 Components (Direct)";
|
|
2530
|
+
}
|
|
2531
|
+
return "Props-based Component Communication";
|
|
2532
|
+
}
|
|
2533
|
+
extractKeyDecisions(files) {
|
|
2534
|
+
const decisions = [];
|
|
2535
|
+
if (files.some((f) => f.includes(".ts") || f.includes(".tsx"))) {
|
|
2536
|
+
decisions.push("TypeScript for type safety");
|
|
2537
|
+
}
|
|
2538
|
+
if (files.some((f) => f.includes("app/") && !f.includes("pages/"))) {
|
|
2539
|
+
decisions.push("App Router architecture (Next.js)");
|
|
2540
|
+
}
|
|
2541
|
+
if (files.some((f) => f.includes("server") || f.includes("api"))) {
|
|
2542
|
+
decisions.push("API routes for backend functionality");
|
|
2543
|
+
}
|
|
2544
|
+
return decisions;
|
|
2545
|
+
}
|
|
2546
|
+
async analyzeComponents(files) {
|
|
2547
|
+
const components = files.filter((f) => f.includes("components/"));
|
|
2548
|
+
const patterns = [];
|
|
2549
|
+
const ui = components.filter(
|
|
2550
|
+
(f) => f.includes("/ui/") || f.includes("/common/")
|
|
2551
|
+
);
|
|
2552
|
+
const feature = components.filter(
|
|
2553
|
+
(f) => ["features/", "modules/"].some((dir) => f.includes(dir))
|
|
2554
|
+
);
|
|
2555
|
+
if (ui.length > 0) {
|
|
2556
|
+
patterns.push({
|
|
2557
|
+
type: "UI Components",
|
|
2558
|
+
count: ui.length,
|
|
2559
|
+
examples: ui.slice(0, 3),
|
|
2560
|
+
conventions: ["Reusable", "Presentation-focused"]
|
|
2561
|
+
});
|
|
2562
|
+
}
|
|
2563
|
+
if (feature.length > 0) {
|
|
2564
|
+
patterns.push({
|
|
2565
|
+
type: "Feature Components",
|
|
2566
|
+
count: feature.length,
|
|
2567
|
+
examples: feature.slice(0, 3),
|
|
2568
|
+
conventions: ["Business logic", "Domain-specific"]
|
|
2569
|
+
});
|
|
2570
|
+
}
|
|
2571
|
+
return patterns;
|
|
2572
|
+
}
|
|
2573
|
+
async analyzeServices(files) {
|
|
2574
|
+
const services = files.filter(
|
|
2575
|
+
(f) => f.includes("services/") || f.includes("api/")
|
|
2576
|
+
);
|
|
2577
|
+
return services.slice(0, 5).map((file) => ({
|
|
2578
|
+
name: file,
|
|
2579
|
+
purpose: "API integration or business logic",
|
|
2580
|
+
integrations: []
|
|
2581
|
+
}));
|
|
2582
|
+
}
|
|
2583
|
+
async analyzeUtilities(files) {
|
|
2584
|
+
const utils = files.filter(
|
|
2585
|
+
(f) => f.includes("utils/") || f.includes("lib/")
|
|
2586
|
+
);
|
|
2587
|
+
return [
|
|
2588
|
+
{
|
|
2589
|
+
category: "Utilities",
|
|
2590
|
+
count: utils.length,
|
|
2591
|
+
usage: "Helper functions and shared utilities"
|
|
2592
|
+
}
|
|
2593
|
+
];
|
|
2594
|
+
}
|
|
2595
|
+
async analyzeTests(files) {
|
|
2596
|
+
const tests = files.filter(
|
|
2597
|
+
(f) => f.includes(".test.") || f.includes(".spec.")
|
|
2598
|
+
);
|
|
2599
|
+
const hasVitest = tests.some((f) => f.includes("vitest"));
|
|
2600
|
+
const hasJest = tests.some((f) => f.includes("jest"));
|
|
2601
|
+
return {
|
|
2602
|
+
framework: hasVitest ? "Vitest" : hasJest ? "Jest" : "Unknown",
|
|
2603
|
+
count: tests.length,
|
|
2604
|
+
types: ["Unit tests"]
|
|
2605
|
+
};
|
|
2606
|
+
}
|
|
2607
|
+
async detectCustomPatterns(files) {
|
|
2608
|
+
const patterns = [];
|
|
2609
|
+
if (files.some((f) => f.includes("hooks/"))) {
|
|
2610
|
+
patterns.push("Custom React Hooks");
|
|
2611
|
+
}
|
|
2612
|
+
if (files.some((f) => f.includes("middleware"))) {
|
|
2613
|
+
patterns.push("Middleware Pattern");
|
|
2614
|
+
}
|
|
2615
|
+
if (files.some((f) => f.includes("providers"))) {
|
|
2616
|
+
patterns.push("Context Providers");
|
|
2617
|
+
}
|
|
2618
|
+
return patterns;
|
|
2619
|
+
}
|
|
2620
|
+
};
|
|
2621
|
+
|
|
2622
|
+
// src/utils/question-generator.ts
|
|
2623
|
+
var QuestionGenerator = class {
|
|
2624
|
+
analysis;
|
|
2625
|
+
config;
|
|
2626
|
+
constructor(analysis, config) {
|
|
2627
|
+
this.analysis = analysis;
|
|
2628
|
+
this.config = config;
|
|
2629
|
+
}
|
|
2630
|
+
/**
|
|
2631
|
+
* Generate all advisory board questions
|
|
2632
|
+
*/
|
|
2633
|
+
generate() {
|
|
2634
|
+
const questions = [];
|
|
2635
|
+
questions.push(...this.generateTechnologyQuestions());
|
|
2636
|
+
questions.push(...this.generatePackageQuestions());
|
|
2637
|
+
questions.push(...this.generatePlatformQuestions());
|
|
2638
|
+
questions.push(...this.generateBusinessQuestions());
|
|
2639
|
+
questions.push(...this.generateTechnicalDebtQuestions());
|
|
2640
|
+
questions.push(...this.generateGrowthQuestions());
|
|
2641
|
+
if (this.config?.customQuestions) {
|
|
2642
|
+
questions.push(
|
|
2643
|
+
...this.formatCustomQuestions(this.config.customQuestions)
|
|
2644
|
+
);
|
|
2645
|
+
}
|
|
2646
|
+
const sorted = this.sortByPriority(questions);
|
|
2647
|
+
return {
|
|
2648
|
+
questions: sorted,
|
|
2649
|
+
summary: this.generateSummary(sorted)
|
|
2650
|
+
};
|
|
2651
|
+
}
|
|
2652
|
+
/**
|
|
2653
|
+
* Technology Decisions questions
|
|
2654
|
+
*/
|
|
2655
|
+
generateTechnologyQuestions() {
|
|
2656
|
+
const questions = [];
|
|
2657
|
+
const { technology, project } = this.analysis;
|
|
2658
|
+
if (technology.framework) {
|
|
2659
|
+
questions.push({
|
|
2660
|
+
category: "Technology Decisions",
|
|
2661
|
+
question: `Why was ${technology.framework} chosen as the primary framework, and does it still align with our strategic goals?`,
|
|
2662
|
+
context: `Project uses ${technology.framework}${technology.frameworkVersion ? ` v${technology.frameworkVersion}` : ""} as the foundation`,
|
|
2663
|
+
findings: [
|
|
2664
|
+
`Framework: ${technology.framework}`,
|
|
2665
|
+
`Language: ${technology.language}`,
|
|
2666
|
+
`Runtime: ${technology.runtime}`
|
|
2667
|
+
],
|
|
2668
|
+
recommendations: [
|
|
2669
|
+
"Validate framework continues to meet performance and scalability needs",
|
|
2670
|
+
"Assess community support and long-term viability",
|
|
2671
|
+
"Consider migration costs vs. benefits if alternatives exist"
|
|
2672
|
+
],
|
|
2673
|
+
priority: "high",
|
|
2674
|
+
businessImpact: "Framework choice affects development velocity, talent acquisition, and long-term maintenance costs"
|
|
2675
|
+
});
|
|
2676
|
+
}
|
|
2677
|
+
if (technology.buildTools && technology.buildTools.length > 0) {
|
|
2678
|
+
questions.push({
|
|
2679
|
+
category: "Technology Decisions",
|
|
2680
|
+
question: `How are our build tools (${technology.buildTools.join(", ")}) optimizing development workflow and deployment efficiency?`,
|
|
2681
|
+
context: `Build pipeline uses ${technology.buildTools.join(", ")}`,
|
|
2682
|
+
findings: technology.buildTools.map(
|
|
2683
|
+
(tool) => `Using ${tool} for build process`
|
|
2684
|
+
),
|
|
2685
|
+
recommendations: [
|
|
2686
|
+
"Measure build time impacts on CI/CD pipeline",
|
|
2687
|
+
"Evaluate if modern alternatives could improve developer experience",
|
|
2688
|
+
"Consider caching strategies to optimize build performance"
|
|
2689
|
+
],
|
|
2690
|
+
priority: "medium",
|
|
2691
|
+
businessImpact: "Build efficiency directly impacts time-to-market for new features"
|
|
2692
|
+
});
|
|
2693
|
+
}
|
|
2694
|
+
if (project.isMonorepo) {
|
|
2695
|
+
questions.push({
|
|
2696
|
+
category: "Technology Decisions",
|
|
2697
|
+
question: `Is the monorepo structure providing expected benefits in code sharing and deployment coordination?`,
|
|
2698
|
+
context: `Project uses monorepo with ${project.workspaceCount || "multiple"} workspaces`,
|
|
2699
|
+
findings: [
|
|
2700
|
+
`Monorepo with ${project.workspaceCount} packages`,
|
|
2701
|
+
`Total ${project.fileCount} files managed`
|
|
2702
|
+
],
|
|
2703
|
+
recommendations: [
|
|
2704
|
+
"Assess if shared code is properly abstracted and reused",
|
|
2705
|
+
"Evaluate build caching and selective deployment strategies",
|
|
2706
|
+
"Consider workspace organization for scaling"
|
|
2707
|
+
],
|
|
2708
|
+
priority: "medium",
|
|
2709
|
+
businessImpact: "Monorepo architecture affects team collaboration and deployment complexity"
|
|
2710
|
+
});
|
|
2711
|
+
}
|
|
2712
|
+
return questions;
|
|
2713
|
+
}
|
|
2714
|
+
/**
|
|
2715
|
+
* Package Utilization questions
|
|
2716
|
+
*/
|
|
2717
|
+
generatePackageQuestions() {
|
|
2718
|
+
const questions = [];
|
|
2719
|
+
const { packages } = this.analysis;
|
|
2720
|
+
if (packages.total > 80) {
|
|
2721
|
+
questions.push({
|
|
2722
|
+
category: "Package Utilization",
|
|
2723
|
+
question: `With ${packages.total} dependencies, are we maintaining optimal balance between functionality and maintenance burden?`,
|
|
2724
|
+
context: `Project has ${packages.production.length} production and ${packages.development.length} development dependencies`,
|
|
2725
|
+
findings: [
|
|
2726
|
+
`Total packages: ${packages.total}`,
|
|
2727
|
+
`Production: ${packages.production.length}`,
|
|
2728
|
+
`Development: ${packages.development.length}`
|
|
2729
|
+
],
|
|
2730
|
+
recommendations: [
|
|
2731
|
+
"Audit for unused or redundant packages",
|
|
2732
|
+
"Evaluate if critical functionality should be internalized",
|
|
2733
|
+
"Implement dependency update strategy"
|
|
2734
|
+
],
|
|
2735
|
+
priority: packages.total > 150 ? "high" : "medium",
|
|
2736
|
+
businessImpact: "Dependency count affects security surface area and maintenance costs"
|
|
2737
|
+
});
|
|
2738
|
+
}
|
|
2739
|
+
if (packages.categories.length > 0) {
|
|
2740
|
+
const topCategories = packages.categories.sort((a, b) => b.count - a.count).slice(0, 3);
|
|
2741
|
+
topCategories.forEach((cat) => {
|
|
2742
|
+
if (cat.count > 5) {
|
|
2743
|
+
questions.push({
|
|
2744
|
+
category: "Package Utilization",
|
|
2745
|
+
question: `How are we leveraging our ${cat.count} ${cat.name} packages to deliver business value?`,
|
|
2746
|
+
context: `${cat.name}: ${cat.packages.slice(0, 3).join(", ")}${cat.count > 3 ? `, +${cat.count - 3} more` : ""}`,
|
|
2747
|
+
findings: [
|
|
2748
|
+
`${cat.count} packages in ${cat.name} category`,
|
|
2749
|
+
cat.businessImpact
|
|
2750
|
+
],
|
|
2751
|
+
recommendations: this.getCategoryRecommendations(cat),
|
|
2752
|
+
priority: this.getCategoryPriority(cat),
|
|
2753
|
+
businessImpact: cat.businessImpact
|
|
2754
|
+
});
|
|
2755
|
+
}
|
|
2756
|
+
});
|
|
2757
|
+
}
|
|
2758
|
+
if (packages.security && packages.security.length > 0) {
|
|
2759
|
+
const criticalCount = packages.security.filter(
|
|
2760
|
+
(s) => s.severity === "critical"
|
|
2761
|
+
).length;
|
|
2762
|
+
questions.push({
|
|
2763
|
+
category: "Package Utilization",
|
|
2764
|
+
question: `What is our strategy for addressing ${packages.security.length} identified security vulnerabilities${criticalCount > 0 ? ` (${criticalCount} critical)` : ""}?`,
|
|
2765
|
+
context: "Security audit identified vulnerabilities in dependencies",
|
|
2766
|
+
findings: packages.security.map(
|
|
2767
|
+
(s) => `${s.package}: ${s.severity} - ${s.issue}`
|
|
2768
|
+
),
|
|
2769
|
+
recommendations: packages.security.map((s) => s.recommendation),
|
|
2770
|
+
priority: criticalCount > 0 ? "high" : "medium",
|
|
2771
|
+
businessImpact: "Security vulnerabilities pose risk to customer data and business operations"
|
|
2772
|
+
});
|
|
2773
|
+
}
|
|
2774
|
+
return questions;
|
|
2775
|
+
}
|
|
2776
|
+
/**
|
|
2777
|
+
* Platform Strategy questions
|
|
2778
|
+
*/
|
|
2779
|
+
generatePlatformQuestions() {
|
|
2780
|
+
const questions = [];
|
|
2781
|
+
const { technology } = this.analysis;
|
|
2782
|
+
if (technology.platforms && technology.platforms.length > 0) {
|
|
2783
|
+
questions.push({
|
|
2784
|
+
category: "Platform Strategy",
|
|
2785
|
+
question: `Are our platform choices (${technology.platforms.join(", ")}) aligned with target market and growth plans?`,
|
|
2786
|
+
context: `Currently deployed on ${technology.platforms.join(" + ")}`,
|
|
2787
|
+
findings: technology.platforms.map((p10) => `Supports ${p10} platform`),
|
|
2788
|
+
recommendations: [
|
|
2789
|
+
"Validate platform coverage matches user demographics",
|
|
2790
|
+
"Assess cross-platform development efficiency",
|
|
2791
|
+
"Consider platform-specific optimization opportunities"
|
|
2792
|
+
],
|
|
2793
|
+
priority: "medium",
|
|
2794
|
+
businessImpact: "Platform strategy determines market reach and development costs"
|
|
2795
|
+
});
|
|
2796
|
+
}
|
|
2797
|
+
if (technology.infrastructure && technology.infrastructure.length > 0) {
|
|
2798
|
+
questions.push({
|
|
2799
|
+
category: "Platform Strategy",
|
|
2800
|
+
question: `How is our infrastructure choice (${technology.infrastructure.join(", ")}) supporting scalability and cost efficiency?`,
|
|
2801
|
+
context: `Infrastructure: ${technology.infrastructure.join(", ")}`,
|
|
2802
|
+
findings: technology.infrastructure.map((i) => `Deployed on ${i}`),
|
|
2803
|
+
recommendations: [
|
|
2804
|
+
"Monitor infrastructure costs vs. usage patterns",
|
|
2805
|
+
"Evaluate auto-scaling and redundancy capabilities",
|
|
2806
|
+
"Consider multi-region deployment for global users"
|
|
2807
|
+
],
|
|
2808
|
+
priority: "high",
|
|
2809
|
+
businessImpact: "Infrastructure directly impacts operational costs, uptime, and scalability"
|
|
2810
|
+
});
|
|
2811
|
+
}
|
|
2812
|
+
return questions;
|
|
2813
|
+
}
|
|
2814
|
+
/**
|
|
2815
|
+
* Business Alignment questions
|
|
2816
|
+
*/
|
|
2817
|
+
generateBusinessQuestions() {
|
|
2818
|
+
const questions = [];
|
|
2819
|
+
const { packages } = this.analysis;
|
|
2820
|
+
const hasAnalytics = packages.categories.some(
|
|
2821
|
+
(cat) => cat.name.toLowerCase().includes("analytics")
|
|
2822
|
+
);
|
|
2823
|
+
if (!hasAnalytics) {
|
|
2824
|
+
questions.push({
|
|
2825
|
+
category: "Business Alignment",
|
|
2826
|
+
question: "Why don't we have analytics infrastructure, and how are we making data-driven product decisions?",
|
|
2827
|
+
context: "No analytics packages detected in the project",
|
|
2828
|
+
findings: ["Missing analytics implementation"],
|
|
2829
|
+
recommendations: [
|
|
2830
|
+
"Implement analytics to track user behavior and feature adoption",
|
|
2831
|
+
"Set up conversion funnels and key metric tracking",
|
|
2832
|
+
"Enable A/B testing capabilities for product experiments"
|
|
2833
|
+
],
|
|
2834
|
+
priority: "high",
|
|
2835
|
+
businessImpact: "Lack of analytics limits ability to measure ROI and optimize user experience"
|
|
2836
|
+
});
|
|
2837
|
+
}
|
|
2838
|
+
const hasAuth = packages.categories.some(
|
|
2839
|
+
(cat) => cat.name.toLowerCase().includes("auth")
|
|
2840
|
+
);
|
|
2841
|
+
if (hasAuth) {
|
|
2842
|
+
const authCat = packages.categories.find(
|
|
2843
|
+
(cat) => cat.name.toLowerCase().includes("auth")
|
|
2844
|
+
);
|
|
2845
|
+
questions.push({
|
|
2846
|
+
category: "Business Alignment",
|
|
2847
|
+
question: `How is our authentication solution (${authCat.packages.join(", ")}) supporting user onboarding and security requirements?`,
|
|
2848
|
+
context: `Using ${authCat.packages.join(" + ")} for authentication`,
|
|
2849
|
+
findings: authCat.packages.map((p10) => `Auth provider: ${p10}`),
|
|
2850
|
+
recommendations: [
|
|
2851
|
+
"Measure authentication success rates and friction points",
|
|
2852
|
+
"Ensure compliance with security standards (SOC2, GDPR, etc.)",
|
|
2853
|
+
"Evaluate social login options to reduce signup friction"
|
|
2854
|
+
],
|
|
2855
|
+
priority: "high",
|
|
2856
|
+
businessImpact: "Authentication affects conversion rates and security posture"
|
|
2857
|
+
});
|
|
2858
|
+
}
|
|
2859
|
+
const hasTestingtools = packages.categories.some(
|
|
2860
|
+
(cat) => cat.name.toLowerCase().includes("testing")
|
|
2861
|
+
);
|
|
2862
|
+
if (hasTestingtools) {
|
|
2863
|
+
questions.push({
|
|
2864
|
+
category: "Business Alignment",
|
|
2865
|
+
question: "How is our testing strategy contributing to product quality and customer satisfaction?",
|
|
2866
|
+
context: "Testing infrastructure is in place",
|
|
2867
|
+
findings: ["Testing tools available"],
|
|
2868
|
+
recommendations: [
|
|
2869
|
+
"Set coverage targets aligned with quality goals",
|
|
2870
|
+
"Implement testing in CI/CD for every release",
|
|
2871
|
+
"Track bugs-in-production as quality metric"
|
|
2872
|
+
],
|
|
2873
|
+
priority: "medium",
|
|
2874
|
+
businessImpact: "Quality assurance directly affects customer satisfaction and support costs"
|
|
2875
|
+
});
|
|
2876
|
+
}
|
|
2877
|
+
return questions;
|
|
2878
|
+
}
|
|
2879
|
+
/**
|
|
2880
|
+
* Technical Debt questions
|
|
2881
|
+
*/
|
|
2882
|
+
generateTechnicalDebtQuestions() {
|
|
2883
|
+
const questions = [];
|
|
2884
|
+
const { risks, packages } = this.analysis;
|
|
2885
|
+
if (risks.high && risks.high.length > 0) {
|
|
2886
|
+
questions.push({
|
|
2887
|
+
category: "Technical Debt",
|
|
2888
|
+
question: `What is our strategy for addressing ${risks.high.length} high-priority technical risks?`,
|
|
2889
|
+
context: "Analysis identified significant technical risks",
|
|
2890
|
+
findings: risks.high,
|
|
2891
|
+
recommendations: [
|
|
2892
|
+
"Prioritize risks by business impact",
|
|
2893
|
+
"Allocate dedicated time for technical debt in sprints",
|
|
2894
|
+
"Track debt reduction as OKR/KPI"
|
|
2895
|
+
],
|
|
2896
|
+
priority: "high",
|
|
2897
|
+
businessImpact: "Unaddressed technical debt slows feature velocity and increases costs"
|
|
2898
|
+
});
|
|
2899
|
+
}
|
|
2900
|
+
if (packages.outdated && packages.outdated.length > 10) {
|
|
2901
|
+
const breakingCount = packages.outdated.filter((p10) => p10.breaking).length;
|
|
2902
|
+
questions.push({
|
|
2903
|
+
category: "Technical Debt",
|
|
2904
|
+
question: `How should we prioritize updating ${packages.outdated.length} outdated packages${breakingCount > 0 ? ` (${breakingCount} with breaking changes)` : ""}?`,
|
|
2905
|
+
context: "Dependency audit found outdated packages",
|
|
2906
|
+
findings: packages.outdated.slice(0, 5).map((p10) => `${p10.name}: ${p10.current} \u2192 ${p10.latest}`),
|
|
2907
|
+
recommendations: [
|
|
2908
|
+
"Create update roadmap starting with non-breaking changes",
|
|
2909
|
+
"Test breaking changes in feature branches",
|
|
2910
|
+
"Consider automated dependency updates (Dependabot, Renovate)"
|
|
2911
|
+
],
|
|
2912
|
+
priority: breakingCount > 5 ? "high" : "medium",
|
|
2913
|
+
businessImpact: "Outdated dependencies miss performance improvements and security patches"
|
|
2914
|
+
});
|
|
2915
|
+
}
|
|
2916
|
+
const complexityRisk = risks.categories.find(
|
|
2917
|
+
(c) => c.name.includes("Complexity")
|
|
2918
|
+
);
|
|
2919
|
+
if (complexityRisk && complexityRisk.score > 0.5) {
|
|
2920
|
+
questions.push({
|
|
2921
|
+
category: "Technical Debt",
|
|
2922
|
+
question: "How are we managing codebase complexity to maintain development velocity?",
|
|
2923
|
+
context: `Complexity score: ${(complexityRisk.score * 100).toFixed(0)}%`,
|
|
2924
|
+
findings: complexityRisk.issues,
|
|
2925
|
+
recommendations: [
|
|
2926
|
+
"Implement code quality metrics and monitoring",
|
|
2927
|
+
"Establish refactoring goals for each sprint",
|
|
2928
|
+
"Consider modularization strategies"
|
|
2929
|
+
],
|
|
2930
|
+
priority: complexityRisk.score > 0.7 ? "high" : "medium",
|
|
2931
|
+
businessImpact: "High complexity slows new feature development and increases bugs"
|
|
2932
|
+
});
|
|
2933
|
+
}
|
|
2934
|
+
return questions;
|
|
2935
|
+
}
|
|
2936
|
+
/**
|
|
2937
|
+
* Growth Opportunities questions
|
|
2938
|
+
*/
|
|
2939
|
+
generateGrowthQuestions() {
|
|
2940
|
+
const questions = [];
|
|
2941
|
+
const { opportunities } = this.analysis;
|
|
2942
|
+
if (opportunities.immediate && opportunities.immediate.length > 0) {
|
|
2943
|
+
questions.push({
|
|
2944
|
+
category: "Growth Opportunities",
|
|
2945
|
+
question: "Which immediate technical opportunities should we prioritize for maximum business impact?",
|
|
2946
|
+
context: `${opportunities.immediate.length} immediate opportunities identified`,
|
|
2947
|
+
findings: opportunities.immediate,
|
|
2948
|
+
recommendations: [
|
|
2949
|
+
"Evaluate ROI and effort for each opportunity",
|
|
2950
|
+
"Align opportunities with business OKRs",
|
|
2951
|
+
"Quick wins can build momentum for larger initiatives"
|
|
2952
|
+
],
|
|
2953
|
+
priority: "high",
|
|
2954
|
+
businessImpact: "Immediate opportunities can deliver quick ROI with minimal investment"
|
|
2955
|
+
});
|
|
2956
|
+
}
|
|
2957
|
+
opportunities.categories.forEach((cat) => {
|
|
2958
|
+
if (cat.potential > 0.6) {
|
|
2959
|
+
questions.push({
|
|
2960
|
+
category: "Growth Opportunities",
|
|
2961
|
+
question: `How can we capitalize on ${cat.name.toLowerCase()} opportunities to drive business growth?`,
|
|
2962
|
+
context: `Potential score: ${(cat.potential * 100).toFixed(0)}%`,
|
|
2963
|
+
findings: cat.recommendations,
|
|
2964
|
+
recommendations: [
|
|
2965
|
+
"Develop roadmap for implementing recommendations",
|
|
2966
|
+
"Assign owners and timelines",
|
|
2967
|
+
"Define success metrics"
|
|
2968
|
+
],
|
|
2969
|
+
priority: cat.potential > 0.8 ? "high" : "medium",
|
|
2970
|
+
businessImpact: cat.businessValue
|
|
2971
|
+
});
|
|
2972
|
+
}
|
|
2973
|
+
});
|
|
2974
|
+
return questions;
|
|
2975
|
+
}
|
|
2976
|
+
/**
|
|
2977
|
+
* Format custom questions from config
|
|
2978
|
+
*/
|
|
2979
|
+
formatCustomQuestions(customQuestions) {
|
|
2980
|
+
return customQuestions.map((q) => ({
|
|
2981
|
+
category: q.category,
|
|
2982
|
+
question: q.question,
|
|
2983
|
+
context: q.context || "Custom advisory question",
|
|
2984
|
+
findings: [],
|
|
2985
|
+
recommendations: [],
|
|
2986
|
+
priority: q.priority || "medium",
|
|
2987
|
+
businessImpact: "Custom question defined by organization"
|
|
2988
|
+
}));
|
|
2989
|
+
}
|
|
2990
|
+
/**
|
|
2991
|
+
* Sort questions by priority
|
|
2992
|
+
*/
|
|
2993
|
+
sortByPriority(questions) {
|
|
2994
|
+
const priorityOrder = { high: 0, medium: 1, low: 2 };
|
|
2995
|
+
return questions.sort(
|
|
2996
|
+
(a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]
|
|
2997
|
+
);
|
|
2998
|
+
}
|
|
2999
|
+
/**
|
|
3000
|
+
* Generate summary of questions
|
|
3001
|
+
*/
|
|
3002
|
+
generateSummary(questions) {
|
|
3003
|
+
const categories = Array.from(new Set(questions.map((q) => q.category)));
|
|
3004
|
+
return {
|
|
3005
|
+
totalQuestions: questions.length,
|
|
3006
|
+
highPriority: questions.filter((q) => q.priority === "high").length,
|
|
3007
|
+
mediumPriority: questions.filter((q) => q.priority === "medium").length,
|
|
3008
|
+
lowPriority: questions.filter((q) => q.priority === "low").length,
|
|
3009
|
+
categories
|
|
3010
|
+
};
|
|
3011
|
+
}
|
|
3012
|
+
/**
|
|
3013
|
+
* Get recommendations for a package category
|
|
3014
|
+
*/
|
|
3015
|
+
getCategoryRecommendations(cat) {
|
|
3016
|
+
const recommendations = [];
|
|
3017
|
+
if (cat.name.includes("UI") || cat.name.includes("Styling")) {
|
|
3018
|
+
recommendations.push(
|
|
3019
|
+
"Ensure design system consistency across components"
|
|
3020
|
+
);
|
|
3021
|
+
recommendations.push("Consider component library optimization");
|
|
3022
|
+
} else if (cat.name.includes("API") || cat.name.includes("Data")) {
|
|
3023
|
+
recommendations.push("Implement proper error handling and retry logic");
|
|
3024
|
+
recommendations.push("Consider caching strategies for performance");
|
|
3025
|
+
} else if (cat.name.includes("Testing")) {
|
|
3026
|
+
recommendations.push("Increase test coverage for critical paths");
|
|
3027
|
+
recommendations.push("Automate testing in CI/CD pipeline");
|
|
3028
|
+
} else if (cat.name.includes("Database") || cat.name.includes("ORM")) {
|
|
3029
|
+
recommendations.push("Optimize queries and indexes");
|
|
3030
|
+
recommendations.push("Implement database migration strategy");
|
|
3031
|
+
}
|
|
3032
|
+
if (recommendations.length === 0) {
|
|
3033
|
+
recommendations.push("Review usage patterns for optimization");
|
|
3034
|
+
recommendations.push("Consider if functionality meets current needs");
|
|
3035
|
+
}
|
|
3036
|
+
return recommendations;
|
|
3037
|
+
}
|
|
3038
|
+
/**
|
|
3039
|
+
* Get priority for a package category
|
|
3040
|
+
*/
|
|
3041
|
+
getCategoryPriority(cat) {
|
|
3042
|
+
if (["Authentication", "Database", "Infrastructure", "API"].some(
|
|
3043
|
+
(key) => cat.name.includes(key)
|
|
3044
|
+
)) {
|
|
3045
|
+
return "high";
|
|
3046
|
+
}
|
|
3047
|
+
if (["UI", "Testing", "State Management", "Forms"].some(
|
|
3048
|
+
(key) => cat.name.includes(key)
|
|
3049
|
+
)) {
|
|
3050
|
+
return "medium";
|
|
3051
|
+
}
|
|
3052
|
+
return "low";
|
|
3053
|
+
}
|
|
3054
|
+
};
|
|
3055
|
+
|
|
3056
|
+
// src/utils/report-comparator.ts
|
|
3057
|
+
var ReportComparator = class {
|
|
3058
|
+
baseline;
|
|
3059
|
+
current;
|
|
3060
|
+
constructor(baseline, current) {
|
|
3061
|
+
this.baseline = baseline;
|
|
3062
|
+
this.current = current;
|
|
3063
|
+
}
|
|
3064
|
+
/**
|
|
3065
|
+
* Compare two reports and generate diff
|
|
3066
|
+
*/
|
|
3067
|
+
compare() {
|
|
3068
|
+
const baselineSummary = this.extractSummary(this.baseline);
|
|
3069
|
+
const currentSummary = this.extractSummary(this.current);
|
|
3070
|
+
const changes = this.calculateChanges(baselineSummary, currentSummary);
|
|
3071
|
+
const details = this.analyzeDetailedChanges();
|
|
3072
|
+
return {
|
|
3073
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3074
|
+
baseline: baselineSummary,
|
|
3075
|
+
current: currentSummary,
|
|
3076
|
+
changes,
|
|
3077
|
+
details
|
|
3078
|
+
};
|
|
3079
|
+
}
|
|
3080
|
+
/**
|
|
3081
|
+
* Extract summary from analysis
|
|
3082
|
+
*/
|
|
3083
|
+
extractSummary(analysis) {
|
|
3084
|
+
return {
|
|
3085
|
+
timestamp: analysis.timestamp,
|
|
3086
|
+
depth: analysis.depth,
|
|
3087
|
+
projectName: analysis.project.name,
|
|
3088
|
+
overallRiskScore: analysis.risks.overall,
|
|
3089
|
+
overallOpportunityScore: analysis.opportunities.overall,
|
|
3090
|
+
packageCount: analysis.packages.total
|
|
3091
|
+
};
|
|
3092
|
+
}
|
|
3093
|
+
/**
|
|
3094
|
+
* Calculate high-level changes
|
|
3095
|
+
*/
|
|
3096
|
+
calculateChanges(baseline, current) {
|
|
3097
|
+
const riskScoreChange = current.overallRiskScore - baseline.overallRiskScore;
|
|
3098
|
+
const opportunityScoreChange = current.overallOpportunityScore - baseline.overallOpportunityScore;
|
|
3099
|
+
const packageCountChange = current.packageCount - baseline.packageCount;
|
|
3100
|
+
const baselineHighRisks = new Set(this.baseline.risks.high);
|
|
3101
|
+
const currentHighRisks = new Set(this.current.risks.high);
|
|
3102
|
+
const newHighRisks = Array.from(currentHighRisks).filter(
|
|
3103
|
+
(risk) => !baselineHighRisks.has(risk)
|
|
3104
|
+
).length;
|
|
3105
|
+
const resolvedHighRisks = Array.from(baselineHighRisks).filter(
|
|
3106
|
+
(risk) => !currentHighRisks.has(risk)
|
|
3107
|
+
).length;
|
|
3108
|
+
const baselineImmediate = new Set(this.baseline.opportunities.immediate);
|
|
3109
|
+
const currentImmediate = new Set(this.current.opportunities.immediate);
|
|
3110
|
+
const newOpportunities = Array.from(currentImmediate).filter(
|
|
3111
|
+
(opp) => !baselineImmediate.has(opp)
|
|
3112
|
+
).length;
|
|
3113
|
+
return {
|
|
3114
|
+
riskScoreChange,
|
|
3115
|
+
opportunityScoreChange,
|
|
3116
|
+
packageCountChange,
|
|
3117
|
+
newHighRisks,
|
|
3118
|
+
resolvedHighRisks,
|
|
3119
|
+
newOpportunities
|
|
3120
|
+
};
|
|
3121
|
+
}
|
|
3122
|
+
/**
|
|
3123
|
+
* Analyze detailed changes
|
|
3124
|
+
*/
|
|
3125
|
+
analyzeDetailedChanges() {
|
|
3126
|
+
return {
|
|
3127
|
+
risks: this.compareRisks(),
|
|
3128
|
+
opportunities: this.compareOpportunities(),
|
|
3129
|
+
packages: this.comparePackages(),
|
|
3130
|
+
technology: this.compareTechnology()
|
|
3131
|
+
};
|
|
3132
|
+
}
|
|
3133
|
+
/**
|
|
3134
|
+
* Compare risks between reports
|
|
3135
|
+
*/
|
|
3136
|
+
compareRisks() {
|
|
3137
|
+
const baselineRisks = /* @__PURE__ */ new Set([
|
|
3138
|
+
...this.baseline.risks.critical,
|
|
3139
|
+
...this.baseline.risks.high,
|
|
3140
|
+
...this.baseline.risks.medium
|
|
3141
|
+
]);
|
|
3142
|
+
const currentRisks = /* @__PURE__ */ new Set([
|
|
3143
|
+
...this.current.risks.critical,
|
|
3144
|
+
...this.current.risks.high,
|
|
3145
|
+
...this.current.risks.medium
|
|
3146
|
+
]);
|
|
3147
|
+
const newRisks = Array.from(currentRisks).filter(
|
|
3148
|
+
(risk) => !baselineRisks.has(risk)
|
|
3149
|
+
);
|
|
3150
|
+
const resolved = Array.from(baselineRisks).filter(
|
|
3151
|
+
(risk) => !currentRisks.has(risk)
|
|
3152
|
+
);
|
|
3153
|
+
const changed = [];
|
|
3154
|
+
const baselineCatMap = new Map(
|
|
3155
|
+
this.baseline.risks.categories.map((c) => [c.name, c])
|
|
3156
|
+
);
|
|
3157
|
+
const currentCatMap = new Map(
|
|
3158
|
+
this.current.risks.categories.map((c) => [c.name, c])
|
|
3159
|
+
);
|
|
3160
|
+
for (const [name, current] of currentCatMap) {
|
|
3161
|
+
const baseline = baselineCatMap.get(name);
|
|
3162
|
+
if (baseline && Math.abs(current.score - baseline.score) > 0.05) {
|
|
3163
|
+
changed.push({
|
|
3164
|
+
category: name,
|
|
3165
|
+
before: baseline.score,
|
|
3166
|
+
after: current.score,
|
|
3167
|
+
change: current.score - baseline.score
|
|
3168
|
+
});
|
|
3169
|
+
}
|
|
3170
|
+
}
|
|
3171
|
+
return { new: newRisks, resolved, changed };
|
|
3172
|
+
}
|
|
3173
|
+
/**
|
|
3174
|
+
* Compare opportunities between reports
|
|
3175
|
+
*/
|
|
3176
|
+
compareOpportunities() {
|
|
3177
|
+
const baselineOpps = /* @__PURE__ */ new Set([
|
|
3178
|
+
...this.baseline.opportunities.immediate,
|
|
3179
|
+
...this.baseline.opportunities.shortTerm
|
|
3180
|
+
]);
|
|
3181
|
+
const currentOpps = /* @__PURE__ */ new Set([
|
|
3182
|
+
...this.current.opportunities.immediate,
|
|
3183
|
+
...this.current.opportunities.shortTerm
|
|
3184
|
+
]);
|
|
3185
|
+
const newOpps = Array.from(currentOpps).filter(
|
|
3186
|
+
(opp) => !baselineOpps.has(opp)
|
|
3187
|
+
);
|
|
3188
|
+
const completed = Array.from(baselineOpps).filter(
|
|
3189
|
+
(opp) => !currentOpps.has(opp)
|
|
3190
|
+
);
|
|
3191
|
+
const changed = [];
|
|
3192
|
+
const baselineCatMap = new Map(
|
|
3193
|
+
this.baseline.opportunities.categories.map((c) => [c.name, c])
|
|
3194
|
+
);
|
|
3195
|
+
const currentCatMap = new Map(
|
|
3196
|
+
this.current.opportunities.categories.map((c) => [c.name, c])
|
|
3197
|
+
);
|
|
3198
|
+
for (const [name, current] of currentCatMap) {
|
|
3199
|
+
const baseline = baselineCatMap.get(name);
|
|
3200
|
+
if (baseline && Math.abs(current.potential - baseline.potential) > 0.05) {
|
|
3201
|
+
changed.push({
|
|
3202
|
+
category: name,
|
|
3203
|
+
before: baseline.potential,
|
|
3204
|
+
after: current.potential,
|
|
3205
|
+
change: current.potential - baseline.potential
|
|
3206
|
+
});
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
return { new: newOpps, completed, changed };
|
|
3210
|
+
}
|
|
3211
|
+
/**
|
|
3212
|
+
* Compare packages between reports
|
|
3213
|
+
*/
|
|
3214
|
+
comparePackages() {
|
|
3215
|
+
const baselineProduction = new Map(
|
|
3216
|
+
this.baseline.packages.production.map((p10) => [p10.name, p10])
|
|
3217
|
+
);
|
|
3218
|
+
const currentProduction = new Map(
|
|
3219
|
+
this.current.packages.production.map((p10) => [p10.name, p10])
|
|
3220
|
+
);
|
|
3221
|
+
const added = [];
|
|
3222
|
+
const removed = [];
|
|
3223
|
+
const updated = [];
|
|
3224
|
+
for (const [name, pkg] of currentProduction) {
|
|
3225
|
+
if (!baselineProduction.has(name)) {
|
|
3226
|
+
added.push({
|
|
3227
|
+
name,
|
|
3228
|
+
version: pkg.version,
|
|
3229
|
+
category: pkg.category
|
|
3230
|
+
});
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3233
|
+
for (const [name, baselinePkg] of baselineProduction) {
|
|
3234
|
+
const currentPkg = currentProduction.get(name);
|
|
3235
|
+
if (!currentPkg) {
|
|
3236
|
+
removed.push({
|
|
3237
|
+
name,
|
|
3238
|
+
version: baselinePkg.version,
|
|
3239
|
+
category: baselinePkg.category
|
|
3240
|
+
});
|
|
3241
|
+
} else if (baselinePkg.version !== currentPkg.version) {
|
|
3242
|
+
updated.push({
|
|
3243
|
+
name,
|
|
3244
|
+
version: currentPkg.version,
|
|
3245
|
+
previousVersion: baselinePkg.version,
|
|
3246
|
+
category: currentPkg.category
|
|
3247
|
+
});
|
|
3248
|
+
}
|
|
3249
|
+
}
|
|
3250
|
+
const baselineDev = new Map(
|
|
3251
|
+
this.baseline.packages.development.map((p10) => [p10.name, p10])
|
|
3252
|
+
);
|
|
3253
|
+
const currentDev = new Map(
|
|
3254
|
+
this.current.packages.development.map((p10) => [p10.name, p10])
|
|
3255
|
+
);
|
|
3256
|
+
for (const [name, pkg] of currentDev) {
|
|
3257
|
+
if (!baselineDev.has(name)) {
|
|
3258
|
+
added.push({
|
|
3259
|
+
name,
|
|
3260
|
+
version: pkg.version,
|
|
3261
|
+
category: pkg.category
|
|
3262
|
+
});
|
|
3263
|
+
}
|
|
3264
|
+
}
|
|
3265
|
+
for (const [name, baselinePkg] of baselineDev) {
|
|
3266
|
+
const currentPkg = currentDev.get(name);
|
|
3267
|
+
if (!currentPkg) {
|
|
3268
|
+
removed.push({
|
|
3269
|
+
name,
|
|
3270
|
+
version: baselinePkg.version,
|
|
3271
|
+
category: baselinePkg.category
|
|
3272
|
+
});
|
|
3273
|
+
} else if (baselinePkg.version !== currentPkg.version) {
|
|
3274
|
+
updated.push({
|
|
3275
|
+
name,
|
|
3276
|
+
version: currentPkg.version,
|
|
3277
|
+
previousVersion: baselinePkg.version,
|
|
3278
|
+
category: currentPkg.category
|
|
3279
|
+
});
|
|
3280
|
+
}
|
|
3281
|
+
}
|
|
3282
|
+
return { added, removed, updated };
|
|
3283
|
+
}
|
|
3284
|
+
/**
|
|
3285
|
+
* Compare technology stack between reports
|
|
3286
|
+
*/
|
|
3287
|
+
compareTechnology() {
|
|
3288
|
+
const baselineTech = this.baseline.technology;
|
|
3289
|
+
const currentTech = this.current.technology;
|
|
3290
|
+
const frameworkChanged = baselineTech.framework !== currentTech.framework || baselineTech.frameworkVersion !== currentTech.frameworkVersion;
|
|
3291
|
+
const baselineTools = new Set(baselineTech.buildTools);
|
|
3292
|
+
const currentTools = new Set(currentTech.buildTools);
|
|
3293
|
+
const newBuildTools = currentTech.buildTools.filter(
|
|
3294
|
+
(tool) => !baselineTools.has(tool)
|
|
3295
|
+
);
|
|
3296
|
+
const removedTools = baselineTech.buildTools.filter(
|
|
3297
|
+
(tool) => !currentTools.has(tool)
|
|
3298
|
+
);
|
|
3299
|
+
const baselineInfra = new Set(baselineTech.infrastructure);
|
|
3300
|
+
const newInfrastructure = currentTech.infrastructure.filter(
|
|
3301
|
+
(infra) => !baselineInfra.has(infra)
|
|
3302
|
+
);
|
|
3303
|
+
return {
|
|
3304
|
+
frameworkChanged,
|
|
3305
|
+
newBuildTools,
|
|
3306
|
+
newInfrastructure,
|
|
3307
|
+
removedTools
|
|
3308
|
+
};
|
|
3309
|
+
}
|
|
3310
|
+
/**
|
|
3311
|
+
* Generate markdown summary of changes
|
|
3312
|
+
*/
|
|
3313
|
+
generateMarkdownSummary() {
|
|
3314
|
+
const comparison = this.compare();
|
|
3315
|
+
const { changes, details } = comparison;
|
|
3316
|
+
let md = `# Advisory Report Comparison
|
|
3317
|
+
|
|
3318
|
+
`;
|
|
3319
|
+
md += `**Baseline:** ${new Date(comparison.baseline.timestamp).toLocaleDateString()}
|
|
3320
|
+
`;
|
|
3321
|
+
md += `**Current:** ${new Date(comparison.current.timestamp).toLocaleDateString()}
|
|
3322
|
+
|
|
3323
|
+
`;
|
|
3324
|
+
md += `## Executive Summary
|
|
3325
|
+
|
|
3326
|
+
`;
|
|
3327
|
+
const riskChange = changes.riskScoreChange;
|
|
3328
|
+
const riskEmoji = riskChange < 0 ? "\u2705" : riskChange > 0 ? "\u26A0\uFE0F" : "\u2796";
|
|
3329
|
+
md += `${riskEmoji} **Risk Score:** ${(comparison.baseline.overallRiskScore * 100).toFixed(0)}% \u2192 ${(comparison.current.overallRiskScore * 100).toFixed(0)}% `;
|
|
3330
|
+
md += `(${riskChange > 0 ? "+" : ""}${(riskChange * 100).toFixed(0)}%)
|
|
3331
|
+
`;
|
|
3332
|
+
const oppChange = changes.opportunityScoreChange;
|
|
3333
|
+
const oppEmoji = oppChange > 0 ? "\u2705" : oppChange < 0 ? "\u26A0\uFE0F" : "\u2796";
|
|
3334
|
+
md += `${oppEmoji} **Opportunity Score:** ${(comparison.baseline.overallOpportunityScore * 100).toFixed(0)}% \u2192 ${(comparison.current.overallOpportunityScore * 100).toFixed(0)}% `;
|
|
3335
|
+
md += `(${oppChange > 0 ? "+" : ""}${(oppChange * 100).toFixed(0)}%)
|
|
3336
|
+
`;
|
|
3337
|
+
md += `\u{1F4E6} **Packages:** ${comparison.baseline.packageCount} \u2192 ${comparison.current.packageCount} `;
|
|
3338
|
+
md += `(${changes.packageCountChange > 0 ? "+" : ""}${changes.packageCountChange})
|
|
3339
|
+
|
|
3340
|
+
`;
|
|
3341
|
+
md += `## Key Changes
|
|
3342
|
+
|
|
3343
|
+
`;
|
|
3344
|
+
if (changes.newHighRisks > 0) {
|
|
3345
|
+
md += `\u26A0\uFE0F **${changes.newHighRisks}** new high-priority risks identified
|
|
3346
|
+
`;
|
|
3347
|
+
}
|
|
3348
|
+
if (changes.resolvedHighRisks > 0) {
|
|
3349
|
+
md += `\u2705 **${changes.resolvedHighRisks}** high-priority risks resolved
|
|
3350
|
+
`;
|
|
3351
|
+
}
|
|
3352
|
+
if (changes.newOpportunities > 0) {
|
|
3353
|
+
md += `\u{1F31F} **${changes.newOpportunities}** new opportunities identified
|
|
3354
|
+
`;
|
|
3355
|
+
}
|
|
3356
|
+
if (details.technology.frameworkChanged) {
|
|
3357
|
+
md += `\u{1F504} Framework changed: ${comparison.baseline.projectName}
|
|
3358
|
+
`;
|
|
3359
|
+
}
|
|
3360
|
+
md += `
|
|
3361
|
+
---
|
|
3362
|
+
|
|
3363
|
+
`;
|
|
3364
|
+
if (details.risks.new.length > 0 || details.risks.resolved.length > 0) {
|
|
3365
|
+
md += `## Risk Changes
|
|
3366
|
+
|
|
3367
|
+
`;
|
|
3368
|
+
if (details.risks.new.length > 0) {
|
|
3369
|
+
md += `### New Risks
|
|
3370
|
+
|
|
3371
|
+
`;
|
|
3372
|
+
details.risks.new.forEach((risk) => {
|
|
3373
|
+
md += `- ${risk}
|
|
3374
|
+
`;
|
|
3375
|
+
});
|
|
3376
|
+
md += `
|
|
3377
|
+
`;
|
|
3378
|
+
}
|
|
3379
|
+
if (details.risks.resolved.length > 0) {
|
|
3380
|
+
md += `### Resolved Risks
|
|
3381
|
+
|
|
3382
|
+
`;
|
|
3383
|
+
details.risks.resolved.forEach((risk) => {
|
|
3384
|
+
md += `- ~~${risk}~~
|
|
3385
|
+
`;
|
|
3386
|
+
});
|
|
3387
|
+
md += `
|
|
3388
|
+
`;
|
|
3389
|
+
}
|
|
3390
|
+
if (details.risks.changed.length > 0) {
|
|
3391
|
+
md += `### Risk Score Changes
|
|
3392
|
+
|
|
3393
|
+
`;
|
|
3394
|
+
md += `| Category | Before | After | Change |
|
|
3395
|
+
`;
|
|
3396
|
+
md += `|----------|--------|-------|--------|
|
|
3397
|
+
`;
|
|
3398
|
+
details.risks.changed.forEach((change) => {
|
|
3399
|
+
const changeStr = change.change > 0 ? `+${(change.change * 100).toFixed(0)}%` : `${(change.change * 100).toFixed(0)}%`;
|
|
3400
|
+
md += `| ${change.category} | ${(change.before * 100).toFixed(0)}% | ${(change.after * 100).toFixed(0)}% | ${changeStr} |
|
|
3401
|
+
`;
|
|
3402
|
+
});
|
|
3403
|
+
md += `
|
|
3404
|
+
`;
|
|
3405
|
+
}
|
|
3406
|
+
}
|
|
3407
|
+
if (details.packages.added.length > 0 || details.packages.removed.length > 0 || details.packages.updated.length > 0) {
|
|
3408
|
+
md += `## Package Changes
|
|
3409
|
+
|
|
3410
|
+
`;
|
|
3411
|
+
if (details.packages.added.length > 0) {
|
|
3412
|
+
md += `### Added (${details.packages.added.length})
|
|
3413
|
+
|
|
3414
|
+
`;
|
|
3415
|
+
details.packages.added.slice(0, 10).forEach((pkg) => {
|
|
3416
|
+
md += `- **${pkg.name}** \`${pkg.version}\` (${pkg.category})
|
|
3417
|
+
`;
|
|
3418
|
+
});
|
|
3419
|
+
if (details.packages.added.length > 10) {
|
|
3420
|
+
md += `
|
|
3421
|
+
_...and ${details.packages.added.length - 10} more_
|
|
3422
|
+
`;
|
|
3423
|
+
}
|
|
3424
|
+
md += `
|
|
3425
|
+
`;
|
|
3426
|
+
}
|
|
3427
|
+
if (details.packages.removed.length > 0) {
|
|
3428
|
+
md += `### Removed (${details.packages.removed.length})
|
|
3429
|
+
|
|
3430
|
+
`;
|
|
3431
|
+
details.packages.removed.slice(0, 10).forEach((pkg) => {
|
|
3432
|
+
md += `- ~~${pkg.name}~~ \`${pkg.version}\`
|
|
3433
|
+
`;
|
|
3434
|
+
});
|
|
3435
|
+
if (details.packages.removed.length > 10) {
|
|
3436
|
+
md += `
|
|
3437
|
+
_...and ${details.packages.removed.length - 10} more_
|
|
3438
|
+
`;
|
|
3439
|
+
}
|
|
3440
|
+
md += `
|
|
3441
|
+
`;
|
|
3442
|
+
}
|
|
3443
|
+
if (details.packages.updated.length > 0) {
|
|
3444
|
+
md += `### Updated (${details.packages.updated.length})
|
|
3445
|
+
|
|
3446
|
+
`;
|
|
3447
|
+
details.packages.updated.slice(0, 10).forEach((pkg) => {
|
|
3448
|
+
md += `- **${pkg.name}** \`${pkg.previousVersion}\` \u2192 \`${pkg.version}\`
|
|
3449
|
+
`;
|
|
3450
|
+
});
|
|
3451
|
+
if (details.packages.updated.length > 10) {
|
|
3452
|
+
md += `
|
|
3453
|
+
_...and ${details.packages.updated.length - 10} more_
|
|
3454
|
+
`;
|
|
3455
|
+
}
|
|
3456
|
+
md += `
|
|
3457
|
+
`;
|
|
3458
|
+
}
|
|
3459
|
+
}
|
|
3460
|
+
return md;
|
|
3461
|
+
}
|
|
3462
|
+
};
|
|
3463
|
+
|
|
3464
|
+
// src/cli/commands/advisory.ts
|
|
3465
|
+
async function advisoryCommand(options) {
|
|
3466
|
+
console.log(chalk11.bold.cyan("\n\u{1F3AF} Advisory Board Analysis\n"));
|
|
3467
|
+
const cwd = process.cwd();
|
|
3468
|
+
let config;
|
|
3469
|
+
try {
|
|
3470
|
+
config = await loadConfig(cwd);
|
|
3471
|
+
} catch (error) {
|
|
3472
|
+
config = void 0;
|
|
3473
|
+
}
|
|
3474
|
+
const advisoryConfig = config?.advisory;
|
|
3475
|
+
const isInteractive = options.interactive || !options.depth && !options.ci;
|
|
3476
|
+
let depth = options.depth || advisoryConfig?.defaultDepth || "standard";
|
|
3477
|
+
let outputDir = options.output || advisoryConfig?.outputDir || "docs/advisory";
|
|
3478
|
+
let includeHealth = options.includeHealth ?? advisoryConfig?.includeHealthMetrics ?? false;
|
|
3479
|
+
if (isInteractive && !options.depth) {
|
|
3480
|
+
const depthChoice = await p7.select({
|
|
3481
|
+
message: "Select analysis depth:",
|
|
3482
|
+
options: [
|
|
3483
|
+
{
|
|
3484
|
+
value: "executive",
|
|
3485
|
+
label: "\u{1F4CA} Executive - Business summary for non-technical stakeholders",
|
|
3486
|
+
hint: "High-level overview only"
|
|
3487
|
+
},
|
|
3488
|
+
{
|
|
3489
|
+
value: "quick",
|
|
3490
|
+
label: "\u26A1 Quick - Package scan and framework detection",
|
|
3491
|
+
hint: "~30 seconds"
|
|
3492
|
+
},
|
|
3493
|
+
{
|
|
3494
|
+
value: "standard",
|
|
3495
|
+
label: "\u{1F50D} Standard - Includes architecture analysis",
|
|
3496
|
+
hint: "~1-2 minutes (recommended)"
|
|
3497
|
+
},
|
|
3498
|
+
{
|
|
3499
|
+
value: "comprehensive",
|
|
3500
|
+
label: "\u{1F52C} Comprehensive - Full code-level pattern detection",
|
|
3501
|
+
hint: "~3-5 minutes"
|
|
3502
|
+
}
|
|
3503
|
+
],
|
|
3504
|
+
initialValue: "standard"
|
|
3505
|
+
});
|
|
3506
|
+
if (p7.isCancel(depthChoice)) {
|
|
3507
|
+
p7.cancel("Analysis cancelled");
|
|
3508
|
+
process.exit(0);
|
|
3509
|
+
}
|
|
3510
|
+
depth = depthChoice;
|
|
3511
|
+
}
|
|
3512
|
+
if (isInteractive && !options.output) {
|
|
3513
|
+
const outputChoice = await p7.text({
|
|
3514
|
+
message: "Output directory:",
|
|
3515
|
+
placeholder: "docs/advisory",
|
|
3516
|
+
defaultValue: outputDir
|
|
3517
|
+
});
|
|
3518
|
+
if (p7.isCancel(outputChoice)) {
|
|
3519
|
+
p7.cancel("Analysis cancelled");
|
|
3520
|
+
process.exit(0);
|
|
3521
|
+
}
|
|
3522
|
+
outputDir = outputChoice;
|
|
3523
|
+
}
|
|
3524
|
+
if (isInteractive && !options.includeHealth) {
|
|
3525
|
+
const healthChoice = await p7.confirm({
|
|
3526
|
+
message: "Include code health metrics?",
|
|
3527
|
+
initialValue: includeHealth
|
|
3528
|
+
});
|
|
3529
|
+
if (p7.isCancel(healthChoice)) {
|
|
3530
|
+
p7.cancel("Analysis cancelled");
|
|
3531
|
+
process.exit(0);
|
|
3532
|
+
}
|
|
3533
|
+
includeHealth = healthChoice;
|
|
3534
|
+
}
|
|
3535
|
+
const analyzer = new AdvisoryAnalyzer({
|
|
3536
|
+
depth,
|
|
3537
|
+
cwd,
|
|
3538
|
+
config,
|
|
3539
|
+
includeHealth,
|
|
3540
|
+
excludePatterns: advisoryConfig?.excludePatterns
|
|
3541
|
+
});
|
|
3542
|
+
const spinner7 = p7.spinner();
|
|
3543
|
+
const depthLabels = {
|
|
3544
|
+
executive: "executive summary",
|
|
3545
|
+
quick: "quick scan",
|
|
3546
|
+
standard: "standard analysis",
|
|
3547
|
+
comprehensive: "comprehensive analysis"
|
|
3548
|
+
};
|
|
3549
|
+
spinner7.start(`Running ${depthLabels[depth]}...`);
|
|
3550
|
+
let analysis;
|
|
3551
|
+
try {
|
|
3552
|
+
analysis = await analyzer.analyze();
|
|
3553
|
+
spinner7.stop(`\u2713 Analysis complete`);
|
|
3554
|
+
} catch (error) {
|
|
3555
|
+
spinner7.stop("\u2717 Analysis failed");
|
|
3556
|
+
console.error(
|
|
3557
|
+
chalk11.red(
|
|
3558
|
+
`Error: ${error instanceof Error ? error.message : String(error)}`
|
|
3559
|
+
)
|
|
3560
|
+
);
|
|
3561
|
+
process.exit(1);
|
|
3562
|
+
}
|
|
3563
|
+
const questionGenerator = new QuestionGenerator(analysis, advisoryConfig);
|
|
3564
|
+
const questions = questionGenerator.generate();
|
|
3565
|
+
console.log("");
|
|
3566
|
+
displaySummary(analysis, questions);
|
|
3567
|
+
let comparisonReport;
|
|
3568
|
+
let comparator;
|
|
3569
|
+
if (options.compare) {
|
|
3570
|
+
const comparisonPath = options.compare.endsWith(".json") ? join6(cwd, options.compare) : join6(cwd, options.compare, "analysis.json");
|
|
3571
|
+
if (existsSync6(comparisonPath)) {
|
|
3572
|
+
spinner7.start("Comparing with previous report...");
|
|
3573
|
+
try {
|
|
3574
|
+
const previousContent = await readFile4(comparisonPath, "utf-8");
|
|
3575
|
+
const previousAnalysis = JSON.parse(previousContent);
|
|
3576
|
+
comparator = new ReportComparator(previousAnalysis, analysis);
|
|
3577
|
+
comparisonReport = comparator.compare();
|
|
3578
|
+
spinner7.stop("\u2713 Comparison complete");
|
|
3579
|
+
console.log("");
|
|
3580
|
+
displayComparisonSummary(comparisonReport);
|
|
3581
|
+
} catch (error) {
|
|
3582
|
+
spinner7.stop("\u26A0 Comparison failed");
|
|
3583
|
+
console.warn(chalk11.yellow(`Could not compare reports: ${error}`));
|
|
3584
|
+
}
|
|
3585
|
+
} else {
|
|
3586
|
+
console.warn(
|
|
3587
|
+
chalk11.yellow(
|
|
3588
|
+
`
|
|
3589
|
+
\u26A0 No previous report found at ${comparisonPath}. Skipping comparison.
|
|
3590
|
+
`
|
|
3591
|
+
)
|
|
3592
|
+
);
|
|
3593
|
+
}
|
|
3594
|
+
}
|
|
3595
|
+
if (options.dryRun) {
|
|
3596
|
+
console.log(chalk11.dim("\n--dry-run mode: No files written.\n"));
|
|
3597
|
+
return;
|
|
3598
|
+
}
|
|
3599
|
+
const fullOutputDir = join6(cwd, outputDir);
|
|
3600
|
+
await mkdir4(fullOutputDir, { recursive: true });
|
|
3601
|
+
spinner7.start("Generating reports...");
|
|
3602
|
+
try {
|
|
3603
|
+
const timestamp = options.timestamp ? (/* @__PURE__ */ new Date()).toISOString().split("T")[0] : "";
|
|
3604
|
+
if (options.format === "json") {
|
|
3605
|
+
await writeAnalysisJson(fullOutputDir, analysis, questions, timestamp);
|
|
3606
|
+
} else {
|
|
3607
|
+
await writeMarkdownReports(
|
|
3608
|
+
fullOutputDir,
|
|
3609
|
+
analysis,
|
|
3610
|
+
questions,
|
|
3611
|
+
comparator,
|
|
3612
|
+
timestamp
|
|
3613
|
+
);
|
|
3614
|
+
}
|
|
3615
|
+
spinner7.stop("\u2713 Reports generated");
|
|
3616
|
+
} catch (error) {
|
|
3617
|
+
spinner7.stop("\u2717 Failed to write reports");
|
|
3618
|
+
console.error(chalk11.red(`Error: ${error}`));
|
|
3619
|
+
process.exit(1);
|
|
3620
|
+
}
|
|
3621
|
+
console.log("");
|
|
3622
|
+
console.log(chalk11.green("\u{1F4DD} Reports written to:"));
|
|
3623
|
+
const suffix = options.timestamp ? `-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}` : "";
|
|
3624
|
+
if (options.format === "json") {
|
|
3625
|
+
console.log(chalk11.dim(` ${join6(outputDir, `analysis${suffix}.json`)}`));
|
|
3626
|
+
console.log(chalk11.dim(` ${join6(outputDir, `questions${suffix}.json`)}`));
|
|
3627
|
+
} else {
|
|
3628
|
+
console.log(
|
|
3629
|
+
chalk11.dim(` ${join6(outputDir, `EXECUTIVE_SUMMARY${suffix}.md`)}`)
|
|
3630
|
+
);
|
|
3631
|
+
console.log(
|
|
3632
|
+
chalk11.dim(` ${join6(outputDir, `TECHNOLOGY_AUDIT${suffix}.md`)}`)
|
|
3633
|
+
);
|
|
3634
|
+
console.log(
|
|
3635
|
+
chalk11.dim(` ${join6(outputDir, `STRATEGIC_ROADMAP${suffix}.md`)}`)
|
|
3636
|
+
);
|
|
3637
|
+
console.log(
|
|
3638
|
+
chalk11.dim(` ${join6(outputDir, `BOARD_QUESTIONS${suffix}.md`)}`)
|
|
3639
|
+
);
|
|
3640
|
+
if (comparisonReport) {
|
|
3641
|
+
console.log(
|
|
3642
|
+
chalk11.dim(` ${join6(outputDir, `DIFF_REPORT${suffix}.md`)}`)
|
|
3643
|
+
);
|
|
3644
|
+
}
|
|
3645
|
+
}
|
|
3646
|
+
if (options.ci) {
|
|
3647
|
+
console.log("");
|
|
3648
|
+
const exitCode = checkCIThresholds(analysis, advisoryConfig);
|
|
3649
|
+
if (exitCode !== 0) {
|
|
3650
|
+
console.log(chalk11.red(`
|
|
3651
|
+
\u274C CI check failed (exit code ${exitCode})
|
|
3652
|
+
`));
|
|
3653
|
+
process.exit(exitCode);
|
|
3654
|
+
} else {
|
|
3655
|
+
console.log(chalk11.green("\n\u2713 CI check passed\n"));
|
|
3656
|
+
}
|
|
3657
|
+
}
|
|
3658
|
+
p7.outro(chalk11.green("\u2713 Advisory analysis complete!"));
|
|
3659
|
+
}
|
|
3660
|
+
function displaySummary(analysis, questions) {
|
|
3661
|
+
console.log(chalk11.bold("\u{1F4CA} Analysis Summary\n"));
|
|
3662
|
+
console.log(
|
|
3663
|
+
chalk11.dim(`Project: ${analysis.project.name} v${analysis.project.version}`)
|
|
3664
|
+
);
|
|
3665
|
+
console.log(
|
|
3666
|
+
chalk11.dim(
|
|
3667
|
+
`Framework: ${analysis.technology.framework || "N/A"} (${analysis.technology.language})`
|
|
3668
|
+
)
|
|
3669
|
+
);
|
|
3670
|
+
console.log(chalk11.dim(`Total Packages: ${analysis.packages.total}`));
|
|
3671
|
+
console.log(chalk11.dim(`Files Analyzed: ${analysis.project.fileCount}`));
|
|
3672
|
+
console.log("");
|
|
3673
|
+
const riskScore = (analysis.risks.overall * 100).toFixed(0);
|
|
3674
|
+
const riskColor = analysis.risks.overall > 0.7 ? chalk11.red : analysis.risks.overall > 0.4 ? chalk11.yellow : chalk11.green;
|
|
3675
|
+
console.log(riskColor(`\u26A0\uFE0F Risk Score: ${riskScore}%`));
|
|
3676
|
+
const oppScore = (analysis.opportunities.overall * 100).toFixed(0);
|
|
3677
|
+
const oppColor = analysis.opportunities.overall > 0.7 ? chalk11.green : analysis.opportunities.overall > 0.5 ? chalk11.yellow : chalk11.dim;
|
|
3678
|
+
console.log(oppColor(`\u{1F31F} Opportunity Score: ${oppScore}%`));
|
|
3679
|
+
console.log("");
|
|
3680
|
+
console.log(chalk11.bold("\u2753 Advisory Questions\n"));
|
|
3681
|
+
console.log(
|
|
3682
|
+
chalk11.dim(`Total Questions: ${questions.summary.totalQuestions}`)
|
|
3683
|
+
);
|
|
3684
|
+
console.log(chalk11.red(` High Priority: ${questions.summary.highPriority}`));
|
|
3685
|
+
console.log(
|
|
3686
|
+
chalk11.yellow(` Medium Priority: ${questions.summary.mediumPriority}`)
|
|
3687
|
+
);
|
|
3688
|
+
console.log(chalk11.dim(` Low Priority: ${questions.summary.lowPriority}`));
|
|
3689
|
+
console.log("");
|
|
3690
|
+
const highPriorityQuestions = questions.questions.filter(
|
|
3691
|
+
(q) => q.priority === "high"
|
|
3692
|
+
);
|
|
3693
|
+
if (highPriorityQuestions.length > 0) {
|
|
3694
|
+
console.log(chalk11.bold.yellow("\u{1F525} Top Priority Questions:\n"));
|
|
3695
|
+
highPriorityQuestions.slice(0, 3).forEach((q, i) => {
|
|
3696
|
+
console.log(chalk11.yellow(`${i + 1}. ${q.question}`));
|
|
3697
|
+
console.log(chalk11.dim(` Category: ${q.category}`));
|
|
3698
|
+
console.log("");
|
|
3699
|
+
});
|
|
3700
|
+
}
|
|
3701
|
+
}
|
|
3702
|
+
function displayComparisonSummary(comparison) {
|
|
3703
|
+
console.log(chalk11.bold("\u{1F4C8} Changes Since Last Report\n"));
|
|
3704
|
+
const riskChange = comparison.changes.riskScoreChange;
|
|
3705
|
+
const riskEmoji = riskChange < 0 ? "\u2705" : riskChange > 0 ? "\u26A0\uFE0F" : "\u2796";
|
|
3706
|
+
console.log(
|
|
3707
|
+
`${riskEmoji} Risk Score: ${(riskChange * 100).toFixed(0)}% change`
|
|
3708
|
+
);
|
|
3709
|
+
const oppChange = comparison.changes.opportunityScoreChange;
|
|
3710
|
+
const oppEmoji = oppChange > 0 ? "\u2705" : oppChange < 0 ? "\u26A0\uFE0F" : "\u2796";
|
|
3711
|
+
console.log(
|
|
3712
|
+
`${oppEmoji} Opportunity Score: ${(oppChange * 100).toFixed(0)}% change`
|
|
3713
|
+
);
|
|
3714
|
+
console.log(
|
|
3715
|
+
`\u{1F4E6} Packages: ${comparison.changes.packageCountChange > 0 ? "+" : ""}${comparison.changes.packageCountChange}`
|
|
3716
|
+
);
|
|
3717
|
+
if (comparison.changes.newHighRisks > 0) {
|
|
3718
|
+
console.log(
|
|
3719
|
+
chalk11.red(
|
|
3720
|
+
`\u26A0\uFE0F ${comparison.changes.newHighRisks} new high-priority risks`
|
|
3721
|
+
)
|
|
3722
|
+
);
|
|
3723
|
+
}
|
|
3724
|
+
if (comparison.changes.resolvedHighRisks > 0) {
|
|
3725
|
+
console.log(
|
|
3726
|
+
chalk11.green(
|
|
3727
|
+
`\u2705 ${comparison.changes.resolvedHighRisks} high-priority risks resolved`
|
|
3728
|
+
)
|
|
3729
|
+
);
|
|
3730
|
+
}
|
|
3731
|
+
console.log("");
|
|
3732
|
+
}
|
|
3733
|
+
async function writeAnalysisJson(outputDir, analysis, questions, timestamp) {
|
|
3734
|
+
const suffix = timestamp ? `-${timestamp}` : "";
|
|
3735
|
+
await writeFile4(
|
|
3736
|
+
join6(outputDir, `analysis${suffix}.json`),
|
|
3737
|
+
JSON.stringify(analysis, null, 2),
|
|
3738
|
+
"utf-8"
|
|
3739
|
+
);
|
|
3740
|
+
await writeFile4(
|
|
3741
|
+
join6(outputDir, `questions${suffix}.json`),
|
|
3742
|
+
JSON.stringify(questions, null, 2),
|
|
3743
|
+
"utf-8"
|
|
3744
|
+
);
|
|
3745
|
+
}
|
|
3746
|
+
async function writeMarkdownReports(outputDir, analysis, questions, comparator, timestamp) {
|
|
3747
|
+
const suffix = timestamp ? `-${timestamp}` : "";
|
|
3748
|
+
await writeFile4(
|
|
3749
|
+
join6(outputDir, `analysis.json`),
|
|
3750
|
+
JSON.stringify(analysis, null, 2),
|
|
3751
|
+
"utf-8"
|
|
3752
|
+
);
|
|
3753
|
+
await writeFile4(
|
|
3754
|
+
join6(outputDir, `EXECUTIVE_SUMMARY${suffix}.md`),
|
|
3755
|
+
generateExecutiveSummary(analysis, questions),
|
|
3756
|
+
"utf-8"
|
|
3757
|
+
);
|
|
3758
|
+
await writeFile4(
|
|
3759
|
+
join6(outputDir, `TECHNOLOGY_AUDIT${suffix}.md`),
|
|
3760
|
+
generateTechnologyAudit(analysis),
|
|
3761
|
+
"utf-8"
|
|
3762
|
+
);
|
|
3763
|
+
await writeFile4(
|
|
3764
|
+
join6(outputDir, `STRATEGIC_ROADMAP${suffix}.md`),
|
|
3765
|
+
generateStrategicRoadmap(analysis, questions),
|
|
3766
|
+
"utf-8"
|
|
3767
|
+
);
|
|
3768
|
+
await writeFile4(
|
|
3769
|
+
join6(outputDir, `BOARD_QUESTIONS${suffix}.md`),
|
|
3770
|
+
generateBoardQuestions(questions),
|
|
3771
|
+
"utf-8"
|
|
3772
|
+
);
|
|
3773
|
+
if (comparator) {
|
|
3774
|
+
await writeFile4(
|
|
3775
|
+
join6(outputDir, `DIFF_REPORT${suffix}.md`),
|
|
3776
|
+
comparator.generateMarkdownSummary(),
|
|
3777
|
+
"utf-8"
|
|
3778
|
+
);
|
|
3779
|
+
}
|
|
3780
|
+
}
|
|
3781
|
+
function generateExecutiveSummary(analysis, questions) {
|
|
3782
|
+
let md = `# Executive Summary
|
|
3783
|
+
|
|
3784
|
+
`;
|
|
3785
|
+
md += `> **Generated:** ${new Date(analysis.timestamp).toLocaleDateString()}
|
|
3786
|
+
|
|
3787
|
+
`;
|
|
3788
|
+
md += `---
|
|
3789
|
+
|
|
3790
|
+
`;
|
|
3791
|
+
md += `## Project Overview
|
|
3792
|
+
|
|
3793
|
+
`;
|
|
3794
|
+
md += `**${analysis.project.name}** is a ${analysis.technology.framework || "modern"} application `;
|
|
3795
|
+
md += `built with ${analysis.technology.language}. `;
|
|
3796
|
+
if (analysis.project.description) {
|
|
3797
|
+
md += `${analysis.project.description}
|
|
3798
|
+
|
|
3799
|
+
`;
|
|
3800
|
+
} else {
|
|
3801
|
+
md += `
|
|
3802
|
+
|
|
3803
|
+
`;
|
|
3804
|
+
}
|
|
3805
|
+
md += `### Key Metrics
|
|
3806
|
+
|
|
3807
|
+
`;
|
|
3808
|
+
md += `| Metric | Value |
|
|
3809
|
+
`;
|
|
3810
|
+
md += `|--------|-------|
|
|
3811
|
+
`;
|
|
3812
|
+
md += `| Version | ${analysis.project.version} |
|
|
3813
|
+
`;
|
|
3814
|
+
md += `| Dependencies | ${analysis.packages.total} packages |
|
|
3815
|
+
`;
|
|
3816
|
+
md += `| Codebase Size | ${analysis.project.fileCount} files (~${(analysis.project.totalLines / 1e3).toFixed(0)}K lines) |
|
|
3817
|
+
`;
|
|
3818
|
+
md += `| Architecture | ${analysis.project.isMonorepo ? "Monorepo" : "Single repository"} |
|
|
3819
|
+
`;
|
|
3820
|
+
md += `
|
|
3821
|
+
`;
|
|
3822
|
+
md += `## Strategic Assessment
|
|
3823
|
+
|
|
3824
|
+
`;
|
|
3825
|
+
const riskScore = analysis.risks.overall;
|
|
3826
|
+
const oppScore = analysis.opportunities.overall;
|
|
3827
|
+
md += `### Risk & Opportunity Matrix
|
|
3828
|
+
|
|
3829
|
+
`;
|
|
3830
|
+
md += `| Category | Score | Status |
|
|
3831
|
+
`;
|
|
3832
|
+
md += `|----------|-------|--------|
|
|
3833
|
+
`;
|
|
3834
|
+
md += `| **Technical Risk** | ${(riskScore * 100).toFixed(0)}% | ${getRiskLabel(riskScore)} |
|
|
3835
|
+
`;
|
|
3836
|
+
md += `| **Growth Opportunity** | ${(oppScore * 100).toFixed(0)}% | ${getOpportunityLabel(oppScore)} |
|
|
3837
|
+
`;
|
|
3838
|
+
md += `
|
|
3839
|
+
`;
|
|
3840
|
+
md += `### Technology Foundation
|
|
3841
|
+
|
|
3842
|
+
`;
|
|
3843
|
+
md += `**Primary Stack:**
|
|
3844
|
+
`;
|
|
3845
|
+
md += `- Framework: ${analysis.technology.framework || "N/A"}
|
|
3846
|
+
`;
|
|
3847
|
+
md += `- Language: ${analysis.technology.language}
|
|
3848
|
+
`;
|
|
3849
|
+
md += `- Runtime: ${analysis.technology.runtime}
|
|
3850
|
+
`;
|
|
3851
|
+
if (analysis.technology.platforms.length > 0) {
|
|
3852
|
+
md += `- Platforms: ${analysis.technology.platforms.join(", ")}
|
|
3853
|
+
`;
|
|
3854
|
+
}
|
|
3855
|
+
md += `
|
|
3856
|
+
`;
|
|
3857
|
+
if (analysis.technology.infrastructure.length > 0) {
|
|
3858
|
+
md += `**Infrastructure:** ${analysis.technology.infrastructure.join(", ")}
|
|
3859
|
+
|
|
3860
|
+
`;
|
|
3861
|
+
}
|
|
3862
|
+
if (analysis.packages.categories.length > 0) {
|
|
3863
|
+
md += `### Technology Categories
|
|
3864
|
+
|
|
3865
|
+
`;
|
|
3866
|
+
const topCategories = analysis.packages.categories.sort((a, b) => b.count - a.count).slice(0, 5);
|
|
3867
|
+
topCategories.forEach((cat) => {
|
|
3868
|
+
md += `- **${cat.name}** (${cat.count} packages) - ${cat.businessImpact}
|
|
3869
|
+
`;
|
|
3870
|
+
});
|
|
3871
|
+
md += `
|
|
3872
|
+
`;
|
|
3873
|
+
}
|
|
3874
|
+
md += `## Key Strategic Questions
|
|
3875
|
+
|
|
3876
|
+
`;
|
|
3877
|
+
const highPriorityQuestions = questions.questions.filter(
|
|
3878
|
+
(q) => q.priority === "high"
|
|
3879
|
+
);
|
|
3880
|
+
if (highPriorityQuestions.length > 0) {
|
|
3881
|
+
highPriorityQuestions.slice(0, 5).forEach((q, i) => {
|
|
3882
|
+
md += `### ${i + 1}. ${q.question}
|
|
3883
|
+
|
|
3884
|
+
`;
|
|
3885
|
+
md += `**Context:** ${q.context}
|
|
3886
|
+
|
|
3887
|
+
`;
|
|
3888
|
+
md += `**Business Impact:** ${q.businessImpact}
|
|
3889
|
+
|
|
3890
|
+
`;
|
|
3891
|
+
});
|
|
3892
|
+
}
|
|
3893
|
+
if (analysis.risks.critical && analysis.risks.critical.length > 0) {
|
|
3894
|
+
md += `## Critical Risks
|
|
3895
|
+
|
|
3896
|
+
`;
|
|
3897
|
+
analysis.risks.critical.forEach((risk) => {
|
|
3898
|
+
md += `- \u26A0\uFE0F ${risk}
|
|
3899
|
+
`;
|
|
3900
|
+
});
|
|
3901
|
+
md += `
|
|
3902
|
+
`;
|
|
3903
|
+
}
|
|
3904
|
+
if (analysis.opportunities.immediate && analysis.opportunities.immediate.length > 0) {
|
|
3905
|
+
md += `## Immediate Opportunities
|
|
3906
|
+
|
|
3907
|
+
`;
|
|
3908
|
+
analysis.opportunities.immediate.slice(0, 5).forEach((opp) => {
|
|
3909
|
+
md += `- \u{1F31F} ${opp}
|
|
3910
|
+
`;
|
|
3911
|
+
});
|
|
3912
|
+
md += `
|
|
3913
|
+
`;
|
|
3914
|
+
}
|
|
3915
|
+
md += `---
|
|
3916
|
+
|
|
3917
|
+
`;
|
|
3918
|
+
md += `*For detailed technical analysis, see [TECHNOLOGY_AUDIT.md](TECHNOLOGY_AUDIT.md)*
|
|
3919
|
+
`;
|
|
3920
|
+
md += `*For strategic recommendations, see [STRATEGIC_ROADMAP.md](STRATEGIC_ROADMAP.md)*
|
|
3921
|
+
`;
|
|
3922
|
+
return md;
|
|
3923
|
+
}
|
|
3924
|
+
function generateTechnologyAudit(analysis) {
|
|
3925
|
+
let md = `# Technology Audit
|
|
3926
|
+
|
|
3927
|
+
`;
|
|
3928
|
+
md += `> **Generated:** ${new Date(analysis.timestamp).toLocaleDateString()}
|
|
3929
|
+
`;
|
|
3930
|
+
md += `> **Analysis Depth:** ${analysis.depth}
|
|
3931
|
+
|
|
3932
|
+
`;
|
|
3933
|
+
md += `---
|
|
3934
|
+
|
|
3935
|
+
`;
|
|
3936
|
+
md += `## Project Details
|
|
3937
|
+
|
|
3938
|
+
`;
|
|
3939
|
+
md += `| Property | Value |
|
|
3940
|
+
`;
|
|
3941
|
+
md += `|----------|-------|
|
|
3942
|
+
`;
|
|
3943
|
+
md += `| Name | ${analysis.project.name} |
|
|
3944
|
+
`;
|
|
3945
|
+
md += `| Version | ${analysis.project.version} |
|
|
3946
|
+
`;
|
|
3947
|
+
md += `| Package Manager | ${analysis.project.packageManager} |
|
|
3948
|
+
`;
|
|
3949
|
+
md += `| Repository Type | ${analysis.project.isMonorepo ? "Monorepo" : "Single"} |
|
|
3950
|
+
`;
|
|
3951
|
+
if (analysis.project.workspaceCount) {
|
|
3952
|
+
md += `| Workspaces | ${analysis.project.workspaceCount} |
|
|
3953
|
+
`;
|
|
3954
|
+
}
|
|
3955
|
+
md += `| Total Files | ${analysis.project.fileCount} |
|
|
3956
|
+
`;
|
|
3957
|
+
md += `| Estimated Lines | ${analysis.project.totalLines.toLocaleString()} |
|
|
3958
|
+
`;
|
|
3959
|
+
md += `
|
|
3960
|
+
`;
|
|
3961
|
+
md += `## Technology Stack
|
|
3962
|
+
|
|
3963
|
+
`;
|
|
3964
|
+
md += `### Core Technologies
|
|
3965
|
+
|
|
3966
|
+
`;
|
|
3967
|
+
md += `| Component | Technology |
|
|
3968
|
+
`;
|
|
3969
|
+
md += `|-----------|------------|
|
|
3970
|
+
`;
|
|
3971
|
+
md += `| Framework | ${analysis.technology.framework || "N/A"} ${analysis.technology.frameworkVersion || ""} |
|
|
3972
|
+
`;
|
|
3973
|
+
md += `| Language | ${analysis.technology.language} |
|
|
3974
|
+
`;
|
|
3975
|
+
md += `| Runtime | ${analysis.technology.runtime} |
|
|
3976
|
+
`;
|
|
3977
|
+
md += `| Platforms | ${analysis.technology.platforms.join(", ")} |
|
|
3978
|
+
`;
|
|
3979
|
+
md += `
|
|
3980
|
+
`;
|
|
3981
|
+
if (analysis.technology.buildTools.length > 0) {
|
|
3982
|
+
md += `### Build Tools
|
|
3983
|
+
|
|
3984
|
+
`;
|
|
3985
|
+
analysis.technology.buildTools.forEach((tool) => {
|
|
3986
|
+
md += `- ${tool}
|
|
3987
|
+
`;
|
|
3988
|
+
});
|
|
3989
|
+
md += `
|
|
3990
|
+
`;
|
|
3991
|
+
}
|
|
3992
|
+
if (analysis.technology.infrastructure.length > 0) {
|
|
3993
|
+
md += `### Infrastructure
|
|
3994
|
+
|
|
3995
|
+
`;
|
|
3996
|
+
analysis.technology.infrastructure.forEach((infra) => {
|
|
3997
|
+
md += `- ${infra}
|
|
3998
|
+
`;
|
|
3999
|
+
});
|
|
4000
|
+
md += `
|
|
4001
|
+
`;
|
|
4002
|
+
}
|
|
4003
|
+
md += `## Dependency Analysis
|
|
4004
|
+
|
|
4005
|
+
`;
|
|
4006
|
+
md += `### Overview
|
|
4007
|
+
|
|
4008
|
+
`;
|
|
4009
|
+
md += `- **Total Packages:** ${analysis.packages.total}
|
|
4010
|
+
`;
|
|
4011
|
+
md += `- **Production:** ${analysis.packages.production.length}
|
|
4012
|
+
`;
|
|
4013
|
+
md += `- **Development:** ${analysis.packages.development.length}
|
|
4014
|
+
`;
|
|
4015
|
+
md += `
|
|
4016
|
+
`;
|
|
4017
|
+
if (analysis.packages.categories.length > 0) {
|
|
4018
|
+
md += `### Package Categories
|
|
4019
|
+
|
|
4020
|
+
`;
|
|
4021
|
+
md += `| Category | Count | Business Impact |
|
|
4022
|
+
`;
|
|
4023
|
+
md += `|----------|-------|----------------|
|
|
4024
|
+
`;
|
|
4025
|
+
analysis.packages.categories.sort((a, b) => b.count - a.count).forEach((cat) => {
|
|
4026
|
+
md += `| ${cat.name} | ${cat.count} | ${cat.businessImpact} |
|
|
4027
|
+
`;
|
|
4028
|
+
});
|
|
4029
|
+
md += `
|
|
4030
|
+
`;
|
|
4031
|
+
}
|
|
4032
|
+
if (analysis.packages.production.length > 0) {
|
|
4033
|
+
md += `### Key Production Dependencies
|
|
4034
|
+
|
|
4035
|
+
`;
|
|
4036
|
+
md += `| Package | Version | Category | Purpose |
|
|
4037
|
+
`;
|
|
4038
|
+
md += `|---------|---------|----------|--------|
|
|
4039
|
+
`;
|
|
4040
|
+
analysis.packages.production.slice(0, 15).forEach((pkg) => {
|
|
4041
|
+
md += `| ${pkg.name} | \`${pkg.version}\` | ${pkg.category} | ${pkg.purpose} |
|
|
4042
|
+
`;
|
|
4043
|
+
});
|
|
4044
|
+
if (analysis.packages.production.length > 15) {
|
|
4045
|
+
md += `
|
|
4046
|
+
*...and ${analysis.packages.production.length - 15} more*
|
|
4047
|
+
`;
|
|
4048
|
+
}
|
|
4049
|
+
md += `
|
|
4050
|
+
`;
|
|
4051
|
+
}
|
|
4052
|
+
if (analysis.architecture) {
|
|
4053
|
+
md += `## Architecture Overview
|
|
4054
|
+
|
|
4055
|
+
`;
|
|
4056
|
+
md += `**Pattern:** ${analysis.architecture.pattern}
|
|
4057
|
+
|
|
4058
|
+
`;
|
|
4059
|
+
md += `### Layers
|
|
4060
|
+
|
|
4061
|
+
`;
|
|
4062
|
+
analysis.architecture.layers.forEach((layer) => {
|
|
4063
|
+
md += `- ${layer}
|
|
4064
|
+
`;
|
|
4065
|
+
});
|
|
4066
|
+
md += `
|
|
4067
|
+
`;
|
|
4068
|
+
md += `**Data Flow:** ${analysis.architecture.dataFlow}
|
|
4069
|
+
|
|
4070
|
+
`;
|
|
4071
|
+
if (analysis.architecture.keyDecisions.length > 0) {
|
|
4072
|
+
md += `### Key Architecture Decisions
|
|
4073
|
+
|
|
4074
|
+
`;
|
|
4075
|
+
analysis.architecture.keyDecisions.forEach((decision) => {
|
|
4076
|
+
md += `- ${decision}
|
|
4077
|
+
`;
|
|
4078
|
+
});
|
|
4079
|
+
md += `
|
|
4080
|
+
`;
|
|
4081
|
+
}
|
|
4082
|
+
}
|
|
4083
|
+
if (analysis.codePatterns) {
|
|
4084
|
+
md += `## Code Patterns
|
|
4085
|
+
|
|
4086
|
+
`;
|
|
4087
|
+
if (analysis.codePatterns.components.length > 0) {
|
|
4088
|
+
md += `### Components
|
|
4089
|
+
|
|
4090
|
+
`;
|
|
4091
|
+
analysis.codePatterns.components.forEach((comp) => {
|
|
4092
|
+
md += `**${comp.type}** (${comp.count})
|
|
4093
|
+
`;
|
|
4094
|
+
md += `- Conventions: ${comp.conventions.join(", ")}
|
|
4095
|
+
`;
|
|
4096
|
+
md += `
|
|
4097
|
+
`;
|
|
4098
|
+
});
|
|
4099
|
+
}
|
|
4100
|
+
if (analysis.codePatterns.tests) {
|
|
4101
|
+
md += `### Testing
|
|
4102
|
+
|
|
4103
|
+
`;
|
|
4104
|
+
md += `- Framework: ${analysis.codePatterns.tests.framework}
|
|
4105
|
+
`;
|
|
4106
|
+
md += `- Test Count: ${analysis.codePatterns.tests.count}
|
|
4107
|
+
`;
|
|
4108
|
+
md += `- Types: ${analysis.codePatterns.tests.types.join(", ")}
|
|
4109
|
+
`;
|
|
4110
|
+
if (analysis.codePatterns.tests.coverage) {
|
|
4111
|
+
md += `- Coverage: ${analysis.codePatterns.tests.coverage}%
|
|
4112
|
+
`;
|
|
4113
|
+
}
|
|
4114
|
+
md += `
|
|
4115
|
+
`;
|
|
4116
|
+
}
|
|
4117
|
+
}
|
|
4118
|
+
if (analysis.health) {
|
|
4119
|
+
md += `## Code Health
|
|
4120
|
+
|
|
4121
|
+
`;
|
|
4122
|
+
md += `| Check | Status |
|
|
4123
|
+
`;
|
|
4124
|
+
md += `|-------|--------|
|
|
4125
|
+
`;
|
|
4126
|
+
md += `| TypeScript | ${analysis.health.typecheck ? "\u2705 Passing" : "\u274C Failing"} |
|
|
4127
|
+
`;
|
|
4128
|
+
md += `| Linting | ${analysis.health.lint ? "\u2705 Passing" : "\u274C Failing"} |
|
|
4129
|
+
`;
|
|
4130
|
+
md += `| Tests | ${analysis.health.tests ? "\u2705 Passing" : "\u274C Failing"} |
|
|
4131
|
+
`;
|
|
4132
|
+
md += `| Build | ${analysis.health.build ? "\u2705 Passing" : "\u274C Failing"} |
|
|
4133
|
+
`;
|
|
4134
|
+
if (analysis.health.coverage) {
|
|
4135
|
+
md += `| Coverage | ${analysis.health.coverage}% |
|
|
4136
|
+
`;
|
|
4137
|
+
}
|
|
4138
|
+
md += `
|
|
4139
|
+
`;
|
|
4140
|
+
}
|
|
4141
|
+
return md;
|
|
4142
|
+
}
|
|
4143
|
+
function generateStrategicRoadmap(analysis, questions) {
|
|
4144
|
+
let md = `# Strategic Roadmap
|
|
4145
|
+
|
|
4146
|
+
`;
|
|
4147
|
+
md += `> **Generated:** ${new Date(analysis.timestamp).toLocaleDateString()}
|
|
4148
|
+
|
|
4149
|
+
`;
|
|
4150
|
+
md += `---
|
|
4151
|
+
|
|
4152
|
+
`;
|
|
4153
|
+
md += `## Overview
|
|
4154
|
+
|
|
4155
|
+
`;
|
|
4156
|
+
md += `This roadmap outlines strategic recommendations based on the technical analysis of ${analysis.project.name}. `;
|
|
4157
|
+
md += `Recommendations are prioritized by business impact and feasibility.
|
|
4158
|
+
|
|
4159
|
+
`;
|
|
4160
|
+
md += `## Risk Management
|
|
4161
|
+
|
|
4162
|
+
`;
|
|
4163
|
+
if (analysis.risks.critical.length > 0) {
|
|
4164
|
+
md += `### \u{1F534} Critical Priorities
|
|
4165
|
+
|
|
4166
|
+
`;
|
|
4167
|
+
analysis.risks.critical.forEach((risk, i) => {
|
|
4168
|
+
md += `${i + 1}. ${risk}
|
|
4169
|
+
`;
|
|
4170
|
+
});
|
|
4171
|
+
md += `
|
|
4172
|
+
`;
|
|
4173
|
+
}
|
|
4174
|
+
if (analysis.risks.high.length > 0) {
|
|
4175
|
+
md += `### \u{1F7E1} High Priority
|
|
4176
|
+
|
|
4177
|
+
`;
|
|
4178
|
+
analysis.risks.high.forEach((risk, i) => {
|
|
4179
|
+
md += `${i + 1}. ${risk}
|
|
4180
|
+
`;
|
|
4181
|
+
});
|
|
4182
|
+
md += `
|
|
4183
|
+
`;
|
|
4184
|
+
}
|
|
4185
|
+
md += `## Growth Opportunities
|
|
4186
|
+
|
|
4187
|
+
`;
|
|
4188
|
+
if (analysis.opportunities.immediate.length > 0) {
|
|
4189
|
+
md += `### \u26A1 Immediate (0-30 days)
|
|
4190
|
+
|
|
4191
|
+
`;
|
|
4192
|
+
analysis.opportunities.immediate.forEach((opp, i) => {
|
|
4193
|
+
md += `${i + 1}. ${opp}
|
|
4194
|
+
`;
|
|
4195
|
+
});
|
|
4196
|
+
md += `
|
|
4197
|
+
`;
|
|
4198
|
+
}
|
|
4199
|
+
if (analysis.opportunities.shortTerm.length > 0) {
|
|
4200
|
+
md += `### \u{1F4C5} Short-term (1-3 months)
|
|
4201
|
+
|
|
4202
|
+
`;
|
|
4203
|
+
analysis.opportunities.shortTerm.forEach((opp, i) => {
|
|
4204
|
+
md += `${i + 1}. ${opp}
|
|
4205
|
+
`;
|
|
4206
|
+
});
|
|
4207
|
+
md += `
|
|
4208
|
+
`;
|
|
4209
|
+
}
|
|
4210
|
+
if (analysis.opportunities.longTerm.length > 0) {
|
|
4211
|
+
md += `### \u{1F3AF} Long-term (3-12 months)
|
|
4212
|
+
|
|
4213
|
+
`;
|
|
4214
|
+
analysis.opportunities.longTerm.forEach((opp, i) => {
|
|
4215
|
+
md += `${i + 1}. ${opp}
|
|
4216
|
+
`;
|
|
4217
|
+
});
|
|
4218
|
+
md += `
|
|
4219
|
+
`;
|
|
4220
|
+
}
|
|
4221
|
+
md += `## Detailed Recommendations
|
|
4222
|
+
|
|
4223
|
+
`;
|
|
4224
|
+
const categoryMap = /* @__PURE__ */ new Map();
|
|
4225
|
+
questions.questions.forEach((q) => {
|
|
4226
|
+
if (!categoryMap.has(q.category)) {
|
|
4227
|
+
categoryMap.set(q.category, []);
|
|
4228
|
+
}
|
|
4229
|
+
categoryMap.get(q.category).push(q);
|
|
4230
|
+
});
|
|
4231
|
+
for (const [category, qs] of categoryMap) {
|
|
4232
|
+
md += `### ${category}
|
|
4233
|
+
|
|
4234
|
+
`;
|
|
4235
|
+
qs.forEach((q) => {
|
|
4236
|
+
md += `#### ${q.question}
|
|
4237
|
+
|
|
4238
|
+
`;
|
|
4239
|
+
if (q.findings.length > 0) {
|
|
4240
|
+
md += `**Current State:**
|
|
4241
|
+
`;
|
|
4242
|
+
q.findings.forEach((f) => {
|
|
4243
|
+
md += `- ${f}
|
|
4244
|
+
`;
|
|
4245
|
+
});
|
|
4246
|
+
md += `
|
|
4247
|
+
`;
|
|
4248
|
+
}
|
|
4249
|
+
if (q.recommendations.length > 0) {
|
|
4250
|
+
md += `**Recommendations:**
|
|
4251
|
+
`;
|
|
4252
|
+
q.recommendations.forEach((r) => {
|
|
4253
|
+
md += `- ${r}
|
|
4254
|
+
`;
|
|
4255
|
+
});
|
|
4256
|
+
md += `
|
|
4257
|
+
`;
|
|
4258
|
+
}
|
|
4259
|
+
md += `**Business Impact:** ${q.businessImpact}
|
|
4260
|
+
|
|
4261
|
+
`;
|
|
4262
|
+
md += `**Priority:** ${q.priority.toUpperCase()}
|
|
4263
|
+
|
|
4264
|
+
`;
|
|
4265
|
+
md += `---
|
|
4266
|
+
|
|
4267
|
+
`;
|
|
4268
|
+
});
|
|
4269
|
+
}
|
|
4270
|
+
return md;
|
|
4271
|
+
}
|
|
4272
|
+
function generateBoardQuestions(questions) {
|
|
4273
|
+
let md = `# Advisory Board Questions
|
|
4274
|
+
|
|
4275
|
+
`;
|
|
4276
|
+
md += `> **Total Questions:** ${questions.summary.totalQuestions}
|
|
4277
|
+
`;
|
|
4278
|
+
md += `> **High Priority:** ${questions.summary.highPriority} | `;
|
|
4279
|
+
md += `**Medium:** ${questions.summary.mediumPriority} | `;
|
|
4280
|
+
md += `**Low:** ${questions.summary.lowPriority}
|
|
4281
|
+
|
|
4282
|
+
`;
|
|
4283
|
+
md += `---
|
|
4284
|
+
|
|
4285
|
+
`;
|
|
4286
|
+
md += `## Table of Contents
|
|
4287
|
+
|
|
4288
|
+
`;
|
|
4289
|
+
questions.summary.categories.forEach((cat) => {
|
|
4290
|
+
const slug = cat.toLowerCase().replace(/\s+/g, "-");
|
|
4291
|
+
md += `- [${cat}](#${slug})
|
|
4292
|
+
`;
|
|
4293
|
+
});
|
|
4294
|
+
md += `
|
|
4295
|
+
---
|
|
4296
|
+
|
|
4297
|
+
`;
|
|
4298
|
+
const categoryMap = /* @__PURE__ */ new Map();
|
|
4299
|
+
questions.questions.forEach((q) => {
|
|
4300
|
+
if (!categoryMap.has(q.category)) {
|
|
4301
|
+
categoryMap.set(q.category, []);
|
|
4302
|
+
}
|
|
4303
|
+
categoryMap.get(q.category).push(q);
|
|
4304
|
+
});
|
|
4305
|
+
for (const [category, qs] of categoryMap) {
|
|
4306
|
+
const slug = category.toLowerCase().replace(/\s+/g, "-");
|
|
4307
|
+
md += `## ${category}
|
|
4308
|
+
|
|
4309
|
+
`;
|
|
4310
|
+
md += `<a id="${slug}"></a>
|
|
4311
|
+
|
|
4312
|
+
`;
|
|
4313
|
+
qs.forEach((q, i) => {
|
|
4314
|
+
const priorityEmoji = q.priority === "high" ? "\u{1F534}" : q.priority === "medium" ? "\u{1F7E1}" : "\u{1F7E2}";
|
|
4315
|
+
md += `### ${i + 1}. ${priorityEmoji} ${q.question}
|
|
4316
|
+
|
|
4317
|
+
`;
|
|
4318
|
+
md += `**Context:** ${q.context}
|
|
4319
|
+
|
|
4320
|
+
`;
|
|
4321
|
+
if (q.findings.length > 0) {
|
|
4322
|
+
md += `**Findings:**
|
|
4323
|
+
`;
|
|
4324
|
+
q.findings.forEach((f) => {
|
|
4325
|
+
md += `- ${f}
|
|
4326
|
+
`;
|
|
4327
|
+
});
|
|
4328
|
+
md += `
|
|
4329
|
+
`;
|
|
4330
|
+
}
|
|
4331
|
+
if (q.recommendations.length > 0) {
|
|
4332
|
+
md += `**Recommendations:**
|
|
4333
|
+
`;
|
|
4334
|
+
q.recommendations.forEach((r) => {
|
|
4335
|
+
md += `- ${r}
|
|
4336
|
+
`;
|
|
4337
|
+
});
|
|
4338
|
+
md += `
|
|
4339
|
+
`;
|
|
4340
|
+
}
|
|
4341
|
+
md += `**Business Impact:** ${q.businessImpact}
|
|
4342
|
+
|
|
4343
|
+
`;
|
|
4344
|
+
md += `---
|
|
4345
|
+
|
|
4346
|
+
`;
|
|
4347
|
+
});
|
|
4348
|
+
}
|
|
4349
|
+
return md;
|
|
4350
|
+
}
|
|
4351
|
+
function getRiskLabel(score) {
|
|
4352
|
+
if (score > 0.7) return "\u{1F534} High Risk";
|
|
4353
|
+
if (score > 0.4) return "\u{1F7E1} Moderate Risk";
|
|
4354
|
+
return "\u{1F7E2} Low Risk";
|
|
4355
|
+
}
|
|
4356
|
+
function getOpportunityLabel(score) {
|
|
4357
|
+
if (score > 0.7) return "\u{1F31F} High Potential";
|
|
4358
|
+
if (score > 0.5) return "\u{1F4C8} Moderate Potential";
|
|
4359
|
+
return "\u{1F4A1} Identified";
|
|
4360
|
+
}
|
|
4361
|
+
function checkCIThresholds(analysis, config) {
|
|
4362
|
+
const thresholds = config?.riskThresholds || {
|
|
4363
|
+
high: 0.7,
|
|
4364
|
+
medium: 0.4
|
|
4365
|
+
};
|
|
4366
|
+
const riskScore = analysis.risks.overall;
|
|
4367
|
+
if (riskScore >= thresholds.high) {
|
|
4368
|
+
console.log(
|
|
4369
|
+
chalk11.red(
|
|
4370
|
+
`High risk threshold exceeded: ${(riskScore * 100).toFixed(0)}%`
|
|
4371
|
+
)
|
|
4372
|
+
);
|
|
4373
|
+
return 1;
|
|
4374
|
+
}
|
|
4375
|
+
if (analysis.packages.security && analysis.packages.security.length > 0) {
|
|
4376
|
+
const criticalCount = analysis.packages.security.filter(
|
|
4377
|
+
(s) => s.severity === "critical"
|
|
4378
|
+
).length;
|
|
4379
|
+
if (criticalCount > 0) {
|
|
4380
|
+
console.log(
|
|
4381
|
+
chalk11.red(`Critical security vulnerabilities found: ${criticalCount}`)
|
|
4382
|
+
);
|
|
4383
|
+
return 1;
|
|
4384
|
+
}
|
|
4385
|
+
}
|
|
4386
|
+
return 0;
|
|
4387
|
+
}
|
|
4388
|
+
|
|
4389
|
+
// src/cli/commands/learn.ts
|
|
4390
|
+
import chalk12 from "chalk";
|
|
4391
|
+
import * as p8 from "@clack/prompts";
|
|
4392
|
+
import {
|
|
4393
|
+
PatternStore as PatternStore2,
|
|
4394
|
+
ContributorManager as ContributorManager2,
|
|
4395
|
+
PatternAnonymizer,
|
|
4396
|
+
TelemetryCollector as TelemetryCollector2
|
|
4397
|
+
} from "@hawkinside_out/workflow-improvement-tracker";
|
|
4398
|
+
function getWorkspacePath() {
|
|
4399
|
+
return process.cwd();
|
|
4400
|
+
}
|
|
4401
|
+
function formatDate(isoString) {
|
|
4402
|
+
return new Date(isoString).toLocaleDateString("en-US", {
|
|
4403
|
+
year: "numeric",
|
|
4404
|
+
month: "short",
|
|
4405
|
+
day: "numeric"
|
|
4406
|
+
});
|
|
4407
|
+
}
|
|
4408
|
+
function formatTags(tags) {
|
|
4409
|
+
return tags.map((t) => `${t.category}:${t.name}`).join(", ");
|
|
4410
|
+
}
|
|
4411
|
+
async function learnRecordCommand(options) {
|
|
4412
|
+
const cwd = getWorkspacePath();
|
|
4413
|
+
const store = new PatternStore2(cwd);
|
|
4414
|
+
console.log(chalk12.cyan("\n\u{1F4DA} Record a Learning Pattern\n"));
|
|
4415
|
+
let patternType = options.type;
|
|
4416
|
+
if (!patternType) {
|
|
4417
|
+
const typeChoice = await p8.select({
|
|
4418
|
+
message: "What type of pattern are you recording?",
|
|
4419
|
+
options: [
|
|
4420
|
+
{
|
|
4421
|
+
value: "fix",
|
|
4422
|
+
label: "\u{1F527} Fix Pattern - A specific solution to a problem"
|
|
4423
|
+
},
|
|
4424
|
+
{
|
|
4425
|
+
value: "blueprint",
|
|
4426
|
+
label: "\u{1F4D0} Blueprint - A project structure template"
|
|
4427
|
+
}
|
|
4428
|
+
]
|
|
4429
|
+
});
|
|
4430
|
+
if (p8.isCancel(typeChoice)) {
|
|
4431
|
+
p8.cancel("Recording cancelled");
|
|
4432
|
+
process.exit(0);
|
|
4433
|
+
}
|
|
4434
|
+
patternType = typeChoice;
|
|
4435
|
+
}
|
|
4436
|
+
let name = options.name;
|
|
4437
|
+
if (!name) {
|
|
4438
|
+
const nameInput = await p8.text({
|
|
4439
|
+
message: "Pattern name:",
|
|
4440
|
+
placeholder: "e.g., Next.js App Router Migration",
|
|
4441
|
+
validate: (value) => {
|
|
4442
|
+
if (!value || value.length < 3)
|
|
4443
|
+
return "Name must be at least 3 characters";
|
|
4444
|
+
if (value.length > 100) return "Name must be less than 100 characters";
|
|
4445
|
+
return void 0;
|
|
4446
|
+
}
|
|
4447
|
+
});
|
|
4448
|
+
if (p8.isCancel(nameInput)) {
|
|
4449
|
+
p8.cancel("Recording cancelled");
|
|
4450
|
+
process.exit(0);
|
|
4451
|
+
}
|
|
4452
|
+
name = nameInput;
|
|
4453
|
+
}
|
|
4454
|
+
let description = options.description;
|
|
4455
|
+
if (!description) {
|
|
4456
|
+
const descInput = await p8.text({
|
|
4457
|
+
message: "Description:",
|
|
4458
|
+
placeholder: "What does this pattern solve?",
|
|
4459
|
+
validate: (value) => {
|
|
4460
|
+
if (!value || value.length < 10)
|
|
4461
|
+
return "Description must be at least 10 characters";
|
|
4462
|
+
if (value.length > 500)
|
|
4463
|
+
return "Description must be less than 500 characters";
|
|
4464
|
+
return void 0;
|
|
4465
|
+
}
|
|
4466
|
+
});
|
|
4467
|
+
if (p8.isCancel(descInput)) {
|
|
4468
|
+
p8.cancel("Recording cancelled");
|
|
4469
|
+
process.exit(0);
|
|
4470
|
+
}
|
|
4471
|
+
description = descInput;
|
|
4472
|
+
}
|
|
4473
|
+
let framework = options.framework;
|
|
4474
|
+
if (!framework) {
|
|
4475
|
+
const fwInput = await p8.text({
|
|
4476
|
+
message: "Framework:",
|
|
4477
|
+
placeholder: "e.g., next, react, vue, express"
|
|
4478
|
+
});
|
|
4479
|
+
if (p8.isCancel(fwInput)) {
|
|
4480
|
+
p8.cancel("Recording cancelled");
|
|
4481
|
+
process.exit(0);
|
|
4482
|
+
}
|
|
4483
|
+
framework = fwInput;
|
|
4484
|
+
}
|
|
4485
|
+
let version = options.version;
|
|
4486
|
+
if (!version) {
|
|
4487
|
+
const versionInput = await p8.text({
|
|
4488
|
+
message: "Framework version (semver range):",
|
|
4489
|
+
placeholder: "e.g., >=14.0.0, ^18.0.0",
|
|
4490
|
+
initialValue: ">=1.0.0"
|
|
4491
|
+
});
|
|
4492
|
+
if (p8.isCancel(versionInput)) {
|
|
4493
|
+
p8.cancel("Recording cancelled");
|
|
4494
|
+
process.exit(0);
|
|
4495
|
+
}
|
|
4496
|
+
version = versionInput;
|
|
4497
|
+
}
|
|
4498
|
+
let category = options.category;
|
|
4499
|
+
if (patternType === "fix" && !category) {
|
|
4500
|
+
const catChoice = await p8.select({
|
|
4501
|
+
message: "Category:",
|
|
4502
|
+
options: [
|
|
4503
|
+
{ value: "migration", label: "\u{1F504} Migration" },
|
|
4504
|
+
{ value: "security", label: "\u{1F512} Security" },
|
|
4505
|
+
{ value: "performance", label: "\u26A1 Performance" },
|
|
4506
|
+
{ value: "compatibility", label: "\u{1F517} Compatibility" },
|
|
4507
|
+
{ value: "deprecation", label: "\u26A0\uFE0F Deprecation" },
|
|
4508
|
+
{ value: "configuration", label: "\u2699\uFE0F Configuration" },
|
|
4509
|
+
{ value: "best-practice", label: "\u2728 Best Practice" },
|
|
4510
|
+
{ value: "error-handling", label: "\u{1F6A8} Error Handling" },
|
|
4511
|
+
{ value: "testing", label: "\u{1F9EA} Testing" },
|
|
4512
|
+
{ value: "other", label: "\u{1F4E6} Other" }
|
|
4513
|
+
]
|
|
4514
|
+
});
|
|
4515
|
+
if (p8.isCancel(catChoice)) {
|
|
4516
|
+
p8.cancel("Recording cancelled");
|
|
4517
|
+
process.exit(0);
|
|
4518
|
+
}
|
|
4519
|
+
category = catChoice;
|
|
4520
|
+
}
|
|
4521
|
+
const tags = [];
|
|
4522
|
+
if (options.tags) {
|
|
4523
|
+
const tagPairs = options.tags.split(",").map((t) => t.trim());
|
|
4524
|
+
for (const pair of tagPairs) {
|
|
4525
|
+
const [cat, val] = pair.split(":");
|
|
4526
|
+
if (cat && val) {
|
|
4527
|
+
tags.push({
|
|
4528
|
+
category: cat,
|
|
4529
|
+
name: val
|
|
4530
|
+
});
|
|
4531
|
+
}
|
|
4532
|
+
}
|
|
4533
|
+
}
|
|
4534
|
+
tags.push({ category: "framework", name: framework });
|
|
4535
|
+
if (patternType === "fix") {
|
|
4536
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4537
|
+
const fixPattern = {
|
|
4538
|
+
id: crypto.randomUUID(),
|
|
4539
|
+
name,
|
|
4540
|
+
description,
|
|
4541
|
+
category,
|
|
4542
|
+
tags,
|
|
4543
|
+
trigger: {
|
|
4544
|
+
errorPattern: ".*",
|
|
4545
|
+
errorMessage: "Generic error pattern",
|
|
4546
|
+
filePattern: "**/*"
|
|
4547
|
+
},
|
|
4548
|
+
solution: {
|
|
4549
|
+
type: "command",
|
|
4550
|
+
steps: [
|
|
4551
|
+
{
|
|
4552
|
+
order: 1,
|
|
4553
|
+
action: "run",
|
|
4554
|
+
target: "npm run fix",
|
|
4555
|
+
description: "Follow the pattern instructions"
|
|
4556
|
+
}
|
|
4557
|
+
]
|
|
4558
|
+
},
|
|
4559
|
+
compatibility: {
|
|
4560
|
+
framework,
|
|
4561
|
+
frameworkVersion: version,
|
|
4562
|
+
runtime: "node",
|
|
4563
|
+
runtimeVersion: ">=18.0.0",
|
|
4564
|
+
dependencies: []
|
|
4565
|
+
},
|
|
4566
|
+
metrics: {
|
|
4567
|
+
applications: 0,
|
|
4568
|
+
successes: 0,
|
|
4569
|
+
failures: 0,
|
|
4570
|
+
successRate: 0
|
|
4571
|
+
},
|
|
4572
|
+
source: "manual",
|
|
4573
|
+
isPrivate: true,
|
|
4574
|
+
createdAt: now,
|
|
4575
|
+
updatedAt: now
|
|
4576
|
+
};
|
|
4577
|
+
const result = await store.saveFixPattern(fixPattern);
|
|
4578
|
+
if (result.success) {
|
|
4579
|
+
console.log(chalk12.green("\n\u2705 Fix pattern recorded successfully!\n"));
|
|
4580
|
+
console.log(chalk12.dim(` ID: ${fixPattern.id}`));
|
|
4581
|
+
console.log(chalk12.dim(` Name: ${name}`));
|
|
4582
|
+
console.log(chalk12.dim(` Category: ${category}`));
|
|
4583
|
+
console.log(chalk12.dim(` Framework: ${framework} ${version}`));
|
|
4584
|
+
} else {
|
|
4585
|
+
console.log(chalk12.red("\n\u274C Failed to record pattern"));
|
|
4586
|
+
console.log(chalk12.dim(` Error: ${result.error}`));
|
|
4587
|
+
process.exit(1);
|
|
4588
|
+
}
|
|
4589
|
+
} else {
|
|
1925
4590
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1926
4591
|
const blueprint = {
|
|
1927
4592
|
id: crypto.randomUUID(),
|
|
@@ -1965,13 +4630,13 @@ async function learnRecordCommand(options) {
|
|
|
1965
4630
|
};
|
|
1966
4631
|
const result = await store.saveBlueprint(blueprint);
|
|
1967
4632
|
if (result.success) {
|
|
1968
|
-
console.log(
|
|
1969
|
-
console.log(
|
|
1970
|
-
console.log(
|
|
1971
|
-
console.log(
|
|
4633
|
+
console.log(chalk12.green("\n\u2705 Blueprint recorded successfully!\n"));
|
|
4634
|
+
console.log(chalk12.dim(` ID: ${blueprint.id}`));
|
|
4635
|
+
console.log(chalk12.dim(` Name: ${name}`));
|
|
4636
|
+
console.log(chalk12.dim(` Framework: ${framework} ${version}`));
|
|
1972
4637
|
} else {
|
|
1973
|
-
console.log(
|
|
1974
|
-
console.log(
|
|
4638
|
+
console.log(chalk12.red("\n\u274C Failed to record blueprint"));
|
|
4639
|
+
console.log(chalk12.dim(` Error: ${result.error}`));
|
|
1975
4640
|
process.exit(1);
|
|
1976
4641
|
}
|
|
1977
4642
|
}
|
|
@@ -1981,7 +4646,7 @@ async function learnListCommand(options) {
|
|
|
1981
4646
|
const store = new PatternStore2(cwd);
|
|
1982
4647
|
const patternType = options.type ?? "all";
|
|
1983
4648
|
const showDeprecated = options.deprecated ?? false;
|
|
1984
|
-
console.log(
|
|
4649
|
+
console.log(chalk12.cyan("\n\u{1F4DA} Recorded Learning Patterns\n"));
|
|
1985
4650
|
if (patternType === "all" || patternType === "fix") {
|
|
1986
4651
|
const fixResult = await store.listFixPatterns({
|
|
1987
4652
|
tags: options.tag ? [{ category: "framework", name: options.tag }] : void 0,
|
|
@@ -1989,27 +4654,29 @@ async function learnListCommand(options) {
|
|
|
1989
4654
|
includeDeprecated: showDeprecated
|
|
1990
4655
|
});
|
|
1991
4656
|
if (fixResult.success && fixResult.data && fixResult.data.length > 0) {
|
|
1992
|
-
console.log(
|
|
4657
|
+
console.log(chalk12.bold.yellow("\u{1F527} Fix Patterns:\n"));
|
|
1993
4658
|
for (const pattern of fixResult.data) {
|
|
1994
4659
|
const isDeprecated = pattern.deprecatedAt !== void 0;
|
|
1995
4660
|
const statusIcon = isDeprecated ? "\u26A0\uFE0F" : "\u2713";
|
|
1996
|
-
const nameColor = isDeprecated ?
|
|
4661
|
+
const nameColor = isDeprecated ? chalk12.dim : chalk12.white;
|
|
1997
4662
|
console.log(` ${statusIcon} ${nameColor(pattern.name)}`);
|
|
1998
|
-
console.log(
|
|
1999
|
-
console.log(
|
|
2000
|
-
console.log(
|
|
4663
|
+
console.log(chalk12.dim(` ID: ${pattern.id}`));
|
|
4664
|
+
console.log(chalk12.dim(` Category: ${pattern.category}`));
|
|
4665
|
+
console.log(
|
|
4666
|
+
chalk12.dim(` Created: ${formatDate(pattern.createdAt)}`)
|
|
4667
|
+
);
|
|
2001
4668
|
console.log(
|
|
2002
|
-
|
|
4669
|
+
chalk12.dim(
|
|
2003
4670
|
` Success Rate: ${(pattern.metrics.successRate * 100).toFixed(0)}% (${pattern.metrics.successes}/${pattern.metrics.applications})`
|
|
2004
4671
|
)
|
|
2005
4672
|
);
|
|
2006
4673
|
if (pattern.tags.length > 0) {
|
|
2007
|
-
console.log(
|
|
4674
|
+
console.log(chalk12.dim(` Tags: ${formatTags(pattern.tags)}`));
|
|
2008
4675
|
}
|
|
2009
4676
|
console.log("");
|
|
2010
4677
|
}
|
|
2011
4678
|
} else if (patternType === "fix") {
|
|
2012
|
-
console.log(
|
|
4679
|
+
console.log(chalk12.dim(" No fix patterns found.\n"));
|
|
2013
4680
|
}
|
|
2014
4681
|
}
|
|
2015
4682
|
if (patternType === "all" || patternType === "blueprint") {
|
|
@@ -2019,44 +4686,46 @@ async function learnListCommand(options) {
|
|
|
2019
4686
|
includeDeprecated: showDeprecated
|
|
2020
4687
|
});
|
|
2021
4688
|
if (bpResult.success && bpResult.data && bpResult.data.length > 0) {
|
|
2022
|
-
console.log(
|
|
4689
|
+
console.log(chalk12.bold.blue("\u{1F4D0} Blueprints:\n"));
|
|
2023
4690
|
for (const blueprint of bpResult.data) {
|
|
2024
4691
|
const isDeprecated = blueprint.deprecatedAt !== void 0;
|
|
2025
4692
|
const statusIcon = isDeprecated ? "\u26A0\uFE0F" : "\u2713";
|
|
2026
|
-
const nameColor = isDeprecated ?
|
|
4693
|
+
const nameColor = isDeprecated ? chalk12.dim : chalk12.white;
|
|
2027
4694
|
console.log(` ${statusIcon} ${nameColor(blueprint.name)}`);
|
|
2028
|
-
console.log(
|
|
2029
|
-
console.log(
|
|
2030
|
-
console.log(chalk11.dim(` Created: ${formatDate(blueprint.createdAt)}`));
|
|
4695
|
+
console.log(chalk12.dim(` ID: ${blueprint.id}`));
|
|
4696
|
+
console.log(chalk12.dim(` Language: ${blueprint.stack.language}`));
|
|
2031
4697
|
console.log(
|
|
2032
|
-
|
|
4698
|
+
chalk12.dim(` Created: ${formatDate(blueprint.createdAt)}`)
|
|
4699
|
+
);
|
|
4700
|
+
console.log(
|
|
4701
|
+
chalk12.dim(
|
|
2033
4702
|
` Success Rate: ${(blueprint.metrics.successRate * 100).toFixed(0)}% (${blueprint.metrics.successes}/${blueprint.metrics.applications})`
|
|
2034
4703
|
)
|
|
2035
4704
|
);
|
|
2036
4705
|
if (blueprint.tags.length > 0) {
|
|
2037
|
-
console.log(
|
|
4706
|
+
console.log(chalk12.dim(` Tags: ${formatTags(blueprint.tags)}`));
|
|
2038
4707
|
}
|
|
2039
4708
|
console.log("");
|
|
2040
4709
|
}
|
|
2041
4710
|
} else if (patternType === "blueprint") {
|
|
2042
|
-
console.log(
|
|
4711
|
+
console.log(chalk12.dim(" No blueprints found.\n"));
|
|
2043
4712
|
}
|
|
2044
4713
|
}
|
|
2045
4714
|
const stats = await store.getStats();
|
|
2046
4715
|
const totalPatterns = stats.totalFixes + stats.totalBlueprints;
|
|
2047
4716
|
const totalDeprecated = stats.deprecatedFixes + stats.deprecatedBlueprints;
|
|
2048
|
-
console.log(
|
|
2049
|
-
console.log(
|
|
2050
|
-
console.log(
|
|
2051
|
-
console.log(
|
|
2052
|
-
console.log(
|
|
4717
|
+
console.log(chalk12.dim("\u2501".repeat(40)));
|
|
4718
|
+
console.log(chalk12.dim(`Total: ${totalPatterns} patterns`));
|
|
4719
|
+
console.log(chalk12.dim(` Fix Patterns: ${stats.totalFixes}`));
|
|
4720
|
+
console.log(chalk12.dim(` Blueprints: ${stats.totalBlueprints}`));
|
|
4721
|
+
console.log(chalk12.dim(` Deprecated: ${totalDeprecated}`));
|
|
2053
4722
|
console.log("");
|
|
2054
4723
|
}
|
|
2055
4724
|
async function learnApplyCommand(patternId, options) {
|
|
2056
4725
|
const cwd = getWorkspacePath();
|
|
2057
4726
|
const store = new PatternStore2(cwd);
|
|
2058
4727
|
const telemetry = new TelemetryCollector2(cwd);
|
|
2059
|
-
console.log(
|
|
4728
|
+
console.log(chalk12.cyan("\n\u{1F527} Apply Learning Pattern\n"));
|
|
2060
4729
|
let pattern = await store.getFixPattern(patternId);
|
|
2061
4730
|
let patternType = "fix";
|
|
2062
4731
|
if (!pattern.success || !pattern.data) {
|
|
@@ -2065,88 +4734,110 @@ async function learnApplyCommand(patternId, options) {
|
|
|
2065
4734
|
pattern = bpResult;
|
|
2066
4735
|
patternType = "blueprint";
|
|
2067
4736
|
} else {
|
|
2068
|
-
console.log(
|
|
4737
|
+
console.log(chalk12.red(`
|
|
2069
4738
|
\u274C Pattern not found: ${patternId}`));
|
|
2070
|
-
console.log(
|
|
4739
|
+
console.log(
|
|
4740
|
+
chalk12.dim(" Use 'workflow learn:list' to see available patterns")
|
|
4741
|
+
);
|
|
2071
4742
|
process.exit(1);
|
|
2072
4743
|
}
|
|
2073
4744
|
}
|
|
2074
4745
|
const patternData = pattern.data;
|
|
2075
|
-
console.log(
|
|
2076
|
-
console.log(
|
|
2077
|
-
console.log(
|
|
4746
|
+
console.log(chalk12.white(` Pattern: ${patternData.name}`));
|
|
4747
|
+
console.log(chalk12.dim(` Type: ${patternType}`));
|
|
4748
|
+
console.log(chalk12.dim(` Description: ${patternData.description}`));
|
|
2078
4749
|
if (options.dryRun) {
|
|
2079
|
-
console.log(
|
|
4750
|
+
console.log(
|
|
4751
|
+
chalk12.yellow("\n\u{1F4CB} DRY-RUN MODE: No changes will be applied\n")
|
|
4752
|
+
);
|
|
2080
4753
|
}
|
|
2081
4754
|
const framework = options.framework ?? patternData.compatibility.frameworks[0]?.name ?? "unknown";
|
|
2082
4755
|
const version = options.version ?? patternData.compatibility.frameworks[0]?.version ?? "0.0.0";
|
|
2083
4756
|
await telemetry.recordApplication(patternId, patternType, framework, version);
|
|
2084
4757
|
if (patternType === "fix") {
|
|
2085
4758
|
const fixPattern = patternData;
|
|
2086
|
-
console.log(
|
|
4759
|
+
console.log(chalk12.cyan("\n\u{1F4CB} Solution Steps:\n"));
|
|
2087
4760
|
if (fixPattern.solution.steps) {
|
|
2088
4761
|
for (let i = 0; i < fixPattern.solution.steps.length; i++) {
|
|
2089
4762
|
const step = fixPattern.solution.steps[i];
|
|
2090
|
-
console.log(
|
|
4763
|
+
console.log(
|
|
4764
|
+
chalk12.white(` ${i + 1}. [${step.action}] ${step.description}`)
|
|
4765
|
+
);
|
|
2091
4766
|
if (step.file) {
|
|
2092
|
-
console.log(
|
|
4767
|
+
console.log(chalk12.dim(` File: ${step.file}`));
|
|
2093
4768
|
}
|
|
2094
4769
|
}
|
|
2095
4770
|
}
|
|
2096
4771
|
} else {
|
|
2097
4772
|
const blueprint = patternData;
|
|
2098
|
-
console.log(
|
|
4773
|
+
console.log(chalk12.cyan("\n\u{1F4CB} Setup Steps:\n"));
|
|
2099
4774
|
if (blueprint.setup.steps) {
|
|
2100
4775
|
for (let i = 0; i < blueprint.setup.steps.length; i++) {
|
|
2101
4776
|
const step = blueprint.setup.steps[i];
|
|
2102
|
-
console.log(
|
|
4777
|
+
console.log(chalk12.white(` ${i + 1}. ${step.description}`));
|
|
2103
4778
|
if (step.command) {
|
|
2104
|
-
console.log(
|
|
4779
|
+
console.log(chalk12.dim(` Command: ${step.command}`));
|
|
2105
4780
|
}
|
|
2106
4781
|
}
|
|
2107
4782
|
}
|
|
2108
4783
|
}
|
|
2109
4784
|
if (!options.dryRun) {
|
|
2110
|
-
const confirmed = await
|
|
4785
|
+
const confirmed = await p8.confirm({
|
|
2111
4786
|
message: "Mark this pattern as successfully applied?",
|
|
2112
4787
|
initialValue: true
|
|
2113
4788
|
});
|
|
2114
|
-
if (
|
|
2115
|
-
|
|
4789
|
+
if (p8.isCancel(confirmed)) {
|
|
4790
|
+
p8.cancel("Application cancelled");
|
|
2116
4791
|
process.exit(0);
|
|
2117
4792
|
}
|
|
2118
4793
|
if (confirmed) {
|
|
2119
4794
|
await store.updatePatternMetrics(patternId, patternType, true);
|
|
2120
4795
|
await telemetry.recordSuccess(patternId, patternType, framework, version);
|
|
2121
|
-
console.log(
|
|
4796
|
+
console.log(chalk12.green("\n\u2705 Pattern marked as successfully applied!"));
|
|
2122
4797
|
} else {
|
|
2123
4798
|
await store.updatePatternMetrics(patternId, patternType, false);
|
|
2124
|
-
await telemetry.recordFailure(
|
|
2125
|
-
|
|
4799
|
+
await telemetry.recordFailure(
|
|
4800
|
+
patternId,
|
|
4801
|
+
patternType,
|
|
4802
|
+
framework,
|
|
4803
|
+
version,
|
|
4804
|
+
"unknown"
|
|
4805
|
+
);
|
|
4806
|
+
console.log(
|
|
4807
|
+
chalk12.yellow("\n\u26A0\uFE0F Pattern application marked as unsuccessful.")
|
|
4808
|
+
);
|
|
2126
4809
|
}
|
|
2127
4810
|
}
|
|
2128
4811
|
}
|
|
2129
4812
|
async function learnSyncCommand(options) {
|
|
2130
4813
|
const cwd = getWorkspacePath();
|
|
2131
4814
|
const contributorManager = new ContributorManager2(cwd);
|
|
2132
|
-
console.log(
|
|
4815
|
+
console.log(chalk12.cyan("\n\u{1F504} Sync Learning Patterns\n"));
|
|
2133
4816
|
const config = await contributorManager.getConfig();
|
|
2134
4817
|
if (!config.success || !config.data?.syncOptIn) {
|
|
2135
|
-
console.log(
|
|
2136
|
-
console.log(
|
|
2137
|
-
console.log(
|
|
2138
|
-
console.log(
|
|
4818
|
+
console.log(chalk12.yellow("\u26A0\uFE0F Sync is not enabled.\n"));
|
|
4819
|
+
console.log(chalk12.dim(" To enable sync, run:"));
|
|
4820
|
+
console.log(chalk12.dim(" workflow learn:config --enable-sync\n"));
|
|
4821
|
+
console.log(
|
|
4822
|
+
chalk12.dim(
|
|
4823
|
+
" This allows you to share anonymized patterns with the community."
|
|
4824
|
+
)
|
|
4825
|
+
);
|
|
2139
4826
|
process.exit(0);
|
|
2140
4827
|
}
|
|
2141
4828
|
if (options.dryRun) {
|
|
2142
|
-
console.log(
|
|
4829
|
+
console.log(chalk12.yellow("\u{1F4CB} DRY-RUN MODE: No changes will be synced\n"));
|
|
2143
4830
|
}
|
|
2144
4831
|
const store = new PatternStore2(cwd);
|
|
2145
4832
|
const anonymizer = new PatternAnonymizer();
|
|
2146
4833
|
const { fixes, blueprints } = await store.getPatternsForSync();
|
|
2147
|
-
console.log(
|
|
4834
|
+
console.log(
|
|
4835
|
+
chalk12.dim(
|
|
4836
|
+
` Patterns ready to sync: ${fixes.length} fixes, ${blueprints.length} blueprints`
|
|
4837
|
+
)
|
|
4838
|
+
);
|
|
2148
4839
|
if (options.push) {
|
|
2149
|
-
console.log(
|
|
4840
|
+
console.log(chalk12.cyan("\n\u{1F4E4} Pushing patterns...\n"));
|
|
2150
4841
|
let anonymizedFixes = 0;
|
|
2151
4842
|
let anonymizedBlueprints = 0;
|
|
2152
4843
|
for (const fix of fixes) {
|
|
@@ -2154,7 +4845,7 @@ async function learnSyncCommand(options) {
|
|
|
2154
4845
|
if (result.success) {
|
|
2155
4846
|
anonymizedFixes++;
|
|
2156
4847
|
if (!options.dryRun) {
|
|
2157
|
-
console.log(
|
|
4848
|
+
console.log(chalk12.dim(` \u2713 Anonymized: ${fix.name}`));
|
|
2158
4849
|
}
|
|
2159
4850
|
}
|
|
2160
4851
|
}
|
|
@@ -2163,102 +4854,126 @@ async function learnSyncCommand(options) {
|
|
|
2163
4854
|
if (result.success) {
|
|
2164
4855
|
anonymizedBlueprints++;
|
|
2165
4856
|
if (!options.dryRun) {
|
|
2166
|
-
console.log(
|
|
4857
|
+
console.log(chalk12.dim(` \u2713 Anonymized: ${bp.name}`));
|
|
2167
4858
|
}
|
|
2168
4859
|
}
|
|
2169
4860
|
}
|
|
2170
4861
|
console.log(
|
|
2171
|
-
|
|
2172
|
-
|
|
4862
|
+
chalk12.green(
|
|
4863
|
+
`
|
|
4864
|
+
\u2705 Ready to push ${anonymizedFixes} fixes and ${anonymizedBlueprints} blueprints`
|
|
4865
|
+
)
|
|
2173
4866
|
);
|
|
2174
|
-
console.log(
|
|
4867
|
+
console.log(chalk12.dim(" (Registry push not yet implemented)"));
|
|
2175
4868
|
}
|
|
2176
4869
|
if (options.pull) {
|
|
2177
|
-
console.log(
|
|
2178
|
-
console.log(
|
|
4870
|
+
console.log(chalk12.cyan("\n\u{1F4E5} Pulling patterns from registry...\n"));
|
|
4871
|
+
console.log(chalk12.dim(" (Registry pull not yet implemented)"));
|
|
2179
4872
|
}
|
|
2180
4873
|
if (!options.push && !options.pull) {
|
|
2181
|
-
console.log(
|
|
4874
|
+
console.log(
|
|
4875
|
+
chalk12.dim(" Specify --push to upload or --pull to download patterns.\n")
|
|
4876
|
+
);
|
|
2182
4877
|
}
|
|
2183
4878
|
}
|
|
2184
4879
|
async function learnConfigCommand(options) {
|
|
2185
4880
|
const cwd = getWorkspacePath();
|
|
2186
4881
|
const contributorManager = new ContributorManager2(cwd);
|
|
2187
|
-
console.log(
|
|
4882
|
+
console.log(chalk12.cyan("\n\u2699\uFE0F Learning Configuration\n"));
|
|
2188
4883
|
if (options.enableSync) {
|
|
2189
4884
|
const result = await contributorManager.enableSync();
|
|
2190
4885
|
if (result.success) {
|
|
2191
|
-
console.log(
|
|
2192
|
-
console.log(
|
|
4886
|
+
console.log(chalk12.green("\u2705 Sync enabled"));
|
|
4887
|
+
console.log(
|
|
4888
|
+
chalk12.dim(" Your patterns will be anonymized before sharing.")
|
|
4889
|
+
);
|
|
2193
4890
|
} else {
|
|
2194
|
-
console.log(
|
|
4891
|
+
console.log(chalk12.red(`\u274C Failed: ${result.error}`));
|
|
2195
4892
|
}
|
|
2196
4893
|
return;
|
|
2197
4894
|
}
|
|
2198
4895
|
if (options.disableSync) {
|
|
2199
4896
|
const result = await contributorManager.disableSync();
|
|
2200
4897
|
if (result.success) {
|
|
2201
|
-
console.log(
|
|
4898
|
+
console.log(chalk12.green("\u2705 Sync disabled"));
|
|
2202
4899
|
} else {
|
|
2203
|
-
console.log(
|
|
4900
|
+
console.log(chalk12.red(`\u274C Failed: ${result.error}`));
|
|
2204
4901
|
}
|
|
2205
4902
|
return;
|
|
2206
4903
|
}
|
|
2207
4904
|
if (options.enableTelemetry) {
|
|
2208
4905
|
const result = await contributorManager.enableTelemetry();
|
|
2209
4906
|
if (result.success) {
|
|
2210
|
-
console.log(
|
|
2211
|
-
console.log(
|
|
4907
|
+
console.log(chalk12.green("\u2705 Telemetry enabled"));
|
|
4908
|
+
console.log(
|
|
4909
|
+
chalk12.dim(
|
|
4910
|
+
" Anonymous usage data helps improve pattern recommendations."
|
|
4911
|
+
)
|
|
4912
|
+
);
|
|
2212
4913
|
} else {
|
|
2213
|
-
console.log(
|
|
4914
|
+
console.log(chalk12.red(`\u274C Failed: ${result.error}`));
|
|
2214
4915
|
}
|
|
2215
4916
|
return;
|
|
2216
4917
|
}
|
|
2217
4918
|
if (options.disableTelemetry) {
|
|
2218
4919
|
const result = await contributorManager.disableTelemetry();
|
|
2219
4920
|
if (result.success) {
|
|
2220
|
-
console.log(
|
|
4921
|
+
console.log(chalk12.green("\u2705 Telemetry disabled"));
|
|
2221
4922
|
} else {
|
|
2222
|
-
console.log(
|
|
4923
|
+
console.log(chalk12.red(`\u274C Failed: ${result.error}`));
|
|
2223
4924
|
}
|
|
2224
4925
|
return;
|
|
2225
4926
|
}
|
|
2226
4927
|
if (options.resetId) {
|
|
2227
|
-
const confirmed = await
|
|
4928
|
+
const confirmed = await p8.confirm({
|
|
2228
4929
|
message: "Are you sure you want to reset your contributor ID? This cannot be undone.",
|
|
2229
4930
|
initialValue: false
|
|
2230
4931
|
});
|
|
2231
|
-
if (
|
|
2232
|
-
|
|
4932
|
+
if (p8.isCancel(confirmed) || !confirmed) {
|
|
4933
|
+
p8.cancel("Reset cancelled");
|
|
2233
4934
|
return;
|
|
2234
4935
|
}
|
|
2235
4936
|
const result = await contributorManager.resetId();
|
|
2236
4937
|
if (result.success) {
|
|
2237
|
-
console.log(
|
|
2238
|
-
console.log(
|
|
4938
|
+
console.log(chalk12.green("\u2705 Contributor ID reset"));
|
|
4939
|
+
console.log(chalk12.dim(` New ID: ${result.data?.id}`));
|
|
2239
4940
|
} else {
|
|
2240
|
-
console.log(
|
|
4941
|
+
console.log(chalk12.red(`\u274C Failed: ${result.error}`));
|
|
2241
4942
|
}
|
|
2242
4943
|
return;
|
|
2243
4944
|
}
|
|
2244
4945
|
const config = await contributorManager.getConfig();
|
|
2245
4946
|
if (config.success && config.data) {
|
|
2246
|
-
console.log(
|
|
2247
|
-
console.log(
|
|
2248
|
-
console.log(
|
|
2249
|
-
console.log(
|
|
2250
|
-
|
|
4947
|
+
console.log(chalk12.white(" Current Settings:\n"));
|
|
4948
|
+
console.log(chalk12.dim(` Contributor ID: ${config.data.id}`));
|
|
4949
|
+
console.log(chalk12.dim(` Created: ${formatDate(config.data.createdAt)}`));
|
|
4950
|
+
console.log(
|
|
4951
|
+
chalk12.dim(` Sync Enabled: ${config.data.syncOptIn ? "Yes" : "No"}`)
|
|
4952
|
+
);
|
|
4953
|
+
console.log(
|
|
4954
|
+
chalk12.dim(
|
|
4955
|
+
` Telemetry Enabled: ${config.data.telemetryEnabled ? "Yes" : "No"}`
|
|
4956
|
+
)
|
|
4957
|
+
);
|
|
2251
4958
|
if (config.data.syncEnabledAt) {
|
|
2252
|
-
console.log(
|
|
4959
|
+
console.log(
|
|
4960
|
+
chalk12.dim(
|
|
4961
|
+
` Sync Enabled At: ${formatDate(config.data.syncEnabledAt)}`
|
|
4962
|
+
)
|
|
4963
|
+
);
|
|
2253
4964
|
}
|
|
2254
4965
|
} else {
|
|
2255
|
-
console.log(
|
|
4966
|
+
console.log(
|
|
4967
|
+
chalk12.dim(
|
|
4968
|
+
" No configuration found. Settings will be created on first use.\n"
|
|
4969
|
+
)
|
|
4970
|
+
);
|
|
2256
4971
|
}
|
|
2257
4972
|
}
|
|
2258
4973
|
async function learnDeprecateCommand(patternId, reason) {
|
|
2259
4974
|
const cwd = getWorkspacePath();
|
|
2260
4975
|
const store = new PatternStore2(cwd);
|
|
2261
|
-
console.log(
|
|
4976
|
+
console.log(chalk12.cyan("\n\u26A0\uFE0F Deprecate Pattern\n"));
|
|
2262
4977
|
let patternType = "fix";
|
|
2263
4978
|
let pattern = await store.getFixPattern(patternId);
|
|
2264
4979
|
if (!pattern.success || !pattern.data) {
|
|
@@ -2267,26 +4982,26 @@ async function learnDeprecateCommand(patternId, reason) {
|
|
|
2267
4982
|
pattern = bpResult;
|
|
2268
4983
|
patternType = "blueprint";
|
|
2269
4984
|
} else {
|
|
2270
|
-
console.log(
|
|
4985
|
+
console.log(chalk12.red(`
|
|
2271
4986
|
\u274C Pattern not found: ${patternId}`));
|
|
2272
4987
|
process.exit(1);
|
|
2273
4988
|
}
|
|
2274
4989
|
}
|
|
2275
|
-
console.log(
|
|
2276
|
-
console.log(
|
|
2277
|
-
const confirmed = await
|
|
4990
|
+
console.log(chalk12.white(` Pattern: ${pattern.data.name}`));
|
|
4991
|
+
console.log(chalk12.dim(` Reason: ${reason}`));
|
|
4992
|
+
const confirmed = await p8.confirm({
|
|
2278
4993
|
message: "Are you sure you want to deprecate this pattern?",
|
|
2279
4994
|
initialValue: false
|
|
2280
4995
|
});
|
|
2281
|
-
if (
|
|
2282
|
-
|
|
4996
|
+
if (p8.isCancel(confirmed) || !confirmed) {
|
|
4997
|
+
p8.cancel("Deprecation cancelled");
|
|
2283
4998
|
return;
|
|
2284
4999
|
}
|
|
2285
5000
|
const result = await store.deprecatePattern(patternId, patternType, reason);
|
|
2286
5001
|
if (result.success) {
|
|
2287
|
-
console.log(
|
|
5002
|
+
console.log(chalk12.green("\n\u2705 Pattern deprecated successfully"));
|
|
2288
5003
|
} else {
|
|
2289
|
-
console.log(
|
|
5004
|
+
console.log(chalk12.red(`
|
|
2290
5005
|
\u274C Failed: ${result.error}`));
|
|
2291
5006
|
process.exit(1);
|
|
2292
5007
|
}
|
|
@@ -2295,28 +5010,32 @@ async function learnStatsCommand() {
|
|
|
2295
5010
|
const cwd = getWorkspacePath();
|
|
2296
5011
|
const store = new PatternStore2(cwd);
|
|
2297
5012
|
const telemetry = new TelemetryCollector2(cwd);
|
|
2298
|
-
console.log(
|
|
5013
|
+
console.log(chalk12.cyan("\n\u{1F4CA} Learning Statistics\n"));
|
|
2299
5014
|
const storeStats = await store.getStats();
|
|
2300
5015
|
const totalPatterns = storeStats.totalFixes + storeStats.totalBlueprints;
|
|
2301
5016
|
const totalDeprecated = storeStats.deprecatedFixes + storeStats.deprecatedBlueprints;
|
|
2302
|
-
console.log(
|
|
2303
|
-
console.log(
|
|
2304
|
-
console.log(
|
|
2305
|
-
console.log(
|
|
2306
|
-
console.log(
|
|
5017
|
+
console.log(chalk12.bold.white(" Patterns:\n"));
|
|
5018
|
+
console.log(chalk12.dim(` Total: ${totalPatterns}`));
|
|
5019
|
+
console.log(chalk12.dim(` Fix Patterns: ${storeStats.totalFixes}`));
|
|
5020
|
+
console.log(chalk12.dim(` Blueprints: ${storeStats.totalBlueprints}`));
|
|
5021
|
+
console.log(chalk12.dim(` Deprecated: ${totalDeprecated}`));
|
|
2307
5022
|
const telemetryStats = await telemetry.getStats();
|
|
2308
|
-
console.log(
|
|
2309
|
-
console.log(
|
|
2310
|
-
console.log(
|
|
5023
|
+
console.log(chalk12.bold.white("\n Telemetry:\n"));
|
|
5024
|
+
console.log(chalk12.dim(` Pending Events: ${telemetryStats.pendingEvents}`));
|
|
5025
|
+
console.log(
|
|
5026
|
+
chalk12.dim(` Total Events Sent: ${telemetryStats.totalEventsSent}`)
|
|
5027
|
+
);
|
|
2311
5028
|
if (telemetryStats.lastFlushAt) {
|
|
2312
|
-
console.log(
|
|
5029
|
+
console.log(
|
|
5030
|
+
chalk12.dim(` Last Flush: ${formatDate(telemetryStats.lastFlushAt)}`)
|
|
5031
|
+
);
|
|
2313
5032
|
}
|
|
2314
5033
|
console.log("");
|
|
2315
5034
|
}
|
|
2316
5035
|
|
|
2317
5036
|
// src/cli/commands/solution.ts
|
|
2318
|
-
import
|
|
2319
|
-
import * as
|
|
5037
|
+
import chalk13 from "chalk";
|
|
5038
|
+
import * as p9 from "@clack/prompts";
|
|
2320
5039
|
import * as path2 from "path";
|
|
2321
5040
|
import {
|
|
2322
5041
|
PatternStore as PatternStore3,
|
|
@@ -2358,10 +5077,10 @@ function truncate(str, maxLen) {
|
|
|
2358
5077
|
async function solutionCaptureCommand(options) {
|
|
2359
5078
|
const cwd = getWorkspacePath2();
|
|
2360
5079
|
const store = new PatternStore3(cwd);
|
|
2361
|
-
console.log(
|
|
5080
|
+
console.log(chalk13.cyan("\n\u{1F4E6} Capture Solution Pattern\n"));
|
|
2362
5081
|
let targetPath = options.path;
|
|
2363
5082
|
if (!targetPath) {
|
|
2364
|
-
const pathInput = await
|
|
5083
|
+
const pathInput = await p9.text({
|
|
2365
5084
|
message: "Path to the solution directory:",
|
|
2366
5085
|
placeholder: "./src/auth",
|
|
2367
5086
|
validate: (val) => {
|
|
@@ -2369,8 +5088,8 @@ async function solutionCaptureCommand(options) {
|
|
|
2369
5088
|
return void 0;
|
|
2370
5089
|
}
|
|
2371
5090
|
});
|
|
2372
|
-
if (
|
|
2373
|
-
|
|
5091
|
+
if (p9.isCancel(pathInput)) {
|
|
5092
|
+
p9.cancel("Operation cancelled");
|
|
2374
5093
|
process.exit(0);
|
|
2375
5094
|
}
|
|
2376
5095
|
targetPath = pathInput;
|
|
@@ -2378,7 +5097,7 @@ async function solutionCaptureCommand(options) {
|
|
|
2378
5097
|
const absolutePath = path2.isAbsolute(targetPath) ? targetPath : path2.resolve(cwd, targetPath);
|
|
2379
5098
|
let name = options.name;
|
|
2380
5099
|
if (!name) {
|
|
2381
|
-
const nameInput = await
|
|
5100
|
+
const nameInput = await p9.text({
|
|
2382
5101
|
message: "Solution name:",
|
|
2383
5102
|
placeholder: "JWT Authentication",
|
|
2384
5103
|
validate: (val) => {
|
|
@@ -2386,15 +5105,15 @@ async function solutionCaptureCommand(options) {
|
|
|
2386
5105
|
return void 0;
|
|
2387
5106
|
}
|
|
2388
5107
|
});
|
|
2389
|
-
if (
|
|
2390
|
-
|
|
5108
|
+
if (p9.isCancel(nameInput)) {
|
|
5109
|
+
p9.cancel("Operation cancelled");
|
|
2391
5110
|
process.exit(0);
|
|
2392
5111
|
}
|
|
2393
5112
|
name = nameInput;
|
|
2394
5113
|
}
|
|
2395
5114
|
let description = options.description;
|
|
2396
5115
|
if (!description) {
|
|
2397
|
-
const descInput = await
|
|
5116
|
+
const descInput = await p9.text({
|
|
2398
5117
|
message: "Solution description:",
|
|
2399
5118
|
placeholder: "Complete JWT-based authentication with refresh tokens",
|
|
2400
5119
|
validate: (val) => {
|
|
@@ -2403,15 +5122,15 @@ async function solutionCaptureCommand(options) {
|
|
|
2403
5122
|
return void 0;
|
|
2404
5123
|
}
|
|
2405
5124
|
});
|
|
2406
|
-
if (
|
|
2407
|
-
|
|
5125
|
+
if (p9.isCancel(descInput)) {
|
|
5126
|
+
p9.cancel("Operation cancelled");
|
|
2408
5127
|
process.exit(0);
|
|
2409
5128
|
}
|
|
2410
5129
|
description = descInput;
|
|
2411
5130
|
}
|
|
2412
5131
|
let category = options.category;
|
|
2413
5132
|
if (!category) {
|
|
2414
|
-
const categoryChoice = await
|
|
5133
|
+
const categoryChoice = await p9.select({
|
|
2415
5134
|
message: "Solution category:",
|
|
2416
5135
|
options: [
|
|
2417
5136
|
{ value: "auth", label: "\u{1F510} Authentication" },
|
|
@@ -2430,8 +5149,8 @@ async function solutionCaptureCommand(options) {
|
|
|
2430
5149
|
{ value: "other", label: "\u{1F4E6} Other" }
|
|
2431
5150
|
]
|
|
2432
5151
|
});
|
|
2433
|
-
if (
|
|
2434
|
-
|
|
5152
|
+
if (p9.isCancel(categoryChoice)) {
|
|
5153
|
+
p9.cancel("Operation cancelled");
|
|
2435
5154
|
process.exit(0);
|
|
2436
5155
|
}
|
|
2437
5156
|
category = categoryChoice;
|
|
@@ -2440,20 +5159,20 @@ async function solutionCaptureCommand(options) {
|
|
|
2440
5159
|
if (options.keywords) {
|
|
2441
5160
|
keywords = options.keywords.split(",").map((k) => k.trim());
|
|
2442
5161
|
} else {
|
|
2443
|
-
const keywordsInput = await
|
|
5162
|
+
const keywordsInput = await p9.text({
|
|
2444
5163
|
message: "Keywords (comma-separated):",
|
|
2445
5164
|
placeholder: "jwt, authentication, login, refresh-token"
|
|
2446
5165
|
});
|
|
2447
|
-
if (
|
|
2448
|
-
|
|
5166
|
+
if (p9.isCancel(keywordsInput)) {
|
|
5167
|
+
p9.cancel("Operation cancelled");
|
|
2449
5168
|
process.exit(0);
|
|
2450
5169
|
}
|
|
2451
5170
|
if (keywordsInput) {
|
|
2452
5171
|
keywords = keywordsInput.split(",").map((k) => k.trim());
|
|
2453
5172
|
}
|
|
2454
5173
|
}
|
|
2455
|
-
const
|
|
2456
|
-
|
|
5174
|
+
const spinner7 = p9.spinner();
|
|
5175
|
+
spinner7.start("Analyzing solution...");
|
|
2457
5176
|
const analyzer = new CodeAnalyzer({
|
|
2458
5177
|
anonymize: options.anonymize ?? false
|
|
2459
5178
|
});
|
|
@@ -2465,34 +5184,38 @@ async function solutionCaptureCommand(options) {
|
|
|
2465
5184
|
category,
|
|
2466
5185
|
keywords
|
|
2467
5186
|
);
|
|
2468
|
-
|
|
2469
|
-
console.log(
|
|
2470
|
-
console.log(
|
|
2471
|
-
console.log(`${
|
|
2472
|
-
console.log(
|
|
2473
|
-
|
|
5187
|
+
spinner7.stop("Solution analyzed");
|
|
5188
|
+
console.log(chalk13.green("\n\u2713 Solution captured successfully!\n"));
|
|
5189
|
+
console.log(chalk13.dim("\u2500".repeat(50)));
|
|
5190
|
+
console.log(`${chalk13.bold("Name:")} ${pattern.name}`);
|
|
5191
|
+
console.log(
|
|
5192
|
+
`${chalk13.bold("Category:")} ${formatCategory(pattern.category)}`
|
|
5193
|
+
);
|
|
5194
|
+
console.log(
|
|
5195
|
+
`${chalk13.bold("Files:")} ${pattern.implementation.files.length}`
|
|
5196
|
+
);
|
|
2474
5197
|
console.log(
|
|
2475
|
-
`${
|
|
5198
|
+
`${chalk13.bold("Dependencies:")} ${pattern.implementation.dependencies.length}`
|
|
2476
5199
|
);
|
|
2477
5200
|
console.log(
|
|
2478
|
-
`${
|
|
5201
|
+
`${chalk13.bold("Framework:")} ${pattern.compatibility.framework || "generic"}`
|
|
2479
5202
|
);
|
|
2480
|
-
console.log(
|
|
2481
|
-
const confirm8 = await
|
|
5203
|
+
console.log(chalk13.dim("\u2500".repeat(50)));
|
|
5204
|
+
const confirm8 = await p9.confirm({
|
|
2482
5205
|
message: "Save this solution pattern?",
|
|
2483
5206
|
initialValue: true
|
|
2484
5207
|
});
|
|
2485
|
-
if (
|
|
2486
|
-
|
|
5208
|
+
if (p9.isCancel(confirm8) || !confirm8) {
|
|
5209
|
+
p9.cancel("Solution not saved");
|
|
2487
5210
|
process.exit(0);
|
|
2488
5211
|
}
|
|
2489
5212
|
await store.saveSolution(pattern);
|
|
2490
|
-
console.log(
|
|
5213
|
+
console.log(chalk13.green(`
|
|
2491
5214
|
\u2713 Solution saved with ID: ${pattern.id}
|
|
2492
5215
|
`));
|
|
2493
5216
|
} catch (error) {
|
|
2494
|
-
|
|
2495
|
-
console.error(
|
|
5217
|
+
spinner7.stop("Analysis failed");
|
|
5218
|
+
console.error(chalk13.red(`
|
|
2496
5219
|
\u2717 Error: ${error.message}
|
|
2497
5220
|
`));
|
|
2498
5221
|
process.exit(1);
|
|
@@ -2501,7 +5224,7 @@ async function solutionCaptureCommand(options) {
|
|
|
2501
5224
|
async function solutionSearchCommand(query, options) {
|
|
2502
5225
|
const cwd = getWorkspacePath2();
|
|
2503
5226
|
const store = new PatternStore3(cwd);
|
|
2504
|
-
console.log(
|
|
5227
|
+
console.log(chalk13.cyan("\n\u{1F50D} Search Solution Patterns\n"));
|
|
2505
5228
|
const keywords = query.split(/\s+/).filter((k) => k.length > 0);
|
|
2506
5229
|
const result = await store.searchSolutions(keywords, {
|
|
2507
5230
|
category: options.category,
|
|
@@ -2509,37 +5232,39 @@ async function solutionSearchCommand(query, options) {
|
|
|
2509
5232
|
limit: options.limit ?? 10
|
|
2510
5233
|
});
|
|
2511
5234
|
if (!result.success || !result.data) {
|
|
2512
|
-
console.error(
|
|
5235
|
+
console.error(chalk13.red(`
|
|
2513
5236
|
\u2717 Search failed: ${result.error}
|
|
2514
5237
|
`));
|
|
2515
5238
|
return;
|
|
2516
5239
|
}
|
|
2517
5240
|
const solutions = result.data;
|
|
2518
5241
|
if (solutions.length === 0) {
|
|
2519
|
-
console.log(
|
|
2520
|
-
console.log(
|
|
5242
|
+
console.log(chalk13.yellow("No solutions found matching your query.\n"));
|
|
5243
|
+
console.log(chalk13.dim("Try different keywords or fewer filters."));
|
|
2521
5244
|
return;
|
|
2522
5245
|
}
|
|
2523
|
-
console.log(
|
|
5246
|
+
console.log(chalk13.green(`Found ${solutions.length} solution(s):
|
|
2524
5247
|
`));
|
|
2525
|
-
console.log(
|
|
5248
|
+
console.log(chalk13.dim("\u2500".repeat(70)));
|
|
2526
5249
|
for (const solution of solutions) {
|
|
2527
5250
|
console.log(
|
|
2528
|
-
`${
|
|
5251
|
+
`${chalk13.bold(solution.name)} ${chalk13.dim(`(${solution.id.slice(0, 8)})`)}`
|
|
2529
5252
|
);
|
|
2530
5253
|
console.log(` ${formatCategory(solution.category)}`);
|
|
2531
|
-
console.log(` ${
|
|
5254
|
+
console.log(` ${chalk13.dim(truncate(solution.description, 60))}`);
|
|
2532
5255
|
console.log(
|
|
2533
5256
|
` Files: ${solution.implementation.files.length} | Framework: ${solution.compatibility.framework || "generic"} | Uses: ${solution.metrics.applications}`
|
|
2534
5257
|
);
|
|
2535
|
-
console.log(
|
|
5258
|
+
console.log(chalk13.dim("\u2500".repeat(70)));
|
|
2536
5259
|
}
|
|
2537
|
-
console.log(
|
|
5260
|
+
console.log(
|
|
5261
|
+
chalk13.dim("\nUse 'workflow solution:apply <id>' to apply a solution.")
|
|
5262
|
+
);
|
|
2538
5263
|
}
|
|
2539
5264
|
async function solutionListCommand(options) {
|
|
2540
5265
|
const cwd = getWorkspacePath2();
|
|
2541
5266
|
const store = new PatternStore3(cwd);
|
|
2542
|
-
console.log(
|
|
5267
|
+
console.log(chalk13.cyan("\n\u{1F4CB} Solution Patterns\n"));
|
|
2543
5268
|
const result = await store.listSolutions({
|
|
2544
5269
|
category: options.category,
|
|
2545
5270
|
framework: options.framework,
|
|
@@ -2547,18 +5272,20 @@ async function solutionListCommand(options) {
|
|
|
2547
5272
|
limit: options.limit ?? 20
|
|
2548
5273
|
});
|
|
2549
5274
|
if (!result.success || !result.data) {
|
|
2550
|
-
console.error(
|
|
5275
|
+
console.error(chalk13.red(`
|
|
2551
5276
|
\u2717 List failed: ${result.error}
|
|
2552
5277
|
`));
|
|
2553
5278
|
return;
|
|
2554
5279
|
}
|
|
2555
5280
|
const solutions = result.data;
|
|
2556
5281
|
if (solutions.length === 0) {
|
|
2557
|
-
console.log(
|
|
2558
|
-
console.log(
|
|
5282
|
+
console.log(chalk13.yellow("No solutions found.\n"));
|
|
5283
|
+
console.log(
|
|
5284
|
+
chalk13.dim("Use 'workflow solution:capture' to capture a solution.")
|
|
5285
|
+
);
|
|
2559
5286
|
return;
|
|
2560
5287
|
}
|
|
2561
|
-
console.log(
|
|
5288
|
+
console.log(chalk13.green(`${solutions.length} solution(s):
|
|
2562
5289
|
`));
|
|
2563
5290
|
const byCategory = /* @__PURE__ */ new Map();
|
|
2564
5291
|
for (const solution of solutions) {
|
|
@@ -2567,33 +5294,35 @@ async function solutionListCommand(options) {
|
|
|
2567
5294
|
byCategory.set(solution.category, list);
|
|
2568
5295
|
}
|
|
2569
5296
|
for (const [category, items] of byCategory) {
|
|
2570
|
-
console.log(
|
|
5297
|
+
console.log(chalk13.bold(`
|
|
2571
5298
|
${formatCategory(category)}`));
|
|
2572
|
-
console.log(
|
|
5299
|
+
console.log(chalk13.dim("\u2500".repeat(50)));
|
|
2573
5300
|
for (const solution of items) {
|
|
2574
|
-
const deprecated = solution.deprecatedAt ?
|
|
5301
|
+
const deprecated = solution.deprecatedAt ? chalk13.red(" [DEPRECATED]") : "";
|
|
2575
5302
|
console.log(
|
|
2576
|
-
` ${
|
|
5303
|
+
` ${chalk13.cyan(solution.id.slice(0, 8))} ${solution.name}${deprecated}`
|
|
2577
5304
|
);
|
|
2578
|
-
console.log(` ${
|
|
5305
|
+
console.log(` ${chalk13.dim(truncate(solution.description, 50))}`);
|
|
2579
5306
|
console.log(
|
|
2580
|
-
|
|
5307
|
+
chalk13.dim(
|
|
2581
5308
|
` Created: ${formatDate2(solution.createdAt)} | Files: ${solution.implementation.files.length}`
|
|
2582
5309
|
)
|
|
2583
5310
|
);
|
|
2584
5311
|
}
|
|
2585
5312
|
}
|
|
2586
5313
|
console.log(
|
|
2587
|
-
|
|
5314
|
+
chalk13.dim(
|
|
5315
|
+
"\nUse 'workflow solution:search <query>' to find specific solutions."
|
|
5316
|
+
)
|
|
2588
5317
|
);
|
|
2589
5318
|
}
|
|
2590
5319
|
async function solutionApplyCommand(solutionId, options) {
|
|
2591
5320
|
const cwd = getWorkspacePath2();
|
|
2592
5321
|
const store = new PatternStore3(cwd);
|
|
2593
|
-
console.log(
|
|
5322
|
+
console.log(chalk13.cyan("\n\u{1F680} Apply Solution Pattern\n"));
|
|
2594
5323
|
const result = await store.getSolution(solutionId);
|
|
2595
5324
|
if (!result.success || !result.data) {
|
|
2596
|
-
console.error(
|
|
5325
|
+
console.error(chalk13.red(`
|
|
2597
5326
|
\u2717 Solution not found: ${solutionId}
|
|
2598
5327
|
`));
|
|
2599
5328
|
process.exit(1);
|
|
@@ -2601,58 +5330,60 @@ async function solutionApplyCommand(solutionId, options) {
|
|
|
2601
5330
|
const solution = result.data;
|
|
2602
5331
|
if (solution.deprecatedAt) {
|
|
2603
5332
|
console.log(
|
|
2604
|
-
|
|
5333
|
+
chalk13.yellow(
|
|
2605
5334
|
`\u26A0\uFE0F This solution is deprecated: ${solution.deprecationReason || "No reason provided"}
|
|
2606
5335
|
`
|
|
2607
5336
|
)
|
|
2608
5337
|
);
|
|
2609
|
-
const proceed = await
|
|
5338
|
+
const proceed = await p9.confirm({
|
|
2610
5339
|
message: "Do you want to continue?",
|
|
2611
5340
|
initialValue: false
|
|
2612
5341
|
});
|
|
2613
|
-
if (
|
|
2614
|
-
|
|
5342
|
+
if (p9.isCancel(proceed) || !proceed) {
|
|
5343
|
+
p9.cancel("Operation cancelled");
|
|
2615
5344
|
process.exit(0);
|
|
2616
5345
|
}
|
|
2617
5346
|
}
|
|
2618
|
-
console.log(
|
|
2619
|
-
console.log(
|
|
5347
|
+
console.log(chalk13.bold(`Solution: ${solution.name}`));
|
|
5348
|
+
console.log(chalk13.dim(solution.description));
|
|
2620
5349
|
console.log();
|
|
2621
|
-
console.log(
|
|
2622
|
-
const filesToApply = options.includeTests ? solution.implementation.files : solution.implementation.files.filter(
|
|
5350
|
+
console.log(chalk13.bold("Files to create:"));
|
|
5351
|
+
const filesToApply = options.includeTests ? solution.implementation.files : solution.implementation.files.filter(
|
|
5352
|
+
(f) => f.role !== "test"
|
|
5353
|
+
);
|
|
2623
5354
|
for (const file of filesToApply) {
|
|
2624
|
-
console.log(
|
|
5355
|
+
console.log(chalk13.dim(` \u2022 ${file.path} (${file.role})`));
|
|
2625
5356
|
}
|
|
2626
5357
|
console.log();
|
|
2627
5358
|
if (solution.implementation.dependencies.length > 0) {
|
|
2628
|
-
console.log(
|
|
5359
|
+
console.log(chalk13.bold("Dependencies to install:"));
|
|
2629
5360
|
for (const dep of solution.implementation.dependencies) {
|
|
2630
|
-
console.log(
|
|
5361
|
+
console.log(chalk13.dim(` \u2022 ${dep.name}@${dep.version}`));
|
|
2631
5362
|
}
|
|
2632
5363
|
console.log();
|
|
2633
5364
|
}
|
|
2634
5365
|
if (solution.implementation.envVars.length > 0) {
|
|
2635
|
-
console.log(
|
|
5366
|
+
console.log(chalk13.bold("Environment variables needed:"));
|
|
2636
5367
|
for (const env of solution.implementation.envVars) {
|
|
2637
|
-
const required = env.required ?
|
|
2638
|
-
console.log(
|
|
5368
|
+
const required = env.required ? chalk13.red("*") : "";
|
|
5369
|
+
console.log(chalk13.dim(` \u2022 ${env.name}${required}`));
|
|
2639
5370
|
}
|
|
2640
5371
|
console.log();
|
|
2641
5372
|
}
|
|
2642
5373
|
if (options.dryRun) {
|
|
2643
|
-
console.log(
|
|
5374
|
+
console.log(chalk13.yellow("Dry run mode - no files were created.\n"));
|
|
2644
5375
|
return;
|
|
2645
5376
|
}
|
|
2646
|
-
const confirm8 = await
|
|
5377
|
+
const confirm8 = await p9.confirm({
|
|
2647
5378
|
message: "Apply this solution?",
|
|
2648
5379
|
initialValue: true
|
|
2649
5380
|
});
|
|
2650
|
-
if (
|
|
2651
|
-
|
|
5381
|
+
if (p9.isCancel(confirm8) || !confirm8) {
|
|
5382
|
+
p9.cancel("Operation cancelled");
|
|
2652
5383
|
process.exit(0);
|
|
2653
5384
|
}
|
|
2654
|
-
const
|
|
2655
|
-
|
|
5385
|
+
const spinner7 = p9.spinner();
|
|
5386
|
+
spinner7.start("Applying solution...");
|
|
2656
5387
|
try {
|
|
2657
5388
|
const outputDir = options.output || cwd;
|
|
2658
5389
|
const fs2 = await import("fs");
|
|
@@ -2664,22 +5395,20 @@ async function solutionApplyCommand(solutionId, options) {
|
|
|
2664
5395
|
await fs2.promises.writeFile(filePath, file.content);
|
|
2665
5396
|
}
|
|
2666
5397
|
await store.updateSolutionMetrics(solution.id, true);
|
|
2667
|
-
|
|
2668
|
-
console.log(
|
|
5398
|
+
spinner7.stop("Solution applied");
|
|
5399
|
+
console.log(chalk13.green(`
|
|
2669
5400
|
\u2713 Solution applied successfully!
|
|
2670
5401
|
`));
|
|
2671
|
-
console.log(
|
|
5402
|
+
console.log(chalk13.dim(`Created ${filesToApply.length} file(s).`));
|
|
2672
5403
|
if (solution.implementation.dependencies.length > 0) {
|
|
2673
|
-
console.log(
|
|
2674
|
-
chalk12.cyan("\nNext step: Install dependencies with:")
|
|
2675
|
-
);
|
|
5404
|
+
console.log(chalk13.cyan("\nNext step: Install dependencies with:"));
|
|
2676
5405
|
const deps = solution.implementation.dependencies.map((d) => `${d.name}@${d.version}`).join(" ");
|
|
2677
|
-
console.log(
|
|
5406
|
+
console.log(chalk13.dim(` npm install ${deps}`));
|
|
2678
5407
|
}
|
|
2679
5408
|
} catch (error) {
|
|
2680
|
-
|
|
5409
|
+
spinner7.stop("Application failed");
|
|
2681
5410
|
await store.updateSolutionMetrics(solution.id, false);
|
|
2682
|
-
console.error(
|
|
5411
|
+
console.error(chalk13.red(`
|
|
2683
5412
|
\u2717 Error: ${error.message}
|
|
2684
5413
|
`));
|
|
2685
5414
|
process.exit(1);
|
|
@@ -2688,48 +5417,48 @@ async function solutionApplyCommand(solutionId, options) {
|
|
|
2688
5417
|
async function solutionDeprecateCommand(solutionId, reason) {
|
|
2689
5418
|
const cwd = getWorkspacePath2();
|
|
2690
5419
|
const store = new PatternStore3(cwd);
|
|
2691
|
-
console.log(
|
|
5420
|
+
console.log(chalk13.cyan("\n\u26A0\uFE0F Deprecate Solution Pattern\n"));
|
|
2692
5421
|
const result = await store.getSolution(solutionId);
|
|
2693
5422
|
if (!result.success || !result.data) {
|
|
2694
|
-
console.error(
|
|
5423
|
+
console.error(chalk13.red(`
|
|
2695
5424
|
\u2717 Solution not found: ${solutionId}
|
|
2696
5425
|
`));
|
|
2697
5426
|
process.exit(1);
|
|
2698
5427
|
}
|
|
2699
5428
|
const solution = result.data;
|
|
2700
|
-
console.log(`Solution: ${
|
|
5429
|
+
console.log(`Solution: ${chalk13.bold(solution.name)}`);
|
|
2701
5430
|
console.log(`Reason: ${reason}
|
|
2702
5431
|
`);
|
|
2703
|
-
const confirm8 = await
|
|
5432
|
+
const confirm8 = await p9.confirm({
|
|
2704
5433
|
message: "Deprecate this solution?",
|
|
2705
5434
|
initialValue: false
|
|
2706
5435
|
});
|
|
2707
|
-
if (
|
|
2708
|
-
|
|
5436
|
+
if (p9.isCancel(confirm8) || !confirm8) {
|
|
5437
|
+
p9.cancel("Operation cancelled");
|
|
2709
5438
|
process.exit(0);
|
|
2710
5439
|
}
|
|
2711
5440
|
await store.deprecateSolution(solutionId, reason);
|
|
2712
|
-
console.log(
|
|
5441
|
+
console.log(chalk13.green(`
|
|
2713
5442
|
\u2713 Solution deprecated.
|
|
2714
5443
|
`));
|
|
2715
5444
|
}
|
|
2716
5445
|
async function solutionStatsCommand() {
|
|
2717
5446
|
const cwd = getWorkspacePath2();
|
|
2718
5447
|
const store = new PatternStore3(cwd);
|
|
2719
|
-
console.log(
|
|
5448
|
+
console.log(chalk13.cyan("\n\u{1F4CA} Solution Pattern Statistics\n"));
|
|
2720
5449
|
const stats = await store.getStats();
|
|
2721
|
-
console.log(
|
|
2722
|
-
console.log(`${
|
|
5450
|
+
console.log(chalk13.dim("\u2500".repeat(40)));
|
|
5451
|
+
console.log(`${chalk13.bold("Solutions:")} ${stats.totalSolutions}`);
|
|
2723
5452
|
console.log(` Active: ${stats.totalSolutions - stats.deprecatedSolutions}`);
|
|
2724
5453
|
console.log(` Deprecated: ${stats.deprecatedSolutions}`);
|
|
2725
5454
|
console.log(` Private: ${stats.privateSolutions}`);
|
|
2726
5455
|
console.log(` Synced: ${stats.syncedSolutions}`);
|
|
2727
|
-
console.log(
|
|
2728
|
-
console.log(`${
|
|
2729
|
-
console.log(`${
|
|
2730
|
-
console.log(
|
|
5456
|
+
console.log(chalk13.dim("\u2500".repeat(40)));
|
|
5457
|
+
console.log(`${chalk13.bold("Fixes:")} ${stats.totalFixes}`);
|
|
5458
|
+
console.log(`${chalk13.bold("Blueprints:")} ${stats.totalBlueprints}`);
|
|
5459
|
+
console.log(chalk13.dim("\u2500".repeat(40)));
|
|
2731
5460
|
console.log(`
|
|
2732
|
-
${
|
|
5461
|
+
${chalk13.bold("By Category:")}`);
|
|
2733
5462
|
const listResult = await store.listSolutions({ limit: 1e3 });
|
|
2734
5463
|
if (listResult.success && listResult.data) {
|
|
2735
5464
|
const categories = /* @__PURE__ */ new Map();
|
|
@@ -2773,14 +5502,27 @@ program.command("scope:create").description("Create a custom scope package").opt
|
|
|
2773
5502
|
program.command("scope:migrate").description("Migrate inline scopes to a custom package").option("--name <name>", "Package name for the preset").option("--output-dir <dir>", "Output directory").option("--keep-config", "Keep inline scopes in config after migration").action(scopeMigrateCommand);
|
|
2774
5503
|
program.command("verify").description("Run all quality checks with fix-and-revalidate pattern").option("--fix", "Enable auto-fix for lint and format issues").option("--max-retries <n>", "Maximum retry cycles (default: 10)", "10").option("--commit", "Commit changes if all checks pass").option("--dry-run", "Preview fixes without applying them").option("--learn", "Record successful fixes as learning patterns").action(verifyCommand);
|
|
2775
5504
|
program.command("auto-setup").description("Automatically configure linting, formatting, testing, and CI").option("-y, --yes", "Auto-approve all prompts").option("--audit", "Show audit report without applying changes").action(autoSetupCommand);
|
|
2776
|
-
program.command("
|
|
5505
|
+
program.command("advisory").description("Generate advisory board analysis and documentation").option(
|
|
5506
|
+
"--depth <level>",
|
|
5507
|
+
"Analysis depth: executive, quick, standard, comprehensive"
|
|
5508
|
+
).option("--output <path>", "Output directory (default: docs/advisory)").option("--interactive", "Enable interactive mode").option("--dry-run", "Preview analysis without writing files").option(
|
|
5509
|
+
"--format <type>",
|
|
5510
|
+
"Output format: markdown, json (default: markdown)"
|
|
5511
|
+
).option("--timestamp", "Append timestamp to filenames").option("--include-health", "Include code health metrics from verify/doctor").option("--ci", "CI mode with exit codes on high-risk findings").option("--compare <path>", "Compare with previous report").action(advisoryCommand);
|
|
5512
|
+
program.command("learn:record").description("Record a new pattern from a successful implementation").option("--name <name>", "Pattern name").option("--description <desc>", "Pattern description").option(
|
|
5513
|
+
"--category <cat>",
|
|
5514
|
+
"Category (migration, security, performance, etc.)"
|
|
5515
|
+
).option("--framework <fw>", "Framework (next, react, vue, etc.)").option("--version <ver>", "Framework version range").option("--tags <tags>", "Comma-separated tags (category:value)").option("--type <type>", "Pattern type (fix, blueprint)").action(learnRecordCommand);
|
|
2777
5516
|
program.command("learn:list").description("List recorded learning patterns").option("--type <type>", "Filter by type (fix, blueprint, all)").option("--framework <fw>", "Filter by framework").option("--tag <tag>", "Filter by tag").option("--deprecated", "Include deprecated patterns").action(learnListCommand);
|
|
2778
5517
|
program.command("learn:apply <patternId>").description("Apply a pattern to the current project").argument("<patternId>", "Pattern ID to apply").option("--framework <fw>", "Override framework").option("--version <ver>", "Override version").option("--dry-run", "Preview without applying").action(learnApplyCommand);
|
|
2779
5518
|
program.command("learn:sync").description("Sync patterns with remote registry").option("--push", "Push local patterns to registry").option("--pull", "Pull patterns from registry").option("--dry-run", "Preview without syncing").action(learnSyncCommand);
|
|
2780
5519
|
program.command("learn:config").description("Configure learning settings").option("--enable-sync", "Enable pattern sync").option("--disable-sync", "Disable pattern sync").option("--enable-telemetry", "Enable anonymous telemetry").option("--disable-telemetry", "Disable telemetry").option("--reset-id", "Reset contributor ID").option("--show", "Show current configuration").action(learnConfigCommand);
|
|
2781
5520
|
program.command("learn:deprecate <patternId> <reason>").description("Deprecate an outdated pattern").argument("<patternId>", "Pattern ID to deprecate").argument("<reason>", "Reason for deprecation").action(learnDeprecateCommand);
|
|
2782
5521
|
program.command("learn:stats").description("Show learning statistics").action(learnStatsCommand);
|
|
2783
|
-
program.command("solution:capture").description("Capture a solution pattern from working code").option("--name <name>", "Solution name").option("--description <desc>", "Solution description").option(
|
|
5522
|
+
program.command("solution:capture").description("Capture a solution pattern from working code").option("--name <name>", "Solution name").option("--description <desc>", "Solution description").option(
|
|
5523
|
+
"--category <cat>",
|
|
5524
|
+
"Category (auth, api, database, ui, testing, deployment, integration, performance, security, other)"
|
|
5525
|
+
).option("--keywords <kw>", "Comma-separated keywords").option("--path <path>", "Path to the solution directory").option("--anonymize", "Anonymize sensitive data in code").action(solutionCaptureCommand);
|
|
2784
5526
|
program.command("solution:search <query>").description("Search for solution patterns").argument("<query>", "Search query (keywords, problem description)").option("--category <cat>", "Filter by category").option("--framework <fw>", "Filter by framework").option("--limit <n>", "Maximum results", "10").action(solutionSearchCommand);
|
|
2785
5527
|
program.command("solution:list").description("List all solution patterns").option("--category <cat>", "Filter by category").option("--framework <fw>", "Filter by framework").option("--deprecated", "Include deprecated solutions").option("--limit <n>", "Maximum results", "20").action(solutionListCommand);
|
|
2786
5528
|
program.command("solution:apply <solutionId>").description("Apply a solution pattern to the current project").argument("<solutionId>", "Solution ID to apply").option("--output <dir>", "Output directory").option("--dry-run", "Preview without applying").option("--include-tests", "Include test files").action(solutionApplyCommand);
|