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