zammy 1.2.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/README.md +322 -239
  2. package/assets/zammy.gif +0 -0
  3. package/dist/index.js +2418 -200
  4. package/package.json +9 -2
  5. package/packages/plugins/docker/README.md +141 -0
  6. package/packages/plugins/docker/dist/index.d.ts +46 -0
  7. package/packages/plugins/docker/dist/index.d.ts.map +1 -0
  8. package/packages/plugins/docker/dist/index.js +402 -0
  9. package/packages/plugins/docker/dist/index.js.map +1 -0
  10. package/packages/plugins/docker/package.json +28 -0
  11. package/packages/plugins/docker/zammy-plugin.json +16 -0
  12. package/packages/plugins/faker/README.md +65 -0
  13. package/packages/plugins/faker/dist/index.d.ts +43 -0
  14. package/packages/plugins/faker/dist/index.d.ts.map +1 -0
  15. package/packages/plugins/faker/dist/index.js +349 -0
  16. package/packages/plugins/faker/dist/index.js.map +1 -0
  17. package/packages/plugins/faker/package.json +28 -0
  18. package/packages/plugins/faker/zammy-plugin.json +14 -0
  19. package/packages/plugins/network/README.md +126 -0
  20. package/packages/plugins/network/dist/index.d.ts +45 -0
  21. package/packages/plugins/network/dist/index.d.ts.map +1 -0
  22. package/packages/plugins/network/dist/index.js +406 -0
  23. package/packages/plugins/network/dist/index.js.map +1 -0
  24. package/packages/plugins/network/package.json +28 -0
  25. package/packages/plugins/network/zammy-plugin.json +17 -0
  26. package/packages/plugins/port/README.md +74 -0
  27. package/packages/plugins/port/dist/index.d.ts +47 -0
  28. package/packages/plugins/port/dist/index.d.ts.map +1 -0
  29. package/packages/plugins/port/dist/index.js +331 -0
  30. package/packages/plugins/port/dist/index.js.map +1 -0
  31. package/packages/plugins/port/package.json +28 -0
  32. package/packages/plugins/port/zammy-plugin.json +16 -0
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import * as readline from "readline";
4
+ import * as readline4 from "readline";
5
5
 
6
6
  // src/ui/banner.ts
7
7
  import figlet from "figlet";
@@ -181,6 +181,15 @@ var box = {
181
181
  return theme.dim(`${chars.tl}${chars.h.repeat(leftPad)} `) + title + theme.dim(` ${chars.h.repeat(rightPad)}${chars.tr}`);
182
182
  }
183
183
  };
184
+ var progressBar = (percent, width = 30, showPercent = true) => {
185
+ const filled = Math.round(percent / 100 * width);
186
+ const empty = width - filled;
187
+ let color = theme.success;
188
+ if (percent > 70) color = theme.warning;
189
+ if (percent > 90) color = theme.error;
190
+ const bar = color("\u2588".repeat(filled)) + theme.dim("\u2591".repeat(empty));
191
+ return showPercent ? `${bar} ${percent.toFixed(0)}%` : bar;
192
+ };
184
193
  var bubble = {
185
194
  say: (text, width = 50) => {
186
195
  const lines = [];
@@ -534,7 +543,7 @@ var MIN_WIDTH_FOR_MASCOT2 = 90;
534
543
  var MIN_WIDTH_FOR_FULL_LOGO = 55;
535
544
  var MIN_WIDTH_FOR_COMPACT = 30;
536
545
  async function displayBanner(simple = false) {
537
- return new Promise((resolve3) => {
546
+ return new Promise((resolve6) => {
538
547
  const termWidth = process.stdout.columns || 80;
539
548
  figlet("ZAMMY", {
540
549
  font: "ANSI Shadow",
@@ -548,7 +557,7 @@ async function displayBanner(simple = false) {
548
557
  console.log(theme.secondary(` "${greeting}"`));
549
558
  console.log(theme.dim(" Type /help for commands"));
550
559
  console.log("");
551
- resolve3();
560
+ resolve6();
552
561
  return;
553
562
  }
554
563
  let figletLines = [];
@@ -581,7 +590,7 @@ async function displayBanner(simple = false) {
581
590
  console.log(theme.dim(` ${symbols.arrow} Type ${theme.primary("/")} to browse commands or ${theme.primary("/help")} for full list`));
582
591
  console.log(theme.dim(` ${symbols.arrow} Shell commands start with ${theme.primary("!")} (e.g., ${theme.primary("!ls")}, ${theme.primary("!git")})`));
583
592
  console.log("");
584
- resolve3();
593
+ resolve6();
585
594
  return;
586
595
  }
587
596
  const figletWidth = 50;
@@ -611,7 +620,7 @@ async function displayBanner(simple = false) {
611
620
  console.log(theme.dim(` ${symbols.arrow} Type ${theme.primary("/")} to browse commands or ${theme.primary("/help")} for full list`));
612
621
  console.log(theme.dim(` ${symbols.arrow} Shell commands start with ${theme.primary("!")} (e.g., ${theme.primary("!ls")}, ${theme.primary("!git")})`));
613
622
  console.log("");
614
- resolve3();
623
+ resolve6();
615
624
  });
616
625
  });
617
626
  }
@@ -624,7 +633,17 @@ function getPrompt() {
624
633
  // src/commands/registry.ts
625
634
  var commands = /* @__PURE__ */ new Map();
626
635
  function registerCommand(command) {
627
- commands.set(command.name, command);
636
+ commands.set(command.name, { ...command, source: "core" });
637
+ }
638
+ function registerPluginCommand(command, pluginName) {
639
+ commands.set(command.name, { ...command, source: "plugin", pluginName });
640
+ }
641
+ function unregisterPluginCommands(pluginName) {
642
+ for (const [name, cmd] of commands.entries()) {
643
+ if (cmd.source === "plugin" && cmd.pluginName === pluginName) {
644
+ commands.delete(name);
645
+ }
646
+ }
628
647
  }
629
648
  function getCommand(name) {
630
649
  return commands.get(name);
@@ -632,6 +651,20 @@ function getCommand(name) {
632
651
  function getAllCommands() {
633
652
  return Array.from(commands.values());
634
653
  }
654
+ function getPluginCommands() {
655
+ return Array.from(commands.values()).filter((cmd) => cmd.source === "plugin");
656
+ }
657
+ function checkCommandConflict(name) {
658
+ const existing = commands.get(name);
659
+ if (!existing) {
660
+ return { exists: false };
661
+ }
662
+ return {
663
+ exists: true,
664
+ source: existing.source,
665
+ pluginName: existing.pluginName
666
+ };
667
+ }
635
668
 
636
669
  // src/commands/utilities/help.ts
637
670
  var categories = {
@@ -639,7 +672,8 @@ var categories = {
639
672
  "Fun": ["joke", "quote", "fortune", "dice", "flip", "pomodoro", "zammy"],
640
673
  "Creative": ["asciiart", "figlet", "lorem", "color"],
641
674
  "Dev": ["hash", "uuid", "encode"],
642
- "Info": ["weather"]
675
+ "Info": ["weather"],
676
+ "System": ["plugin"]
643
677
  };
644
678
  registerCommand({
645
679
  name: "help",
@@ -692,8 +726,24 @@ registerCommand({
692
726
  }
693
727
  console.log("");
694
728
  }
729
+ const pluginCmds = getPluginCommands();
730
+ if (pluginCmds.length > 0) {
731
+ console.log(` ${symbols.gear} ${theme.b.secondary("Plugins")}`);
732
+ console.log(theme.dim(" " + "\u2500".repeat(46)));
733
+ for (const cmd of pluginCmds) {
734
+ const paddedName = cmd.name.padEnd(maxNameLength + 2);
735
+ const pluginBadge = cmd.pluginName ? theme.dim(`[${cmd.pluginName}]`) : "";
736
+ console.log(
737
+ ` ${theme.command("/" + paddedName)} ${theme.dim("\u2502")} ${theme.dim(cmd.description)} ${pluginBadge}`
738
+ );
739
+ }
740
+ console.log("");
741
+ }
695
742
  const categorizedNames = Object.values(categories).flat();
696
- const uncategorized = commands2.filter((c) => !categorizedNames.includes(c.name));
743
+ const pluginCmdNames = pluginCmds.map((c) => c.name);
744
+ const uncategorized = commands2.filter(
745
+ (c) => !categorizedNames.includes(c.name) && !pluginCmdNames.includes(c.name) && c.source === "core"
746
+ );
697
747
  if (uncategorized.length > 0) {
698
748
  console.log(` ${symbols.folder} ${theme.b.secondary("Other")}`);
699
749
  console.log(theme.dim(" " + "\u2500".repeat(46)));
@@ -1142,7 +1192,7 @@ registerCommand({
1142
1192
  console.log("");
1143
1193
  let remaining = totalSeconds;
1144
1194
  let spinnerIndex = 0;
1145
- return new Promise((resolve3) => {
1195
+ return new Promise((resolve6) => {
1146
1196
  const interval = setInterval(() => {
1147
1197
  process.stdout.write("\r\x1B[K");
1148
1198
  if (remaining <= 0) {
@@ -1150,7 +1200,7 @@ registerCommand({
1150
1200
  console.log(` ${symbols.sparkle} ${theme.success("TIME'S UP!")} ${symbols.sparkle}`);
1151
1201
  console.log("");
1152
1202
  process.stdout.write("\x07");
1153
- resolve3();
1203
+ resolve6();
1154
1204
  return;
1155
1205
  }
1156
1206
  const spinner = spinnerFrames[spinnerIndex % spinnerFrames.length];
@@ -1468,58 +1518,368 @@ registerCommand({
1468
1518
  }
1469
1519
  });
1470
1520
 
1521
+ // src/handlers/utilities/env.ts
1522
+ function getAllEnvVars() {
1523
+ return Object.entries(process.env).filter(([, value]) => value !== void 0).map(([name, value]) => ({ name, value })).sort((a, b3) => a.name.localeCompare(b3.name));
1524
+ }
1525
+ function getEnvVar(name) {
1526
+ const exactMatch = process.env[name];
1527
+ if (exactMatch !== void 0) return exactMatch;
1528
+ const upperName = name.toUpperCase();
1529
+ for (const [key, value] of Object.entries(process.env)) {
1530
+ if (key.toUpperCase() === upperName) {
1531
+ return value;
1532
+ }
1533
+ }
1534
+ return void 0;
1535
+ }
1536
+ function searchEnvVars(query) {
1537
+ const lowerQuery = query.toLowerCase();
1538
+ return getAllEnvVars().filter(
1539
+ (env) => env.name.toLowerCase().includes(lowerQuery) || env.value.toLowerCase().includes(lowerQuery)
1540
+ );
1541
+ }
1542
+ function getPathEntries() {
1543
+ const pathVar = process.env.PATH || process.env.Path || "";
1544
+ const separator = process.platform === "win32" ? ";" : ":";
1545
+ return pathVar.split(separator).filter(Boolean);
1546
+ }
1547
+
1548
+ // src/commands/utilities/env.ts
1549
+ registerCommand({
1550
+ name: "env",
1551
+ description: "View environment variables",
1552
+ usage: "/env [name|search|path]",
1553
+ async execute(args2) {
1554
+ const action = args2[0];
1555
+ console.log("");
1556
+ if (!action) {
1557
+ const vars = getAllEnvVars();
1558
+ console.log(` ${symbols.sparkle} ${theme.gradient("ENVIRONMENT VARIABLES")} ${theme.dim(`(${vars.length})`)}`);
1559
+ console.log("");
1560
+ for (const env of vars.slice(0, 30)) {
1561
+ const displayValue = env.value.length > 50 ? env.value.slice(0, 47) + "..." : env.value;
1562
+ console.log(` ${theme.primary(env.name.padEnd(20))} ${theme.dim("=")} ${displayValue}`);
1563
+ }
1564
+ if (vars.length > 30) {
1565
+ console.log("");
1566
+ console.log(` ${theme.dim(`... and ${vars.length - 30} more. Use /env search <query> to filter.`)}`);
1567
+ }
1568
+ console.log("");
1569
+ return;
1570
+ }
1571
+ if (action.toLowerCase() === "path") {
1572
+ const paths = getPathEntries();
1573
+ console.log(` ${symbols.sparkle} ${theme.gradient("PATH ENTRIES")} ${theme.dim(`(${paths.length})`)}`);
1574
+ console.log("");
1575
+ paths.forEach((p, i) => {
1576
+ console.log(` ${theme.dim(`${(i + 1).toString().padStart(2)}.`)} ${theme.primary(p)}`);
1577
+ });
1578
+ console.log("");
1579
+ return;
1580
+ }
1581
+ if (action.toLowerCase() === "search" && args2[1]) {
1582
+ const query = args2.slice(1).join(" ");
1583
+ const results = searchEnvVars(query);
1584
+ console.log(` ${symbols.sparkle} ${theme.gradient(`SEARCH: "${query}"`)} ${theme.dim(`(${results.length} matches)`)}`);
1585
+ console.log("");
1586
+ if (results.length === 0) {
1587
+ console.log(` ${theme.dim("No matches found")}`);
1588
+ } else {
1589
+ for (const env of results) {
1590
+ const displayValue = env.value.length > 50 ? env.value.slice(0, 47) + "..." : env.value;
1591
+ console.log(` ${theme.primary(env.name.padEnd(20))} ${theme.dim("=")} ${displayValue}`);
1592
+ }
1593
+ }
1594
+ console.log("");
1595
+ return;
1596
+ }
1597
+ const value = getEnvVar(action);
1598
+ if (value !== void 0) {
1599
+ console.log(` ${theme.primary(action)} ${theme.dim("=")}`);
1600
+ console.log("");
1601
+ if (value.includes(process.platform === "win32" ? ";" : ":") && value.length > 100) {
1602
+ const separator = process.platform === "win32" ? ";" : ":";
1603
+ const parts = value.split(separator);
1604
+ parts.forEach((p, i) => {
1605
+ console.log(` ${theme.dim(`${(i + 1).toString().padStart(2)}.`)} ${p}`);
1606
+ });
1607
+ } else {
1608
+ console.log(` ${theme.success(value)}`);
1609
+ }
1610
+ } else {
1611
+ console.log(` ${symbols.cross} ${theme.error(`Environment variable not found: ${action}`)}`);
1612
+ }
1613
+ console.log("");
1614
+ }
1615
+ });
1616
+
1617
+ // src/handlers/utilities/size.ts
1618
+ import { statSync, readdirSync, existsSync as existsSync3 } from "fs";
1619
+ import { join as join3, basename } from "path";
1620
+ function formatBytes2(bytes) {
1621
+ if (bytes === 0) return "0 B";
1622
+ const units = ["B", "KB", "MB", "GB", "TB"];
1623
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
1624
+ const size = bytes / Math.pow(1024, i);
1625
+ return `${size.toFixed(i > 0 ? 1 : 0)} ${units[i]}`;
1626
+ }
1627
+ function getSize(path) {
1628
+ if (!existsSync3(path)) return null;
1629
+ try {
1630
+ const stats = statSync(path);
1631
+ if (stats.isFile()) {
1632
+ return {
1633
+ path,
1634
+ name: basename(path),
1635
+ size: stats.size,
1636
+ isDirectory: false
1637
+ };
1638
+ }
1639
+ if (stats.isDirectory()) {
1640
+ return getDirSize(path);
1641
+ }
1642
+ return null;
1643
+ } catch {
1644
+ return null;
1645
+ }
1646
+ }
1647
+ function getDirSize(dirPath) {
1648
+ const children = [];
1649
+ let totalSize = 0;
1650
+ try {
1651
+ const entries = readdirSync(dirPath, { withFileTypes: true });
1652
+ for (const entry of entries) {
1653
+ const entryPath = join3(dirPath, entry.name);
1654
+ try {
1655
+ if (entry.isFile()) {
1656
+ const stats = statSync(entryPath);
1657
+ totalSize += stats.size;
1658
+ children.push({
1659
+ path: entryPath,
1660
+ name: entry.name,
1661
+ size: stats.size,
1662
+ isDirectory: false
1663
+ });
1664
+ } else if (entry.isDirectory()) {
1665
+ if (["node_modules", ".git", "dist", "build", ".next", "coverage"].includes(entry.name)) {
1666
+ const subSize = getQuickDirSize(entryPath);
1667
+ totalSize += subSize;
1668
+ children.push({
1669
+ path: entryPath,
1670
+ name: entry.name,
1671
+ size: subSize,
1672
+ isDirectory: true
1673
+ });
1674
+ } else {
1675
+ const subInfo = getDirSize(entryPath);
1676
+ totalSize += subInfo.size;
1677
+ children.push(subInfo);
1678
+ }
1679
+ }
1680
+ } catch {
1681
+ }
1682
+ }
1683
+ } catch {
1684
+ }
1685
+ children.sort((a, b3) => b3.size - a.size);
1686
+ return {
1687
+ path: dirPath,
1688
+ name: basename(dirPath),
1689
+ size: totalSize,
1690
+ isDirectory: true,
1691
+ children
1692
+ };
1693
+ }
1694
+ function getQuickDirSize(dirPath) {
1695
+ let totalSize = 0;
1696
+ try {
1697
+ const entries = readdirSync(dirPath, { withFileTypes: true });
1698
+ for (const entry of entries) {
1699
+ const entryPath = join3(dirPath, entry.name);
1700
+ try {
1701
+ if (entry.isFile()) {
1702
+ totalSize += statSync(entryPath).size;
1703
+ } else if (entry.isDirectory()) {
1704
+ totalSize += getQuickDirSize(entryPath);
1705
+ }
1706
+ } catch {
1707
+ }
1708
+ }
1709
+ } catch {
1710
+ }
1711
+ return totalSize;
1712
+ }
1713
+ function findLargestFiles(dirPath, count = 10) {
1714
+ const allFiles = [];
1715
+ function collectFiles(dir) {
1716
+ try {
1717
+ const entries = readdirSync(dir, { withFileTypes: true });
1718
+ for (const entry of entries) {
1719
+ const entryPath = join3(dir, entry.name);
1720
+ try {
1721
+ if (entry.isFile()) {
1722
+ const stats = statSync(entryPath);
1723
+ allFiles.push({
1724
+ path: entryPath,
1725
+ name: entry.name,
1726
+ size: stats.size,
1727
+ isDirectory: false
1728
+ });
1729
+ } else if (entry.isDirectory()) {
1730
+ if (!["node_modules", ".git"].includes(entry.name)) {
1731
+ collectFiles(entryPath);
1732
+ }
1733
+ }
1734
+ } catch {
1735
+ }
1736
+ }
1737
+ } catch {
1738
+ }
1739
+ }
1740
+ collectFiles(dirPath);
1741
+ allFiles.sort((a, b3) => b3.size - a.size);
1742
+ return allFiles.slice(0, count);
1743
+ }
1744
+
1745
+ // src/commands/utilities/size.ts
1746
+ import { resolve } from "path";
1747
+ registerCommand({
1748
+ name: "size",
1749
+ description: "Analyze file/folder sizes",
1750
+ usage: "/size [path] [--top N]",
1751
+ async execute(args2) {
1752
+ const targetPath = args2[0] || ".";
1753
+ const absPath = resolve(targetPath);
1754
+ const topIndex = args2.indexOf("--top");
1755
+ const showTop = topIndex !== -1 ? parseInt(args2[topIndex + 1]) || 10 : 0;
1756
+ console.log("");
1757
+ if (showTop > 0) {
1758
+ console.log(` ${symbols.sparkle} ${theme.gradient("LARGEST FILES")}`);
1759
+ console.log(` ${theme.dim(`in ${absPath}`)}`);
1760
+ console.log("");
1761
+ const largestFiles = findLargestFiles(absPath, showTop);
1762
+ if (largestFiles.length === 0) {
1763
+ console.log(` ${theme.dim("No files found")}`);
1764
+ } else {
1765
+ const maxSize = largestFiles[0].size;
1766
+ for (let i = 0; i < largestFiles.length; i++) {
1767
+ const file = largestFiles[i];
1768
+ const percent = file.size / maxSize * 100;
1769
+ const bar = progressBar(percent, 20);
1770
+ const relativePath = file.path.replace(absPath, ".").replace(/\\/g, "/");
1771
+ console.log(
1772
+ ` ${theme.dim(`${(i + 1).toString().padStart(2)}.`)} ${bar} ${theme.primary(formatBytes2(file.size).padStart(10))} ${relativePath}`
1773
+ );
1774
+ }
1775
+ }
1776
+ console.log("");
1777
+ return;
1778
+ }
1779
+ const info = getSize(absPath);
1780
+ if (!info) {
1781
+ console.log(` ${symbols.cross} ${theme.error(`Path not found: ${absPath}`)}`);
1782
+ console.log("");
1783
+ return;
1784
+ }
1785
+ console.log(` ${symbols.sparkle} ${theme.gradient("SIZE ANALYSIS")}`);
1786
+ console.log(` ${theme.dim(absPath)}`);
1787
+ console.log("");
1788
+ if (!info.isDirectory) {
1789
+ console.log(` ${theme.primary(info.name)}: ${theme.success(formatBytes2(info.size))}`);
1790
+ } else {
1791
+ console.log(` ${theme.secondary("Total:")} ${theme.success(formatBytes2(info.size))}`);
1792
+ console.log("");
1793
+ if (info.children && info.children.length > 0) {
1794
+ const maxSize = info.children[0].size;
1795
+ const displayCount = Math.min(15, info.children.length);
1796
+ for (let i = 0; i < displayCount; i++) {
1797
+ const child = info.children[i];
1798
+ const percent = child.size / maxSize * 100;
1799
+ const bar = progressBar(percent, 15);
1800
+ const icon = child.isDirectory ? symbols.folder : symbols.bullet;
1801
+ console.log(
1802
+ ` ${bar} ${theme.primary(formatBytes2(child.size).padStart(10))} ${icon} ${child.name}`
1803
+ );
1804
+ }
1805
+ if (info.children.length > displayCount) {
1806
+ console.log("");
1807
+ console.log(` ${theme.dim(`... and ${info.children.length - displayCount} more items`)}`);
1808
+ }
1809
+ } else {
1810
+ console.log(` ${theme.dim("Directory is empty")}`);
1811
+ }
1812
+ }
1813
+ console.log("");
1814
+ console.log(` ${theme.dim("Tip: /size . --top 10 shows largest files")}`);
1815
+ console.log("");
1816
+ }
1817
+ });
1818
+
1471
1819
  // src/commands/fun/joke.ts
1472
1820
  var fallbackJokes = [
1473
1821
  { setup: "Why do programmers prefer dark mode?", punchline: "Because light attracts bugs!" },
1474
1822
  { setup: "Why do Java developers wear glasses?", punchline: "Because they can't C#!" },
1475
1823
  { setup: "A SQL query walks into a bar, walks up to two tables and asks...", punchline: "Can I join you?" },
1476
1824
  { setup: "Why did the developer go broke?", punchline: "Because he used up all his cache!" },
1477
- { setup: "How many programmers does it take to change a light bulb?", punchline: "None, that's a hardware problem!" }
1825
+ { setup: "How many programmers does it take to change a light bulb?", punchline: "None, that's a hardware problem!" },
1826
+ { setup: "Why do programmers hate nature?", punchline: "It has too many bugs!" },
1827
+ { setup: "What's a programmer's favorite hangout place?", punchline: "Foo Bar!" },
1828
+ { setup: "Why was the JavaScript developer sad?", punchline: "Because he didn't Node how to Express himself!" },
1829
+ { setup: "What do you call a computer that sings?", punchline: "A-Dell!" },
1830
+ { setup: "Why did the developer quit his job?", punchline: "Because he didn't get arrays (a raise)!" }
1478
1831
  ];
1832
+ async function fetchJoke() {
1833
+ try {
1834
+ const response = await fetch("https://v2.jokeapi.dev/joke/Programming?type=twopart&safe-mode", {
1835
+ signal: AbortSignal.timeout(3e3)
1836
+ });
1837
+ if (response.ok) {
1838
+ const data = await response.json();
1839
+ if (!data.error && data.setup && data.delivery) {
1840
+ return { setup: data.setup, punchline: data.delivery };
1841
+ }
1842
+ }
1843
+ } catch {
1844
+ }
1845
+ try {
1846
+ const response = await fetch("https://official-joke-api.appspot.com/jokes/programming/random", {
1847
+ signal: AbortSignal.timeout(3e3)
1848
+ });
1849
+ if (response.ok) {
1850
+ const data = await response.json();
1851
+ if (data && data[0]) {
1852
+ return { setup: data[0].setup, punchline: data[0].punchline };
1853
+ }
1854
+ }
1855
+ } catch {
1856
+ }
1857
+ return fallbackJokes[Math.floor(Math.random() * fallbackJokes.length)];
1858
+ }
1479
1859
  registerCommand({
1480
1860
  name: "joke",
1481
- description: "Get a random joke",
1861
+ description: "Get a random programming joke",
1482
1862
  usage: "/joke",
1483
1863
  async execute(_args) {
1484
1864
  console.log("");
1485
1865
  console.log(` ${symbols.dice} ${theme.sunset("Getting a joke...")}`);
1486
- try {
1487
- const response = await fetch("https://official-joke-api.appspot.com/random_joke", {
1488
- signal: AbortSignal.timeout(3e3)
1489
- });
1490
- let joke;
1491
- if (!response.ok) {
1492
- joke = fallbackJokes[Math.floor(Math.random() * fallbackJokes.length)];
1493
- } else {
1494
- joke = await response.json();
1495
- }
1496
- process.stdout.write("\x1B[1A\x1B[2K");
1497
- console.log("");
1498
- console.log(bubble.say(joke.setup, 55));
1499
- console.log(` ${symbols.sparkle}`);
1500
- await new Promise((resolve3) => setTimeout(resolve3, 1500));
1501
- console.log("");
1502
- console.log(` ${theme.gold(" \u2726")} ${theme.b.success(joke.punchline)} ${theme.gold("\u2726")}`);
1503
- console.log("");
1504
- console.log(` ${theme.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}`);
1505
- console.log(` ${symbols.dice} ${theme.dim("Run /joke again for another!")}`);
1506
- console.log("");
1507
- } catch (error) {
1508
- process.stdout.write("\x1B[1A\x1B[2K");
1509
- const joke = fallbackJokes[Math.floor(Math.random() * fallbackJokes.length)];
1510
- console.log("");
1511
- console.log(bubble.say(joke.setup, 55));
1512
- console.log(` ${symbols.sparkle}`);
1513
- await new Promise((resolve3) => setTimeout(resolve3, 1500));
1514
- console.log("");
1515
- console.log(` ${theme.gold(" \u2726")} ${theme.b.success(joke.punchline)} ${theme.gold("\u2726")}`);
1516
- console.log("");
1517
- }
1866
+ const joke = await fetchJoke();
1867
+ process.stdout.write("\x1B[1A\x1B[2K");
1868
+ console.log("");
1869
+ console.log(bubble.say(joke.setup, 55));
1870
+ console.log(` ${symbols.sparkle}`);
1871
+ await new Promise((resolve6) => setTimeout(resolve6, 1500));
1872
+ console.log("");
1873
+ console.log(` ${theme.gold(" \u2726")} ${theme.b.success(joke.punchline)} ${theme.gold("\u2726")}`);
1874
+ console.log("");
1875
+ console.log(` ${theme.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}`);
1876
+ console.log(` ${symbols.dice} ${theme.dim("Run /joke again for another!")}`);
1877
+ console.log("");
1518
1878
  }
1519
1879
  });
1520
1880
 
1521
1881
  // src/commands/fun/quote.ts
1522
- var quotes = [
1882
+ var fallbackQuotes = [
1523
1883
  { text: "The only way to do great work is to love what you do.", author: "Steve Jobs" },
1524
1884
  { text: "Code is like humor. When you have to explain it, it's bad.", author: "Cory House" },
1525
1885
  { text: "First, solve the problem. Then, write the code.", author: "John Johnson" },
@@ -1536,6 +1896,33 @@ var quotes = [
1536
1896
  { text: "There are only two hard things in Computer Science: cache invalidation and naming things.", author: "Phil Karlton" },
1537
1897
  { text: "The best time to plant a tree was 20 years ago. The second best time is now.", author: "Chinese Proverb" }
1538
1898
  ];
1899
+ async function fetchQuote() {
1900
+ try {
1901
+ const response = await fetch("https://zenquotes.io/api/random", {
1902
+ signal: AbortSignal.timeout(3e3)
1903
+ });
1904
+ if (response.ok) {
1905
+ const data = await response.json();
1906
+ if (data && data[0]) {
1907
+ return { text: data[0].q, author: data[0].a };
1908
+ }
1909
+ }
1910
+ } catch {
1911
+ }
1912
+ try {
1913
+ const response = await fetch("https://api.quotable.io/random", {
1914
+ signal: AbortSignal.timeout(3e3)
1915
+ });
1916
+ if (response.ok) {
1917
+ const data = await response.json();
1918
+ if (data && data.content) {
1919
+ return { text: data.content, author: data.author };
1920
+ }
1921
+ }
1922
+ } catch {
1923
+ }
1924
+ return fallbackQuotes[Math.floor(Math.random() * fallbackQuotes.length)];
1925
+ }
1539
1926
  function wrapText(text, maxWidth) {
1540
1927
  const words = text.split(" ");
1541
1928
  const lines = [];
@@ -1556,7 +1943,10 @@ registerCommand({
1556
1943
  description: "Get an inspirational quote",
1557
1944
  usage: "/quote",
1558
1945
  async execute(_args) {
1559
- const quote = quotes[Math.floor(Math.random() * quotes.length)];
1946
+ console.log("");
1947
+ console.log(` ${symbols.sparkle} ${theme.dim("Fetching wisdom...")}`);
1948
+ const quote = await fetchQuote();
1949
+ process.stdout.write("\x1B[1A\x1B[2K\x1B[1A\x1B[2K");
1560
1950
  const wrapped = wrapText(quote.text, 55);
1561
1951
  console.log("");
1562
1952
  console.log(` ${theme.dim("\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E")}`);
@@ -2106,8 +2496,8 @@ registerCommand({
2106
2496
 
2107
2497
  // src/commands/creative/asciiart.ts
2108
2498
  import Jimp from "jimp";
2109
- import { existsSync as existsSync3 } from "fs";
2110
- import { resolve } from "path";
2499
+ import { existsSync as existsSync4 } from "fs";
2500
+ import { resolve as resolve2 } from "path";
2111
2501
  var CHAR_RAMPS = {
2112
2502
  // Standard - 10 levels, balanced
2113
2503
  standard: " .:-=+*#%@",
@@ -2212,8 +2602,8 @@ registerCommand({
2212
2602
  i++;
2213
2603
  }
2214
2604
  }
2215
- const fullPath = resolve(process.cwd(), imagePath);
2216
- if (!existsSync3(fullPath)) {
2605
+ const fullPath = resolve2(process.cwd(), imagePath);
2606
+ if (!existsSync4(fullPath)) {
2217
2607
  console.log(theme.error(`File not found: ${imagePath}`));
2218
2608
  return;
2219
2609
  }
@@ -2865,53 +3255,1880 @@ registerCommand({
2865
3255
  }
2866
3256
  });
2867
3257
 
2868
- // src/commands/info/weather.ts
3258
+ // src/handlers/dev/json.ts
3259
+ function validateJson(input) {
3260
+ try {
3261
+ const data = JSON.parse(input);
3262
+ return { valid: true, data };
3263
+ } catch (error) {
3264
+ const message = error instanceof Error ? error.message : "Invalid JSON";
3265
+ return { valid: false, error: message };
3266
+ }
3267
+ }
3268
+ function formatJson(input, indent = 2) {
3269
+ try {
3270
+ const data = JSON.parse(input);
3271
+ const formatted = JSON.stringify(data, null, indent);
3272
+ return { valid: true, data, formatted };
3273
+ } catch (error) {
3274
+ const message = error instanceof Error ? error.message : "Invalid JSON";
3275
+ return { valid: false, error: message };
3276
+ }
3277
+ }
3278
+ function minifyJson(input) {
3279
+ try {
3280
+ const data = JSON.parse(input);
3281
+ const formatted = JSON.stringify(data);
3282
+ return { valid: true, data, formatted };
3283
+ } catch (error) {
3284
+ const message = error instanceof Error ? error.message : "Invalid JSON";
3285
+ return { valid: false, error: message };
3286
+ }
3287
+ }
3288
+ function queryJson(input, path) {
3289
+ try {
3290
+ const data = JSON.parse(input);
3291
+ const parts = path.replace(/^\$\.?/, "").split(".").filter(Boolean);
3292
+ let current = data;
3293
+ for (const part of parts) {
3294
+ const arrayMatch = part.match(/^(\w*)\[(\d+)\]$/);
3295
+ if (arrayMatch) {
3296
+ const [, key, index] = arrayMatch;
3297
+ if (key) {
3298
+ current = current[key];
3299
+ }
3300
+ if (Array.isArray(current)) {
3301
+ current = current[parseInt(index)];
3302
+ } else {
3303
+ return { valid: false, error: `Not an array at ${part}` };
3304
+ }
3305
+ } else {
3306
+ if (current && typeof current === "object") {
3307
+ current = current[part];
3308
+ } else {
3309
+ return { valid: false, error: `Cannot access ${part}` };
3310
+ }
3311
+ }
3312
+ }
3313
+ return { valid: true, data: current, formatted: JSON.stringify(current, null, 2) };
3314
+ } catch (error) {
3315
+ const message = error instanceof Error ? error.message : "Invalid JSON";
3316
+ return { valid: false, error: message };
3317
+ }
3318
+ }
3319
+ function getJsonStats(input) {
3320
+ try {
3321
+ let countKeys2 = function(obj, depth = 0) {
3322
+ if (typeof obj !== "object" || obj === null) {
3323
+ return { keys: 0, maxDepth: depth };
3324
+ }
3325
+ let keys = 0;
3326
+ let maxDepth = depth;
3327
+ if (Array.isArray(obj)) {
3328
+ for (const item of obj) {
3329
+ const result = countKeys2(item, depth + 1);
3330
+ keys += result.keys;
3331
+ maxDepth = Math.max(maxDepth, result.maxDepth);
3332
+ }
3333
+ } else {
3334
+ keys = Object.keys(obj).length;
3335
+ for (const value of Object.values(obj)) {
3336
+ const result = countKeys2(value, depth + 1);
3337
+ keys += result.keys;
3338
+ maxDepth = Math.max(maxDepth, result.maxDepth);
3339
+ }
3340
+ }
3341
+ return { keys, maxDepth };
3342
+ };
3343
+ var countKeys = countKeys2;
3344
+ const data = JSON.parse(input);
3345
+ const stats = countKeys2(data);
3346
+ const size = new Blob([input]).size;
3347
+ const sizeStr = size < 1024 ? `${size}B` : size < 1024 * 1024 ? `${(size / 1024).toFixed(1)}KB` : `${(size / 1024 / 1024).toFixed(1)}MB`;
3348
+ return { keys: stats.keys, depth: stats.maxDepth, size: sizeStr };
3349
+ } catch {
3350
+ return null;
3351
+ }
3352
+ }
3353
+
3354
+ // src/commands/dev/json.ts
3355
+ import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
2869
3356
  registerCommand({
2870
- name: "weather",
2871
- description: "Get current weather for a city",
2872
- usage: "/weather <city>",
3357
+ name: "json",
3358
+ description: "JSON tools (validate, format, query)",
3359
+ usage: "/json <action> <input>\n\n Actions: validate, format, minify, query, stats",
2873
3360
  async execute(args2) {
2874
- const city = args2.join(" ") || "London";
2875
- console.log(theme.dim(`Fetching weather for ${city}...`));
2876
- try {
2877
- const response = await fetch(`https://wttr.in/${encodeURIComponent(city)}?format=j1`);
2878
- if (!response.ok) {
2879
- console.log(theme.error(`Could not fetch weather for "${city}"`));
2880
- return;
2881
- }
2882
- const data = await response.json();
2883
- const current = data.current_condition[0];
2884
- const location = data.nearest_area[0];
2885
- const temp = current.temp_C;
2886
- const feelsLike = current.FeelsLikeC;
2887
- const desc = current.weatherDesc[0].value;
2888
- const humidity = current.humidity;
2889
- const wind = current.windspeedKmph;
2890
- const cityName = location.areaName[0].value;
2891
- const country = location.country[0].value;
3361
+ const action = args2[0]?.toLowerCase();
3362
+ const input = args2.slice(1).join(" ");
3363
+ if (!action) {
2892
3364
  console.log("");
2893
- console.log(theme.highlight(`${cityName}, ${country}`));
3365
+ console.log(` ${symbols.sparkle} ${theme.gradient("JSON TOOLS")}`);
2894
3366
  console.log("");
2895
- console.log(` ${theme.primary(desc)}`);
2896
- console.log(` Temperature: ${theme.warning(temp + "\xB0C")} (feels like ${feelsLike}\xB0C)`);
2897
- console.log(` Humidity: ${humidity}%`);
3367
+ console.log(` ${theme.dim("Usage:")} /json <action> <input>`);
3368
+ console.log("");
3369
+ console.log(` ${theme.dim("Actions:")}`);
3370
+ console.log(` ${theme.primary("validate")} <json|@file> ${theme.dim("Check if JSON is valid")}`);
3371
+ console.log(` ${theme.primary("format")} <json|@file> ${theme.dim("Pretty-print JSON")}`);
3372
+ console.log(` ${theme.primary("minify")} <json|@file> ${theme.dim("Minify JSON")}`);
3373
+ console.log(` ${theme.primary("query")} <path> <json> ${theme.dim("Query with path (e.g., users[0].name)")}`);
3374
+ console.log(` ${theme.primary("stats")} <json|@file> ${theme.dim("Show JSON statistics")}`);
3375
+ console.log("");
3376
+ console.log(` ${theme.dim("Use @filename to read from file")}`);
3377
+ console.log("");
3378
+ return;
3379
+ }
3380
+ let jsonContent = input;
3381
+ if (input.startsWith("@")) {
3382
+ const filePath = input.slice(1);
3383
+ if (!existsSync5(filePath)) {
3384
+ console.log("");
3385
+ console.log(` ${symbols.cross} ${theme.error(`File not found: ${filePath}`)}`);
3386
+ console.log("");
3387
+ return;
3388
+ }
3389
+ jsonContent = readFileSync3(filePath, "utf-8");
3390
+ }
3391
+ console.log("");
3392
+ switch (action) {
3393
+ case "validate": {
3394
+ if (!jsonContent) {
3395
+ console.log(` ${symbols.warning} ${theme.warning("Usage:")} /json validate <json|@file>`);
3396
+ break;
3397
+ }
3398
+ const result = validateJson(jsonContent);
3399
+ if (result.valid) {
3400
+ console.log(` ${symbols.check} ${theme.success("Valid JSON")}`);
3401
+ } else {
3402
+ console.log(` ${symbols.cross} ${theme.error("Invalid JSON")}`);
3403
+ console.log(` ${theme.dim(result.error || "")}`);
3404
+ }
3405
+ break;
3406
+ }
3407
+ case "format":
3408
+ case "pretty": {
3409
+ if (!jsonContent) {
3410
+ console.log(` ${symbols.warning} ${theme.warning("Usage:")} /json format <json|@file>`);
3411
+ break;
3412
+ }
3413
+ const result = formatJson(jsonContent);
3414
+ if (result.valid && result.formatted) {
3415
+ console.log(` ${symbols.check} ${theme.success("Formatted JSON:")}`);
3416
+ console.log("");
3417
+ for (const line of result.formatted.split("\n")) {
3418
+ console.log(` ${theme.primary(line)}`);
3419
+ }
3420
+ } else {
3421
+ console.log(` ${symbols.cross} ${theme.error("Invalid JSON")}`);
3422
+ console.log(` ${theme.dim(result.error || "")}`);
3423
+ }
3424
+ break;
3425
+ }
3426
+ case "minify":
3427
+ case "min": {
3428
+ if (!jsonContent) {
3429
+ console.log(` ${symbols.warning} ${theme.warning("Usage:")} /json minify <json|@file>`);
3430
+ break;
3431
+ }
3432
+ const result = minifyJson(jsonContent);
3433
+ if (result.valid && result.formatted) {
3434
+ console.log(` ${symbols.check} ${theme.success("Minified:")}`);
3435
+ console.log("");
3436
+ console.log(` ${theme.primary(result.formatted)}`);
3437
+ } else {
3438
+ console.log(` ${symbols.cross} ${theme.error("Invalid JSON")}`);
3439
+ console.log(` ${theme.dim(result.error || "")}`);
3440
+ }
3441
+ break;
3442
+ }
3443
+ case "query":
3444
+ case "get": {
3445
+ const path = args2[1];
3446
+ const queryInput = args2.slice(2).join(" ");
3447
+ if (!path || !queryInput) {
3448
+ console.log(` ${symbols.warning} ${theme.warning("Usage:")} /json query <path> <json|@file>`);
3449
+ console.log("");
3450
+ console.log(` ${theme.dim("Examples:")}`);
3451
+ console.log(` /json query name '{"name": "John"}'`);
3452
+ console.log(` /json query users[0].email @data.json`);
3453
+ break;
3454
+ }
3455
+ let queryJsonContent = queryInput;
3456
+ if (queryInput.startsWith("@")) {
3457
+ const filePath = queryInput.slice(1);
3458
+ if (!existsSync5(filePath)) {
3459
+ console.log(` ${symbols.cross} ${theme.error(`File not found: ${filePath}`)}`);
3460
+ break;
3461
+ }
3462
+ queryJsonContent = readFileSync3(filePath, "utf-8");
3463
+ }
3464
+ const result = queryJson(queryJsonContent, path);
3465
+ if (result.valid) {
3466
+ console.log(` ${symbols.check} ${theme.success(`Result for "${path}":`)} `);
3467
+ console.log("");
3468
+ if (result.formatted) {
3469
+ for (const line of result.formatted.split("\n")) {
3470
+ console.log(` ${theme.primary(line)}`);
3471
+ }
3472
+ } else {
3473
+ console.log(` ${theme.dim("undefined")}`);
3474
+ }
3475
+ } else {
3476
+ console.log(` ${symbols.cross} ${theme.error(result.error || "Query failed")}`);
3477
+ }
3478
+ break;
3479
+ }
3480
+ case "stats":
3481
+ case "info": {
3482
+ if (!jsonContent) {
3483
+ console.log(` ${symbols.warning} ${theme.warning("Usage:")} /json stats <json|@file>`);
3484
+ break;
3485
+ }
3486
+ const stats = getJsonStats(jsonContent);
3487
+ if (stats) {
3488
+ console.log(` ${symbols.sparkle} ${theme.gradient("JSON STATS")}`);
3489
+ console.log("");
3490
+ console.log(` ${theme.dim("Total keys:")} ${theme.primary(stats.keys.toString())}`);
3491
+ console.log(` ${theme.dim("Max depth:")} ${theme.primary(stats.depth.toString())}`);
3492
+ console.log(` ${theme.dim("Size:")} ${theme.primary(stats.size)}`);
3493
+ } else {
3494
+ console.log(` ${symbols.cross} ${theme.error("Invalid JSON")}`);
3495
+ }
3496
+ break;
3497
+ }
3498
+ default:
3499
+ console.log(` ${symbols.cross} ${theme.error(`Unknown action: ${action}`)}`);
3500
+ console.log(` ${theme.dim("Run /json to see available actions")}`);
3501
+ }
3502
+ console.log("");
3503
+ }
3504
+ });
3505
+
3506
+ // src/handlers/dev/request.ts
3507
+ import https from "https";
3508
+ import http from "http";
3509
+ import { URL } from "url";
3510
+ async function makeRequest(urlStr, options = { method: "GET" }) {
3511
+ const startTime2 = Date.now();
3512
+ return new Promise((resolve6) => {
3513
+ const timeout = setTimeout(() => {
3514
+ resolve6({ success: false, error: "Request timed out" });
3515
+ }, options.timeout || 3e4);
3516
+ try {
3517
+ const url = new URL(urlStr.startsWith("http") ? urlStr : `https://${urlStr}`);
3518
+ const client = url.protocol === "https:" ? https : http;
3519
+ const reqOptions = {
3520
+ hostname: url.hostname,
3521
+ port: url.port || (url.protocol === "https:" ? 443 : 80),
3522
+ path: url.pathname + url.search,
3523
+ method: options.method.toUpperCase(),
3524
+ headers: {
3525
+ "User-Agent": "Zammy-CLI/1.0",
3526
+ ...options.headers
3527
+ }
3528
+ };
3529
+ const req = client.request(reqOptions, (res) => {
3530
+ let body = "";
3531
+ res.on("data", (chunk) => {
3532
+ body += chunk.toString();
3533
+ });
3534
+ res.on("end", () => {
3535
+ clearTimeout(timeout);
3536
+ resolve6({
3537
+ success: true,
3538
+ statusCode: res.statusCode,
3539
+ statusMessage: res.statusMessage,
3540
+ headers: res.headers,
3541
+ body,
3542
+ time: Date.now() - startTime2
3543
+ });
3544
+ });
3545
+ });
3546
+ req.on("error", (error) => {
3547
+ clearTimeout(timeout);
3548
+ resolve6({ success: false, error: error.message });
3549
+ });
3550
+ if (options.body) {
3551
+ req.write(options.body);
3552
+ }
3553
+ req.end();
3554
+ } catch (error) {
3555
+ clearTimeout(timeout);
3556
+ resolve6({ success: false, error: error instanceof Error ? error.message : "Request failed" });
3557
+ }
3558
+ });
3559
+ }
3560
+ function tryParseJson(body) {
3561
+ try {
3562
+ const parsed = JSON.parse(body);
3563
+ return { isJson: true, formatted: JSON.stringify(parsed, null, 2) };
3564
+ } catch {
3565
+ return { isJson: false };
3566
+ }
3567
+ }
3568
+
3569
+ // src/commands/dev/request.ts
3570
+ registerCommand({
3571
+ name: "request",
3572
+ description: "Make HTTP requests",
3573
+ usage: "/request <method> <url> [options]",
3574
+ async execute(args2) {
3575
+ if (args2.length < 1) {
3576
+ console.log("");
3577
+ console.log(` ${symbols.sparkle} ${theme.gradient("HTTP REQUEST")}`);
3578
+ console.log("");
3579
+ console.log(` ${theme.dim("Usage:")} /request <method> <url> [options]`);
3580
+ console.log("");
3581
+ console.log(` ${theme.dim("Methods:")} GET, POST, PUT, DELETE, PATCH, HEAD`);
3582
+ console.log("");
3583
+ console.log(` ${theme.dim("Examples:")}`);
3584
+ console.log(` /request GET https://api.github.com`);
3585
+ console.log(` /request POST https://httpbin.org/post --body '{"name":"test"}'`);
3586
+ console.log(` /request GET api.example.com/users`);
3587
+ console.log("");
3588
+ return;
3589
+ }
3590
+ let method = "GET";
3591
+ let url = args2[0];
3592
+ const methods = ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"];
3593
+ if (methods.includes(args2[0].toUpperCase())) {
3594
+ method = args2[0].toUpperCase();
3595
+ url = args2[1];
3596
+ }
3597
+ if (!url) {
3598
+ console.log("");
3599
+ console.log(` ${symbols.cross} ${theme.error("URL is required")}`);
3600
+ console.log("");
3601
+ return;
3602
+ }
3603
+ const headers = {};
3604
+ let body;
3605
+ for (let i = 2; i < args2.length; i++) {
3606
+ if (args2[i] === "--header" || args2[i] === "-H") {
3607
+ const header = args2[++i];
3608
+ if (header) {
3609
+ const [key, ...valueParts] = header.split(":");
3610
+ headers[key.trim()] = valueParts.join(":").trim();
3611
+ }
3612
+ } else if (args2[i] === "--body" || args2[i] === "-d") {
3613
+ body = args2[++i];
3614
+ }
3615
+ }
3616
+ console.log("");
3617
+ console.log(` ${theme.dim(`${method} ${url}...`)}`);
3618
+ const result = await makeRequest(url, { method, headers, body });
3619
+ process.stdout.write("\x1B[1A\x1B[2K");
3620
+ if (!result.success) {
3621
+ console.log(` ${symbols.cross} ${theme.error(result.error || "Request failed")}`);
3622
+ console.log("");
3623
+ return;
3624
+ }
3625
+ const statusColor = result.statusCode && result.statusCode >= 200 && result.statusCode < 300 ? theme.success : result.statusCode && result.statusCode >= 400 ? theme.error : theme.warning;
3626
+ console.log(` ${statusColor(`${result.statusCode} ${result.statusMessage}`)} ${theme.dim(`(${result.time}ms)`)}`);
3627
+ console.log("");
3628
+ if (result.headers) {
3629
+ const importantHeaders = ["content-type", "content-length", "server", "date"];
3630
+ console.log(` ${theme.dim("Headers:")}`);
3631
+ for (const key of importantHeaders) {
3632
+ if (result.headers[key]) {
3633
+ console.log(` ${theme.secondary(key)}: ${result.headers[key]}`);
3634
+ }
3635
+ }
3636
+ console.log("");
3637
+ }
3638
+ if (result.body && method !== "HEAD") {
3639
+ console.log(` ${theme.dim("Body:")}`);
3640
+ const jsonResult = tryParseJson(result.body);
3641
+ if (jsonResult.isJson && jsonResult.formatted) {
3642
+ const lines = jsonResult.formatted.split("\n");
3643
+ const displayLines = lines.slice(0, 30);
3644
+ for (const line of displayLines) {
3645
+ console.log(` ${theme.primary(line)}`);
3646
+ }
3647
+ if (lines.length > 30) {
3648
+ console.log(` ${theme.dim(`... and ${lines.length - 30} more lines`)}`);
3649
+ }
3650
+ } else {
3651
+ const lines = result.body.split("\n").slice(0, 20);
3652
+ for (const line of lines) {
3653
+ console.log(` ${line.slice(0, 100)}`);
3654
+ }
3655
+ if (result.body.split("\n").length > 20) {
3656
+ console.log(` ${theme.dim("... (truncated)")}`);
3657
+ }
3658
+ }
3659
+ }
3660
+ console.log("");
3661
+ }
3662
+ });
3663
+
3664
+ // src/handlers/dev/diff.ts
3665
+ import { readFileSync as readFileSync4, existsSync as existsSync6 } from "fs";
3666
+ function diffFiles(file1, file2) {
3667
+ if (!existsSync6(file1)) {
3668
+ return { success: false, lines: [], stats: { additions: 0, deletions: 0, unchanged: 0 }, error: `File not found: ${file1}` };
3669
+ }
3670
+ if (!existsSync6(file2)) {
3671
+ return { success: false, lines: [], stats: { additions: 0, deletions: 0, unchanged: 0 }, error: `File not found: ${file2}` };
3672
+ }
3673
+ try {
3674
+ const content1 = readFileSync4(file1, "utf-8");
3675
+ const content2 = readFileSync4(file2, "utf-8");
3676
+ return diffStrings(content1, content2);
3677
+ } catch (error) {
3678
+ return {
3679
+ success: false,
3680
+ lines: [],
3681
+ stats: { additions: 0, deletions: 0, unchanged: 0 },
3682
+ error: error instanceof Error ? error.message : "Failed to read files"
3683
+ };
3684
+ }
3685
+ }
3686
+ function diffStrings(str1, str2) {
3687
+ const lines1 = str1.split("\n");
3688
+ const lines2 = str2.split("\n");
3689
+ const lcs = computeLCS(lines1, lines2);
3690
+ const diff = buildDiff(lines1, lines2, lcs);
3691
+ let additions = 0;
3692
+ let deletions = 0;
3693
+ let unchanged = 0;
3694
+ for (const line of diff) {
3695
+ if (line.type === "add") additions++;
3696
+ else if (line.type === "remove") deletions++;
3697
+ else if (line.type === "same") unchanged++;
3698
+ }
3699
+ return {
3700
+ success: true,
3701
+ lines: diff,
3702
+ stats: { additions, deletions, unchanged }
3703
+ };
3704
+ }
3705
+ function computeLCS(lines1, lines2) {
3706
+ const m = lines1.length;
3707
+ const n = lines2.length;
3708
+ const dp = Array(m + 1).fill(null).map(() => Array(n + 1).fill(0));
3709
+ for (let i = 1; i <= m; i++) {
3710
+ for (let j = 1; j <= n; j++) {
3711
+ if (lines1[i - 1] === lines2[j - 1]) {
3712
+ dp[i][j] = dp[i - 1][j - 1] + 1;
3713
+ } else {
3714
+ dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
3715
+ }
3716
+ }
3717
+ }
3718
+ return dp;
3719
+ }
3720
+ function buildDiff(lines1, lines2, lcs) {
3721
+ const result = [];
3722
+ let i = lines1.length;
3723
+ let j = lines2.length;
3724
+ const temp = [];
3725
+ while (i > 0 || j > 0) {
3726
+ if (i > 0 && j > 0 && lines1[i - 1] === lines2[j - 1]) {
3727
+ temp.push({ type: "same", content: lines1[i - 1], lineNum1: i, lineNum2: j });
3728
+ i--;
3729
+ j--;
3730
+ } else if (j > 0 && (i === 0 || lcs[i][j - 1] >= lcs[i - 1][j])) {
3731
+ temp.push({ type: "add", content: lines2[j - 1], lineNum2: j });
3732
+ j--;
3733
+ } else if (i > 0) {
3734
+ temp.push({ type: "remove", content: lines1[i - 1], lineNum1: i });
3735
+ i--;
3736
+ }
3737
+ }
3738
+ return temp.reverse();
3739
+ }
3740
+ function formatDiffStats(stats) {
3741
+ const parts = [];
3742
+ if (stats.additions > 0) parts.push(`+${stats.additions}`);
3743
+ if (stats.deletions > 0) parts.push(`-${stats.deletions}`);
3744
+ if (stats.unchanged > 0) parts.push(`=${stats.unchanged}`);
3745
+ return parts.join(", ");
3746
+ }
3747
+
3748
+ // src/commands/dev/diff.ts
3749
+ import { basename as basename2 } from "path";
3750
+ registerCommand({
3751
+ name: "diff",
3752
+ description: "Compare two files",
3753
+ usage: "/diff <file1> <file2>",
3754
+ async execute(args2) {
3755
+ if (args2.length < 2) {
3756
+ console.log("");
3757
+ console.log(` ${symbols.sparkle} ${theme.gradient("FILE DIFF")}`);
3758
+ console.log("");
3759
+ console.log(` ${theme.dim("Usage:")} /diff <file1> <file2>`);
3760
+ console.log("");
3761
+ console.log(` ${theme.dim("Example:")}`);
3762
+ console.log(` /diff old.json new.json`);
3763
+ console.log(` /diff config.ts config.backup.ts`);
3764
+ console.log("");
3765
+ return;
3766
+ }
3767
+ const file1 = args2[0];
3768
+ const file2 = args2[1];
3769
+ console.log("");
3770
+ const result = diffFiles(file1, file2);
3771
+ if (!result.success) {
3772
+ console.log(` ${symbols.cross} ${theme.error(result.error || "Diff failed")}`);
3773
+ console.log("");
3774
+ return;
3775
+ }
3776
+ console.log(` ${symbols.sparkle} ${theme.gradient("DIFF")}: ${theme.primary(basename2(file1))} ${theme.dim("\u2192")} ${theme.primary(basename2(file2))}`);
3777
+ console.log(` ${theme.dim(formatDiffStats(result.stats))}`);
3778
+ console.log("");
3779
+ if (result.stats.additions === 0 && result.stats.deletions === 0) {
3780
+ console.log(` ${symbols.check} ${theme.success("Files are identical")}`);
3781
+ console.log("");
3782
+ return;
3783
+ }
3784
+ let contextLines = 3;
3785
+ let lastShownIndex = -contextLines - 1;
3786
+ const linesToShow = [];
3787
+ result.lines.forEach((line, i) => {
3788
+ if (line.type !== "same") {
3789
+ for (let j = Math.max(0, i - contextLines); j <= Math.min(result.lines.length - 1, i + contextLines); j++) {
3790
+ if (!linesToShow.includes(j)) {
3791
+ linesToShow.push(j);
3792
+ }
3793
+ }
3794
+ }
3795
+ });
3796
+ linesToShow.sort((a, b3) => a - b3);
3797
+ for (let i = 0; i < linesToShow.length; i++) {
3798
+ const lineIndex = linesToShow[i];
3799
+ const line = result.lines[lineIndex];
3800
+ if (i > 0 && linesToShow[i] - linesToShow[i - 1] > 1) {
3801
+ console.log(` ${theme.dim("...")}`);
3802
+ }
3803
+ const lineNum = line.lineNum1 || line.lineNum2 || "";
3804
+ const numStr = lineNum.toString().padStart(4);
3805
+ if (line.type === "add") {
3806
+ console.log(` ${theme.success("+")} ${theme.dim(numStr)} ${theme.success(line.content)}`);
3807
+ } else if (line.type === "remove") {
3808
+ console.log(` ${theme.error("-")} ${theme.dim(numStr)} ${theme.error(line.content)}`);
3809
+ } else {
3810
+ console.log(` ${theme.dim(" ")} ${theme.dim(numStr)} ${line.content}`);
3811
+ }
3812
+ }
3813
+ console.log("");
3814
+ }
3815
+ });
3816
+
3817
+ // src/commands/info/weather.ts
3818
+ registerCommand({
3819
+ name: "weather",
3820
+ description: "Get current weather for a city",
3821
+ usage: "/weather <city>",
3822
+ async execute(args2) {
3823
+ const city = args2.join(" ") || "London";
3824
+ console.log(theme.dim(`Fetching weather for ${city}...`));
3825
+ try {
3826
+ const response = await fetch(`https://wttr.in/${encodeURIComponent(city)}?format=j1`);
3827
+ if (!response.ok) {
3828
+ console.log(theme.error(`Could not fetch weather for "${city}"`));
3829
+ return;
3830
+ }
3831
+ const data = await response.json();
3832
+ const current = data.current_condition[0];
3833
+ const location = data.nearest_area[0];
3834
+ const temp = current.temp_C;
3835
+ const feelsLike = current.FeelsLikeC;
3836
+ const desc = current.weatherDesc[0].value;
3837
+ const humidity = current.humidity;
3838
+ const wind = current.windspeedKmph;
3839
+ const cityName = location.areaName[0].value;
3840
+ const country = location.country[0].value;
3841
+ console.log("");
3842
+ console.log(theme.highlight(`${cityName}, ${country}`));
3843
+ console.log("");
3844
+ console.log(` ${theme.primary(desc)}`);
3845
+ console.log(` Temperature: ${theme.warning(temp + "\xB0C")} (feels like ${feelsLike}\xB0C)`);
3846
+ console.log(` Humidity: ${humidity}%`);
2898
3847
  console.log(` Wind: ${wind} km/h`);
2899
3848
  console.log("");
2900
- } catch (error) {
2901
- console.log(theme.error(`Error fetching weather: ${error}`));
3849
+ } catch (error) {
3850
+ console.log(theme.error(`Error fetching weather: ${error}`));
3851
+ }
3852
+ }
3853
+ });
3854
+
3855
+ // src/plugins/loader.ts
3856
+ import { existsSync as existsSync8, readdirSync as readdirSync2, readFileSync as readFileSync7, mkdirSync as mkdirSync2 } from "fs";
3857
+ import { join as join6, dirname as dirname3 } from "path";
3858
+ import { homedir as homedir3 } from "os";
3859
+ import { pathToFileURL, fileURLToPath as fileURLToPath2 } from "url";
3860
+
3861
+ // src/plugins/api.ts
3862
+ import { execSync, spawn as nodeSpawn } from "child_process";
3863
+ import { join as join5 } from "path";
3864
+
3865
+ // src/plugins/storage.ts
3866
+ import { existsSync as existsSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync } from "fs";
3867
+ import { join as join4 } from "path";
3868
+ function createPluginStorage(pluginName, dataDir) {
3869
+ const storagePath = join4(dataDir, "data.json");
3870
+ if (!existsSync7(dataDir)) {
3871
+ mkdirSync(dataDir, { recursive: true });
3872
+ }
3873
+ function loadData() {
3874
+ try {
3875
+ if (existsSync7(storagePath)) {
3876
+ const content = readFileSync5(storagePath, "utf-8");
3877
+ return JSON.parse(content);
3878
+ }
3879
+ } catch {
3880
+ }
3881
+ return {};
3882
+ }
3883
+ function saveData(data) {
3884
+ try {
3885
+ writeFileSync3(storagePath, JSON.stringify(data, null, 2), "utf-8");
3886
+ } catch (error) {
3887
+ console.error(`Failed to save plugin storage for ${pluginName}:`, error);
3888
+ }
3889
+ }
3890
+ return {
3891
+ get(key) {
3892
+ const data = loadData();
3893
+ return data[key];
3894
+ },
3895
+ set(key, value) {
3896
+ const data = loadData();
3897
+ data[key] = value;
3898
+ saveData(data);
3899
+ },
3900
+ delete(key) {
3901
+ const data = loadData();
3902
+ delete data[key];
3903
+ saveData(data);
3904
+ },
3905
+ clear() {
3906
+ saveData({});
3907
+ },
3908
+ getAll() {
3909
+ return loadData();
3910
+ }
3911
+ };
3912
+ }
3913
+
3914
+ // src/plugins/api.ts
3915
+ import { readFileSync as readFileSync6 } from "fs";
3916
+ import { fileURLToPath } from "url";
3917
+ import { dirname as dirname2 } from "path";
3918
+ function getZammyVersion() {
3919
+ try {
3920
+ const __filename = fileURLToPath(import.meta.url);
3921
+ const __dirname = dirname2(__filename);
3922
+ const pkgPath = join5(__dirname, "..", "..", "package.json");
3923
+ const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
3924
+ return pkg.version || "0.0.0";
3925
+ } catch {
3926
+ return "0.0.0";
3927
+ }
3928
+ }
3929
+ function createPluginUI() {
3930
+ return {
3931
+ theme: {
3932
+ primary: theme.primary,
3933
+ secondary: theme.secondary,
3934
+ accent: theme.accent,
3935
+ success: theme.success,
3936
+ warning: theme.warning,
3937
+ error: theme.error,
3938
+ info: theme.info,
3939
+ dim: theme.dim,
3940
+ gradient: theme.gradient
3941
+ },
3942
+ symbols: {
3943
+ check: symbols.check,
3944
+ cross: symbols.cross,
3945
+ star: symbols.star,
3946
+ arrow: symbols.arrow,
3947
+ bullet: symbols.bullet,
3948
+ folder: symbols.folder,
3949
+ file: "\u{1F4C4}",
3950
+ // 📄
3951
+ warning: symbols.warning,
3952
+ info: symbols.info,
3953
+ rocket: symbols.rocket,
3954
+ sparkles: symbols.sparkle
3955
+ },
3956
+ box: (content, options) => {
3957
+ const lines = content.split("\n");
3958
+ const width = Math.max(...lines.map((l) => l.replace(/\x1B\[[0-9;]*m/g, "").length)) + 4;
3959
+ return box.draw(lines, width);
3960
+ },
3961
+ progressBar: (current, total, width) => {
3962
+ const percent = Math.round(current / total * 100);
3963
+ return progressBar(percent, width || 30);
3964
+ }
3965
+ };
3966
+ }
3967
+ function createPluginLogger(pluginName) {
3968
+ const prefix = theme.dim(`[${pluginName}]`);
3969
+ return {
3970
+ info: (message) => console.log(`${prefix} ${theme.info(message)}`),
3971
+ warn: (message) => console.log(`${prefix} ${theme.warning(message)}`),
3972
+ error: (message) => console.log(`${prefix} ${theme.error(message)}`),
3973
+ debug: (message) => {
3974
+ if (process.env.ZAMMY_DEBUG) {
3975
+ console.log(`${prefix} ${theme.dim(message)}`);
3976
+ }
3977
+ }
3978
+ };
3979
+ }
3980
+ function createPluginShell(manifest) {
3981
+ if (!manifest.permissions?.shell) {
3982
+ return void 0;
3983
+ }
3984
+ return {
3985
+ exec: (command, options) => {
3986
+ try {
3987
+ return execSync(command, {
3988
+ encoding: "utf-8",
3989
+ timeout: options?.timeout || 3e4,
3990
+ stdio: ["pipe", "pipe", "pipe"]
3991
+ });
3992
+ } catch (error) {
3993
+ if (error && typeof error === "object" && "stdout" in error) {
3994
+ return error.stdout || "";
3995
+ }
3996
+ throw error;
3997
+ }
3998
+ },
3999
+ spawn: (command, args2) => {
4000
+ return new Promise((resolve6) => {
4001
+ const proc = nodeSpawn(command, args2 || [], {
4002
+ shell: true,
4003
+ stdio: ["pipe", "pipe", "pipe"]
4004
+ });
4005
+ let stdout = "";
4006
+ let stderr = "";
4007
+ proc.stdout?.on("data", (data) => {
4008
+ stdout += data.toString();
4009
+ });
4010
+ proc.stderr?.on("data", (data) => {
4011
+ stderr += data.toString();
4012
+ });
4013
+ proc.on("close", (code) => {
4014
+ resolve6({ stdout, stderr, code: code || 0 });
4015
+ });
4016
+ proc.on("error", () => {
4017
+ resolve6({ stdout, stderr, code: 1 });
4018
+ });
4019
+ });
4020
+ }
4021
+ };
4022
+ }
4023
+ function createPluginAPI(manifest, pluginPath) {
4024
+ const dataDir = pluginPath;
4025
+ const context = {
4026
+ pluginName: manifest.name,
4027
+ pluginVersion: manifest.version,
4028
+ zammyVersion: getZammyVersion(),
4029
+ dataDir,
4030
+ cwd: process.cwd()
4031
+ };
4032
+ return {
4033
+ registerCommand: (command) => {
4034
+ registerPluginCommand(command, manifest.name);
4035
+ },
4036
+ registerCommands: (commands2) => {
4037
+ for (const command of commands2) {
4038
+ registerPluginCommand(command, manifest.name);
4039
+ }
4040
+ },
4041
+ ui: createPluginUI(),
4042
+ storage: createPluginStorage(manifest.name, dataDir),
4043
+ log: createPluginLogger(manifest.name),
4044
+ context,
4045
+ shell: createPluginShell(manifest)
4046
+ };
4047
+ }
4048
+
4049
+ // src/plugins/loader.ts
4050
+ var PLUGINS_DIR = join6(homedir3(), ".zammy", "plugins");
4051
+ function getZammyVersion2() {
4052
+ try {
4053
+ const __filename = fileURLToPath2(import.meta.url);
4054
+ const __dirname = dirname3(__filename);
4055
+ const possiblePaths = [
4056
+ join6(__dirname, "package.json"),
4057
+ // Same dir (bundled: dist/)
4058
+ join6(__dirname, "..", "package.json"),
4059
+ // One up (bundled: project root)
4060
+ join6(__dirname, "..", "..", "package.json")
4061
+ // Two up (source: src/plugins/)
4062
+ ];
4063
+ for (const pkgPath of possiblePaths) {
4064
+ if (existsSync8(pkgPath)) {
4065
+ const pkg = JSON.parse(readFileSync7(pkgPath, "utf-8"));
4066
+ if (pkg.name === "zammy" && pkg.version) {
4067
+ return pkg.version;
4068
+ }
4069
+ }
4070
+ }
4071
+ return "0.0.0";
4072
+ } catch {
4073
+ return "0.0.0";
4074
+ }
4075
+ }
4076
+ function compareVersions(a, b3) {
4077
+ const partsA = a.split(".").map((n) => parseInt(n, 10) || 0);
4078
+ const partsB = b3.split(".").map((n) => parseInt(n, 10) || 0);
4079
+ for (let i = 0; i < 3; i++) {
4080
+ const numA = partsA[i] || 0;
4081
+ const numB = partsB[i] || 0;
4082
+ if (numA < numB) return -1;
4083
+ if (numA > numB) return 1;
4084
+ }
4085
+ return 0;
4086
+ }
4087
+ function checkVersionCompatibility(manifest, zammyVersion) {
4088
+ const minVersion = manifest.zammy?.minVersion;
4089
+ const maxVersion = manifest.zammy?.maxVersion;
4090
+ if (minVersion && compareVersions(zammyVersion, minVersion) < 0) {
4091
+ return {
4092
+ compatible: false,
4093
+ reason: `Requires Zammy v${minVersion}+, but you have v${zammyVersion}`
4094
+ };
4095
+ }
4096
+ if (maxVersion && compareVersions(zammyVersion, maxVersion) > 0) {
4097
+ return {
4098
+ compatible: false,
4099
+ reason: `Incompatible with Zammy v${zammyVersion} (max: v${maxVersion})`
4100
+ };
4101
+ }
4102
+ return { compatible: true };
4103
+ }
4104
+ var discoveredPlugins = /* @__PURE__ */ new Map();
4105
+ var loadedPlugins = /* @__PURE__ */ new Map();
4106
+ function ensurePluginsDir() {
4107
+ if (!existsSync8(PLUGINS_DIR)) {
4108
+ mkdirSync2(PLUGINS_DIR, { recursive: true });
4109
+ }
4110
+ }
4111
+ function getPluginsDir() {
4112
+ return PLUGINS_DIR;
4113
+ }
4114
+ async function discoverPlugins() {
4115
+ ensurePluginsDir();
4116
+ discoveredPlugins.clear();
4117
+ if (!existsSync8(PLUGINS_DIR)) {
4118
+ return [];
4119
+ }
4120
+ const zammyVersion = getZammyVersion2();
4121
+ const entries = readdirSync2(PLUGINS_DIR, { withFileTypes: true });
4122
+ const manifests = [];
4123
+ for (const entry of entries) {
4124
+ if (!entry.isDirectory()) continue;
4125
+ const pluginPath = join6(PLUGINS_DIR, entry.name);
4126
+ const manifestPath = join6(pluginPath, "zammy-plugin.json");
4127
+ if (!existsSync8(manifestPath)) continue;
4128
+ try {
4129
+ const manifestContent = readFileSync7(manifestPath, "utf-8");
4130
+ const manifest = JSON.parse(manifestContent);
4131
+ if (!manifest.name || !manifest.version || !manifest.main || !manifest.commands) {
4132
+ console.log(theme.warning(` ${symbols.warning} Invalid manifest for plugin in ${entry.name}`));
4133
+ continue;
4134
+ }
4135
+ const compatibility = checkVersionCompatibility(manifest, zammyVersion);
4136
+ if (!compatibility.compatible) {
4137
+ console.log(theme.warning(` ${symbols.warning} Plugin '${manifest.name}' skipped: ${compatibility.reason}`));
4138
+ continue;
4139
+ }
4140
+ discoveredPlugins.set(manifest.name, { manifest, path: pluginPath });
4141
+ manifests.push(manifest);
4142
+ } catch (error) {
4143
+ console.log(theme.warning(` ${symbols.warning} Failed to read manifest for ${entry.name}`));
4144
+ }
4145
+ }
4146
+ return manifests;
4147
+ }
4148
+ async function loadPlugin(name) {
4149
+ if (loadedPlugins.has(name)) {
4150
+ const existing = loadedPlugins.get(name);
4151
+ if (existing.state === "error") {
4152
+ return null;
4153
+ }
4154
+ return existing;
4155
+ }
4156
+ const discovered = discoveredPlugins.get(name);
4157
+ if (!discovered) {
4158
+ return null;
4159
+ }
4160
+ const { manifest, path: pluginPath } = discovered;
4161
+ try {
4162
+ const mainPath = join6(pluginPath, manifest.main);
4163
+ if (!existsSync8(mainPath)) {
4164
+ throw new Error(`Plugin entry point not found: ${mainPath}`);
4165
+ }
4166
+ const moduleUrl = pathToFileURL(mainPath).href;
4167
+ const module = await import(moduleUrl);
4168
+ const plugin = module.default;
4169
+ if (!plugin || typeof plugin.activate !== "function") {
4170
+ throw new Error("Plugin must export a default object with an activate function");
4171
+ }
4172
+ const api = createPluginAPI(manifest, pluginPath);
4173
+ try {
4174
+ await plugin.activate(api);
4175
+ } catch (activationError) {
4176
+ const msg = activationError instanceof Error ? activationError.message : String(activationError);
4177
+ throw new Error(`Plugin activation failed: ${msg}`);
4178
+ }
4179
+ const loaded = {
4180
+ manifest,
4181
+ instance: plugin,
4182
+ path: pluginPath,
4183
+ state: "active"
4184
+ };
4185
+ loadedPlugins.set(name, loaded);
4186
+ return loaded;
4187
+ } catch (error) {
4188
+ const errorMessage = error instanceof Error ? error.message : String(error);
4189
+ console.log(theme.error(` ${symbols.cross} Failed to load plugin '${name}': ${errorMessage}`));
4190
+ const failedPlugin = {
4191
+ manifest,
4192
+ instance: { activate: async () => {
4193
+ } },
4194
+ path: pluginPath,
4195
+ state: "error"
4196
+ };
4197
+ loadedPlugins.set(name, failedPlugin);
4198
+ return null;
4199
+ }
4200
+ }
4201
+ async function unloadPlugin(name) {
4202
+ const loaded = loadedPlugins.get(name);
4203
+ if (!loaded) {
4204
+ return false;
4205
+ }
4206
+ try {
4207
+ if (loaded.instance.deactivate) {
4208
+ await loaded.instance.deactivate();
4209
+ }
4210
+ unregisterPluginCommands(name);
4211
+ loadedPlugins.delete(name);
4212
+ return true;
4213
+ } catch (error) {
4214
+ const errorMessage = error instanceof Error ? error.message : String(error);
4215
+ console.log(theme.error(` ${symbols.cross} Failed to unload plugin '${name}': ${errorMessage}`));
4216
+ return false;
4217
+ }
4218
+ }
4219
+ function getDiscoveredPlugins() {
4220
+ return Array.from(discoveredPlugins.values()).map((p) => p.manifest);
4221
+ }
4222
+ function isPluginLoaded(name) {
4223
+ return loadedPlugins.has(name);
4224
+ }
4225
+ async function initPluginLoader() {
4226
+ const manifests = await discoverPlugins();
4227
+ for (const manifest of manifests) {
4228
+ for (const cmdName of manifest.commands) {
4229
+ const lazyExecute = async (args2) => {
4230
+ const loaded = await loadPlugin(manifest.name);
4231
+ if (!loaded) {
4232
+ console.log(theme.error(` ${symbols.cross} Failed to load plugin '${manifest.name}'`));
4233
+ console.log(theme.dim(` Try reinstalling: /plugin remove ${manifest.name} && /plugin install ${manifest.name}`));
4234
+ return;
4235
+ }
4236
+ const realCommand = getCommand(cmdName);
4237
+ if (realCommand && realCommand.execute !== lazyExecute) {
4238
+ await realCommand.execute(args2);
4239
+ } else {
4240
+ console.log(theme.error(` ${symbols.cross} Plugin '${manifest.name}' did not register command '${cmdName}'`));
4241
+ console.log(theme.dim(` The plugin may be misconfigured or corrupted.`));
4242
+ }
4243
+ };
4244
+ registerPluginCommand(
4245
+ {
4246
+ name: cmdName,
4247
+ description: `[${manifest.displayName || manifest.name}] Plugin command`,
4248
+ usage: `/${cmdName}`,
4249
+ execute: lazyExecute
4250
+ },
4251
+ manifest.name
4252
+ );
4253
+ }
4254
+ }
4255
+ }
4256
+
4257
+ // src/commands/plugin/list.ts
4258
+ async function listPlugins() {
4259
+ await discoverPlugins();
4260
+ const plugins = getDiscoveredPlugins();
4261
+ console.log("");
4262
+ if (plugins.length === 0) {
4263
+ console.log(` ${symbols.info} ${theme.dim("No plugins installed")}`);
4264
+ console.log("");
4265
+ console.log(` ${theme.dim("Install a plugin with:")} ${theme.primary("/plugin install <source>")}`);
4266
+ console.log(` ${theme.dim("Create a new plugin with:")} ${theme.primary("/plugin create")}`);
4267
+ console.log("");
4268
+ return;
4269
+ }
4270
+ console.log(` ${symbols.folder} ${theme.gradient("INSTALLED PLUGINS")} ${theme.dim(`(${plugins.length})`)}`);
4271
+ console.log("");
4272
+ for (const plugin of plugins) {
4273
+ const loaded = isPluginLoaded(plugin.name);
4274
+ const status = loaded ? theme.success("active") : theme.dim("idle");
4275
+ const statusIcon = loaded ? symbols.check : symbols.bullet;
4276
+ console.log(` ${statusIcon} ${theme.primary(plugin.displayName || plugin.name)} ${theme.dim(`v${plugin.version}`)}`);
4277
+ if (plugin.description) {
4278
+ console.log(` ${theme.dim(plugin.description)}`);
4279
+ }
4280
+ console.log(` ${theme.dim("Commands:")} ${plugin.commands.map((c) => theme.accent("/" + c)).join(", ")}`);
4281
+ if (plugin.permissions) {
4282
+ const perms = [];
4283
+ if (plugin.permissions.shell) perms.push("shell");
4284
+ if (plugin.permissions.filesystem) perms.push("fs");
4285
+ if (plugin.permissions.network) perms.push("net");
4286
+ if (perms.length > 0) {
4287
+ console.log(` ${theme.dim("Permissions:")} ${theme.warning(perms.join(", "))}`);
4288
+ }
4289
+ }
4290
+ console.log("");
4291
+ }
4292
+ }
4293
+
4294
+ // src/plugins/installer.ts
4295
+ import { existsSync as existsSync9, mkdirSync as mkdirSync3, cpSync, rmSync, readFileSync as readFileSync8, readdirSync as readdirSync3, createReadStream, createWriteStream } from "fs";
4296
+ import { join as join7, resolve as resolve3, dirname as dirname4 } from "path";
4297
+ import { execSync as execSync2 } from "child_process";
4298
+ import { tmpdir, platform as platform2 } from "os";
4299
+ import { createGunzip } from "zlib";
4300
+ import { pipeline } from "stream/promises";
4301
+ import { fileURLToPath as fileURLToPath3 } from "url";
4302
+ var isWindows = platform2() === "win32";
4303
+ function getZammyVersion3() {
4304
+ try {
4305
+ const __filename = fileURLToPath3(import.meta.url);
4306
+ const __dirname = dirname4(__filename);
4307
+ const possiblePaths = [
4308
+ join7(__dirname, "package.json"),
4309
+ join7(__dirname, "..", "package.json"),
4310
+ join7(__dirname, "..", "..", "package.json")
4311
+ ];
4312
+ for (const pkgPath of possiblePaths) {
4313
+ if (existsSync9(pkgPath)) {
4314
+ const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
4315
+ if (pkg.name === "zammy" && pkg.version) {
4316
+ return pkg.version;
4317
+ }
4318
+ }
4319
+ }
4320
+ return "0.0.0";
4321
+ } catch {
4322
+ return "0.0.0";
4323
+ }
4324
+ }
4325
+ function compareVersions2(a, b3) {
4326
+ const partsA = a.split(".").map((n) => parseInt(n, 10) || 0);
4327
+ const partsB = b3.split(".").map((n) => parseInt(n, 10) || 0);
4328
+ for (let i = 0; i < 3; i++) {
4329
+ const numA = partsA[i] || 0;
4330
+ const numB = partsB[i] || 0;
4331
+ if (numA < numB) return -1;
4332
+ if (numA > numB) return 1;
4333
+ }
4334
+ return 0;
4335
+ }
4336
+ function checkVersionCompatibility2(manifest) {
4337
+ const zammyVersion = getZammyVersion3();
4338
+ const minVersion = manifest.zammy?.minVersion;
4339
+ const maxVersion = manifest.zammy?.maxVersion;
4340
+ if (minVersion && compareVersions2(zammyVersion, minVersion) < 0) {
4341
+ return {
4342
+ compatible: false,
4343
+ reason: `Requires Zammy v${minVersion}+, but you have v${zammyVersion}`
4344
+ };
4345
+ }
4346
+ if (maxVersion && compareVersions2(zammyVersion, maxVersion) > 0) {
4347
+ return {
4348
+ compatible: false,
4349
+ reason: `Incompatible with Zammy v${zammyVersion} (max supported: v${maxVersion})`
4350
+ };
4351
+ }
4352
+ return { compatible: true };
4353
+ }
4354
+ async function extractTarGz(tarGzPath, destDir) {
4355
+ try {
4356
+ execSync2(`tar -xzf "${tarGzPath}" -C "${destDir}"`, {
4357
+ encoding: "utf-8",
4358
+ stdio: ["pipe", "pipe", "pipe"]
4359
+ });
4360
+ return;
4361
+ } catch {
4362
+ }
4363
+ const tarPath = tarGzPath.replace(/\.tgz$|\.tar\.gz$/, ".tar");
4364
+ await pipeline(
4365
+ createReadStream(tarGzPath),
4366
+ createGunzip(),
4367
+ createWriteStream(tarPath)
4368
+ );
4369
+ try {
4370
+ execSync2(`tar -xf "${tarPath}" -C "${destDir}"`, {
4371
+ encoding: "utf-8",
4372
+ stdio: ["pipe", "pipe", "pipe"]
4373
+ });
4374
+ rmSync(tarPath, { force: true });
4375
+ return;
4376
+ } catch {
4377
+ rmSync(tarPath, { force: true });
4378
+ }
4379
+ if (isWindows) {
4380
+ try {
4381
+ execSync2(`powershell -Command "tar -xf '${tarPath}' -C '${destDir}'"`, {
4382
+ encoding: "utf-8",
4383
+ stdio: ["pipe", "pipe", "pipe"]
4384
+ });
4385
+ return;
4386
+ } catch {
4387
+ throw new Error(
4388
+ "Unable to extract plugin archive. Please ensure tar is available.\nOn Windows 10+, tar should be built-in. Try running: tar --version"
4389
+ );
4390
+ }
4391
+ }
4392
+ throw new Error("Unable to extract plugin archive. Please ensure tar is installed.");
4393
+ }
4394
+ function validateManifest(manifest) {
4395
+ const errors = [];
4396
+ if (!manifest || typeof manifest !== "object") {
4397
+ return { valid: false, errors: ["Manifest must be an object"] };
4398
+ }
4399
+ const m = manifest;
4400
+ if (!m.name || typeof m.name !== "string") {
4401
+ errors.push('Missing or invalid "name" field');
4402
+ }
4403
+ if (!m.version || typeof m.version !== "string") {
4404
+ errors.push('Missing or invalid "version" field');
4405
+ }
4406
+ if (!m.main || typeof m.main !== "string") {
4407
+ errors.push('Missing or invalid "main" field');
4408
+ }
4409
+ if (!m.commands || !Array.isArray(m.commands) || m.commands.length === 0) {
4410
+ errors.push('Missing or invalid "commands" field (must be non-empty array)');
4411
+ }
4412
+ if (!m.zammy || typeof m.zammy !== "object") {
4413
+ errors.push('Missing or invalid "zammy" field');
4414
+ } else {
4415
+ const zammy = m.zammy;
4416
+ if (!zammy.minVersion || typeof zammy.minVersion !== "string") {
4417
+ errors.push('Missing or invalid "zammy.minVersion" field');
4418
+ }
4419
+ }
4420
+ return { valid: errors.length === 0, errors };
4421
+ }
4422
+ function checkConflicts(manifest) {
4423
+ const conflicts = [];
4424
+ for (const cmd of manifest.commands) {
4425
+ const conflict = checkCommandConflict(cmd);
4426
+ if (conflict.exists) {
4427
+ if (conflict.source === "core") {
4428
+ conflicts.push(`Command '/${cmd}' conflicts with core zammy command`);
4429
+ } else {
4430
+ conflicts.push(`Command '/${cmd}' conflicts with plugin '${conflict.pluginName}'`);
4431
+ }
4432
+ }
4433
+ }
4434
+ return { hasConflicts: conflicts.length > 0, conflicts };
4435
+ }
4436
+ function formatPermissions(manifest) {
4437
+ const perms = [];
4438
+ const p = manifest.permissions;
4439
+ if (!p) return perms;
4440
+ if (p.shell) {
4441
+ perms.push(`${symbols.warning} shell: Can run system commands`);
4442
+ }
4443
+ if (p.filesystem) {
4444
+ if (p.filesystem === true) {
4445
+ perms.push(`${symbols.warning} filesystem: Full file system access`);
4446
+ } else if (Array.isArray(p.filesystem)) {
4447
+ perms.push(`${symbols.info} filesystem: Access to ${p.filesystem.join(", ")}`);
4448
+ }
4449
+ }
4450
+ if (p.network) {
4451
+ if (p.network === true) {
4452
+ perms.push(`${symbols.warning} network: Full network access`);
4453
+ } else if (Array.isArray(p.network)) {
4454
+ perms.push(`${symbols.info} network: Access to ${p.network.join(", ")}`);
4455
+ }
4456
+ }
4457
+ return perms;
4458
+ }
4459
+ async function installFromLocal(sourcePath) {
4460
+ try {
4461
+ const absPath = resolve3(sourcePath);
4462
+ if (!existsSync9(absPath)) {
4463
+ return { success: false, error: `Path not found: ${absPath}` };
4464
+ }
4465
+ const manifestPath = join7(absPath, "zammy-plugin.json");
4466
+ if (!existsSync9(manifestPath)) {
4467
+ return { success: false, error: "No zammy-plugin.json found in source directory" };
4468
+ }
4469
+ const manifestContent = readFileSync8(manifestPath, "utf-8");
4470
+ let manifest;
4471
+ try {
4472
+ manifest = JSON.parse(manifestContent);
4473
+ } catch {
4474
+ return { success: false, error: "Invalid JSON in zammy-plugin.json" };
4475
+ }
4476
+ const validation = validateManifest(manifest);
4477
+ if (!validation.valid) {
4478
+ return { success: false, error: `Invalid manifest: ${validation.errors.join(", ")}` };
4479
+ }
4480
+ const versionCheck = checkVersionCompatibility2(manifest);
4481
+ if (!versionCheck.compatible) {
4482
+ return { success: false, error: versionCheck.reason };
4483
+ }
4484
+ const mainPath = join7(absPath, manifest.main);
4485
+ if (!existsSync9(mainPath)) {
4486
+ return { success: false, error: `Entry point not found: ${manifest.main}` };
4487
+ }
4488
+ ensurePluginsDir();
4489
+ const targetDir = join7(getPluginsDir(), manifest.name);
4490
+ if (existsSync9(targetDir)) {
4491
+ rmSync(targetDir, { recursive: true, force: true });
4492
+ }
4493
+ cpSync(absPath, targetDir, { recursive: true });
4494
+ return { success: true, manifest };
4495
+ } catch (error) {
4496
+ const message = error instanceof Error ? error.message : String(error);
4497
+ return { success: false, error: message };
4498
+ }
4499
+ }
4500
+ async function installFromNpm(packageName) {
4501
+ const tempDir = join7(tmpdir(), `zammy-plugin-${Date.now()}`);
4502
+ try {
4503
+ mkdirSync3(tempDir, { recursive: true });
4504
+ console.log(theme.dim(` Downloading ${packageName}...`));
4505
+ execSync2(`npm pack ${packageName} --pack-destination="${tempDir}"`, {
4506
+ encoding: "utf-8",
4507
+ stdio: ["pipe", "pipe", "pipe"],
4508
+ timeout: 6e4
4509
+ });
4510
+ const files = readdirSync3(tempDir);
4511
+ const tarball = files.find((f) => f.endsWith(".tgz"));
4512
+ if (!tarball) {
4513
+ return { success: false, error: "Failed to download package" };
4514
+ }
4515
+ const extractDir = join7(tempDir, "extract");
4516
+ mkdirSync3(extractDir);
4517
+ await extractTarGz(join7(tempDir, tarball), extractDir);
4518
+ const packageDir = join7(extractDir, "package");
4519
+ if (!existsSync9(packageDir)) {
4520
+ return { success: false, error: "Invalid package structure" };
4521
+ }
4522
+ return await installFromLocal(packageDir);
4523
+ } catch (error) {
4524
+ const message = error instanceof Error ? error.message : String(error);
4525
+ return { success: false, error: `npm install failed: ${message}` };
4526
+ } finally {
4527
+ try {
4528
+ rmSync(tempDir, { recursive: true, force: true });
4529
+ } catch {
4530
+ }
4531
+ }
4532
+ }
4533
+ async function installFromGithub(repo) {
4534
+ const tempDir = join7(tmpdir(), `zammy-plugin-${Date.now()}`);
4535
+ try {
4536
+ let repoPath = repo.replace(/^github:/, "");
4537
+ let branch = "main";
4538
+ if (repoPath.includes("#")) {
4539
+ [repoPath, branch] = repoPath.split("#");
4540
+ }
4541
+ mkdirSync3(tempDir, { recursive: true });
4542
+ console.log(theme.dim(` Cloning ${repoPath}...`));
4543
+ execSync2(`git clone --depth 1 --branch ${branch} https://github.com/${repoPath}.git "${tempDir}"`, {
4544
+ encoding: "utf-8",
4545
+ stdio: ["pipe", "pipe", "pipe"],
4546
+ timeout: 12e4
4547
+ });
4548
+ const pkgJsonPath = join7(tempDir, "package.json");
4549
+ if (existsSync9(pkgJsonPath)) {
4550
+ const pkgJson = JSON.parse(readFileSync8(pkgJsonPath, "utf-8"));
4551
+ if (pkgJson.scripts?.build) {
4552
+ console.log(theme.dim(` Installing dependencies...`));
4553
+ execSync2("npm install", { cwd: tempDir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 12e4 });
4554
+ console.log(theme.dim(` Building...`));
4555
+ execSync2("npm run build", { cwd: tempDir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 12e4 });
4556
+ }
4557
+ }
4558
+ return await installFromLocal(tempDir);
4559
+ } catch (error) {
4560
+ const message = error instanceof Error ? error.message : String(error);
4561
+ return { success: false, error: `GitHub install failed: ${message}` };
4562
+ } finally {
4563
+ try {
4564
+ rmSync(tempDir, { recursive: true, force: true });
4565
+ } catch {
4566
+ }
4567
+ }
4568
+ }
4569
+ async function installFromGit(url) {
4570
+ const tempDir = join7(tmpdir(), `zammy-plugin-${Date.now()}`);
4571
+ try {
4572
+ mkdirSync3(tempDir, { recursive: true });
4573
+ console.log(theme.dim(` Cloning from ${url}...`));
4574
+ execSync2(`git clone --depth 1 "${url}" "${tempDir}"`, {
4575
+ encoding: "utf-8",
4576
+ stdio: ["pipe", "pipe", "pipe"],
4577
+ timeout: 12e4
4578
+ });
4579
+ const pkgJsonPath = join7(tempDir, "package.json");
4580
+ if (existsSync9(pkgJsonPath)) {
4581
+ const pkgJson = JSON.parse(readFileSync8(pkgJsonPath, "utf-8"));
4582
+ if (pkgJson.scripts?.build) {
4583
+ console.log(theme.dim(` Installing dependencies...`));
4584
+ execSync2("npm install", { cwd: tempDir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 12e4 });
4585
+ console.log(theme.dim(` Building...`));
4586
+ execSync2("npm run build", { cwd: tempDir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 12e4 });
4587
+ }
4588
+ }
4589
+ return await installFromLocal(tempDir);
4590
+ } catch (error) {
4591
+ const message = error instanceof Error ? error.message : String(error);
4592
+ return { success: false, error: `Git install failed: ${message}` };
4593
+ } finally {
4594
+ try {
4595
+ rmSync(tempDir, { recursive: true, force: true });
4596
+ } catch {
4597
+ }
4598
+ }
4599
+ }
4600
+ function removePlugin(name) {
4601
+ try {
4602
+ const pluginDir = join7(getPluginsDir(), name);
4603
+ if (!existsSync9(pluginDir)) {
4604
+ return { success: false, error: `Plugin '${name}' not found` };
4605
+ }
4606
+ rmSync(pluginDir, { recursive: true, force: true });
4607
+ return { success: true };
4608
+ } catch (error) {
4609
+ const message = error instanceof Error ? error.message : String(error);
4610
+ return { success: false, error: message };
4611
+ }
4612
+ }
4613
+ function detectSourceType(source) {
4614
+ if (source.startsWith("./") || source.startsWith("/") || source.startsWith("..") || /^[A-Za-z]:/.test(source)) {
4615
+ return "local";
4616
+ }
4617
+ if (source.startsWith("github:")) {
4618
+ return "github";
4619
+ }
4620
+ if (source.includes("github.com")) {
4621
+ return "github";
4622
+ }
4623
+ if (source.endsWith(".git") || source.startsWith("git@") || source.startsWith("git://")) {
4624
+ return "git";
4625
+ }
4626
+ if (/^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(source)) {
4627
+ return "npm";
4628
+ }
4629
+ return "unknown";
4630
+ }
4631
+
4632
+ // src/commands/plugin/install.ts
4633
+ import * as readline from "readline";
4634
+ async function confirm(message) {
4635
+ const rl = readline.createInterface({
4636
+ input: process.stdin,
4637
+ output: process.stdout
4638
+ });
4639
+ return new Promise((resolve6) => {
4640
+ rl.question(`${message} [y/N] `, (answer) => {
4641
+ rl.close();
4642
+ resolve6(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
4643
+ });
4644
+ });
4645
+ }
4646
+ async function installPlugin(args2) {
4647
+ const source = args2[0];
4648
+ if (!source) {
4649
+ console.log(theme.error(` ${symbols.cross} No source specified`));
4650
+ console.log("");
4651
+ console.log(` ${theme.primary("Usage:")} /plugin install <source>`);
4652
+ console.log("");
4653
+ console.log(` ${theme.dim("Examples:")}`);
4654
+ console.log(` ${theme.dim("/plugin install ./my-plugin")}`);
4655
+ console.log(` ${theme.dim("/plugin install zammy-plugin-git")}`);
4656
+ console.log(` ${theme.dim("/plugin install github:user/repo")}`);
4657
+ console.log("");
4658
+ return;
4659
+ }
4660
+ console.log("");
4661
+ console.log(` ${symbols.rocket} ${theme.primary("Installing plugin...")}`);
4662
+ const sourceType = detectSourceType(source);
4663
+ if (sourceType === "unknown") {
4664
+ console.log(theme.error(` ${symbols.cross} Could not determine source type for: ${source}`));
4665
+ return;
4666
+ }
4667
+ console.log(theme.dim(` Source type: ${sourceType}`));
4668
+ let result;
4669
+ switch (sourceType) {
4670
+ case "local":
4671
+ result = await installFromLocal(source);
4672
+ break;
4673
+ case "npm":
4674
+ result = await installFromNpm(source);
4675
+ break;
4676
+ case "github":
4677
+ result = await installFromGithub(source);
4678
+ break;
4679
+ case "git":
4680
+ result = await installFromGit(source);
4681
+ break;
4682
+ default:
4683
+ result = { success: false, error: "Unknown source type" };
4684
+ }
4685
+ if (!result.success) {
4686
+ console.log(theme.error(` ${symbols.cross} Installation failed: ${result.error}`));
4687
+ return;
4688
+ }
4689
+ const manifest = result.manifest;
4690
+ const conflicts = checkConflicts(manifest);
4691
+ if (conflicts.hasConflicts) {
4692
+ console.log("");
4693
+ console.log(theme.warning(` ${symbols.warning} Command conflicts detected:`));
4694
+ for (const conflict of conflicts.conflicts) {
4695
+ console.log(` ${theme.dim("-")} ${conflict}`);
4696
+ }
4697
+ console.log("");
4698
+ const proceed = await confirm(` ${theme.warning("Continue anyway?")}`);
4699
+ if (!proceed) {
4700
+ console.log(theme.dim(" Installation cancelled"));
4701
+ return;
4702
+ }
4703
+ }
4704
+ const permissions = formatPermissions(manifest);
4705
+ if (permissions.length > 0) {
4706
+ console.log("");
4707
+ console.log(theme.warning(` ${symbols.warning} Plugin requests permissions:`));
4708
+ for (const perm of permissions) {
4709
+ console.log(` ${perm}`);
4710
+ }
4711
+ console.log("");
4712
+ }
4713
+ await discoverPlugins();
4714
+ for (const cmdName of manifest.commands) {
4715
+ if (getCommand(cmdName)) continue;
4716
+ const lazyExecute = async (args3) => {
4717
+ const loaded = await loadPlugin(manifest.name);
4718
+ if (!loaded) {
4719
+ console.log(theme.error(` ${symbols.cross} Failed to load plugin '${manifest.name}'`));
4720
+ return;
4721
+ }
4722
+ const realCommand = getCommand(cmdName);
4723
+ if (realCommand && realCommand.execute !== lazyExecute) {
4724
+ await realCommand.execute(args3);
4725
+ } else {
4726
+ console.log(theme.error(` ${symbols.cross} Plugin '${manifest.name}' did not register command '${cmdName}'`));
4727
+ }
4728
+ };
4729
+ registerPluginCommand(
4730
+ {
4731
+ name: cmdName,
4732
+ description: `[${manifest.displayName || manifest.name}] ${manifest.description || "Plugin command"}`,
4733
+ usage: `/${cmdName}`,
4734
+ execute: lazyExecute
4735
+ },
4736
+ manifest.name
4737
+ );
4738
+ }
4739
+ console.log("");
4740
+ console.log(` ${symbols.check} ${theme.success("Plugin installed successfully!")}`);
4741
+ console.log("");
4742
+ console.log(` ${theme.primary(manifest.displayName || manifest.name)} ${theme.dim(`v${manifest.version}`)}`);
4743
+ if (manifest.description) {
4744
+ console.log(` ${theme.dim(manifest.description)}`);
4745
+ }
4746
+ console.log("");
4747
+ console.log(` ${theme.dim("Commands added:")} ${manifest.commands.map((c) => theme.accent("/" + c)).join(", ")}`);
4748
+ console.log("");
4749
+ }
4750
+
4751
+ // src/commands/plugin/remove.ts
4752
+ import * as readline2 from "readline";
4753
+ async function confirm2(message) {
4754
+ const rl = readline2.createInterface({
4755
+ input: process.stdin,
4756
+ output: process.stdout
4757
+ });
4758
+ return new Promise((resolve6) => {
4759
+ rl.question(`${message} [y/N] `, (answer) => {
4760
+ rl.close();
4761
+ resolve6(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
4762
+ });
4763
+ });
4764
+ }
4765
+ async function removePluginCommand(args2) {
4766
+ const forceIndex = args2.findIndex((a) => a === "-y" || a === "--yes");
4767
+ const skipConfirm = forceIndex !== -1;
4768
+ if (skipConfirm) {
4769
+ args2.splice(forceIndex, 1);
4770
+ }
4771
+ const name = args2[0];
4772
+ if (!name) {
4773
+ console.log(theme.error(` ${symbols.cross} No plugin name specified`));
4774
+ console.log("");
4775
+ console.log(` ${theme.primary("Usage:")} /plugin remove <name>`);
4776
+ console.log("");
4777
+ await discoverPlugins();
4778
+ const plugins2 = getDiscoveredPlugins();
4779
+ if (plugins2.length > 0) {
4780
+ console.log(` ${theme.dim("Installed plugins:")}`);
4781
+ for (const p of plugins2) {
4782
+ console.log(` ${theme.accent(p.name)}`);
4783
+ }
4784
+ console.log("");
4785
+ }
4786
+ return;
4787
+ }
4788
+ await discoverPlugins();
4789
+ const plugins = getDiscoveredPlugins();
4790
+ const plugin = plugins.find((p) => p.name === name || p.displayName?.toLowerCase() === name.toLowerCase());
4791
+ if (!plugin) {
4792
+ console.log(theme.error(` ${symbols.cross} Plugin '${name}' not found`));
4793
+ console.log("");
4794
+ const similar = plugins.filter(
4795
+ (p) => p.name.includes(name) || name.includes(p.name) || p.displayName?.toLowerCase().includes(name.toLowerCase())
4796
+ );
4797
+ if (similar.length > 0) {
4798
+ console.log(` ${theme.dim("Did you mean:")}`);
4799
+ for (const p of similar) {
4800
+ console.log(` ${theme.accent(p.name)}`);
4801
+ }
4802
+ console.log("");
4803
+ }
4804
+ return;
4805
+ }
4806
+ console.log("");
4807
+ console.log(` ${theme.warning("About to remove:")}`);
4808
+ console.log(` ${theme.primary(plugin.displayName || plugin.name)} ${theme.dim(`v${plugin.version}`)}`);
4809
+ console.log(` ${theme.dim("Commands:")} ${plugin.commands.map((c) => "/" + c).join(", ")}`);
4810
+ console.log("");
4811
+ if (!skipConfirm) {
4812
+ const proceed = await confirm2(` ${theme.warning("Remove this plugin?")}`);
4813
+ if (!proceed) {
4814
+ console.log(theme.dim(" Removal cancelled"));
4815
+ return;
4816
+ }
4817
+ }
4818
+ await unloadPlugin(plugin.name);
4819
+ const result = removePlugin(plugin.name);
4820
+ if (!result.success) {
4821
+ console.log(theme.error(` ${symbols.cross} Failed to remove: ${result.error}`));
4822
+ return;
4823
+ }
4824
+ await discoverPlugins();
4825
+ console.log("");
4826
+ console.log(` ${symbols.check} ${theme.success("Plugin removed successfully")}`);
4827
+ console.log("");
4828
+ }
4829
+
4830
+ // src/commands/plugin/create.ts
4831
+ import { existsSync as existsSync10, mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
4832
+ import { join as join8, resolve as resolve4 } from "path";
4833
+ import * as readline3 from "readline";
4834
+ async function prompt(message, defaultValue) {
4835
+ const rl = readline3.createInterface({
4836
+ input: process.stdin,
4837
+ output: process.stdout
4838
+ });
4839
+ const suffix = defaultValue ? ` ${theme.dim(`(${defaultValue})`)}` : "";
4840
+ return new Promise((resolvePromise) => {
4841
+ rl.question(` ${message}${suffix}: `, (answer) => {
4842
+ rl.close();
4843
+ resolvePromise(answer.trim() || defaultValue || "");
4844
+ });
4845
+ });
4846
+ }
4847
+ function generateManifest(name, displayName, description, commandName) {
4848
+ return JSON.stringify({
4849
+ name,
4850
+ version: "1.0.0",
4851
+ displayName,
4852
+ description,
4853
+ main: "./dist/index.js",
4854
+ commands: [commandName],
4855
+ zammy: {
4856
+ minVersion: "1.3.0"
4857
+ },
4858
+ permissions: {}
4859
+ }, null, 2);
4860
+ }
4861
+ function generatePackageJson(name, description) {
4862
+ return JSON.stringify({
4863
+ name,
4864
+ version: "1.0.0",
4865
+ description,
4866
+ type: "module",
4867
+ main: "dist/index.js",
4868
+ scripts: {
4869
+ build: "tsc",
4870
+ dev: "tsc --watch"
4871
+ },
4872
+ keywords: ["zammy-plugin"],
4873
+ devDependencies: {
4874
+ typescript: "^5.3.0"
4875
+ }
4876
+ }, null, 2);
4877
+ }
4878
+ function generateTsConfig() {
4879
+ return JSON.stringify({
4880
+ compilerOptions: {
4881
+ target: "ES2022",
4882
+ module: "NodeNext",
4883
+ moduleResolution: "NodeNext",
4884
+ outDir: "./dist",
4885
+ rootDir: "./src",
4886
+ strict: true,
4887
+ esModuleInterop: true,
4888
+ skipLibCheck: true,
4889
+ declaration: true
4890
+ },
4891
+ include: ["src/**/*"]
4892
+ }, null, 2);
4893
+ }
4894
+ function generateEntryPoint(commandName, displayName) {
4895
+ return `// ${displayName} - A zammy plugin
4896
+
4897
+ // Plugin types (these match zammy's PluginAPI interface)
4898
+ interface Command {
4899
+ name: string;
4900
+ description: string;
4901
+ usage: string;
4902
+ execute: (args: string[]) => Promise<void>;
4903
+ }
4904
+
4905
+ interface PluginAPI {
4906
+ registerCommand(command: Command): void;
4907
+ ui: {
4908
+ theme: {
4909
+ primary: (text: string) => string;
4910
+ secondary: (text: string) => string;
4911
+ accent: (text: string) => string;
4912
+ success: (text: string) => string;
4913
+ warning: (text: string) => string;
4914
+ error: (text: string) => string;
4915
+ info: (text: string) => string;
4916
+ dim: (text: string) => string;
4917
+ gradient: (text: string) => string;
4918
+ };
4919
+ symbols: {
4920
+ check: string;
4921
+ cross: string;
4922
+ star: string;
4923
+ arrow: string;
4924
+ bullet: string;
4925
+ folder: string;
4926
+ file: string;
4927
+ warning: string;
4928
+ info: string;
4929
+ rocket: string;
4930
+ sparkles: string;
4931
+ };
4932
+ };
4933
+ storage: {
4934
+ get<T>(key: string): T | undefined;
4935
+ set<T>(key: string, value: T): void;
4936
+ delete(key: string): void;
4937
+ };
4938
+ log: {
4939
+ info(message: string): void;
4940
+ warn(message: string): void;
4941
+ error(message: string): void;
4942
+ };
4943
+ context: {
4944
+ pluginName: string;
4945
+ pluginVersion: string;
4946
+ zammyVersion: string;
4947
+ dataDir: string;
4948
+ cwd: string;
4949
+ };
4950
+ }
4951
+
4952
+ interface ZammyPlugin {
4953
+ activate(api: PluginAPI): Promise<void> | void;
4954
+ deactivate?(): Promise<void> | void;
4955
+ }
4956
+
4957
+ const plugin: ZammyPlugin = {
4958
+ activate(api: PluginAPI) {
4959
+ const { theme, symbols } = api.ui;
4960
+
4961
+ api.registerCommand({
4962
+ name: '${commandName}',
4963
+ description: 'My custom command',
4964
+ usage: '/${commandName} [args]',
4965
+ async execute(args: string[]) {
4966
+ console.log('');
4967
+ console.log(\` \${symbols.star} \${theme.gradient('${displayName.toUpperCase()}')}\`);
4968
+ console.log('');
4969
+ console.log(\` \${theme.success('Hello from ${displayName}!')}\`);
4970
+
4971
+ if (args.length > 0) {
4972
+ console.log(\` \${theme.dim('Arguments:')} \${args.join(' ')}\`);
4973
+ }
4974
+
4975
+ console.log('');
4976
+ },
4977
+ });
4978
+
4979
+ api.log.info('Plugin activated!');
4980
+ },
4981
+
4982
+ deactivate() {
4983
+ // Cleanup if needed
4984
+ },
4985
+ };
4986
+
4987
+ export default plugin;
4988
+ `;
4989
+ }
4990
+ function generateReadme(name, displayName, description, commandName) {
4991
+ return `# ${displayName}
4992
+
4993
+ ${description}
4994
+
4995
+ ## Installation
4996
+
4997
+ \`\`\`bash
4998
+ /plugin install ./${name}
4999
+ \`\`\`
5000
+
5001
+ ## Usage
5002
+
5003
+ \`\`\`bash
5004
+ /${commandName} [args]
5005
+ \`\`\`
5006
+
5007
+ ## Development
5008
+
5009
+ \`\`\`bash
5010
+ # Build the plugin
5011
+ npm run build
5012
+
5013
+ # Watch for changes
5014
+ npm run dev
5015
+ \`\`\`
5016
+
5017
+ ## License
5018
+
5019
+ MIT
5020
+ `;
5021
+ }
5022
+ async function createPlugin(args2) {
5023
+ console.log("");
5024
+ console.log(` ${symbols.sparkle} ${theme.gradient("CREATE NEW PLUGIN")}`);
5025
+ console.log("");
5026
+ let name = args2[0] || "";
5027
+ if (!name) {
5028
+ name = await prompt("Plugin name (e.g., my-plugin)", "zammy-plugin-example");
5029
+ }
5030
+ name = name.toLowerCase().replace(/\s+/g, "-");
5031
+ if (!name.startsWith("zammy-plugin-")) {
5032
+ }
5033
+ const displayName = await prompt("Display name", name.replace(/^zammy-plugin-/, "").replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()));
5034
+ const description = await prompt("Description", "A zammy plugin");
5035
+ const commandName = await prompt("Main command name", name.replace(/^zammy-plugin-/, "").replace(/-/g, ""));
5036
+ const targetDir = resolve4(process.cwd(), name);
5037
+ if (existsSync10(targetDir)) {
5038
+ console.log(theme.error(` ${symbols.cross} Directory already exists: ${name}`));
5039
+ return;
5040
+ }
5041
+ console.log("");
5042
+ console.log(theme.dim(` Creating plugin in ${targetDir}...`));
5043
+ try {
5044
+ mkdirSync4(targetDir);
5045
+ mkdirSync4(join8(targetDir, "src"));
5046
+ mkdirSync4(join8(targetDir, "dist"));
5047
+ writeFileSync5(join8(targetDir, "zammy-plugin.json"), generateManifest(name, displayName, description, commandName));
5048
+ writeFileSync5(join8(targetDir, "package.json"), generatePackageJson(name, description));
5049
+ writeFileSync5(join8(targetDir, "tsconfig.json"), generateTsConfig());
5050
+ writeFileSync5(join8(targetDir, "src", "index.ts"), generateEntryPoint(commandName, displayName));
5051
+ writeFileSync5(join8(targetDir, "README.md"), generateReadme(name, displayName, description, commandName));
5052
+ console.log("");
5053
+ console.log(` ${symbols.check} ${theme.success("Plugin created successfully!")}`);
5054
+ console.log("");
5055
+ console.log(` ${theme.primary("Next steps:")}`);
5056
+ console.log(` ${theme.dim("1.")} cd ${name}`);
5057
+ console.log(` ${theme.dim("2.")} npm install`);
5058
+ console.log(` ${theme.dim("3.")} npm run build`);
5059
+ console.log(` ${theme.dim("4.")} /plugin install ./${name}`);
5060
+ console.log("");
5061
+ console.log(` ${theme.dim("Edit")} ${theme.accent("src/index.ts")} ${theme.dim("to customize your plugin")}`);
5062
+ console.log("");
5063
+ } catch (error) {
5064
+ const message = error instanceof Error ? error.message : String(error);
5065
+ console.log(theme.error(` ${symbols.cross} Failed to create plugin: ${message}`));
5066
+ }
5067
+ }
5068
+
5069
+ // src/commands/plugin/index.ts
5070
+ registerCommand({
5071
+ name: "plugin",
5072
+ description: "Manage zammy plugins",
5073
+ usage: "/plugin <list|install|remove|create> [args]",
5074
+ async execute(args2) {
5075
+ const subcommand = args2[0]?.toLowerCase();
5076
+ if (!subcommand || subcommand === "help") {
5077
+ console.log("");
5078
+ console.log(` ${symbols.gear} ${theme.gradient("PLUGIN MANAGER")}`);
5079
+ console.log("");
5080
+ console.log(` ${theme.primary("Usage:")} /plugin <command> [args]`);
5081
+ console.log("");
5082
+ console.log(` ${theme.primary("Commands:")}`);
5083
+ console.log(` ${theme.accent("list")} ${theme.dim("Show installed plugins")}`);
5084
+ console.log(` ${theme.accent("install")} <source> ${theme.dim("Install a plugin")}`);
5085
+ console.log(` ${theme.accent("remove")} <name> ${theme.dim("Remove a plugin")}`);
5086
+ console.log(` ${theme.accent("create")} [name] ${theme.dim("Create a new plugin")}`);
5087
+ console.log("");
5088
+ console.log(` ${theme.primary("Install sources:")}`);
5089
+ console.log(` ${theme.dim("./path/to/plugin")} ${theme.dim("Local directory")}`);
5090
+ console.log(` ${theme.dim("package-name")} ${theme.dim("npm package")}`);
5091
+ console.log(` ${theme.dim("github:user/repo")} ${theme.dim("GitHub repository")}`);
5092
+ console.log(` ${theme.dim("https://...git")} ${theme.dim("Git URL")}`);
5093
+ console.log("");
5094
+ return;
5095
+ }
5096
+ switch (subcommand) {
5097
+ case "list":
5098
+ case "ls":
5099
+ await listPlugins();
5100
+ break;
5101
+ case "install":
5102
+ case "i":
5103
+ case "add":
5104
+ await installPlugin(args2.slice(1));
5105
+ break;
5106
+ case "remove":
5107
+ case "rm":
5108
+ case "uninstall":
5109
+ await removePluginCommand(args2.slice(1));
5110
+ break;
5111
+ case "create":
5112
+ case "new":
5113
+ case "init":
5114
+ await createPlugin(args2.slice(1));
5115
+ break;
5116
+ default:
5117
+ console.log(theme.error(` ${symbols.cross} Unknown subcommand: ${subcommand}`));
5118
+ console.log(theme.dim(` Use '/plugin help' to see available commands`));
2902
5119
  }
2903
5120
  }
2904
5121
  });
2905
5122
 
2906
5123
  // src/cli.ts
2907
- import { exec, execSync, spawn } from "child_process";
2908
- import { existsSync as existsSync4, statSync, readFileSync as readFileSync3, readdirSync, writeFileSync as writeFileSync3, watchFile, unwatchFile } from "fs";
2909
- import { resolve as resolve2, extname, basename, join as join3 } from "path";
2910
- import { homedir as homedir3, platform as platform2, networkInterfaces } from "os";
5124
+ import { exec, execSync as execSync3, spawn } from "child_process";
5125
+ import { existsSync as existsSync11, statSync as statSync2, readFileSync as readFileSync9, readdirSync as readdirSync4, writeFileSync as writeFileSync6, watchFile, unwatchFile } from "fs";
5126
+ import { resolve as resolve5, extname, basename as basename4, join as join9 } from "path";
5127
+ import { homedir as homedir5, platform as platform3, networkInterfaces } from "os";
2911
5128
  import chalk5 from "chalk";
2912
- var isWindows = platform2() === "win32";
5129
+ var isWindows2 = platform3() === "win32";
2913
5130
  function translateCommand(cmd) {
2914
- if (!isWindows) return cmd;
5131
+ if (!isWindows2) return cmd;
2915
5132
  const parts = cmd.trim().split(/\s+/);
2916
5133
  const command = parts[0].toLowerCase();
2917
5134
  const args2 = parts.slice(1);
@@ -2963,21 +5180,21 @@ function translateCommand(cmd) {
2963
5180
  function handleCd(args2) {
2964
5181
  let targetPath = args2.trim();
2965
5182
  if (!targetPath || targetPath === "~") {
2966
- targetPath = homedir3();
5183
+ targetPath = homedir5();
2967
5184
  } else if (targetPath.startsWith("~/")) {
2968
- targetPath = resolve2(homedir3(), targetPath.slice(2));
5185
+ targetPath = resolve5(homedir5(), targetPath.slice(2));
2969
5186
  } else if (targetPath === "-") {
2970
5187
  console.log(theme.dim(process.cwd()));
2971
5188
  return;
2972
5189
  } else {
2973
- targetPath = resolve2(process.cwd(), targetPath);
5190
+ targetPath = resolve5(process.cwd(), targetPath);
2974
5191
  }
2975
- if (!existsSync4(targetPath)) {
5192
+ if (!existsSync11(targetPath)) {
2976
5193
  console.log(`${miniSlime.sad} ${theme.error(`Directory not found: ${targetPath}`)}`);
2977
5194
  return;
2978
5195
  }
2979
5196
  try {
2980
- const stats = statSync(targetPath);
5197
+ const stats = statSync2(targetPath);
2981
5198
  if (!stats.isDirectory()) {
2982
5199
  console.log(`${miniSlime.sad} ${theme.error(`Not a directory: ${targetPath}`)}`);
2983
5200
  return;
@@ -2992,13 +5209,13 @@ function handlePwd() {
2992
5209
  console.log(theme.primary(process.cwd()));
2993
5210
  }
2994
5211
  function handleCat(args2) {
2995
- const filePath = resolve2(process.cwd(), args2.trim());
2996
- if (!existsSync4(filePath)) {
5212
+ const filePath = resolve5(process.cwd(), args2.trim());
5213
+ if (!existsSync11(filePath)) {
2997
5214
  console.log(`${miniSlime.sad} ${theme.error(`File not found: ${args2}`)}`);
2998
5215
  return;
2999
5216
  }
3000
5217
  try {
3001
- const content = readFileSync3(filePath, "utf-8");
5218
+ const content = readFileSync9(filePath, "utf-8");
3002
5219
  const ext = extname(filePath).toLowerCase();
3003
5220
  if ([".js", ".ts", ".jsx", ".tsx", ".json", ".css", ".html", ".py", ".go", ".rs"].includes(ext)) {
3004
5221
  console.log(highlightSyntax(content, ext));
@@ -3097,13 +5314,13 @@ function handleLs(args2) {
3097
5314
  const showAll = parts.some((a) => a === "-a" || a === "-la" || a === "-al");
3098
5315
  const showLong = parts.some((a) => a === "-l" || a === "-la" || a === "-al");
3099
5316
  const pathArgs = parts.filter((a) => !a.startsWith("-"));
3100
- const targetPath = pathArgs.length > 0 ? resolve2(process.cwd(), pathArgs[0]) : process.cwd();
3101
- if (!existsSync4(targetPath)) {
5317
+ const targetPath = pathArgs.length > 0 ? resolve5(process.cwd(), pathArgs[0]) : process.cwd();
5318
+ if (!existsSync11(targetPath)) {
3102
5319
  console.log(theme.error(`Directory not found: ${targetPath}`));
3103
5320
  return;
3104
5321
  }
3105
5322
  try {
3106
- const entries = readdirSync(targetPath, { withFileTypes: true });
5323
+ const entries = readdirSync4(targetPath, { withFileTypes: true });
3107
5324
  const filtered = showAll ? entries : entries.filter((e) => !e.name.startsWith("."));
3108
5325
  console.log("");
3109
5326
  console.log(theme.dim(` ${targetPath}`));
@@ -3119,14 +5336,14 @@ function handleLs(args2) {
3119
5336
  return a.name.localeCompare(b3.name);
3120
5337
  });
3121
5338
  for (const entry of sorted) {
3122
- const fullPath = resolve2(targetPath, entry.name);
5339
+ const fullPath = resolve5(targetPath, entry.name);
3123
5340
  const isDir = entry.isDirectory();
3124
5341
  const ext = isDir ? "dir" : extname(entry.name).toLowerCase();
3125
5342
  const iconInfo = fileIcons[ext] || fileIcons["default"];
3126
5343
  let line = ` ${iconInfo.icon} `;
3127
5344
  if (showLong) {
3128
5345
  try {
3129
- const stats = statSync(fullPath);
5346
+ const stats = statSync2(fullPath);
3130
5347
  const size = isDir ? chalk5.dim(" <DIR>") : formatSize(stats.size);
3131
5348
  const date = stats.mtime.toLocaleDateString("en-US", { month: "short", day: "2-digit", year: "numeric" });
3132
5349
  line += chalk5.dim(date.padEnd(13)) + size + " ";
@@ -3148,19 +5365,19 @@ function handleLs(args2) {
3148
5365
  function handleTree(args2, maxDepth = 3) {
3149
5366
  const parts = args2.trim().split(/\s+/).filter(Boolean);
3150
5367
  const pathArgs = parts.filter((a) => !a.startsWith("-"));
3151
- const targetPath = pathArgs.length > 0 ? resolve2(process.cwd(), pathArgs[0]) : process.cwd();
3152
- if (!existsSync4(targetPath)) {
5368
+ const targetPath = pathArgs.length > 0 ? resolve5(process.cwd(), pathArgs[0]) : process.cwd();
5369
+ if (!existsSync11(targetPath)) {
3153
5370
  console.log(theme.error(`Directory not found: ${targetPath}`));
3154
5371
  return;
3155
5372
  }
3156
5373
  console.log("");
3157
- console.log(theme.primary(` \u{1F4C1} ${basename(targetPath)}/`));
5374
+ console.log(theme.primary(` \u{1F4C1} ${basename4(targetPath)}/`));
3158
5375
  let dirCount = 0;
3159
5376
  let fileCount = 0;
3160
5377
  function printTree(dir, prefix, depth) {
3161
5378
  if (depth > maxDepth) return;
3162
5379
  try {
3163
- const entries = readdirSync(dir, { withFileTypes: true }).filter((e) => !e.name.startsWith(".") && !["node_modules", ".git", "dist", "build"].includes(e.name)).sort((a, b3) => {
5380
+ const entries = readdirSync4(dir, { withFileTypes: true }).filter((e) => !e.name.startsWith(".") && !["node_modules", ".git", "dist", "build"].includes(e.name)).sort((a, b3) => {
3164
5381
  if (a.isDirectory() && !b3.isDirectory()) return -1;
3165
5382
  if (!a.isDirectory() && b3.isDirectory()) return 1;
3166
5383
  return a.name.localeCompare(b3.name);
@@ -3173,7 +5390,7 @@ function handleTree(args2, maxDepth = 3) {
3173
5390
  if (entry.isDirectory()) {
3174
5391
  dirCount++;
3175
5392
  console.log(theme.dim(prefix + connector) + iconInfo.icon + " " + chalk5.hex(iconInfo.color).bold(entry.name + "/"));
3176
- printTree(resolve2(dir, entry.name), prefix + (isLast ? " " : "\u2502 "), depth + 1);
5393
+ printTree(resolve5(dir, entry.name), prefix + (isLast ? " " : "\u2502 "), depth + 1);
3177
5394
  } else {
3178
5395
  fileCount++;
3179
5396
  console.log(theme.dim(prefix + connector) + iconInfo.icon + " " + chalk5.hex(iconInfo.color)(entry.name));
@@ -3187,18 +5404,18 @@ function handleTree(args2, maxDepth = 3) {
3187
5404
  console.log(theme.dim(` ${dirCount} directories, ${fileCount} files`));
3188
5405
  console.log("");
3189
5406
  }
3190
- var bookmarksFile = join3(homedir3(), ".zammy-bookmarks.json");
5407
+ var bookmarksFile = join9(homedir5(), ".zammy-bookmarks.json");
3191
5408
  function loadBookmarks() {
3192
5409
  try {
3193
- if (existsSync4(bookmarksFile)) {
3194
- return JSON.parse(readFileSync3(bookmarksFile, "utf-8"));
5410
+ if (existsSync11(bookmarksFile)) {
5411
+ return JSON.parse(readFileSync9(bookmarksFile, "utf-8"));
3195
5412
  }
3196
5413
  } catch {
3197
5414
  }
3198
5415
  return {};
3199
5416
  }
3200
5417
  function saveBookmarks(bookmarks) {
3201
- writeFileSync3(bookmarksFile, JSON.stringify(bookmarks, null, 2));
5418
+ writeFileSync6(bookmarksFile, JSON.stringify(bookmarks, null, 2));
3202
5419
  }
3203
5420
  function handleBookmark(args2) {
3204
5421
  const parts = args2.trim().split(/\s+/);
@@ -3254,7 +5471,7 @@ function handleBookmark(args2) {
3254
5471
  console.log(theme.primary(" \u{1F4CD} Directory Bookmarks"));
3255
5472
  console.log("");
3256
5473
  for (const key of keys.sort()) {
3257
- const exists = existsSync4(bookmarks[key]);
5474
+ const exists = existsSync11(bookmarks[key]);
3258
5475
  const status = exists ? theme.success(symbols.check) : theme.error(symbols.cross);
3259
5476
  console.log(` ${status} ${theme.primary(key.padEnd(15))} ${theme.dim("\u2192")} ${bookmarks[key]}`);
3260
5477
  }
@@ -3271,10 +5488,10 @@ function handleFind(args2) {
3271
5488
  function searchDir(dir, depth = 0) {
3272
5489
  if (depth > 5 || results.length >= maxResults) return;
3273
5490
  try {
3274
- const entries = readdirSync(dir, { withFileTypes: true });
5491
+ const entries = readdirSync4(dir, { withFileTypes: true });
3275
5492
  for (const entry of entries) {
3276
5493
  if (entry.name.startsWith(".") || ["node_modules", ".git", "dist", "build"].includes(entry.name)) continue;
3277
- const fullPath = resolve2(dir, entry.name);
5494
+ const fullPath = resolve5(dir, entry.name);
3278
5495
  const matchPattern = pattern.replace(/\*/g, ".*").replace(/\?/g, ".");
3279
5496
  const regex = new RegExp(matchPattern, "i");
3280
5497
  if (regex.test(entry.name)) {
@@ -3298,7 +5515,7 @@ function handleFind(args2) {
3298
5515
  const relativePath = result.path.replace(searchPath, ".").replace(/\\/g, "/");
3299
5516
  const ext = result.isDir ? "dir" : extname(result.path).toLowerCase();
3300
5517
  const iconInfo = fileIcons[ext] || fileIcons["default"];
3301
- const fileName = basename(result.path);
5518
+ const fileName = basename4(result.path);
3302
5519
  const dirPath = relativePath.slice(0, -fileName.length);
3303
5520
  console.log(` ${iconInfo.icon} ${theme.dim(dirPath)}${chalk5.hex(iconInfo.color)(fileName)}${result.isDir ? "/" : ""}`);
3304
5521
  }
@@ -3310,8 +5527,8 @@ function handleFind(args2) {
3310
5527
  console.log("");
3311
5528
  }
3312
5529
  function handleDu(args2) {
3313
- const targetPath = args2.trim() ? resolve2(process.cwd(), args2.trim()) : process.cwd();
3314
- if (!existsSync4(targetPath)) {
5530
+ const targetPath = args2.trim() ? resolve5(process.cwd(), args2.trim()) : process.cwd();
5531
+ if (!existsSync11(targetPath)) {
3315
5532
  console.log(theme.error(` Path not found: ${targetPath}`));
3316
5533
  return;
3317
5534
  }
@@ -3319,10 +5536,10 @@ function handleDu(args2) {
3319
5536
  console.log("");
3320
5537
  console.log(theme.dim(" Calculating sizes..."));
3321
5538
  try {
3322
- const entries = readdirSync(targetPath, { withFileTypes: true });
5539
+ const entries = readdirSync4(targetPath, { withFileTypes: true });
3323
5540
  for (const entry of entries) {
3324
5541
  if (entry.name.startsWith(".")) continue;
3325
- const fullPath = resolve2(targetPath, entry.name);
5542
+ const fullPath = resolve5(targetPath, entry.name);
3326
5543
  let size = 0;
3327
5544
  let skipped = false;
3328
5545
  try {
@@ -3331,10 +5548,10 @@ function handleDu(args2) {
3331
5548
  skipped = true;
3332
5549
  size = 0;
3333
5550
  } else {
3334
- size = getDirSize(fullPath);
5551
+ size = getDirSize2(fullPath);
3335
5552
  }
3336
5553
  } else {
3337
- size = statSync(fullPath).size;
5554
+ size = statSync2(fullPath).size;
3338
5555
  }
3339
5556
  items.push({ name: entry.name, size, isDir: entry.isDirectory(), skipped });
3340
5557
  } catch {
@@ -3348,7 +5565,7 @@ function handleDu(args2) {
3348
5565
  items.sort((a, b3) => b3.size - a.size);
3349
5566
  const totalSize = items.reduce((sum, item) => sum + item.size, 0);
3350
5567
  console.log("");
3351
- console.log(theme.primary(` \u{1F4CA} Disk Usage: ${basename(targetPath)}`));
5568
+ console.log(theme.primary(` \u{1F4CA} Disk Usage: ${basename4(targetPath)}`));
3352
5569
  console.log(theme.dim(` Total: ${formatSizeSimple(totalSize)}`));
3353
5570
  console.log("");
3354
5571
  const maxItems = 15;
@@ -3377,19 +5594,19 @@ function handleDu(args2) {
3377
5594
  console.log("");
3378
5595
  }
3379
5596
  var skipDirs = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", ".nuxt", "coverage", ".cache", "__pycache__", "venv", ".venv"]);
3380
- function getDirSize(dir, depth = 0, maxDepth = 4) {
5597
+ function getDirSize2(dir, depth = 0, maxDepth = 4) {
3381
5598
  if (depth > maxDepth) return 0;
3382
5599
  let size = 0;
3383
5600
  try {
3384
- const entries = readdirSync(dir, { withFileTypes: true });
5601
+ const entries = readdirSync4(dir, { withFileTypes: true });
3385
5602
  for (const entry of entries) {
3386
5603
  if (skipDirs.has(entry.name)) continue;
3387
- const fullPath = resolve2(dir, entry.name);
5604
+ const fullPath = resolve5(dir, entry.name);
3388
5605
  if (entry.isDirectory()) {
3389
- size += getDirSize(fullPath, depth + 1, maxDepth);
5606
+ size += getDirSize2(fullPath, depth + 1, maxDepth);
3390
5607
  } else {
3391
5608
  try {
3392
- size += statSync(fullPath).size;
5609
+ size += statSync2(fullPath).size;
3393
5610
  } catch {
3394
5611
  }
3395
5612
  }
@@ -3408,7 +5625,7 @@ function handleGit(args2) {
3408
5625
  const subcommand = args2.trim().split(/\s+/)[0] || "status";
3409
5626
  console.log("");
3410
5627
  try {
3411
- execSync("git rev-parse --is-inside-work-tree", { stdio: "pipe", timeout: 5e3 });
5628
+ execSync3("git rev-parse --is-inside-work-tree", { stdio: "pipe", timeout: 5e3 });
3412
5629
  } catch {
3413
5630
  console.log(theme.error(" Not a git repository"));
3414
5631
  console.log("");
@@ -3418,10 +5635,10 @@ function handleGit(args2) {
3418
5635
  switch (subcommand) {
3419
5636
  case "status":
3420
5637
  case "s": {
3421
- const branch = execSync("git branch --show-current", { encoding: "utf-8", timeout: 5e3 }).trim();
5638
+ const branch = execSync3("git branch --show-current", { encoding: "utf-8", timeout: 5e3 }).trim();
3422
5639
  console.log(` ${symbols.rocket} ${theme.primary("Branch:")} ${chalk5.hex("#98C379")(branch)}`);
3423
5640
  console.log("");
3424
- const status = execSync("git status --porcelain", { encoding: "utf-8", timeout: 5e3 });
5641
+ const status = execSync3("git status --porcelain", { encoding: "utf-8", timeout: 5e3 });
3425
5642
  if (!status.trim()) {
3426
5643
  console.log(` ${symbols.check} ${theme.success("Working tree clean")}`);
3427
5644
  } else {
@@ -3458,7 +5675,7 @@ function handleGit(args2) {
3458
5675
  console.log("");
3459
5676
  }
3460
5677
  }
3461
- const log = execSync("git log --oneline -3", { encoding: "utf-8", timeout: 3e3 }).trim();
5678
+ const log = execSync3("git log --oneline -3", { encoding: "utf-8", timeout: 3e3 }).trim();
3462
5679
  if (log) {
3463
5680
  console.log(theme.dim(" Recent commits:"));
3464
5681
  log.split("\n").forEach((line) => {
@@ -3470,7 +5687,7 @@ function handleGit(args2) {
3470
5687
  }
3471
5688
  case "log":
3472
5689
  case "l": {
3473
- const log = execSync("git log --oneline -10", { encoding: "utf-8", timeout: 3e3 }).trim();
5690
+ const log = execSync3("git log --oneline -10", { encoding: "utf-8", timeout: 3e3 }).trim();
3474
5691
  console.log(theme.primary(" \u{1F4DC} Recent Commits"));
3475
5692
  console.log("");
3476
5693
  log.split("\n").forEach((line) => {
@@ -3481,7 +5698,7 @@ function handleGit(args2) {
3481
5698
  }
3482
5699
  case "branch":
3483
5700
  case "b": {
3484
- const branches = execSync("git branch -a", { encoding: "utf-8", timeout: 3e3 }).trim();
5701
+ const branches = execSync3("git branch -a", { encoding: "utf-8", timeout: 3e3 }).trim();
3485
5702
  console.log(theme.primary(" \u{1F33F} Branches"));
3486
5703
  console.log("");
3487
5704
  branches.split("\n").forEach((line) => {
@@ -3494,7 +5711,7 @@ function handleGit(args2) {
3494
5711
  break;
3495
5712
  }
3496
5713
  default:
3497
- const result = execSync(`git ${args2}`, { encoding: "utf-8", timeout: 1e4 });
5714
+ const result = execSync3(`git ${args2}`, { encoding: "utf-8", timeout: 1e4 });
3498
5715
  console.log(result);
3499
5716
  }
3500
5717
  } catch (error) {
@@ -3503,23 +5720,23 @@ function handleGit(args2) {
3503
5720
  }
3504
5721
  console.log("");
3505
5722
  }
3506
- var isMac = platform2() === "darwin";
3507
- var isLinux = platform2() === "linux";
5723
+ var isMac = platform3() === "darwin";
5724
+ var isLinux = platform3() === "linux";
3508
5725
  function getClipboardCopyCmd() {
3509
- if (isWindows) return "clip";
5726
+ if (isWindows2) return "clip";
3510
5727
  if (isMac) return "pbcopy";
3511
5728
  try {
3512
- execSync("which xclip", { stdio: "pipe" });
5729
+ execSync3("which xclip", { stdio: "pipe" });
3513
5730
  return "xclip -selection clipboard";
3514
5731
  } catch {
3515
5732
  return "xsel --clipboard --input";
3516
5733
  }
3517
5734
  }
3518
5735
  function getClipboardPasteCmd() {
3519
- if (isWindows) return 'powershell -command "Get-Clipboard"';
5736
+ if (isWindows2) return 'powershell -command "Get-Clipboard"';
3520
5737
  if (isMac) return "pbpaste";
3521
5738
  try {
3522
- execSync("which xclip", { stdio: "pipe" });
5739
+ execSync3("which xclip", { stdio: "pipe" });
3523
5740
  return "xclip -selection clipboard -o";
3524
5741
  } catch {
3525
5742
  return "xsel --clipboard --output";
@@ -3533,10 +5750,10 @@ function handleClipboard(args2) {
3533
5750
  if (action === "copy" && content) {
3534
5751
  try {
3535
5752
  const copyCmd = getClipboardCopyCmd();
3536
- if (isWindows) {
3537
- execSync(`echo ${content} | ${copyCmd}`, { stdio: "pipe", timeout: 3e3 });
5753
+ if (isWindows2) {
5754
+ execSync3(`echo ${content} | ${copyCmd}`, { stdio: "pipe", timeout: 3e3 });
3538
5755
  } else {
3539
- execSync(`echo "${content}" | ${copyCmd}`, { stdio: "pipe", timeout: 3e3 });
5756
+ execSync3(`echo "${content}" | ${copyCmd}`, { stdio: "pipe", timeout: 3e3 });
3540
5757
  }
3541
5758
  console.log(` ${symbols.check} ${theme.success("Copied to clipboard")}`);
3542
5759
  } catch {
@@ -3548,7 +5765,7 @@ function handleClipboard(args2) {
3548
5765
  } else if (action === "paste") {
3549
5766
  try {
3550
5767
  const pasteCmd = getClipboardPasteCmd();
3551
- const result = execSync(pasteCmd, { encoding: "utf-8", timeout: 3e3 }).trim();
5768
+ const result = execSync3(pasteCmd, { encoding: "utf-8", timeout: 3e3 }).trim();
3552
5769
  console.log(` ${symbols.clipboard} ${theme.dim("Clipboard contents:")}`);
3553
5770
  console.log("");
3554
5771
  console.log(result);
@@ -3559,14 +5776,14 @@ function handleClipboard(args2) {
3559
5776
  }
3560
5777
  }
3561
5778
  } else if (action === "file" && parts[1]) {
3562
- const filePath = resolve2(process.cwd(), parts[1]);
3563
- if (existsSync4(filePath)) {
5779
+ const filePath = resolve5(process.cwd(), parts[1]);
5780
+ if (existsSync11(filePath)) {
3564
5781
  try {
3565
5782
  const copyCmd = getClipboardCopyCmd();
3566
- if (isWindows) {
3567
- execSync(`type "${filePath}" | ${copyCmd}`, { stdio: "pipe", timeout: 5e3 });
5783
+ if (isWindows2) {
5784
+ execSync3(`type "${filePath}" | ${copyCmd}`, { stdio: "pipe", timeout: 5e3 });
3568
5785
  } else {
3569
- execSync(`cat "${filePath}" | ${copyCmd}`, { stdio: "pipe", timeout: 5e3 });
5786
+ execSync3(`cat "${filePath}" | ${copyCmd}`, { stdio: "pipe", timeout: 5e3 });
3570
5787
  }
3571
5788
  console.log(` ${symbols.check} ${theme.success("File contents copied to clipboard")}`);
3572
5789
  } catch {
@@ -3592,14 +5809,14 @@ function handleClipboard(args2) {
3592
5809
  console.log("");
3593
5810
  }
3594
5811
  function handlePretty(args2) {
3595
- const filePath = resolve2(process.cwd(), args2.trim());
3596
- if (!existsSync4(filePath)) {
5812
+ const filePath = resolve5(process.cwd(), args2.trim());
5813
+ if (!existsSync11(filePath)) {
3597
5814
  console.log(theme.error(` File not found: ${args2}`));
3598
5815
  return;
3599
5816
  }
3600
5817
  console.log("");
3601
5818
  try {
3602
- const content = readFileSync3(filePath, "utf-8");
5819
+ const content = readFileSync9(filePath, "utf-8");
3603
5820
  const ext = extname(filePath).toLowerCase();
3604
5821
  if (ext === ".json") {
3605
5822
  const parsed = JSON.parse(content);
@@ -3646,14 +5863,14 @@ function handleWatch(args2) {
3646
5863
  console.log("");
3647
5864
  return;
3648
5865
  }
3649
- const filePath = resolve2(process.cwd(), action);
3650
- if (!existsSync4(filePath)) {
5866
+ const filePath = resolve5(process.cwd(), action);
5867
+ if (!existsSync11(filePath)) {
3651
5868
  console.log(theme.error(` File not found: ${action}`));
3652
5869
  console.log("");
3653
5870
  return;
3654
5871
  }
3655
5872
  try {
3656
- const stats = statSync(filePath);
5873
+ const stats = statSync2(filePath);
3657
5874
  if (stats.isDirectory()) {
3658
5875
  console.log(theme.error(` Cannot watch a directory: ${action}`));
3659
5876
  console.log(theme.dim(" Please specify a file path"));
@@ -3669,12 +5886,12 @@ function handleWatch(args2) {
3669
5886
  unwatchFile(activeWatcher);
3670
5887
  }
3671
5888
  activeWatcher = filePath;
3672
- let lastSize = statSync(filePath).size;
5889
+ let lastSize = statSync2(filePath).size;
3673
5890
  console.log(` ${symbols.info} ${theme.primary("Watching:")} ${filePath}`);
3674
5891
  console.log(theme.dim(' (Type "!watch stop" to stop watching)'));
3675
5892
  console.log("");
3676
5893
  try {
3677
- const content = readFileSync3(filePath, "utf-8");
5894
+ const content = readFileSync9(filePath, "utf-8");
3678
5895
  const lines = content.split("\n").slice(-10);
3679
5896
  lines.forEach((line) => console.log(theme.dim(line)));
3680
5897
  } catch {
@@ -3683,7 +5900,7 @@ function handleWatch(args2) {
3683
5900
  watchFile(filePath, { interval: 500 }, (curr, prev) => {
3684
5901
  if (curr.size > lastSize) {
3685
5902
  try {
3686
- const newContent = readFileSync3(filePath, "utf-8");
5903
+ const newContent = readFileSync9(filePath, "utf-8");
3687
5904
  const allLines = newContent.split("\n");
3688
5905
  const oldLines = Math.floor(prev.size / 50);
3689
5906
  const newLines = allLines.slice(-Math.max(1, allLines.length - oldLines));
@@ -3706,7 +5923,7 @@ function handleServe(args2) {
3706
5923
  console.log(theme.dim(" Press Ctrl+C to stop"));
3707
5924
  console.log("");
3708
5925
  try {
3709
- const npx = isWindows ? "npx.cmd" : "npx";
5926
+ const npx = isWindows2 ? "npx.cmd" : "npx";
3710
5927
  spawn(npx, ["serve", "-p", String(port)], {
3711
5928
  cwd: process.cwd(),
3712
5929
  stdio: "inherit"
@@ -3721,8 +5938,8 @@ function handlePs() {
3721
5938
  console.log("");
3722
5939
  try {
3723
5940
  let result;
3724
- if (isWindows) {
3725
- result = execSync('tasklist /FO CSV /NH | findstr /V "System Idle"', { encoding: "utf-8", timeout: 5e3 });
5941
+ if (isWindows2) {
5942
+ result = execSync3('tasklist /FO CSV /NH | findstr /V "System Idle"', { encoding: "utf-8", timeout: 5e3 });
3726
5943
  const lines = result.trim().split("\n").slice(0, 15);
3727
5944
  console.log(theme.dim(" Name PID Memory"));
3728
5945
  console.log(theme.dim(" \u2500".repeat(25)));
@@ -3736,7 +5953,7 @@ function handlePs() {
3736
5953
  }
3737
5954
  });
3738
5955
  } else {
3739
- result = execSync("ps aux | head -15", { encoding: "utf-8", timeout: 5e3 });
5956
+ result = execSync3("ps aux | head -15", { encoding: "utf-8", timeout: 5e3 });
3740
5957
  console.log(result);
3741
5958
  }
3742
5959
  } catch {
@@ -3795,10 +6012,10 @@ function handleIp() {
3795
6012
  try {
3796
6013
  let result;
3797
6014
  try {
3798
- result = execSync("curl -s ifconfig.me", { encoding: "utf-8", timeout: 5e3 }).trim();
6015
+ result = execSync3("curl -s ifconfig.me", { encoding: "utf-8", timeout: 5e3 }).trim();
3799
6016
  } catch {
3800
- if (isWindows) {
3801
- result = execSync('powershell -command "(Invoke-WebRequest -Uri ifconfig.me -UseBasicParsing).Content"', { encoding: "utf-8", timeout: 5e3 }).trim();
6017
+ if (isWindows2) {
6018
+ result = execSync3('powershell -command "(Invoke-WebRequest -Uri ifconfig.me -UseBasicParsing).Content"', { encoding: "utf-8", timeout: 5e3 }).trim();
3802
6019
  } else {
3803
6020
  throw new Error("curl not available");
3804
6021
  }
@@ -3864,13 +6081,13 @@ async function handleHttp(args2) {
3864
6081
  let result;
3865
6082
  try {
3866
6083
  const curlCmd = method === "HEAD" ? `curl -sI "${fullUrl}"` : `curl -s -X ${method} "${fullUrl}"`;
3867
- result = execSync(curlCmd, { encoding: "utf-8", timeout: 1e4 });
6084
+ result = execSync3(curlCmd, { encoding: "utf-8", timeout: 1e4 });
3868
6085
  } catch {
3869
- if (isWindows) {
6086
+ if (isWindows2) {
3870
6087
  if (method === "HEAD") {
3871
- result = execSync(`powershell -command "(Invoke-WebRequest -Uri '${fullUrl}' -Method Head -UseBasicParsing).Headers | ConvertTo-Json"`, { encoding: "utf-8", timeout: 1e4 });
6088
+ result = execSync3(`powershell -command "(Invoke-WebRequest -Uri '${fullUrl}' -Method Head -UseBasicParsing).Headers | ConvertTo-Json"`, { encoding: "utf-8", timeout: 1e4 });
3872
6089
  } else {
3873
- result = execSync(`powershell -command "(Invoke-WebRequest -Uri '${fullUrl}' -Method ${method} -UseBasicParsing).Content"`, { encoding: "utf-8", timeout: 1e4 });
6090
+ result = execSync3(`powershell -command "(Invoke-WebRequest -Uri '${fullUrl}' -Method ${method} -UseBasicParsing).Content"`, { encoding: "utf-8", timeout: 1e4 });
3874
6091
  }
3875
6092
  } else {
3876
6093
  throw new Error("curl not available");
@@ -3908,22 +6125,22 @@ function handleDiff(args2) {
3908
6125
  console.log("");
3909
6126
  return;
3910
6127
  }
3911
- const path1 = resolve2(process.cwd(), file1);
3912
- const path2 = resolve2(process.cwd(), file2);
3913
- if (!existsSync4(path1)) {
6128
+ const path1 = resolve5(process.cwd(), file1);
6129
+ const path2 = resolve5(process.cwd(), file2);
6130
+ if (!existsSync11(path1)) {
3914
6131
  console.log(theme.error(` File not found: ${file1}`));
3915
6132
  console.log("");
3916
6133
  return;
3917
6134
  }
3918
- if (!existsSync4(path2)) {
6135
+ if (!existsSync11(path2)) {
3919
6136
  console.log(theme.error(` File not found: ${file2}`));
3920
6137
  console.log("");
3921
6138
  return;
3922
6139
  }
3923
6140
  try {
3924
- const content1 = readFileSync3(path1, "utf-8").split("\n");
3925
- const content2 = readFileSync3(path2, "utf-8").split("\n");
3926
- console.log(theme.primary(` Comparing: ${basename(file1)} \u2194 ${basename(file2)}`));
6141
+ const content1 = readFileSync9(path1, "utf-8").split("\n");
6142
+ const content2 = readFileSync9(path2, "utf-8").split("\n");
6143
+ console.log(theme.primary(` Comparing: ${basename4(file1)} \u2194 ${basename4(file2)}`));
3927
6144
  console.log("");
3928
6145
  const maxLines = Math.max(content1.length, content2.length);
3929
6146
  let differences = 0;
@@ -3950,18 +6167,18 @@ function handleDiff(args2) {
3950
6167
  }
3951
6168
  console.log("");
3952
6169
  }
3953
- var aliasesFile = join3(homedir3(), ".zammy-aliases.json");
6170
+ var aliasesFile = join9(homedir5(), ".zammy-aliases.json");
3954
6171
  function loadAliases() {
3955
6172
  try {
3956
- if (existsSync4(aliasesFile)) {
3957
- return JSON.parse(readFileSync3(aliasesFile, "utf-8"));
6173
+ if (existsSync11(aliasesFile)) {
6174
+ return JSON.parse(readFileSync9(aliasesFile, "utf-8"));
3958
6175
  }
3959
6176
  } catch {
3960
6177
  }
3961
6178
  return {};
3962
6179
  }
3963
6180
  function saveAliases(aliases) {
3964
- writeFileSync3(aliasesFile, JSON.stringify(aliases, null, 2));
6181
+ writeFileSync6(aliasesFile, JSON.stringify(aliases, null, 2));
3965
6182
  }
3966
6183
  function handleAlias(args2) {
3967
6184
  const parts = args2.trim().split(/\s+/);
@@ -3991,7 +6208,7 @@ function handleAlias(args2) {
3991
6208
  console.log(theme.dim(` Running: ${aliases[name]}`));
3992
6209
  console.log("");
3993
6210
  try {
3994
- const result = execSync(aliases[name], { encoding: "utf-8", cwd: process.cwd(), timeout: 3e4 });
6211
+ const result = execSync3(aliases[name], { encoding: "utf-8", cwd: process.cwd(), timeout: 3e4 });
3995
6212
  console.log(result);
3996
6213
  } catch (error) {
3997
6214
  const err = error;
@@ -4023,7 +6240,7 @@ function handleNotify(args2) {
4023
6240
  const message = args2.trim() || "Notification from Zammy CLI";
4024
6241
  console.log("");
4025
6242
  try {
4026
- if (isWindows) {
6243
+ if (isWindows2) {
4027
6244
  const ps = `
4028
6245
  [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null
4029
6246
  [Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] | Out-Null
@@ -4032,10 +6249,10 @@ function handleNotify(args2) {
4032
6249
  $notifier = [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier("Zammy CLI")
4033
6250
  $notifier.Show([Windows.UI.Notifications.ToastNotification]::new($template))
4034
6251
  `;
4035
- execSync(`powershell -command "${ps.replace(/\n/g, " ")}"`, { stdio: "pipe", timeout: 5e3 });
6252
+ execSync3(`powershell -command "${ps.replace(/\n/g, " ")}"`, { stdio: "pipe", timeout: 5e3 });
4036
6253
  } else {
4037
- const cmd = platform2() === "darwin" ? `osascript -e 'display notification "${message}" with title "Zammy CLI"'` : `notify-send "Zammy CLI" "${message}"`;
4038
- execSync(cmd, { stdio: "pipe", timeout: 3e3 });
6254
+ const cmd = platform3() === "darwin" ? `osascript -e 'display notification "${message}" with title "Zammy CLI"'` : `notify-send "Zammy CLI" "${message}"`;
6255
+ execSync3(cmd, { stdio: "pipe", timeout: 3e3 });
4039
6256
  }
4040
6257
  console.log(` ${symbols.check} ${theme.success("Notification sent")}`);
4041
6258
  } catch {
@@ -4065,7 +6282,7 @@ function handleGrep(args2) {
4065
6282
  function searchFile(filePath) {
4066
6283
  if (results.length >= maxResults) return;
4067
6284
  try {
4068
- const content = readFileSync3(filePath, "utf-8");
6285
+ const content = readFileSync9(filePath, "utf-8");
4069
6286
  const lines = content.split("\n");
4070
6287
  lines.forEach((line, index) => {
4071
6288
  if (results.length >= maxResults) return;
@@ -4083,11 +6300,11 @@ function handleGrep(args2) {
4083
6300
  function searchDir(dir, depth = 0) {
4084
6301
  if (depth > 4 || results.length >= maxResults) return;
4085
6302
  try {
4086
- const entries = readdirSync(dir, { withFileTypes: true });
6303
+ const entries = readdirSync4(dir, { withFileTypes: true });
4087
6304
  for (const entry of entries) {
4088
6305
  if (results.length >= maxResults) break;
4089
6306
  if (entry.name.startsWith(".") || ["node_modules", ".git", "dist", "build"].includes(entry.name)) continue;
4090
- const fullPath = resolve2(dir, entry.name);
6307
+ const fullPath = resolve5(dir, entry.name);
4091
6308
  if (entry.isDirectory()) {
4092
6309
  searchDir(fullPath, depth + 1);
4093
6310
  } else {
@@ -4123,25 +6340,25 @@ function handleGrep(args2) {
4123
6340
  console.log("");
4124
6341
  }
4125
6342
  function handleWc(args2) {
4126
- const filePath = resolve2(process.cwd(), args2.trim());
6343
+ const filePath = resolve5(process.cwd(), args2.trim());
4127
6344
  console.log("");
4128
6345
  if (!args2.trim()) {
4129
6346
  console.log(theme.error(" Usage: wc <file>"));
4130
6347
  console.log("");
4131
6348
  return;
4132
6349
  }
4133
- if (!existsSync4(filePath)) {
6350
+ if (!existsSync11(filePath)) {
4134
6351
  console.log(theme.error(` File not found: ${args2}`));
4135
6352
  console.log("");
4136
6353
  return;
4137
6354
  }
4138
6355
  try {
4139
- const content = readFileSync3(filePath, "utf-8");
6356
+ const content = readFileSync9(filePath, "utf-8");
4140
6357
  const lines = content.split("\n").length;
4141
6358
  const words = content.split(/\s+/).filter((w) => w.length > 0).length;
4142
6359
  const chars = content.length;
4143
6360
  const bytes = Buffer.byteLength(content, "utf-8");
4144
- console.log(theme.primary(` \u{1F4CA} ${basename(args2)}`));
6361
+ console.log(theme.primary(` \u{1F4CA} ${basename4(args2)}`));
4145
6362
  console.log("");
4146
6363
  console.log(` ${chalk5.hex("#61AFEF")(lines.toLocaleString().padStart(8))} ${theme.dim("lines")}`);
4147
6364
  console.log(` ${chalk5.hex("#98C379")(words.toLocaleString().padStart(8))} ${theme.dim("words")}`);
@@ -4166,16 +6383,16 @@ function handleHead(args2) {
4166
6383
  console.log("");
4167
6384
  return;
4168
6385
  }
4169
- const fullPath = resolve2(process.cwd(), filePath);
4170
- if (!existsSync4(fullPath)) {
6386
+ const fullPath = resolve5(process.cwd(), filePath);
6387
+ if (!existsSync11(fullPath)) {
4171
6388
  console.log(theme.error(` File not found: ${filePath}`));
4172
6389
  console.log("");
4173
6390
  return;
4174
6391
  }
4175
6392
  try {
4176
- const content = readFileSync3(fullPath, "utf-8");
6393
+ const content = readFileSync9(fullPath, "utf-8");
4177
6394
  const fileLines = content.split("\n").slice(0, lines);
4178
- console.log(theme.dim(` First ${lines} lines of ${basename(filePath)}:`));
6395
+ console.log(theme.dim(` First ${lines} lines of ${basename4(filePath)}:`));
4179
6396
  console.log("");
4180
6397
  fileLines.forEach((line, i) => {
4181
6398
  console.log(` ${chalk5.dim((i + 1).toString().padStart(4))} ${line}`);
@@ -4380,9 +6597,9 @@ async function parseAndExecute(input) {
4380
6597
  }
4381
6598
 
4382
6599
  // src/index.ts
4383
- import { readdirSync as readdirSync2, readFileSync as readFileSync4 } from "fs";
4384
- import { fileURLToPath } from "url";
4385
- import { dirname, join as join4 } from "path";
6600
+ import { readdirSync as readdirSync5, readFileSync as readFileSync10 } from "fs";
6601
+ import { fileURLToPath as fileURLToPath4 } from "url";
6602
+ import { dirname as dirname5, join as join10 } from "path";
4386
6603
  import chalk6 from "chalk";
4387
6604
  var SLIME_COLOR = "#9B59B6";
4388
6605
  var EYE_COLOR = "#1A1A2E";
@@ -4467,10 +6684,10 @@ function setIdle() {
4467
6684
  var args = process.argv.slice(2);
4468
6685
  if (args.includes("--version") || args.includes("-v")) {
4469
6686
  try {
4470
- const __filename = fileURLToPath(import.meta.url);
4471
- const __dirname = dirname(__filename);
4472
- const pkgPath = join4(__dirname, "..", "package.json");
4473
- const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
6687
+ const __filename = fileURLToPath4(import.meta.url);
6688
+ const __dirname = dirname5(__filename);
6689
+ const pkgPath = join10(__dirname, "..", "package.json");
6690
+ const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
4474
6691
  console.log(`zammy v${pkg.version}`);
4475
6692
  } catch {
4476
6693
  console.log("zammy v1.0.0");
@@ -4691,7 +6908,7 @@ function completer(line) {
4691
6908
  const afterCommand = line.slice("/asciiart ".length);
4692
6909
  const searchTerm = afterCommand.startsWith("@") ? afterCommand.slice(1) : afterCommand;
4693
6910
  try {
4694
- const files = readdirSync2(process.cwd());
6911
+ const files = readdirSync5(process.cwd());
4695
6912
  const imageFiles = files.filter((f) => {
4696
6913
  const ext = f.toLowerCase().slice(f.lastIndexOf("."));
4697
6914
  return IMAGE_EXTENSIONS.includes(ext);
@@ -4721,7 +6938,8 @@ async function main() {
4721
6938
  console.log(theme.dim("For full features, run in a proper terminal or use: zammy --simple\n"));
4722
6939
  }
4723
6940
  await displayBanner(isSimpleMode);
4724
- const rl = readline.createInterface({
6941
+ await initPluginLoader();
6942
+ const rl = readline4.createInterface({
4725
6943
  input: process.stdin,
4726
6944
  output: process.stdout,
4727
6945
  terminal: isTTY,