14
README.md
14
README.md
@ -1,8 +1,16 @@
|
||||
# PromptX
|
||||
<div align="center">
|
||||
<img src="assets/logo/Creative PromptX Duck Logo 4.svg" alt="PromptX Logo" width="120" height="120"/>
|
||||
<p><strong>AI应用原生的专业能力增强系统</strong></p>
|
||||
<p>通过MCP协议为Claude Desktop等AI应用提供专业角色、记忆管理和知识体系</p>
|
||||
|
||||
> **AI应用原生的专业能力增强系统** - 通过MCP协议为Claude Desktop等AI应用提供专业角色、记忆管理和知识体系
|
||||
<p>
|
||||
<strong><a href="README.md">中文</a></strong> |
|
||||
<a href="README_EN.md">English</a> |
|
||||
<a href="https://github.com/Deepractice/PromptX/issues">Issues</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
**中文** | [English](README_EN.md) | [Issues](https://github.com/Deepractice/PromptX/issues)
|
||||
<br/>
|
||||
|
||||
## 🚀 一键启动 - AI应用直连
|
||||
|
||||
|
||||
14
README_EN.md
14
README_EN.md
@ -1,3 +1,17 @@
|
||||
<div align="center">
|
||||
<img src="assets/logo/Creative PromptX Duck Logo 4.svg" alt="PromptX Logo" width="120" height="120"/>
|
||||
<p><strong>AI-native professional capability enhancement system</strong></p>
|
||||
<p>Provides specialized roles, memory management, and knowledge systems for AI applications through MCP protocol</p>
|
||||
|
||||
<p>
|
||||
<a href="README.md">中文</a> |
|
||||
<strong><a href="README_EN.md">English</a></strong> |
|
||||
<a href="https://github.com/Deepractice/PromptX/issues">Issues</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
|
||||
# PromptX
|
||||
|
||||
> **AI-native professional capability enhancement system** - Provides specialized roles, memory management, and knowledge systems for AI applications through MCP protocol
|
||||
|
||||
24
assets/logo/Creative PromptX Duck Logo 4.svg
Normal file
24
assets/logo/Creative PromptX Duck Logo 4.svg
Normal file
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generator: visioncortex VTracer 0.6.4 -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="1024" height="1024">
|
||||
<path d="M0 0 C337.92 0 675.84 0 1024 0 C1024 337.92 1024 675.84 1024 1024 C686.08 1024 348.16 1024 0 1024 C0 686.08 0 348.16 0 0 Z " fill="#F6FCFE" transform="translate(0,0)"/>
|
||||
<path d="M0 0 C0.91 0 1.82 0 2.76 0.01 C68.96 0.26 132.08 20.16 183.75 62.31 C184.28 62.74 184.8 63.16 185.34 63.6 C200.13 75.59 214.22 89.13 225.75 104.31 C226.74 105.55 227.72 106.78 228.71 108.02 C258.28 145.27 276.91 189.54 284.75 236.31 C285 237.77 285 237.77 285.25 239.25 C290.21 269.69 290.35 304.33 282.75 334.31 C281.47 334.32 280.19 334.32 278.88 334.32 C266.8 334.36 254.72 334.42 242.64 334.5 C236.43 334.54 230.22 334.57 224.01 334.58 C218.02 334.6 212.02 334.63 206.02 334.68 C203.74 334.7 201.46 334.7 199.17 334.7 C195.96 334.71 192.76 334.73 189.55 334.77 C188.61 334.76 187.68 334.76 186.72 334.75 C175.31 334.93 166.27 339.15 158.07 346.9 C151.8 353.54 148.7 362.5 148.54 371.54 C148.52 372.58 148.5 373.62 148.48 374.69 C148.47 375.81 148.45 376.93 148.43 378.08 C148.4 379.8 148.4 379.8 148.37 381.56 C148.31 385.23 148.25 388.89 148.19 392.56 C148.14 395.05 148.1 397.54 148.06 400.02 C147.95 406.12 147.85 412.22 147.75 418.31 C113.26 424.27 77.33 426.45 46.79 406.5 C33.83 397.15 24.81 386.29 21.75 370.31 C19.27 346.8 30.37 326.38 43.89 308.04 C44.72 306.9 44.72 306.9 45.57 305.74 C46.67 304.23 47.79 302.73 48.93 301.24 C51.91 297.36 51.91 297.36 52.74 292.71 C52.41 291.92 52.09 291.13 51.75 290.31 C51.39 289.29 51.03 288.27 50.66 287.22 C50.24 286.09 49.81 284.97 49.38 283.81 C38.83 254.76 39.4 220.13 52.55 191.93 C58.54 179.75 67.34 167.92 78.75 160.31 C72.68 143 53.48 130.66 37.71 123.01 C31.2 119.96 24.64 117.36 17.75 115.31 C17.1 115.12 16.46 114.92 15.79 114.72 C-12.08 106.6 -43.53 109.86 -69.19 123.17 C-74.93 126.37 -80.12 130.24 -85.25 134.31 C-86.02 134.91 -86.78 135.5 -87.57 136.12 C-105.98 151.24 -116.24 174.5 -124.52 196.17 C-133.32 218.85 -148.02 233.64 -170.24 243.49 C-179.16 247.15 -188.6 249.3 -198.23 249.59 C-199.33 249.63 -200.43 249.67 -201.57 249.71 C-203.87 249.78 -206.18 249.85 -208.48 249.92 C-209.57 249.96 -210.67 249.99 -211.8 250.03 C-212.8 250.06 -213.79 250.09 -214.82 250.12 C-217.26 250.16 -217.26 250.16 -219.25 251.31 C-219.82 255.91 -219.55 258.45 -216.81 262.25 C-199.99 282.5 -173.42 290.67 -148.02 293.12 C-143.3 293.45 -138.6 293.37 -133.87 293.3 C-131.71 293.29 -129.55 293.27 -127.39 293.25 C-124.03 293.22 -120.67 293.19 -117.31 293.14 C-114.04 293.1 -110.77 293.08 -107.5 293.05 C-106.01 293.03 -106.01 293.03 -104.48 293 C-97.99 292.97 -90.71 293.22 -85.63 297.76 C-82.17 301.81 -80.87 304.64 -80.98 309.98 C-82.71 325.19 -96.67 334.64 -107.51 343.83 C-139.48 371.04 -163.25 404.63 -168.25 447.31 C-169.36 467.61 -166.4 486.05 -160.25 505.31 C-159.64 507.33 -159.04 509.35 -158.44 511.38 C-158.04 512.69 -157.65 514 -157.25 515.31 C-165.09 510.58 -171.84 504.7 -178.69 498.68 C-179.96 497.57 -181.23 496.47 -182.5 495.37 C-184.43 493.7 -186.35 492.01 -188.25 490.31 C-189.18 489.48 -190.12 488.65 -191.08 487.8 C-241.86 442.28 -270.63 376.19 -278.25 309.31 C-278.4 307.99 -278.4 307.99 -278.56 306.65 C-286.01 235.31 -262.89 161.36 -218.05 105.74 C-217.12 104.6 -216.19 103.45 -215.25 102.31 C-214.54 101.45 -214.54 101.45 -213.83 100.57 C-203.89 88.51 -193.22 77.12 -180.92 67.43 C-179.13 66.01 -177.37 64.54 -175.64 63.04 C-127.99 21.51 -62.71 -0.27 0 0 Z " fill="#000000" transform="translate(476.25,97.6875)"/>
|
||||
<path d="M0 0 C1.12 -0.01 1.12 -0.01 2.26 -0.02 C4.75 -0.04 7.23 -0.03 9.71 -0.03 C11.5 -0.03 13.29 -0.04 15.08 -0.05 C19.92 -0.07 24.77 -0.08 29.61 -0.07 C33.66 -0.07 37.71 -0.08 41.76 -0.09 C51.32 -0.11 60.88 -0.11 70.44 -0.11 C80.29 -0.1 90.14 -0.12 99.99 -0.16 C108.45 -0.19 116.92 -0.2 125.39 -0.2 C130.44 -0.2 135.49 -0.2 140.54 -0.23 C145.3 -0.25 150.05 -0.25 154.8 -0.23 C156.54 -0.22 158.28 -0.23 160.02 -0.24 C177.71 -0.38 177.71 -0.38 185.28 5.39 C193.17 13.56 193.58 21.82 193.55 32.72 C193.56 33.95 193.56 35.17 193.57 36.44 C193.58 39.79 193.58 43.14 193.58 46.49 C193.58 49.29 193.58 52.09 193.59 54.89 C193.6 61.51 193.6 68.13 193.59 74.74 C193.59 81.55 193.6 88.36 193.62 95.17 C193.64 101.04 193.64 106.9 193.64 112.76 C193.64 116.25 193.64 119.75 193.66 123.24 C193.67 127.14 193.66 131.04 193.65 134.94 C193.66 136.09 193.67 137.23 193.67 138.42 C193.62 146.49 192.65 153.32 187.15 159.64 C186.63 160.27 186.12 160.89 185.58 161.54 C176.91 168.53 167.26 167.97 156.68 167.91 C154.89 167.92 153.11 167.92 151.33 167.93 C146.51 167.95 141.7 167.94 136.88 167.93 C131.83 167.92 126.77 167.93 121.72 167.94 C113.24 167.94 104.76 167.93 96.28 167.92 C86.49 167.89 76.69 167.9 66.9 167.92 C58.47 167.94 50.05 167.94 41.63 167.93 C36.6 167.93 31.58 167.93 26.55 167.94 C21.83 167.95 17.11 167.94 12.38 167.92 C10.65 167.91 8.92 167.92 7.19 167.93 C-10.44 168.01 -10.44 168.01 -18.18 161.25 C-18.81 160.51 -19.45 159.78 -20.1 159.02 C-20.74 158.29 -21.39 157.56 -22.06 156.81 C-26.51 150.35 -26.02 143.48 -26.03 135.94 C-26.04 134.69 -26.04 133.43 -26.05 132.14 C-26.07 128.72 -26.08 125.29 -26.09 121.86 C-26.09 119.72 -26.1 117.58 -26.11 115.43 C-26.13 107.94 -26.14 100.44 -26.15 92.95 C-26.15 85.98 -26.18 79.02 -26.22 72.05 C-26.26 66.06 -26.27 60.06 -26.27 54.07 C-26.27 50.49 -26.28 46.92 -26.31 43.34 C-26.34 39.35 -26.33 35.36 -26.32 31.37 C-26.34 30.2 -26.35 29.03 -26.37 27.82 C-26.29 19.75 -24.74 13 -19.31 6.78 C-13.61 1.63 -7.48 -0.06 0 0 Z " fill="#000000" transform="translate(672.7218780517578,451.6055908203125)"/>
|
||||
<path d="M0 0 C23.31 20.18 40.49 49.84 42.8 81.07 C42.85 81.98 42.9 82.9 42.94 83.84 C43.1 95.47 43.1 95.47 49.23 104.83 C50.12 105.55 51.02 106.27 51.93 107.02 C59.84 114.04 63.99 125.42 66.61 135.39 C66.94 136.62 66.94 136.62 67.29 137.88 C72.32 160.05 67.57 185.25 56.32 204.73 C48.48 216.87 38.67 225.65 24.73 229.97 C14.67 231.95 5.2 230.28 -3.57 225.11 C-17.15 215.62 -23.73 201.52 -26.7 185.6 C-30.77 159.01 -26.3 130.95 -11.39 108.39 C-6.05 101.22 1.1 96.17 9.61 93.39 C12.33 93.17 14.87 93.25 17.61 93.39 C15.14 64.13 0.52 38.58 -21.39 19.39 C-48.99 -3.17 -84.11 -10.42 -119 -7.38 C-123.9 -6.8 -128.61 -5.82 -133.39 -4.61 C-134.04 -4.45 -134.68 -4.29 -135.34 -4.12 C-156.63 1.38 -176.27 12.27 -192.24 27.36 C-194.21 29.22 -196.24 30.92 -198.33 32.64 C-213.15 45.64 -228.15 66.66 -234.39 85.39 C-235.93 86.79 -235.93 86.79 -237.89 88.33 C-250.89 99.47 -262.29 119.38 -263.65 136.65 C-263.73 137.7 -263.81 138.75 -263.89 139.83 C-264.45 143.64 -264.82 144.94 -267.87 147.37 C-270.99 148.97 -274.08 150.24 -277.39 151.39 C-278.32 151.73 -279.24 152.06 -280.19 152.41 C-282.24 153.12 -284.31 153.77 -286.39 154.39 C-290.36 138.12 -286.53 121.75 -278.37 107.49 C-273.74 100.23 -268.96 94.38 -261.83 89.52 C-255.65 84.7 -253.92 79.05 -251.44 71.89 C-237.71 32.18 -207.61 -1.08 -170.25 -19.78 C-116.31 -45.87 -46.18 -38.68 0 0 Z " fill="#F5FBFD" transform="translate(578.39404296875,173.609375)"/>
|
||||
<path d="M0 0 C11.35 8.53 19.5 20.77 22.61 34.74 C24.85 53.42 22.91 70.8 11 86 C1.03 97.9 -12.26 103.65 -27.47 105.26 C-38.45 105.78 -49.28 101.52 -58 95 C-59.65 93.35 -61.3 91.7 -63 90 C-63.33 108.15 -63.66 126.3 -64 145 C-72.25 145 -80.5 145 -89 145 C-89 94.84 -89 44.68 -89 -7 C-80.75 -7 -72.5 -7 -64 -7 C-63.67 -3.04 -63.34 0.92 -63 5 C-61.85 3.93 -60.69 2.86 -59.5 1.75 C-55.57 -1.67 -51.83 -4.07 -47 -6 C-46.26 -6.3 -45.53 -6.6 -44.77 -6.91 C-29.58 -12.27 -13.12 -8.79 0 0 Z " fill="#000101" transform="translate(683,737)"/>
|
||||
<path d="M0 0 C84.35 0 84.35 0 100.78 15.59 C104.29 19.4 106.77 23.33 109 28 C109.41 28.83 109.82 29.67 110.25 30.53 C114.77 41.47 115.2 56.99 111.38 68.25 C110.64 69.86 109.85 71.45 109 73 C108.55 73.87 108.11 74.73 107.64 75.62 C101.17 87.28 91.37 94.53 78.93 99.14 C70.81 101.45 62.75 102.16 54.34 102.1 C53.64 102.1 52.94 102.1 52.22 102.09 C49.65 102.09 47.07 102.08 44.5 102.06 C38.72 102.04 32.95 102.02 27 102 C27 118.17 27 134.34 27 151 C18.09 151 9.18 151 0 151 C0 101.17 0 51.34 0 0 Z " fill="#000001" transform="translate(86,688)"/>
|
||||
<path d="M0 0 C11.38 8.93 19.44 20.62 22.45 34.93 C22.87 38.87 22.93 42.77 22.89 46.74 C22.88 47.8 22.87 48.85 22.87 49.94 C22.55 64.57 17.85 77.52 7.58 88.3 C-4.91 99.79 -19.99 104.31 -36.82 104.14 C-52.29 103.26 -65.52 96.67 -76.55 85.93 C-87.02 73.87 -91.11 58.98 -90.79 43.25 C-89.65 27.8 -83.03 13.78 -71.68 3.18 C-50.88 -13.48 -21.99 -15.37 0 0 Z " fill="#010102" transform="translate(369.546875,738.07421875)"/>
|
||||
<path d="M0 0 C3.4 2.75 6.37 5.96 8.89 9.53 C8.89 10.19 8.89 10.85 8.89 11.53 C9.55 11.53 10.21 11.53 10.89 11.53 C11.16 10.94 11.43 10.35 11.71 9.75 C16.18 1.41 24.55 -2.65 33.16 -5.58 C45.13 -8.43 58.52 -7.41 69.19 -1.11 C78.27 5.25 83.95 13.89 86.89 24.53 C87.72 29.41 88.02 34.08 88.01 39.03 C88.01 39.74 88.01 40.45 88.01 41.19 C88.01 43.52 88 45.85 87.99 48.18 C87.99 49.81 87.99 51.44 87.99 53.06 C87.98 57.33 87.97 61.59 87.96 65.85 C87.95 70.21 87.95 74.57 87.94 78.92 C87.93 87.46 87.92 96 87.89 104.53 C79.31 104.53 70.73 104.53 61.89 104.53 C61.89 102.4 61.89 100.27 61.88 98.08 C61.86 91.02 61.82 83.95 61.76 76.89 C61.73 72.61 61.7 68.33 61.7 64.05 C61.69 59.91 61.67 55.77 61.62 51.64 C61.61 50.07 61.6 48.49 61.61 46.92 C61.63 38 61.44 30.02 55.89 22.53 C49 16.76 41.59 16.79 32.89 17.53 C26.94 19.19 23.31 22.5 19.89 27.53 C18.07 32.04 17.63 35.89 17.58 40.73 C17.57 41.42 17.56 42.12 17.55 42.83 C17.51 45.1 17.5 47.38 17.48 49.65 C17.46 51.23 17.44 52.81 17.42 54.4 C17.37 58.55 17.33 62.7 17.29 66.86 C17.25 71.1 17.2 75.34 17.15 79.59 C17.06 87.9 16.97 96.22 16.89 104.53 C8.64 104.53 0.39 104.53 -8.11 104.53 C-8.12 102.52 -8.13 100.52 -8.15 98.45 C-8.2 91.78 -8.27 85.12 -8.34 78.45 C-8.39 74.41 -8.43 70.38 -8.45 66.34 C-8.48 62.43 -8.52 58.53 -8.57 54.63 C-8.59 53.15 -8.6 51.66 -8.6 50.18 C-8.58 35.88 -8.58 35.88 -14.36 23.09 C-20.64 17.23 -26.34 16.98 -34.72 17.18 C-40.8 17.81 -44.88 19.92 -48.98 24.41 C-52.6 29.72 -53.36 34.37 -53.42 40.73 C-53.43 41.42 -53.44 42.12 -53.45 42.83 C-53.49 45.1 -53.5 47.38 -53.52 49.65 C-53.54 51.23 -53.56 52.81 -53.58 54.4 C-53.63 58.55 -53.67 62.7 -53.71 66.86 C-53.75 71.1 -53.8 75.34 -53.85 79.59 C-53.94 87.9 -54.03 96.22 -54.11 104.53 C-62.36 104.53 -70.61 104.53 -79.11 104.53 C-79.11 68.56 -79.11 32.59 -79.11 -4.47 C-71.52 -4.47 -63.93 -4.47 -56.11 -4.47 C-55.78 -1.17 -55.45 2.13 -55.11 5.53 C-54.45 5 -53.8 4.47 -53.12 3.92 C-37.62 -8.07 -17 -11.36 0 0 Z " fill="#010102" transform="translate(485.10546875,734.46875)"/>
|
||||
<path d="M0 0 C23.23 -3.45 23.23 -3.45 33 0 C39.76 5.01 43.02 12.85 46.37 20.36 C49.27 26.83 52.97 32.7 56.79 38.67 C58.51 41.37 60.19 44.1 61.87 46.84 C62.43 47.74 62.98 48.64 63.56 49.57 C64.81 51.68 65.94 53.79 67 56 C71.6 49.84 75.59 43.42 79.5 36.81 C86.92 24.28 94.92 12.12 103 0 C105.48 -0.27 107.71 -0.35 110.2 -0.29 C111.26 -0.29 111.26 -0.29 112.34 -0.28 C114.6 -0.26 116.86 -0.23 119.12 -0.19 C120.66 -0.17 122.19 -0.16 123.72 -0.15 C127.48 -0.11 131.24 -0.06 135 0 C132.23 4.99 129.24 9.71 125.96 14.38 C124.98 15.8 124 17.21 123.02 18.63 C122.52 19.35 122.02 20.07 121.51 20.82 C119.09 24.32 116.7 27.84 114.32 31.37 C113.61 32.41 113.61 32.41 112.89 33.47 C111.45 35.6 110.01 37.74 108.56 39.88 C108.06 40.61 107.57 41.35 107.05 42.11 C99.66 53.04 92.29 63.99 85 75 C87.6 79.7 90.39 84.11 93.56 88.44 C97.25 93.54 100.88 98.68 104.44 103.88 C104.9 104.54 105.35 105.21 105.83 105.9 C106.76 107.26 107.69 108.62 108.62 109.98 C110.42 112.61 112.22 115.23 114.03 117.85 C115.38 119.82 116.73 121.79 118.08 123.75 C120.22 126.87 122.37 129.98 124.51 133.09 C125.7 134.81 126.88 136.53 128.06 138.25 C128.62 139.06 129.18 139.87 129.76 140.71 C130.28 141.47 130.81 142.23 131.35 143.02 C131.81 143.69 132.28 144.36 132.75 145.06 C133.99 146.98 135.02 148.94 136 151 C131.57 151.27 127.15 151.47 122.71 151.6 C121.21 151.66 119.7 151.73 118.2 151.83 C105.9 152.59 105.9 152.59 101.9 149.24 C100.41 146.89 99.2 144.49 98.02 141.98 C95.86 137.79 93.1 133.96 90.48 130.06 C89.28 128.24 88.08 126.43 86.88 124.61 C85.65 122.74 84.42 120.87 83.19 119 C81.94 117.1 80.68 115.2 79.43 113.3 C77.15 109.84 74.87 106.39 72.59 102.93 C72.14 102.24 71.69 101.55 71.22 100.84 C70.15 99.23 69.08 97.61 68 96 C62.56 103.37 57.36 110.85 52.31 118.5 C47.11 126.35 41.88 134.17 36.56 141.94 C35.74 143.14 35.74 143.14 34.9 144.38 C31.13 149.87 31.13 149.87 30 151 C27.71 151.09 25.41 151.11 23.12 151.1 C22.09 151.1 22.09 151.1 21.04 151.09 C18.84 151.09 16.64 151.08 14.44 151.06 C12.95 151.06 11.46 151.05 9.97 151.05 C6.31 151.04 2.66 151.02 -1 151 C3.2 144.34 7.47 137.74 11.94 131.25 C12.49 130.45 13.03 129.65 13.6 128.83 C18.02 122.39 22.48 115.98 26.94 109.56 C27.6 108.61 28.27 107.65 28.95 106.66 C32.31 101.83 35.7 97.02 39.13 92.23 C39.78 91.32 40.43 90.41 41.1 89.47 C42.34 87.74 43.58 86.01 44.84 84.29 C45.38 83.52 45.93 82.75 46.5 81.96 C46.98 81.29 47.47 80.62 47.97 79.93 C49.31 77.78 49.31 77.78 49 74 C47.61 71.35 47.61 71.35 45.66 68.58 C44.94 67.51 44.21 66.43 43.46 65.32 C42.66 64.15 41.86 62.98 41.06 61.81 C40.24 60.6 39.42 59.39 38.6 58.17 C36.49 55.05 34.37 51.93 32.24 48.81 C30.32 46.01 28.42 43.2 26.51 40.39 C24.39 37.28 22.28 34.16 20.15 31.05 C15.39 24.05 10.65 17.04 5.94 10 C5.34 9.1 4.73 8.21 4.11 7.28 C3.57 6.47 3.02 5.65 2.46 4.81 C1.75 3.74 1.75 3.74 1.02 2.64 C0 1 0 1 0 0 Z " fill="#010101" transform="translate(794,688)"/>
|
||||
<path d="M0 0 C7.92 0 15.84 0 24 0 C24 10.23 24 20.46 24 31 C33.57 31 43.14 31 53 31 C53 38.26 53 45.52 53 53 C43.76 53 34.52 53 25 53 C25.07 60.32 25.14 67.64 25.24 74.96 C25.28 78.37 25.32 81.78 25.35 85.19 C25.38 89.11 25.43 93.03 25.49 96.95 C25.49 98.16 25.5 99.38 25.51 100.63 C25.59 108.75 25.59 108.75 29 116 C32.21 118.43 35.38 118.27 39.31 118.25 C40.95 118.26 40.95 118.26 42.61 118.27 C46.67 118.06 46.67 118.06 53 116 C53 123.26 53 130.52 53 138 C41.09 143.95 30.59 144.38 18.02 140.2 C10.47 137.21 5.65 132.52 2.13 125.2 C-0.65 117.89 -0.38 110.61 -0.29 102.9 C-0.29 101.46 -0.28 100.01 -0.28 98.57 C-0.27 94.8 -0.24 91.02 -0.21 87.25 C-0.18 83.39 -0.16 79.53 -0.15 75.67 C-0.11 68.11 -0.06 60.56 0 53 C-6.93 53 -13.86 53 -21 53 C-21 45.74 -21 38.48 -21 31 C-14.07 31 -7.14 31 0 31 C0 20.77 0 10.54 0 0 Z " fill="#010203" transform="translate(730,699)"/>
|
||||
<path d="M0 0 C2.56 0.02 5.12 0.04 7.75 0.06 C7.75 8.31 7.75 16.56 7.75 25.06 C4.99 24.98 2.22 24.9 -0.62 24.81 C-8.7 24.74 -15.57 25.21 -22.04 30.54 C-30.34 39.19 -30.85 49.87 -30.86 61.26 C-30.87 62.7 -30.89 64.14 -30.91 65.59 C-30.95 69.35 -30.97 73.12 -30.99 76.88 C-31.01 80.74 -31.05 84.59 -31.09 88.45 C-31.16 95.98 -31.21 103.52 -31.25 111.06 C-39.5 111.06 -47.75 111.06 -56.25 111.06 C-56.25 75.09 -56.25 39.12 -56.25 2.06 C-48 2.06 -39.75 2.06 -31.25 2.06 C-30.92 6.02 -30.59 9.98 -30.25 14.06 C-29.14 12.85 -28.02 11.63 -26.88 10.38 C-19.08 2.56 -10.79 -0.12 0 0 Z " fill="#020203" transform="translate(269.25,727.9375)"/>
|
||||
<path d="M0 0 C7.55 5.42 12.57 11.98 15 21 C16.21 31.97 15.76 42.19 9.3 51.42 C4.02 57.97 -2.66 62.37 -11 64 C-20.89 64.96 -29.5 62.64 -37.41 56.54 C-45.1 49.13 -48.1 40.11 -48.5 29.69 C-48.27 19.54 -44.99 11.07 -37.96 3.69 C-27.55 -5.25 -11.84 -7.75 0 0 Z " fill="#F5FBFE" transform="translate(666,755)"/>
|
||||
<path d="M0 0 C8.95 5.14 13.83 12.52 16.92 22.36 C18.56 31.33 17.43 41.62 12.99 49.63 C11.53 51.68 10 53.49 8.28 55.31 C7.51 56.14 7.51 56.14 6.72 56.99 C1.09 62.42 -6.42 65.39 -14.22 65.81 C-25.29 65.19 -32.78 59.65 -40.04 51.81 C-45.77 43.31 -46.53 34.29 -45.72 24.31 C-43.33 13.75 -38.08 6.38 -29.07 0.54 C-19.95 -4.23 -9.31 -4.36 0 0 Z " fill="#F5FBFE" transform="translate(350.72265625,753.69140625)"/>
|
||||
<path d="M0 0 C7.62 4.26 11.8 11.85 14.19 20.02 C15.01 23.41 15.51 26.73 15.71 30.2 C15.77 31.07 15.82 31.94 15.88 32.84 C16.33 47.63 12.97 62.41 3.4 74 C-4.19 81.82 -4.19 81.82 -10.09 82.53 C-20.59 82.66 -20.59 82.66 -25.29 78.2 C-35.14 67.85 -35.87 56.12 -35.76 42.49 C-35.4 29.4 -32.51 15.92 -23.26 6.03 C-16.36 -0.2 -9.33 -3.28 0 0 Z " fill="#000101" transform="translate(622.28515625,297.8046875)"/>
|
||||
<path d="M0 0 C6.02 -0.08 12.05 -0.16 18.25 -0.25 C20.12 -0.29 22 -0.32 23.93 -0.36 C44.33 -0.59 44.33 -0.59 52.24 5.82 C57.75 11.29 59.99 18.35 60.38 26 C60.02 33.33 57.9 40.15 52.81 45.53 C45.65 51.35 37.32 52.16 28.42 52.1 C27.7 52.1 26.97 52.1 26.23 52.09 C23.55 52.09 20.87 52.08 18.19 52.06 C12.19 52.04 6.18 52.02 0 52 C0 34.84 0 17.68 0 0 Z " fill="#F6FCFE" transform="translate(113,713)"/>
|
||||
<path d="M0 0 C9.96 3.23 19.99 17.36 27.25 24.64 C29.57 26.97 31.91 29.3 34.24 31.62 C35.73 33.12 37.22 34.61 38.71 36.1 C39.4 36.79 40.09 37.48 40.81 38.19 C50 47.43 50 47.43 50.69 52.15 C49.68 59.32 44.24 63.95 39.3 68.86 C38.65 69.52 37.99 70.17 37.32 70.85 C35.94 72.23 34.56 73.61 33.18 74.98 C31.06 77.09 28.94 79.21 26.83 81.34 C25.49 82.68 24.15 84.02 22.8 85.36 C22.17 86 21.54 86.63 20.89 87.29 C16.42 91.71 16.42 91.71 14.19 91.71 C13.95 92.27 13.72 92.83 13.48 93.41 C10.83 98.13 6.53 101.57 2.19 104.71 C-1.63 105.58 -1.63 105.58 -4.81 104.71 C-8.27 102.23 -9.56 100.37 -11.06 96.4 C-10.79 92.32 -10.39 91.81 -7.76 88.95 C-6.8 87.9 -6.8 87.9 -5.83 86.83 C-2.71 83.55 0.44 80.33 3.65 77.15 C4.33 76.47 5.01 75.79 5.71 75.1 C7.84 72.96 9.98 70.84 12.12 68.71 C13.59 67.25 15.05 65.8 16.51 64.34 C20.07 60.79 23.63 57.25 27.19 53.71 C23.05 48.77 18.74 44.15 14.14 39.64 C12.81 38.32 11.48 37 10.14 35.67 C8.06 33.62 5.98 31.57 3.9 29.52 C1.87 27.52 -0.15 25.52 -2.17 23.51 C-2.79 22.91 -3.41 22.3 -4.05 21.67 C-7.16 18.58 -9.89 15.71 -11.81 11.71 C-11.81 7.78 -11.05 6.04 -8.75 2.83 C-5.75 0.66 -3.75 -0.29 0 0 Z " fill="#F2F8FB" transform="translate(688.812744140625,481.29052734375)"/>
|
||||
<path d="M0 0 C5.23 3.11 8.86 7.09 11.92 12.34 C13.68 19.38 13.17 25.72 9.61 32.09 C5.97 37.39 1.94 40.76 -4.39 42.09 C-10.91 42.84 -16.02 41.96 -21.39 38.09 C-25.95 33.83 -29.03 29.54 -29.69 23.19 C-29.72 15.67 -29.15 11.14 -23.95 5.41 C-17.09 -0.79 -9.15 -3.47 0 0 Z " fill="#010102" transform="translate(433.390625,287.90625)"/>
|
||||
<path d="M0 0 C1.6 -0.02 1.6 -0.02 3.24 -0.04 C4.96 -0.03 4.96 -0.03 6.71 -0.01 C7.9 -0.02 9.08 -0.02 10.3 -0.03 C12.8 -0.03 15.3 -0.02 17.81 -0 C21.64 0.02 25.47 -0 29.3 -0.03 C31.73 -0.03 34.16 -0.02 36.59 -0.01 C37.74 -0.02 38.89 -0.03 40.07 -0.04 C41.14 -0.03 42.2 -0.01 43.3 0 C44.71 0 44.71 0 46.15 0.01 C49.41 0.67 50.6 1.95 52.65 4.52 C53.22 7.17 53.22 7.17 53.21 10.02 C53.24 10.96 53.26 11.89 53.28 12.86 C52.65 15.52 52.65 15.52 50.84 17.65 C46.01 20.48 41.47 20.11 35.96 20.08 C34.26 20.09 34.26 20.09 32.52 20.1 C30.13 20.11 27.73 20.11 25.33 20.09 C21.67 20.08 18 20.11 14.33 20.15 C12 20.15 9.67 20.15 7.34 20.15 C6.24 20.16 5.15 20.17 4.02 20.19 C-3.58 20.09 -3.58 20.09 -7.54 17.14 C-9.51 14.28 -10 12.89 -9.91 9.46 C-9.91 8.62 -9.91 7.78 -9.91 6.92 C-8.65 1.57 -5.15 0.02 0 0 Z " fill="#F5FBFE" transform="translate(757.34765625,567.482421875)"/>
|
||||
<path d="M0 0 C0.66 0.33 1.32 0.66 2 1 C1.67 19.48 1.34 37.96 1 57 C-0.65 56.67 -2.3 56.34 -4 56 C-3.01 55.5 -3.01 55.5 -2 55 C-2 37.18 -2 19.36 -2 1 C-1.34 0.67 -0.68 0.34 0 0 Z " fill="#181D27" transform="translate(618,825)"/>
|
||||
<path d="M0 0 C2.28 3.42 2.22 4.68 2.12 8.69 C2.11 9.68 2.09 10.68 2.07 11.7 C2.04 12.84 2.04 12.84 2 14 C1.01 14 0.02 14 -1 14 C-1.03 12.23 -1.05 10.46 -1.06 8.69 C-1.07 7.7 -1.09 6.72 -1.1 5.7 C-1 3 -1 3 0 0 Z " fill="#1A1E28" transform="translate(682,775)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 19 KiB |
465
docs/user-role-creation-system.md
Normal file
465
docs/user-role-creation-system.md
Normal file
@ -0,0 +1,465 @@
|
||||
# 用户资源发现系统设计
|
||||
|
||||
## 📋 概述
|
||||
|
||||
基于ResourceManager的用户资源发现机制,支持用户创建自定义角色、执行流程、思维模式等资源,实现即创即用的体验。
|
||||
|
||||
## 🎯 核心问题
|
||||
|
||||
**现状分析**:
|
||||
- 系统资源已在 `src/resource.registry.json` 静态注册
|
||||
- 当前 `HelloCommand.discoverLocalRoles()` 错误扫描系统路径,造成重复处理
|
||||
- 用户需要在项目级别创建和管理自定义资源
|
||||
|
||||
**解决目标**:
|
||||
- 仅发现用户资源,不重复处理系统资源
|
||||
- 支持多种资源类型:角色、执行流程、思维模式
|
||||
- 实现用户资源覆盖系统资源的能力
|
||||
|
||||
## 🏗️ 架构设计
|
||||
|
||||
### 资源分层
|
||||
|
||||
```
|
||||
系统资源 (静态注册)
|
||||
├── src/resource.registry.json # 系统资源注册表
|
||||
└── prompt/domain/{role}/ # 系统资源文件
|
||||
|
||||
用户资源 (动态发现)
|
||||
└── .promptx/resource/domain/{role}/ # 用户资源文件
|
||||
```
|
||||
|
||||
### 目录结构规范
|
||||
|
||||
#### 用户资源目录
|
||||
```
|
||||
.promptx/
|
||||
├── resource/
|
||||
│ └── domain/
|
||||
│ └── {role-id}/
|
||||
│ ├── {role-id}.role.md
|
||||
│ ├── thought/
|
||||
│ │ └── {name}.thought.md
|
||||
│ └── execution/
|
||||
│ └── {name}.execution.md
|
||||
└── memory/ # 现有目录
|
||||
```
|
||||
|
||||
**设计原则**:
|
||||
- **镜像结构**:用户目录结构镜像系统结构,降低认知负载
|
||||
- **资源聚合**:角色相关资源统一管理在角色目录下
|
||||
- **类型支持**:支持 role、thought、execution 等多种资源类型
|
||||
|
||||
### 发现机制重构
|
||||
|
||||
#### ResourceManager 扩展
|
||||
```javascript
|
||||
// src/lib/core/resource/resourceManager.js
|
||||
class ResourceManager {
|
||||
async discoverUserResources() {
|
||||
const userResourcePath = path.join(packageRoot, '.promptx', 'resource', 'domain')
|
||||
return await this.scanResourceDirectory(userResourcePath)
|
||||
}
|
||||
|
||||
async scanResourceDirectory(basePath) {
|
||||
// 使用 Node.js 原生 API,移除 glob 依赖
|
||||
// 支持 role、thought、execution 等多种资源类型
|
||||
}
|
||||
|
||||
async loadUnifiedRegistry() {
|
||||
const systemResources = await this.loadSystemRegistry()
|
||||
const userResources = await this.discoverUserResources()
|
||||
|
||||
// 用户资源覆盖系统资源
|
||||
return { ...systemResources, ...userResources }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### HelloCommand 简化
|
||||
```javascript
|
||||
// src/lib/core/pouch/commands/HelloCommand.js
|
||||
class HelloCommand {
|
||||
async loadRoleRegistry() {
|
||||
// 移除错误的本地发现逻辑
|
||||
// 直接从 ResourceManager 获取统一注册表
|
||||
return await this.resourceManager.loadUnifiedRegistry()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🤖 nuwa 角色设计
|
||||
|
||||
### 核心职责
|
||||
- **需求理解**:通过自然对话收集用户场景需求
|
||||
- **资源生成**:基于 DPML 协议生成角色文件
|
||||
- **文件管理**:将生成内容保存到正确的用户资源路径
|
||||
|
||||
### 对话策略
|
||||
```
|
||||
收集目标信息:
|
||||
├── scenario: 用户工作场景
|
||||
├── painPoint: 主要痛点
|
||||
└── expectation: 期望结果
|
||||
|
||||
生成时机:三项信息齐全即可生成
|
||||
```
|
||||
|
||||
### 生成模板
|
||||
```xml
|
||||
<role>
|
||||
<personality>
|
||||
[基于场景的思维模式]
|
||||
</personality>
|
||||
|
||||
<principle>
|
||||
[基于痛点的行为原则]
|
||||
</principle>
|
||||
|
||||
<knowledge>
|
||||
[基于期望的知识体系]
|
||||
</knowledge>
|
||||
</role>
|
||||
```
|
||||
|
||||
## 🔧 技术实现
|
||||
|
||||
### 实现优先级
|
||||
|
||||
#### Phase 1: 核心功能
|
||||
1. **ResourceManager 扩展**
|
||||
- 实现 `discoverUserResources()` 方法
|
||||
- 使用 Node.js 原生 API 替代 glob
|
||||
- 支持多种资源类型扫描
|
||||
|
||||
2. **HelloCommand 重构**
|
||||
- 移除错误的系统路径扫描
|
||||
- 集成 ResourceManager 统一注册表
|
||||
|
||||
3. **nuwa 角色实现**
|
||||
- DPML 协议掌握和文件生成
|
||||
- 用户资源路径文件保存
|
||||
|
||||
#### Phase 2: 完善功能
|
||||
1. **错误处理**:跨平台兼容性和容错机制
|
||||
2. **性能优化**:资源发现缓存机制
|
||||
3. **用户体验**:更智能的对话策略
|
||||
|
||||
### 关键技术要点
|
||||
|
||||
#### 1. 跨平台路径处理
|
||||
```javascript
|
||||
// 使用 Node.js 原生 API,避免 glob 兼容性问题
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
|
||||
async function discoverUserResources() {
|
||||
const userResourcePath = path.join(
|
||||
await packageProtocol.getPackageRoot(),
|
||||
'.promptx', 'resource', 'domain'
|
||||
)
|
||||
|
||||
if (!await fs.pathExists(userResourcePath)) {
|
||||
return {}
|
||||
}
|
||||
|
||||
// 使用原生 readdir 和 stat 扫描
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 资源覆盖机制
|
||||
```javascript
|
||||
// 用户资源优先级高于系统资源
|
||||
const unifiedRegistry = {
|
||||
...systemResources, // 系统资源作为基础
|
||||
...userResources // 用户资源覆盖同名项
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. DPML 元数据提取
|
||||
```javascript
|
||||
function extractRoleMetadata(content, roleId) {
|
||||
// 从 DPML 标签中提取角色元信息
|
||||
// 用于角色发现和展示
|
||||
}
|
||||
```
|
||||
|
||||
## 🧪 测试策略与设计
|
||||
|
||||
### 测试架构分层
|
||||
|
||||
```
|
||||
单元测试层 (Unit Tests)
|
||||
├── ResourceManager.unit.test.js # 资源管理器核心逻辑测试
|
||||
├── HelloCommand.unit.test.js # 命令行接口测试
|
||||
├── UserResourceDiscovery.unit.test.js # 用户资源发现测试
|
||||
└── DPMLParser.unit.test.js # DPML格式解析测试
|
||||
|
||||
集成测试层 (Integration Tests)
|
||||
├── ResourceDiscovery.integration.test.js # 资源发现完整流程测试
|
||||
├── NuwaRoleGeneration.integration.test.js # nuwa角色生成端到端测试
|
||||
└── CrossPlatform.integration.test.js # 跨平台兼容性测试
|
||||
|
||||
端到端测试层 (E2E Tests)
|
||||
└── UserWorkflow.e2e.test.js # 完整用户工作流程测试
|
||||
```
|
||||
|
||||
### 核心测试组件
|
||||
|
||||
#### 1. ResourceManager 单元测试
|
||||
```javascript
|
||||
// src/tests/core/resource/ResourceManager.unit.test.js
|
||||
describe('ResourceManager', () => {
|
||||
describe('discoverUserResources', () => {
|
||||
it('应该正确扫描用户资源目录', async () => {
|
||||
// 模拟用户资源文件结构
|
||||
// 验证发现结果的正确性
|
||||
})
|
||||
|
||||
it('应该支持多种资源类型', async () => {
|
||||
// 测试 role、thought、execution 类型
|
||||
})
|
||||
|
||||
it('应该处理不存在的目录', async () => {
|
||||
// 测试容错机制
|
||||
})
|
||||
})
|
||||
|
||||
describe('loadUnifiedRegistry', () => {
|
||||
it('应该正确合并系统和用户资源', async () => {
|
||||
// 验证用户资源覆盖系统资源
|
||||
})
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
#### 2. HelloCommand 重构测试
|
||||
```javascript
|
||||
// src/tests/commands/HelloCommand.unit.test.js
|
||||
describe('HelloCommand - 重构后', () => {
|
||||
it('应该移除错误的系统路径扫描', async () => {
|
||||
// 验证不再扫描 prompt/domain/ 路径
|
||||
})
|
||||
|
||||
it('应该集成ResourceManager统一注册表', async () => {
|
||||
// 验证使用ResourceManager.loadUnifiedRegistry()
|
||||
})
|
||||
|
||||
it('应该正确显示用户自定义角色', async () => {
|
||||
// 验证用户角色在hello命令中的展示
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
#### 3. 用户资源发现集成测试
|
||||
```javascript
|
||||
// src/tests/integration/UserResourceDiscovery.integration.test.js
|
||||
describe('用户资源发现机制', () => {
|
||||
beforeEach(async () => {
|
||||
// 创建测试用的用户资源结构
|
||||
await createTestUserResourceStructure()
|
||||
})
|
||||
|
||||
it('应该发现用户创建的角色', async () => {
|
||||
// 创建测试角色文件
|
||||
// 验证ResourceManager能正确发现
|
||||
})
|
||||
|
||||
it('应该支持资源类型扩展', async () => {
|
||||
// 测试thought、execution文件的发现
|
||||
})
|
||||
|
||||
it('应该处理DPML格式验证', async () => {
|
||||
// 测试格式错误的文件处理
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
#### 4. nuwa 角色生成端到端测试
|
||||
```javascript
|
||||
// src/tests/integration/NuwaRoleGeneration.integration.test.js
|
||||
describe('nuwa 角色生成完整流程', () => {
|
||||
it('应该根据用户需求生成角色文件', async () => {
|
||||
// 模拟用户输入
|
||||
// 验证生成的文件内容和位置
|
||||
})
|
||||
|
||||
it('应该生成符合DPML规范的角色', async () => {
|
||||
// 验证生成文件的DPML格式正确性
|
||||
})
|
||||
|
||||
it('应该创建正确的目录结构', async () => {
|
||||
// 验证镜像系统结构的目录创建
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
#### 5. 跨平台兼容性测试
|
||||
```javascript
|
||||
// src/tests/integration/CrossPlatform.integration.test.js
|
||||
describe('跨平台兼容性', () => {
|
||||
it('应该在Windows上正确处理路径', () => {
|
||||
// 模拟Windows路径分隔符
|
||||
// 验证路径处理的正确性
|
||||
})
|
||||
|
||||
it('应该在macOS/Linux上正确处理路径', () => {
|
||||
// 验证Unix风格路径处理
|
||||
})
|
||||
|
||||
it('应该使用Node.js原生API替代glob', () => {
|
||||
// 验证不使用glob库的实现
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### 测试数据和环境
|
||||
|
||||
#### 测试数据结构
|
||||
```
|
||||
src/tests/fixtures/
|
||||
├── user-resources/
|
||||
│ └── domain/
|
||||
│ ├── test-role/
|
||||
│ │ ├── test-role.role.md # 标准DPML格式
|
||||
│ │ ├── thought/
|
||||
│ │ │ └── test.thought.md
|
||||
│ │ └── execution/
|
||||
│ │ └── test.execution.md
|
||||
│ ├── invalid-role/
|
||||
│ │ └── invalid.role.md # 格式错误的文件
|
||||
│ └── sales-analyst/
|
||||
│ └── sales-analyst.role.md # nuwa生成测试样例
|
||||
├── system-resources/
|
||||
│ └── mock-registry.json # 模拟系统注册表
|
||||
└── dpml-samples/
|
||||
├── valid-role.md # 有效DPML样例
|
||||
└── invalid-role.md # 无效DPML样例
|
||||
```
|
||||
|
||||
#### 测试环境配置
|
||||
```javascript
|
||||
// src/tests/setup/testEnvironment.js
|
||||
export class TestEnvironment {
|
||||
async setup() {
|
||||
// 创建临时测试目录
|
||||
this.testDir = await createTempTestDirectory()
|
||||
|
||||
// 模拟 .promptx 结构
|
||||
await this.createMockUserResourceStructure()
|
||||
|
||||
// 设置环境变量
|
||||
process.env.PROMPTX_TEST_MODE = 'true'
|
||||
}
|
||||
|
||||
async teardown() {
|
||||
// 清理测试文件
|
||||
await fs.remove(this.testDir)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 测试覆盖率要求
|
||||
|
||||
#### 覆盖率目标
|
||||
- **整体代码覆盖率**: ≥ 85%
|
||||
- **ResourceManager核心逻辑**: ≥ 95%
|
||||
- **HelloCommand重构部分**: ≥ 90%
|
||||
- **DPML解析逻辑**: ≥ 95%
|
||||
- **跨平台路径处理**: 100%
|
||||
|
||||
#### 关键测试场景
|
||||
```
|
||||
✅ 用户资源发现功能
|
||||
✅ 系统资源静态加载
|
||||
✅ 资源覆盖机制
|
||||
✅ DPML格式验证
|
||||
✅ 跨平台路径处理
|
||||
✅ 错误处理和容错
|
||||
✅ nuwa角色生成流程
|
||||
✅ 文件系统操作安全性
|
||||
✅ 缓存机制有效性
|
||||
✅ CLI集成正确性
|
||||
```
|
||||
|
||||
### 测试执行策略
|
||||
|
||||
#### 测试运行配置
|
||||
```json
|
||||
// package.json scripts
|
||||
{
|
||||
"test": "jest",
|
||||
"test:unit": "jest --testPathPattern=unit",
|
||||
"test:integration": "jest --testPathPattern=integration",
|
||||
"test:e2e": "jest --testPathPattern=e2e",
|
||||
"test:coverage": "jest --coverage",
|
||||
"test:watch": "jest --watch"
|
||||
}
|
||||
```
|
||||
|
||||
#### CI/CD 集成
|
||||
```yaml
|
||||
# .github/workflows/test.yml
|
||||
name: Test Suite
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
node: [16, 18, 20]
|
||||
steps:
|
||||
- name: Run Unit Tests
|
||||
run: npm run test:unit
|
||||
- name: Run Integration Tests
|
||||
run: npm run test:integration
|
||||
- name: Check Coverage
|
||||
run: npm run test:coverage
|
||||
```
|
||||
|
||||
## 📊 用户体验流程
|
||||
|
||||
```bash
|
||||
# 1. 创建角色
|
||||
npx promptx action nuwa
|
||||
# 对话生成: .promptx/resource/domain/sales-analyst/sales-analyst.role.md
|
||||
|
||||
# 2. 立即可用(自动发现)
|
||||
npx promptx hello
|
||||
# 显示新角色: sales-analyst
|
||||
|
||||
# 3. 直接使用
|
||||
npx promptx action sales-analyst
|
||||
```
|
||||
|
||||
## 🔄 设计决策
|
||||
|
||||
### 为什么选择 .promptx/resource/domain 结构?
|
||||
- **镜像一致性**:与系统 `prompt/domain` 结构保持一致
|
||||
- **类型扩展性**:未来可支持 thought、execution 等资源类型
|
||||
- **认知简单性**:用户理解成本最低
|
||||
|
||||
### 为什么移除 HelloCommand 的发现逻辑?
|
||||
- **职责单一**:ResourceManager 专门负责资源管理
|
||||
- **避免重复**:系统资源已静态注册,无需重复发现
|
||||
- **架构清晰**:分层明确,便于维护
|
||||
|
||||
### 为什么使用 Node.js 原生 API?
|
||||
- **兼容性**:完全跨平台,无第三方库依赖问题
|
||||
- **性能**:原生 API 性能更优
|
||||
- **维护性**:减少依赖复杂度
|
||||
|
||||
## 📚 相关文档
|
||||
|
||||
- [DPML协议](../prompt/protocol/dpml.protocol.md)
|
||||
- [ResourceManager 架构](../src/lib/core/resource/)
|
||||
- [角色标签规范](../prompt/protocol/tag/role.tag.md)
|
||||
|
||||
---
|
||||
|
||||
**实现要点**:
|
||||
1. ResourceManager 统一资源发现
|
||||
2. 用户资源镜像系统结构
|
||||
3. nuwa 基于 DPML 生成角色
|
||||
4. 即创即用的无缝体验
|
||||
5. 完整测试覆盖和质量保证
|
||||
148
prompt/core/execution/execution-authoring.execution.md
Normal file
148
prompt/core/execution/execution-authoring.execution.md
Normal file
@ -0,0 +1,148 @@
|
||||
<execution>
|
||||
<constraint>
|
||||
## 客观技术限制
|
||||
- **DPML语法约束**:必须遵循EBNF定义的execution语法结构
|
||||
- **XML格式要求**:标签必须正确闭合,属性值必须用双引号包围
|
||||
- **Markdown兼容性**:内容部分必须是有效的Markdown格式
|
||||
- **文件编码**:必须使用UTF-8编码
|
||||
- **优先级固化**:五个子标签的优先级关系不可改变
|
||||
</constraint>
|
||||
|
||||
<rule>
|
||||
## 强制性编写规则
|
||||
- **纯XML结构**:execution文件必须从`<execution>`标签开始,不得包含任何XML结构外的内容(如Markdown标题、注释等)
|
||||
- **根标签强制**:文件必须使用`<execution>`作为根标签包装全部内容
|
||||
- **子标签命名**:只能使用规范定义的五个子标签:constraint, rule, guideline, process, criteria
|
||||
- **优先级顺序**:子标签必须按constraint → rule → guideline → process → criteria顺序排列
|
||||
- **内容完整性**:每个子标签都必须包含实质性内容,不得为空
|
||||
- **语义一致性**:子标签内容必须与其语义定义保持一致
|
||||
- **文件纯净性**:除了`<execution>`标签结构外,不得包含任何其他内容
|
||||
</rule>
|
||||
|
||||
<guideline>
|
||||
## 编写指导原则
|
||||
- **语义明确性**:每个子标签的内容应清晰表达其特定语义
|
||||
- **内容层次化**:使用Markdown的标题、列表等结构组织内容
|
||||
- **实用性导向**:内容应具有实际操作指导价值
|
||||
- **简洁性原则**:避免冗长表述,保持核心要点突出
|
||||
- **一致性维护**:在整个文件中保持术语和表达方式的一致性
|
||||
</guideline>
|
||||
|
||||
<process>
|
||||
## 编写执行流程
|
||||
|
||||
### Phase 1: 分析需求和上下文
|
||||
1. **明确execution目的**:确定这个execution要解决什么问题
|
||||
2. **识别客观限制**:分析技术、环境、资源等客观约束条件
|
||||
3. **定义强制要求**:确定必须遵守的规则和底线要求
|
||||
4. **收集最佳实践**:整理相关领域的指导原则和建议
|
||||
|
||||
### Phase 2: 内容规划和结构设计
|
||||
1. **约束条件梳理**:
|
||||
- 列出所有客观存在的限制条件
|
||||
- 按重要性和影响程度排序
|
||||
- 确保约束条件的客观性和不可变性
|
||||
|
||||
2. **规则定义设计**:
|
||||
- 识别必须严格遵守的行为准则
|
||||
- 明确违反规则的后果和风险
|
||||
- 确保规则与约束条件不冲突
|
||||
|
||||
3. **指导原则制定**:
|
||||
- 提供灵活性建议和最佳实践
|
||||
- 允许根据具体情况调整
|
||||
- 确保不违反已定义的规则和约束
|
||||
|
||||
4. **流程步骤设计**:
|
||||
- 在约束和规则框架内设计执行路径
|
||||
- 包含正常流程和异常处理
|
||||
- 确保步骤的可操作性和逻辑性
|
||||
|
||||
5. **评价标准确立**:
|
||||
- 定义成功完成的判断依据
|
||||
- 考虑所有优先级更高元素的要求
|
||||
- 提供可量化的评估方法
|
||||
|
||||
### Phase 3: DPML结构实现
|
||||
|
||||
**关键要求:文件必须从`<execution>`标签直接开始**
|
||||
|
||||
```xml
|
||||
<execution>
|
||||
<constraint>
|
||||
[客观限制条件内容]
|
||||
</constraint>
|
||||
|
||||
<rule>
|
||||
[强制性规则内容]
|
||||
</rule>
|
||||
|
||||
<guideline>
|
||||
[指导原则内容]
|
||||
</guideline>
|
||||
|
||||
<process>
|
||||
[具体执行步骤]
|
||||
</process>
|
||||
|
||||
<criteria>
|
||||
[评价标准内容]
|
||||
</criteria>
|
||||
</execution>
|
||||
```
|
||||
|
||||
**错误示例(禁止):**
|
||||
```markdown
|
||||
# 标题
|
||||
这是描述内容...
|
||||
|
||||
<execution>
|
||||
...
|
||||
</execution>
|
||||
```
|
||||
|
||||
### Phase 4: 质量检查和优化
|
||||
1. **语法验证**:确保DPML语法正确性
|
||||
2. **语义一致性检查**:验证各部分逻辑关系
|
||||
3. **优先级关系验证**:确认无冲突和矛盾
|
||||
4. **实用性测试**:验证内容的可操作性
|
||||
5. **完整性审核**:确保覆盖所有必要方面
|
||||
6. **纯净性检查**:确认文件从`<execution>`标签开始,无多余内容
|
||||
</process>
|
||||
|
||||
<criteria>
|
||||
## 质量评价标准
|
||||
|
||||
### 格式合规性
|
||||
- ✅ 文件从`<execution>`标签直接开始,无额外内容
|
||||
- ✅ 使用正确的DPML execution标签结构
|
||||
- ✅ 五个子标签按规定顺序排列
|
||||
- ✅ XML语法正确,标签正确闭合
|
||||
- ✅ Markdown格式规范,层次清晰
|
||||
|
||||
### 内容完整性
|
||||
- ✅ 每个子标签都包含实质性内容
|
||||
- ✅ 约束条件体现客观性和不可变性
|
||||
- ✅ 规则体现强制性和明确性
|
||||
- ✅ 指导原则体现建议性和灵活性
|
||||
- ✅ 流程步骤具有可操作性和逻辑性
|
||||
- ✅ 评价标准具有可验证性
|
||||
|
||||
### 语义一致性
|
||||
- ✅ 各子标签内容与其语义定义匹配
|
||||
- ✅ 优先级关系得到正确体现
|
||||
- ✅ 不存在逻辑冲突和矛盾
|
||||
- ✅ 术语使用保持一致性
|
||||
|
||||
### 实用价值
|
||||
- ✅ 内容具有实际指导意义
|
||||
- ✅ 步骤和标准可以实际执行
|
||||
- ✅ 能够解决实际问题
|
||||
- ✅ 适用于目标场景和用户
|
||||
|
||||
### 文件纯净性
|
||||
- ✅ 文件结构完全符合DPML execution规范
|
||||
- ✅ 无任何XML结构外的多余内容
|
||||
- ✅ 体现execution文件的标准格式
|
||||
</criteria>
|
||||
</execution>
|
||||
223
prompt/core/execution/resource-authoring.execution.md
Normal file
223
prompt/core/execution/resource-authoring.execution.md
Normal file
@ -0,0 +1,223 @@
|
||||
<execution>
|
||||
<constraint>
|
||||
## 客观技术限制
|
||||
- **DPML语法约束**:必须遵循EBNF定义的resource语法结构
|
||||
- **XML格式要求**:标签必须正确闭合,属性值必须用双引号包围
|
||||
- **protocol属性强制**:resource标签必须包含protocol属性指定协议名
|
||||
- **文件编码**:必须使用UTF-8编码
|
||||
- **代码实现约束**:必须与ResourceManager、ResourceProtocol基类兼容
|
||||
- **注册表集成**:必须与resource.registry.json统一注册表集成
|
||||
- **查询参数限制**:查询参数必须符合URL标准格式
|
||||
</constraint>
|
||||
|
||||
<rule>
|
||||
## 强制性编写规则
|
||||
- **纯XML结构**:resource文件必须从`<resource>`标签开始,不得包含任何XML结构外的内容
|
||||
- **根标签强制**:文件必须使用`<resource protocol="协议名">`作为根标签
|
||||
- **三组件完整**:必须包含location、params、registry三个子标签
|
||||
- **组件顺序固定**:子标签必须按location → params → registry顺序排列
|
||||
- **protocol属性必需**:根标签必须包含protocol属性且值唯一
|
||||
- **文件纯净性**:除了`<resource>`标签结构外,不得包含任何其他内容
|
||||
- **EBNF规范性**:location标签内容必须使用EBNF语法定义路径格式
|
||||
</rule>
|
||||
|
||||
<guideline>
|
||||
## 编写指导原则
|
||||
- **协议名称清晰**:protocol属性值应简洁明了,符合kebab-case命名规范
|
||||
- **路径格式标准化**:使用EBNF语法精确定义资源路径结构
|
||||
- **参数文档完整**:详细说明所有支持的查询参数及其类型和用途
|
||||
- **注册表合理性**:注册表映射应体现抽象性和实用性的平衡
|
||||
- **兼容性考虑**:确保与PromptX资源管理系统的无缝集成
|
||||
- **示例丰富性**:提供足够的使用示例帮助理解协议用法
|
||||
</guideline>
|
||||
|
||||
<process>
|
||||
## 编写执行流程
|
||||
|
||||
### Phase 1: 协议概念设计
|
||||
1. **确定协议用途**:明确这个资源协议要解决什么资源访问问题
|
||||
2. **分析资源特征**:识别目标资源的组织方式、访问模式和参数需求
|
||||
3. **设计协议名称**:选择简洁清晰的协议标识符
|
||||
4. **评估系统集成**:确认与PromptX现有协议的兼容性和差异性
|
||||
|
||||
### Phase 2: 路径格式设计(location组件)
|
||||
1. **路径结构分析**:
|
||||
- 确定资源的层次结构和定位方式
|
||||
- 分析是否需要支持参数化路径
|
||||
- 设计路径的语义表达
|
||||
|
||||
2. **EBNF语法定义**:
|
||||
```ebnf
|
||||
location ::= protocol_name '://' path_structure
|
||||
path_structure ::= segment {'/' segment}
|
||||
segment ::= literal | parameter
|
||||
parameter ::= '{' parameter_name '}'
|
||||
```
|
||||
|
||||
3. **路径规范示例**:
|
||||
- 简单路径:`protocol://resource_id`
|
||||
- 参数化路径:`protocol://{category}/{id}`
|
||||
- 复杂路径:`protocol://{domain}/{namespace}/{resource}`
|
||||
|
||||
### Phase 3: 查询参数设计(params组件)
|
||||
1. **参数分类规划**:
|
||||
- **格式控制参数**:如format、encoding等
|
||||
- **行为控制参数**:如cache、timeout等
|
||||
- **过滤参数**:如line、type等
|
||||
- **特定功能参数**:协议专有的参数
|
||||
|
||||
2. **参数文档格式**:
|
||||
```markdown
|
||||
| 参数名 | 类型 | 描述 | 默认值 | 示例 |
|
||||
|-------|------|------|--------|------|
|
||||
| format | string | 输出格式 | text | json, xml |
|
||||
| cache | boolean | 是否缓存 | true | true, false |
|
||||
```
|
||||
|
||||
3. **参数验证考虑**:
|
||||
- 参数类型验证
|
||||
- 参数值范围限制
|
||||
- 参数组合逻辑
|
||||
|
||||
### Phase 4: 注册表设计(registry组件)
|
||||
1. **注册表策略选择**:
|
||||
- **有注册表协议**:需要ID到路径的映射(如thought, execution)
|
||||
- **无注册表协议**:直接使用路径(如file, http)
|
||||
|
||||
2. **映射关系设计**(适用于有注册表协议):
|
||||
```markdown
|
||||
| 资源ID | 实际路径 | 描述 |
|
||||
|--------|----------|------|
|
||||
| resource-id | @package://path/to/file.md | 资源描述 |
|
||||
```
|
||||
|
||||
3. **路径引用规范**:
|
||||
- 支持@package://前缀引用包资源
|
||||
- 支持@project://前缀引用项目资源
|
||||
- 支持@file://前缀引用文件系统资源
|
||||
- 支持嵌套协议引用
|
||||
|
||||
### Phase 5: DPML结构实现
|
||||
|
||||
**关键要求:文件必须从`<resource>`标签直接开始**
|
||||
|
||||
**有注册表协议示例:**
|
||||
```xml
|
||||
<resource protocol="custom-protocol">
|
||||
<location>
|
||||
```ebnf
|
||||
location ::= custom-protocol://{resource_id}
|
||||
resource_id ::= [a-zA-Z][a-zA-Z0-9_-]*
|
||||
```
|
||||
</location>
|
||||
|
||||
<params>
|
||||
| 参数名 | 类型 | 描述 | 默认值 |
|
||||
|-------|------|------|--------|
|
||||
| format | string | 输出格式(text\|json\|xml) | text |
|
||||
| cache | boolean | 是否缓存结果 | true |
|
||||
| encoding | string | 文件编码 | utf8 |
|
||||
</params>
|
||||
|
||||
<registry>
|
||||
| 资源ID | 文件路径 |
|
||||
|--------|----------|
|
||||
| example-resource | @package://path/to/example.md |
|
||||
| another-resource | @project://config/another.md |
|
||||
</registry>
|
||||
</resource>
|
||||
```
|
||||
|
||||
**无注册表协议示例:**
|
||||
```xml
|
||||
<resource protocol="direct-access">
|
||||
<location>
|
||||
```ebnf
|
||||
location ::= direct-access://{path}
|
||||
path ::= absolute_path | relative_path
|
||||
absolute_path ::= '/' path_segment {'/' path_segment}
|
||||
relative_path ::= path_segment {'/' path_segment}
|
||||
path_segment ::= [^/]+
|
||||
```
|
||||
</location>
|
||||
|
||||
<params>
|
||||
| 参数名 | 类型 | 描述 | 默认值 |
|
||||
|-------|------|------|--------|
|
||||
| encoding | string | 文件编码 | utf8 |
|
||||
| line | string | 行范围(如"1-10") | - |
|
||||
</params>
|
||||
|
||||
<registry>
|
||||
<!-- 此协议不使用注册表,直接通过路径访问资源 -->
|
||||
</registry>
|
||||
</resource>
|
||||
```
|
||||
|
||||
### Phase 6: 系统集成验证
|
||||
1. **注册表集成**:确保协议定义与resource.registry.json格式兼容
|
||||
2. **代码实现检查**:验证是否需要创建对应的Protocol类文件
|
||||
3. **ResourceManager集成**:确认协议能被ResourceManager正确加载
|
||||
4. **加载语义支持**:验证@、@!、@?前缀的正确处理
|
||||
5. **查询参数解析**:确保参数能被正确解析和应用
|
||||
|
||||
### Phase 7: 质量检查和测试
|
||||
1. **语法验证**:确保DPML resource语法正确性
|
||||
2. **EBNF验证**:验证location部分的EBNF语法正确性
|
||||
3. **参数完整性**:确认所有参数都有清晰的类型和描述
|
||||
4. **注册表一致性**:验证注册表映射的逻辑正确性
|
||||
5. **纯净性检查**:确认文件从`<resource>`标签开始,无多余内容
|
||||
</process>
|
||||
|
||||
<criteria>
|
||||
## 质量评价标准
|
||||
|
||||
### 格式合规性
|
||||
- ✅ 文件从`<resource protocol="协议名">`标签直接开始
|
||||
- ✅ 使用正确的DPML resource标签结构
|
||||
- ✅ 三个子标签按location → params → registry顺序排列
|
||||
- ✅ XML语法正确,标签正确闭合
|
||||
- ✅ protocol属性值符合命名规范
|
||||
|
||||
### 路径格式规范性
|
||||
- ✅ location部分使用正确的EBNF语法
|
||||
- ✅ 路径格式清晰明确,无歧义
|
||||
- ✅ 参数化路径使用`{parameter}`格式
|
||||
- ✅ 路径结构与协议用途匹配
|
||||
- ✅ 支持协议的典型使用场景
|
||||
|
||||
### 参数文档完整性
|
||||
- ✅ 所有参数都有清晰的类型定义
|
||||
- ✅ 参数描述详细且准确
|
||||
- ✅ 提供了合理的默认值
|
||||
- ✅ 参数示例有助于理解
|
||||
- ✅ 参数组合逻辑合理
|
||||
|
||||
### 注册表设计合理性
|
||||
- ✅ 注册表策略与协议特性匹配
|
||||
- ✅ 映射关系清晰且实用
|
||||
- ✅ 路径引用符合PromptX规范
|
||||
- ✅ 抽象性和具体性平衡适当
|
||||
- ✅ 支持嵌套协议引用
|
||||
|
||||
### 系统集成性
|
||||
- ✅ 与ResourceManager兼容
|
||||
- ✅ 与resource.registry.json格式一致
|
||||
- ✅ 支持标准加载语义(@、@!、@?)
|
||||
- ✅ 查询参数能被正确解析
|
||||
- ✅ 与现有协议生态协调
|
||||
|
||||
### 实用价值
|
||||
- ✅ 解决了实际的资源访问需求
|
||||
- ✅ 路径格式简洁易用
|
||||
- ✅ 参数设计灵活且必要
|
||||
- ✅ 注册表提供了实际价值
|
||||
- ✅ 整体设计具有可扩展性
|
||||
|
||||
### 文件纯净性
|
||||
- ✅ 文件结构完全符合DPML resource规范
|
||||
- ✅ 无任何XML结构外的多余内容
|
||||
- ✅ 体现resource协议定义的标准格式
|
||||
- ✅ 三组件内容充实且相互配合
|
||||
</criteria>
|
||||
</execution>
|
||||
241
prompt/core/execution/role-authoring.execution.md
Normal file
241
prompt/core/execution/role-authoring.execution.md
Normal file
@ -0,0 +1,241 @@
|
||||
<execution>
|
||||
<constraint>
|
||||
## 客观技术限制
|
||||
- **DPML语法约束**:必须遵循EBNF定义的role语法结构
|
||||
- **XML格式要求**:标签必须正确闭合,属性值必须用双引号包围
|
||||
- **三组件架构固化**:personality、principle、knowledge三组件的语义边界固定
|
||||
- **文件编码**:必须使用UTF-8编码
|
||||
- **引用协议约束**:@!引用必须指向实际存在的资源
|
||||
- **PromptX系统集成**:必须与promptx命令行工具和ResourceManager兼容
|
||||
</constraint>
|
||||
|
||||
<rule>
|
||||
## 强制性编写规则
|
||||
- **纯XML结构**:role文件必须从`<role>`标签开始,不得包含任何XML结构外的内容
|
||||
- **根标签强制**:文件必须使用`<role>`作为根标签包装全部内容
|
||||
- **三组件完整**:必须包含personality、principle、knowledge三个子标签
|
||||
- **组件顺序固定**:子标签必须按personality → principle → knowledge顺序排列
|
||||
- **文件纯净性**:除了`<role>`标签结构外,不得包含任何其他内容
|
||||
- **引用规范性**:使用@!引用时必须遵循resource协议语法
|
||||
- **镜像结构约束**:用户资源必须遵循`.promptx/resource/domain/`结构,镜像系统`prompt/domain/`
|
||||
</rule>
|
||||
|
||||
<guideline>
|
||||
## 编写指导原则
|
||||
- **编排优先**:role文件主要职责是编排组合,推荐使用@!引用机制而非直接内容
|
||||
- **简洁性原则**:保持role文件的简洁和清晰,避免冗长的直接内容
|
||||
- **模块化思维**:将具体内容抽离到独立的thought、execution、knowledge文件中
|
||||
- **引用一致性**:在同一role文件中保持引用风格的一致性
|
||||
- **可维护性**:通过引用机制实现内容的独立维护和复用
|
||||
- **灵活性保留**:允许在引用和直接内容之间选择,但推荐引用
|
||||
- **镜像一致性**:用户资源结构与系统资源保持一致,降低认知负载
|
||||
</guideline>
|
||||
|
||||
<process>
|
||||
## 编写执行流程
|
||||
|
||||
### Phase 1: 角色概念设计
|
||||
1. **明确角色定位**:确定AI角色的核心身份和专业领域
|
||||
2. **分析能力需求**:识别角色需要的思维特征、行为原则和专业知识
|
||||
3. **规划组件结构**:决定三个组件的具体内容来源和组织方式
|
||||
4. **选择编排策略**:决定使用引用机制还是直接内容
|
||||
|
||||
### Phase 2: 资源组织规划
|
||||
|
||||
#### 用户资源目录结构(镜像系统结构):
|
||||
```
|
||||
.promptx/resource/domain/{roleId}/
|
||||
├── {roleId}.role.md # 主角色文件
|
||||
├── thought/ # 思维模式目录
|
||||
│ └── {name}.thought.md # 专业思维模式
|
||||
└── execution/ # 执行流程目录
|
||||
└── {name}.execution.md # 专业执行流程
|
||||
```
|
||||
|
||||
#### 内容来源规划:
|
||||
1. **思维模式来源**(personality组件):
|
||||
- 核心引用:`@!thought://remember`(记忆能力)
|
||||
- 核心引用:`@!thought://recall`(回忆能力)
|
||||
- 专业引用:`@!thought://[role-specific]`(角色特定思维)
|
||||
- 或直接定义角色的思维特征和认知偏好
|
||||
|
||||
2. **行为原则来源**(principle组件):
|
||||
- 专业引用:`@!execution://[role-specific]`(角色特定执行原则)
|
||||
- 或直接定义角色的行为准则和工作流程
|
||||
|
||||
3. **专业知识来源**(knowledge组件):
|
||||
- 领域引用:`@!knowledge://[domain-specific]`(领域专业知识)
|
||||
- 或直接定义角色的知识体系和技能框架
|
||||
|
||||
### Phase 3: DPML结构实现
|
||||
|
||||
**关键要求:文件必须从`<role>`标签直接开始**
|
||||
|
||||
**推荐编排风格(引用优先):**
|
||||
```xml
|
||||
<role>
|
||||
<personality>
|
||||
@!thought://remember
|
||||
@!thought://recall
|
||||
@!thought://[role-specific-thought]
|
||||
</personality>
|
||||
|
||||
<principle>
|
||||
@!execution://[role-specific-execution]
|
||||
</principle>
|
||||
|
||||
<knowledge>
|
||||
@!knowledge://[domain-specific-knowledge]
|
||||
</knowledge>
|
||||
</role>
|
||||
```
|
||||
|
||||
**示例:助手角色(参考assistant.role.md)**
|
||||
```xml
|
||||
<role>
|
||||
<personality>
|
||||
@!thought://remember
|
||||
@!thought://recall
|
||||
@!thought://assistant
|
||||
</personality>
|
||||
|
||||
<principle>
|
||||
@!execution://assistant
|
||||
</principle>
|
||||
|
||||
<knowledge>
|
||||
@!knowledge://general-assistant
|
||||
</knowledge>
|
||||
</role>
|
||||
```
|
||||
|
||||
**用户资源示例(自定义销售分析师):**
|
||||
```xml
|
||||
<role>
|
||||
<personality>
|
||||
@!thought://remember
|
||||
@!thought://recall
|
||||
@!thought://sales-analyst
|
||||
</personality>
|
||||
|
||||
<principle>
|
||||
@!execution://sales-data-analysis
|
||||
</principle>
|
||||
|
||||
<knowledge>
|
||||
@!knowledge://business-intelligence
|
||||
</knowledge>
|
||||
</role>
|
||||
```
|
||||
|
||||
**混合风格(引用+直接内容):**
|
||||
```xml
|
||||
<role>
|
||||
<personality>
|
||||
@!thought://remember
|
||||
@!thought://recall
|
||||
|
||||
## 角色特定思维特征
|
||||
- **用户导向思维**:始终以用户需求为中心
|
||||
- **解决方案思维**:专注于提供实用的解决方案
|
||||
</personality>
|
||||
|
||||
<principle>
|
||||
@!execution://assistant
|
||||
|
||||
## 补充行为原则
|
||||
- 保持耐心和友善的交互风格
|
||||
- 承认不确定性,不臆测答案
|
||||
</principle>
|
||||
|
||||
<knowledge>
|
||||
@!knowledge://general-assistant
|
||||
</knowledge>
|
||||
</role>
|
||||
```
|
||||
|
||||
**纯直接内容风格(不推荐但允许):**
|
||||
```xml
|
||||
<role>
|
||||
<personality>
|
||||
# 角色思维模式
|
||||
## 核心思维特征
|
||||
- **特征1**:描述
|
||||
- **特征2**:描述
|
||||
</personality>
|
||||
|
||||
<principle>
|
||||
# 角色行为原则
|
||||
## 核心原则
|
||||
- **原则1**:描述
|
||||
- **原则2**:描述
|
||||
</principle>
|
||||
|
||||
<knowledge>
|
||||
# 角色专业知识
|
||||
## 知识领域
|
||||
- **领域1**:描述
|
||||
- **领域2**:描述
|
||||
</knowledge>
|
||||
</role>
|
||||
```
|
||||
|
||||
### Phase 4: 质量检查和集成验证
|
||||
1. **结构验证**:确保DPML role语法正确性
|
||||
2. **引用检查**:验证所有@!引用的资源实际存在
|
||||
3. **三组件完整性**:确认personality、principle、knowledge都有实质内容
|
||||
4. **系统集成测试**:验证与promptx命令和ResourceManager的兼容性
|
||||
5. **纯净性检查**:确认文件从`<role>`标签开始,无多余内容
|
||||
6. **镜像结构验证**:确认用户资源目录结构符合镜像规范
|
||||
</process>
|
||||
|
||||
<criteria>
|
||||
## 质量评价标准
|
||||
|
||||
### 格式合规性
|
||||
- ✅ 文件从`<role>`标签直接开始,无额外内容
|
||||
- ✅ 使用正确的DPML role标签结构
|
||||
- ✅ 三个子标签按personality → principle → knowledge顺序排列
|
||||
- ✅ XML语法正确,标签正确闭合
|
||||
- ✅ Markdown格式规范(如有直接内容)
|
||||
|
||||
### 编排质量
|
||||
- ✅ 体现role文件的编排组合职责
|
||||
- ✅ 合理使用@!引用机制实现模块化
|
||||
- ✅ 保持文件的简洁性和可读性
|
||||
- ✅ 引用风格在文件内保持一致
|
||||
- ✅ 避免不必要的冗长直接内容
|
||||
|
||||
### 三组件完整性
|
||||
- ✅ personality组件包含思维特征定义或引用
|
||||
- ✅ principle组件包含行为原则定义或引用
|
||||
- ✅ knowledge组件包含专业知识定义或引用
|
||||
- ✅ 三组件逻辑一致,共同构建完整角色
|
||||
- ✅ 组件内容与角色定位匹配
|
||||
|
||||
### 引用有效性
|
||||
- ✅ 所有@!引用遵循resource协议语法
|
||||
- ✅ 引用的资源路径正确且存在
|
||||
- ✅ 引用内容与组件语义匹配
|
||||
- ✅ 引用关系清晰,无循环依赖
|
||||
|
||||
### 系统集成性
|
||||
- ✅ 与PromptX锦囊串联系统兼容
|
||||
- ✅ 支持promptx action命令激活
|
||||
- ✅ 角色定义可被AI系统正确解析
|
||||
- ✅ 实现角色的即时专家化能力
|
||||
- ✅ ResourceManager可正确发现和加载
|
||||
|
||||
### 文件纯净性
|
||||
- ✅ 文件结构完全符合DPML role规范
|
||||
- ✅ 无任何XML结构外的多余内容
|
||||
- ✅ 体现role文件的标准编排格式
|
||||
- ✅ 维持role文件的简洁优雅特性
|
||||
|
||||
### 架构合规性
|
||||
- ✅ 用户资源目录结构镜像系统结构
|
||||
- ✅ 文件组织符合`.promptx/resource/domain/`规范
|
||||
- ✅ 与系统资源结构保持一致性
|
||||
- ✅ 降低用户认知负载和学习成本
|
||||
</criteria>
|
||||
</execution>
|
||||
138
prompt/core/execution/role-generation.execution.md
Normal file
138
prompt/core/execution/role-generation.execution.md
Normal file
@ -0,0 +1,138 @@
|
||||
<execution>
|
||||
<constraint>
|
||||
## 客观技术限制
|
||||
- **DPML协议约束**:生成的角色必须严格遵循DPML `<role>`标签框架和三组件架构
|
||||
- **文件格式要求**:生成的角色文件必须是有效的Markdown格式并符合XML语法
|
||||
- **系统集成约束**:生成的角色必须与PromptX系统兼容,支持ResourceManager发现机制
|
||||
- **快速生成要求**:整个创建过程应在1-2分钟内完成
|
||||
- **目录结构约束**:用户资源必须创建在`.promptx/resource/domain/{roleId}/`目录,镜像系统结构
|
||||
- **文件组织约束**:角色相关的所有文件(execution、thought等)必须统一存放在角色目录下
|
||||
</constraint>
|
||||
|
||||
<rule>
|
||||
## 强制性执行规则
|
||||
- **三组件完整性**:每个生成的角色必须包含personality、principle、knowledge三个完整组件
|
||||
- **DPML语法严格性**:生成内容必须使用正确的XML标签语法,标签必须正确闭合
|
||||
- **领域识别准确性**:必须准确识别用户需求的专业领域
|
||||
- **模板化生成**:基于标准模板快速生成,避免复杂的定制化过程
|
||||
- **一次性交付**:生成后直接交付,避免反复确认和修改
|
||||
- **镜像结构强制**:用户资源必须创建在`.promptx/resource/domain/{roleId}/`,镜像系统`prompt/domain/`结构
|
||||
- **文件统一管理**:角色的execution、thought等扩展文件必须放在同一角色目录下,便于统一管理
|
||||
- **引用路径准确**:使用@!引用时必须指向正确的文件路径,确保引用关系有效
|
||||
</rule>
|
||||
|
||||
<guideline>
|
||||
## 执行指导原则
|
||||
- **简洁高效**:优先速度和效率,避免冗长对话
|
||||
- **标准化优先**:使用领域标准能力,而非深度定制
|
||||
- **即用原则**:生成的角色应立即可用,无需额外配置
|
||||
- **用户友好**:保持简单明了的交互体验
|
||||
- **镜像一致**:与系统结构保持一致,降低认知负载
|
||||
</guideline>
|
||||
|
||||
<process>
|
||||
## 极简3步生成流程
|
||||
|
||||
### Step 1: 领域快速识别 (30秒内)
|
||||
```
|
||||
目标:从用户描述中快速提取领域关键词
|
||||
|
||||
识别策略:
|
||||
- 提取技术栈关键词(如:微信小程序、React、Python等)
|
||||
- 识别职业角色关键词(如:产品经理、设计师、运营等)
|
||||
- 理解功能需求关键词(如:开发、分析、营销等)
|
||||
|
||||
快速确认:
|
||||
"明白了!您需要一个【X领域】的专业AI助手,对吗?"
|
||||
|
||||
处理原则:
|
||||
- 最多1次确认,用户确认后立即进入生成
|
||||
- 如果领域明确,跳过确认直接生成
|
||||
```
|
||||
|
||||
### Step 2: 模板化角色生成 (60秒内)
|
||||
```
|
||||
基于识别的领域,调用标准模板:
|
||||
|
||||
模板选择逻辑:
|
||||
- 微信小程序 → 小程序开发专家模板
|
||||
- 前端开发 → 前端工程师模板
|
||||
- 产品管理 → 产品经理模板
|
||||
- 数据分析 → 数据分析师模板
|
||||
- 更多领域... → 对应专业模板
|
||||
|
||||
文件组织结构(镜像系统结构):
|
||||
.promptx/resource/domain/{roleId}/
|
||||
├── {roleId}.role.md # 主角色文件
|
||||
├── thought/ # 思维模式目录(需要时创建)
|
||||
│ └── {specific}.thought.md # 专业思维模式
|
||||
└── execution/ # 执行模式目录(需要时创建)
|
||||
└── {specific}.execution.md # 专业执行流程
|
||||
|
||||
三组件自动填充:
|
||||
personality: 引用该领域的标准思维模式(remember + recall + 专业思维)
|
||||
principle: 引用该领域的标准执行流程(可独立创建execution文件)
|
||||
knowledge: 引用该领域的专业知识体系(或直接定义)
|
||||
|
||||
质量检查:
|
||||
☐ DPML格式正确
|
||||
☐ 三组件完整
|
||||
☐ 引用资源有效
|
||||
☐ 目录结构规范(镜像系统结构)
|
||||
☐ 文件路径正确
|
||||
☐ ResourceManager可发现
|
||||
```
|
||||
|
||||
### Step 3: 结果直接交付 (30秒内)
|
||||
```
|
||||
呈现格式:
|
||||
1. 角色价值简述
|
||||
2. 文件创建确认(正确目录结构)
|
||||
3. 激活命令说明
|
||||
4. 使用建议(可选)
|
||||
|
||||
目录结构展示(镜像系统结构):
|
||||
.promptx/resource/domain/{roleId}/
|
||||
├── {roleId}.role.md # ✅ 已创建
|
||||
└── [其他扩展文件] # ✅ 按需创建
|
||||
|
||||
交付策略:
|
||||
- 确认角色已正确创建在用户资源目录
|
||||
- 提供立即可用的激活命令
|
||||
- 说明文件组织规范(与系统结构一致)
|
||||
- 说明ResourceManager自动发现机制
|
||||
|
||||
后续支持:
|
||||
- 如果用户满意,直接结束
|
||||
- 如果需要扩展功能,指导创建execution/thought文件
|
||||
```
|
||||
</process>
|
||||
|
||||
<criteria>
|
||||
## 质量评价标准
|
||||
|
||||
### 效率指标
|
||||
- ✅ 总用时 ≤ 2分钟
|
||||
- ✅ 对话轮次 ≤ 3轮
|
||||
- ✅ 一次性生成成功率 ≥ 90%
|
||||
- ✅ 用户满意度 ≥ 85%
|
||||
|
||||
### 角色质量
|
||||
- ✅ DPML协议完全合规
|
||||
- ✅ 三组件内容实用
|
||||
- ✅ 角色定位准确
|
||||
- ✅ 立即可激活使用
|
||||
|
||||
### 架构合规
|
||||
- ✅ 目录结构镜像系统结构
|
||||
- ✅ ResourceManager可发现
|
||||
- ✅ 用户资源路径正确
|
||||
- ✅ 引用关系有效
|
||||
|
||||
### 用户体验
|
||||
- ✅ 交互流程简洁
|
||||
- ✅ 生成结果清晰
|
||||
- ✅ 激活方法明确
|
||||
- ✅ 学习成本极低
|
||||
</criteria>
|
||||
</execution>
|
||||
183
prompt/core/execution/thought-authoring.execution.md
Normal file
183
prompt/core/execution/thought-authoring.execution.md
Normal file
@ -0,0 +1,183 @@
|
||||
<execution>
|
||||
<constraint>
|
||||
## 客观技术限制
|
||||
- **DPML语法约束**:必须遵循EBNF定义的thought语法结构
|
||||
- **XML格式要求**:标签必须正确闭合,属性值必须用双引号包围
|
||||
- **Markdown兼容性**:内容部分必须是有效的Markdown格式,支持Mermaid图表
|
||||
- **文件编码**:必须使用UTF-8编码
|
||||
- **思维模式固化**:四种思维模式的语义特征不可改变
|
||||
- **可视化限制**:Mermaid图表必须符合语法规范
|
||||
</constraint>
|
||||
|
||||
<rule>
|
||||
## 强制性编写规则
|
||||
- **纯XML结构**:thought文件必须从`<thought>`标签开始,不得包含任何XML结构外的内容
|
||||
- **根标签强制**:文件必须使用`<thought>`作为根标签包装全部内容
|
||||
- **子标签命名**:只能使用规范定义的四个思维模式子标签:exploration, reasoning, plan, challenge
|
||||
- **语义一致性**:每个子标签内容必须与其思维模式语义定义保持一致
|
||||
- **文件纯净性**:除了`<thought>`标签结构外,不得包含任何其他内容
|
||||
- **内容实质性**:包含的子标签都必须有实质性思考内容,不得为空
|
||||
</rule>
|
||||
|
||||
<guideline>
|
||||
## 编写指导原则
|
||||
- **思维模式清晰性**:每个子标签的内容应明确体现对应的思维特征
|
||||
- **推荐思考顺序**:建议按exploration → challenge → reasoning → plan顺序组织思考
|
||||
- **可视化优先**:优先使用Mermaid图表表达复杂的思维关系和逻辑结构
|
||||
- **内容层次化**:使用Markdown的标题、列表等结构组织思考内容
|
||||
- **思维完整性**:确保思考覆盖问题的关键维度,避免思维盲区
|
||||
- **灵活组合**:根据实际需求选择合适的思维模式组合,无需全部使用
|
||||
</guideline>
|
||||
|
||||
<process>
|
||||
## 编写执行流程
|
||||
|
||||
### Phase 1: 思考需求分析
|
||||
1. **明确思考目标**:确定这个thought要解决什么问题或分析什么议题
|
||||
2. **识别思考复杂度**:判断问题是否需要多维度思考
|
||||
3. **选择思维模式**:根据问题特点选择合适的思维模式组合
|
||||
4. **确定思考深度**:决定每个思维模式需要的分析深度
|
||||
|
||||
### Phase 2: 思维模式规划
|
||||
1. **探索思维规划**:
|
||||
- 确定需要发散思考的维度
|
||||
- 列出要探索的可能性和创新点
|
||||
- 规划关联性分析的范围
|
||||
|
||||
2. **挑战思维规划**:
|
||||
- 识别需要质疑的假设和观点
|
||||
- 列出潜在风险和问题点
|
||||
- 规划批判性分析的角度
|
||||
|
||||
3. **推理思维规划**:
|
||||
- 确定需要验证的逻辑链条
|
||||
- 规划因果关系分析路径
|
||||
- 设计系统性推理结构
|
||||
|
||||
4. **计划思维规划**:
|
||||
- 明确需要设计的行动方案
|
||||
- 规划决策路径和组织结构
|
||||
- 确定系统架构的层次
|
||||
|
||||
### Phase 3: DPML结构实现
|
||||
|
||||
**关键要求:文件必须从`<thought>`标签直接开始**
|
||||
|
||||
```xml
|
||||
<thought>
|
||||
<exploration>
|
||||
# 探索思维:发散性思考
|
||||
[跳跃思考、生成可能性、寻找创新点和关联性]
|
||||
</exploration>
|
||||
|
||||
<challenge>
|
||||
# 挑战思维:批判性思考
|
||||
[逆向思考、质疑假设、识别风险和问题点]
|
||||
</challenge>
|
||||
|
||||
<reasoning>
|
||||
# 推理思维:系统性思考
|
||||
[连续推理、验证逻辑、分析因果关系]
|
||||
</reasoning>
|
||||
|
||||
<plan>
|
||||
# 计划思维:结构性思考
|
||||
[设计方案、决策路径、组织架构]
|
||||
</plan>
|
||||
</thought>
|
||||
```
|
||||
|
||||
**推荐思考顺序示例:**
|
||||
```xml
|
||||
<thought>
|
||||
<!-- 1. 先发散探索 -->
|
||||
<exploration>
|
||||
## 可能性探索
|
||||
- 方向A:...
|
||||
- 方向B:...
|
||||
|
||||
## 关联性分析
|
||||
```mermaid
|
||||
mindmap
|
||||
root)问题核心(
|
||||
分支1
|
||||
分支2
|
||||
分支3
|
||||
```
|
||||
</exploration>
|
||||
|
||||
<!-- 2. 再批判质疑 -->
|
||||
<challenge>
|
||||
## 假设质疑
|
||||
- 对方向A的质疑:...
|
||||
- 对方向B的质疑:...
|
||||
|
||||
## 风险识别
|
||||
- 潜在风险1:...
|
||||
- 潜在风险2:...
|
||||
</challenge>
|
||||
|
||||
<!-- 3. 然后系统推理 -->
|
||||
<reasoning>
|
||||
## 逻辑验证
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[前提] --> B[推理]
|
||||
B --> C[结论]
|
||||
```
|
||||
</reasoning>
|
||||
|
||||
<!-- 4. 最后制定计划 -->
|
||||
<plan>
|
||||
## 行动方案
|
||||
1. 步骤一:...
|
||||
2. 步骤二:...
|
||||
</plan>
|
||||
</thought>
|
||||
```
|
||||
|
||||
### Phase 4: 思维质量检查
|
||||
1. **思维模式验证**:确保每个子标签体现正确的思维特征
|
||||
2. **逻辑一致性检查**:验证不同思维模式间的逻辑关系
|
||||
3. **思考完整性审核**:确认思考覆盖问题的关键维度
|
||||
4. **可视化检查**:验证Mermaid图表语法正确性和表达清晰性
|
||||
5. **纯净性检查**:确认文件从`<thought>`标签开始,无多余内容
|
||||
</process>
|
||||
|
||||
<criteria>
|
||||
## 质量评价标准
|
||||
|
||||
### 格式合规性
|
||||
- ✅ 文件从`<thought>`标签直接开始,无额外内容
|
||||
- ✅ 使用正确的DPML thought标签结构
|
||||
- ✅ 子标签命名符合四种思维模式规范
|
||||
- ✅ XML语法正确,标签正确闭合
|
||||
- ✅ Markdown格式规范,Mermaid图表有效
|
||||
|
||||
### 思维模式准确性
|
||||
- ✅ exploration体现发散性、跳跃性思考特征
|
||||
- ✅ challenge体现批判性、逆向思考特征
|
||||
- ✅ reasoning体现系统性、连续性推理特征
|
||||
- ✅ plan体现结构性、秩序性思考特征
|
||||
- ✅ 各思维模式语义边界清晰,不混淆
|
||||
|
||||
### 思考质量
|
||||
- ✅ 思考内容具有实质性和深度
|
||||
- ✅ 逻辑关系清晰,推理链条完整
|
||||
- ✅ 覆盖问题的关键维度,无明显盲区
|
||||
- ✅ 思维过程系统化,层次分明
|
||||
- ✅ 结论或方案具有可操作性
|
||||
|
||||
### 可视化效果
|
||||
- ✅ 恰当使用Mermaid图表表达复杂关系
|
||||
- ✅ 图表类型选择合适(mindmap, flowchart, graph等)
|
||||
- ✅ 可视化内容与文字描述相互补充
|
||||
- ✅ 图表简洁清晰,易于理解
|
||||
|
||||
### 文件纯净性
|
||||
- ✅ 文件结构完全符合DPML thought规范
|
||||
- ✅ 无任何XML结构外的多余内容
|
||||
- ✅ 体现thought文件的标准格式
|
||||
- ✅ 思维模式组合合理,符合实际需求
|
||||
</criteria>
|
||||
</execution>
|
||||
19
prompt/core/nuwa/nuwa.role.md
Normal file
19
prompt/core/nuwa/nuwa.role.md
Normal file
@ -0,0 +1,19 @@
|
||||
<role>
|
||||
<personality>
|
||||
@!thought://remember
|
||||
@!thought://recall
|
||||
@!thought://role-creation
|
||||
</personality>
|
||||
|
||||
<principle>
|
||||
@!execution://role-generation
|
||||
@!execution://role-authoring
|
||||
@!execution://thought-authoring
|
||||
@!execution://execution-authoring
|
||||
@!execution://resource-authoring
|
||||
</principle>
|
||||
|
||||
<knowledge>
|
||||
<!-- 未来可以在这里添加其他协议资源引用 -->
|
||||
</knowledge>
|
||||
</role>
|
||||
63
prompt/core/thought/role-creation.thought.md
Normal file
63
prompt/core/thought/role-creation.thought.md
Normal file
@ -0,0 +1,63 @@
|
||||
<thought>
|
||||
<exploration>
|
||||
## 领域快速识别
|
||||
|
||||
### 从用户描述中提取核心信息
|
||||
- **领域关键词**:用户提到的技术栈、职业、业务领域
|
||||
- **功能期望**:用户希望AI助手具备的核心能力
|
||||
- **应用场景**:主要的使用场景和工作环境
|
||||
|
||||
### 领域标准化映射
|
||||
- **技术领域**:前端开发、后端开发、移动开发、数据分析等
|
||||
- **业务领域**:产品管理、市场营销、设计创意、运营管理等
|
||||
- **综合领域**:项目管理、技术架构、创业咨询、教育培训等
|
||||
|
||||
### 快速能力框架识别
|
||||
- 该领域的核心技能需求
|
||||
- 该领域的典型工作流程
|
||||
- 该领域的专业知识体系
|
||||
</exploration>
|
||||
|
||||
<reasoning>
|
||||
## 基于ResourceManager的资源生成逻辑
|
||||
|
||||
### 架构驱动的生成策略
|
||||
```
|
||||
用户描述 → 领域识别 → 资源规划 → 文件生成 → ResourceManager发现
|
||||
```
|
||||
|
||||
### 镜像结构思维模式
|
||||
- **结构一致性**:用户资源目录镜像系统`prompt/domain/`结构
|
||||
- **认知负载最小化**:与系统结构保持一致,降低学习成本
|
||||
- **资源聚合原则**:角色相关的所有文件统一管理在角色目录下
|
||||
|
||||
### 三组件标准化填充策略
|
||||
- **Personality设计**:
|
||||
- 基于领域的通用思维特征
|
||||
- 该领域专业人士的认知偏好
|
||||
- 高效协作的交互风格
|
||||
|
||||
- **Principle设计**:
|
||||
- 该领域的标准工作流程
|
||||
- 通用的质量标准和最佳实践
|
||||
- 常见问题的处理原则
|
||||
|
||||
- **Knowledge设计**:
|
||||
- 该领域的核心技能栈
|
||||
- 必备的专业知识体系
|
||||
- 常用工具和方法论
|
||||
|
||||
### 文件组织优化思维
|
||||
- **目录结构规划**:`.promptx/resource/domain/{roleId}/`
|
||||
- **扩展文件支持**:thought/、execution/子目录按需创建
|
||||
- **引用关系设计**:优先使用@!引用机制,实现模块化
|
||||
- **发现机制适配**:确保ResourceManager能正确发现和加载
|
||||
|
||||
### 质量保证机制
|
||||
- 确保三组件逻辑一致
|
||||
- 验证角色定位清晰准确
|
||||
- 保证实用性和可操作性
|
||||
- 符合DPML协议规范
|
||||
- 满足ResourceManager发现要求
|
||||
</reasoning>
|
||||
</thought>
|
||||
@ -0,0 +1,244 @@
|
||||
<execution>
|
||||
<constraint>
|
||||
## 微信平台技术约束
|
||||
- **包大小限制**:主包不超过2MB,总包不超过20MB
|
||||
- **代码包限制**:分包不超过2MB,最多使用20个分包
|
||||
- **API调用限制**:网络请求并发限制,部分API有调用频次限制
|
||||
- **性能要求**:页面渲染时间不超过2秒,交互响应时间不超过300ms
|
||||
- **平台兼容性**:需兼容微信不同版本和iOS/Android双平台
|
||||
- **审核规范**:必须遵守微信小程序平台规则和内容规范
|
||||
- **开发工具依赖**:必须使用微信开发者工具进行开发和调试
|
||||
</constraint>
|
||||
|
||||
<rule>
|
||||
## 强制性开发规则
|
||||
- **代码规范强制**:必须遵循微信小程序开发规范和ESLint配置
|
||||
- **文件结构固定**:页面必须包含.js/.wxml/.wxss/.json四个文件
|
||||
- **生命周期规范**:严格按照小程序生命周期进行开发,避免内存泄漏
|
||||
- **API使用规范**:必须进行用户授权检查,合规使用敏感API
|
||||
- **安全要求**:敏感数据必须加密传输,用户隐私信息严格保护
|
||||
- **性能底线**:setData调用频次控制,避免频繁的数据更新
|
||||
- **审核合规**:代码和内容必须符合微信审核要求,避免违规操作
|
||||
</rule>
|
||||
|
||||
<guideline>
|
||||
## 开发最佳实践指导
|
||||
- **组件化优先**:优先封装可复用组件,提高开发效率和代码质量
|
||||
- **性能优化导向**:关注首屏加载时间、内存使用、用户体验流畅度
|
||||
- **用户体验优先**:遵循微信设计规范,保持界面一致性和易用性
|
||||
- **渐进增强**:优雅降级处理,确保在低版本微信中基本功能可用
|
||||
- **错误处理友好**:提供清晰的错误提示和用户引导
|
||||
- **测试覆盖全面**:真机测试、兼容性测试、性能测试并重
|
||||
- **版本管理规范**:使用语义化版本控制,合理规划版本发布
|
||||
</guideline>
|
||||
|
||||
<process>
|
||||
## 微信小程序开发执行流程
|
||||
|
||||
### Phase 1: 项目规划与准备
|
||||
```
|
||||
需求分析:
|
||||
1. 明确功能需求和用户场景
|
||||
2. 分析技术可行性和平台限制
|
||||
3. 确定MVP功能范围和迭代计划
|
||||
4. 评估开发周期和资源需求
|
||||
|
||||
技术选型:
|
||||
1. 框架选择:原生/uni-app/Taro/WePY
|
||||
2. UI组件库:WeUI/Vant Weapp/ColorUI
|
||||
3. 状态管理:原生/MobX/Redux
|
||||
4. 后端服务:传统后端/云开发
|
||||
|
||||
项目初始化:
|
||||
1. 创建小程序项目,配置基础信息
|
||||
2. 搭建项目目录结构
|
||||
3. 配置开发环境和构建工具
|
||||
4. 设置代码规范和Git工作流
|
||||
```
|
||||
|
||||
### Phase 2: 架构设计与环境配置
|
||||
```
|
||||
目录结构设计:
|
||||
├── pages/ # 页面文件
|
||||
│ ├── index/ # 首页
|
||||
│ └── detail/ # 详情页
|
||||
├── components/ # 公共组件
|
||||
├── utils/ # 工具函数
|
||||
├── api/ # 接口封装
|
||||
├── store/ # 状态管理
|
||||
├── styles/ # 公共样式
|
||||
├── static/ # 静态资源
|
||||
└── app.js/wxss/json # 全局配置
|
||||
|
||||
架构设计原则:
|
||||
1. 模块化:功能模块独立,便于维护
|
||||
2. 组件化:UI组件可复用,提高效率
|
||||
3. 服务化:API接口统一封装管理
|
||||
4. 配置化:可配置参数集中管理
|
||||
```
|
||||
|
||||
### Phase 3: UI开发与组件封装
|
||||
```
|
||||
页面开发流程:
|
||||
1. 分析设计稿,拆解页面结构
|
||||
2. 使用WXML构建页面骨架
|
||||
3. 使用WXSS实现样式效果
|
||||
4. 处理响应式布局和适配
|
||||
|
||||
组件开发策略:
|
||||
1. 识别可复用的UI模块
|
||||
2. 封装自定义组件
|
||||
3. 定义组件属性和事件
|
||||
4. 编写组件文档和使用示例
|
||||
|
||||
样式开发规范:
|
||||
1. 使用rpx单位适配不同屏幕
|
||||
2. 遵循BEM命名规范
|
||||
3. 使用CSS变量管理主题色彩
|
||||
4. 优化CSS性能,避免复杂选择器
|
||||
```
|
||||
|
||||
### Phase 4: 功能开发与API集成
|
||||
```
|
||||
业务逻辑开发:
|
||||
1. 实现页面交互逻辑
|
||||
2. 处理用户输入和表单验证
|
||||
3. 实现路由跳转和参数传递
|
||||
4. 处理页面状态管理
|
||||
|
||||
API集成开发:
|
||||
1. 封装网络请求模块
|
||||
2. 实现接口调用和数据处理
|
||||
3. 添加请求拦截器和错误处理
|
||||
4. 实现数据缓存和同步策略
|
||||
|
||||
数据管理:
|
||||
1. 设计数据流向和状态管理
|
||||
2. 实现本地存储和缓存策略
|
||||
3. 处理数据同步和更新
|
||||
4. 优化setData性能
|
||||
```
|
||||
|
||||
### Phase 5: 性能优化与调试
|
||||
```
|
||||
性能优化策略:
|
||||
1. 代码分包:合理拆分主包和分包
|
||||
2. 懒加载:按需加载页面和组件
|
||||
3. 图片优化:压缩图片,使用WebP格式
|
||||
4. 缓存策略:合理使用缓存减少请求
|
||||
|
||||
调试与测试:
|
||||
1. 开发者工具调试
|
||||
2. 真机预览和调试
|
||||
3. 性能分析和优化
|
||||
4. 兼容性测试
|
||||
|
||||
代码质量保证:
|
||||
1. ESLint代码检查
|
||||
2. 单元测试编写
|
||||
3. 代码审查
|
||||
4. 性能监控
|
||||
```
|
||||
|
||||
### Phase 6: 测试发布与上线
|
||||
```
|
||||
测试阶段:
|
||||
1. 功能测试:验证所有功能正常
|
||||
2. 兼容性测试:测试不同设备和版本
|
||||
3. 性能测试:检查加载速度和流畅度
|
||||
4. 安全测试:验证数据安全和权限控制
|
||||
|
||||
发布准备:
|
||||
1. 准备小程序基础信息和资料
|
||||
2. 配置服务器域名和业务域名
|
||||
3. 设置用户隐私保护指引
|
||||
4. 准备审核说明和测试账号
|
||||
|
||||
审核发布:
|
||||
1. 提交代码审核
|
||||
2. 响应审核反馈
|
||||
3. 发布正式版本
|
||||
4. 监控线上表现
|
||||
```
|
||||
|
||||
### Phase 7: 运营维护与迭代
|
||||
```
|
||||
上线监控:
|
||||
1. 监控小程序性能指标
|
||||
2. 收集用户反馈和错误日志
|
||||
3. 分析用户行为数据
|
||||
4. 优化用户体验
|
||||
|
||||
迭代优化:
|
||||
1. 根据数据分析优化功能
|
||||
2. 修复发现的Bug
|
||||
3. 新功能开发和上线
|
||||
4. 持续性能优化
|
||||
|
||||
版本管理:
|
||||
1. 制定版本发布计划
|
||||
2. 管理版本兼容性
|
||||
3. 处理版本回滚
|
||||
4. 维护发布文档
|
||||
```
|
||||
</process>
|
||||
|
||||
<criteria>
|
||||
## 质量评价标准
|
||||
|
||||
### 代码质量指标
|
||||
- ✅ **规范性检查**:通过ESLint检查,无语法错误
|
||||
- ✅ **结构清晰**:目录结构合理,文件命名规范
|
||||
- ✅ **组件化程度**:公共组件复用率≥60%
|
||||
- ✅ **代码注释**:关键业务逻辑注释覆盖率≥80%
|
||||
- ✅ **函数复杂度**:单个函数行数不超过50行
|
||||
|
||||
### 性能质量指标
|
||||
- ✅ **首屏加载**:首屏渲染时间≤2秒
|
||||
- ✅ **包大小控制**:主包大小≤1.5MB,分包合理使用
|
||||
- ✅ **内存使用**:页面内存占用≤50MB
|
||||
- ✅ **网络请求**:接口响应时间≤1秒
|
||||
- ✅ **用户体验**:页面切换流畅,无明显卡顿
|
||||
|
||||
### 功能质量指标
|
||||
- ✅ **功能完整性**:核心功能100%实现
|
||||
- ✅ **交互友好性**:操作响应及时,反馈明确
|
||||
- ✅ **兼容性**:支持微信6.6.3以上版本
|
||||
- ✅ **错误处理**:异常情况有友好提示
|
||||
- ✅ **权限管理**:合规申请和使用用户权限
|
||||
|
||||
### 安全合规指标
|
||||
- ✅ **数据安全**:敏感数据加密传输和存储
|
||||
- ✅ **隐私保护**:用户隐私信息保护到位
|
||||
- ✅ **内容合规**:内容符合平台规范
|
||||
- ✅ **API合规**:API使用符合官方要求
|
||||
- ✅ **审核通过**:能够顺利通过微信审核
|
||||
|
||||
### 用户体验指标
|
||||
- ✅ **界面美观**:UI设计符合微信规范
|
||||
- ✅ **操作便捷**:用户操作路径简洁明了
|
||||
- ✅ **信息架构**:信息层次清晰,导航明确
|
||||
- ✅ **反馈及时**:操作反馈及时准确
|
||||
- ✅ **错误容错**:用户误操作有友好处理
|
||||
|
||||
## 持续改进标准
|
||||
|
||||
### 技术债务管理
|
||||
- 定期代码重构,消除技术债务
|
||||
- 升级依赖库版本,保持技术栈新鲜度
|
||||
- 优化陈旧代码,提高维护效率
|
||||
- 完善文档,降低维护成本
|
||||
|
||||
### 性能持续优化
|
||||
- 建立性能监控体系
|
||||
- 定期性能分析和优化
|
||||
- 关注新技术和最佳实践
|
||||
- 优化用户体验细节
|
||||
|
||||
### 团队协作效率
|
||||
- 完善开发规范和流程
|
||||
- 建立代码审查机制
|
||||
- 提升自动化程度
|
||||
- 知识分享和技能提升
|
||||
</criteria>
|
||||
</execution>
|
||||
1
scripts/start-mcp.sh
Normal file
1
scripts/start-mcp.sh
Normal file
@ -0,0 +1 @@
|
||||
cd /Users/sean/WorkSpaces/DeepracticeProjects/PromptX && pnpm start mcp-server
|
||||
@ -85,10 +85,16 @@ ${COMMANDS.HELLO}
|
||||
*/
|
||||
async analyzeRoleDependencies (roleInfo) {
|
||||
try {
|
||||
// 处理文件路径,将@package://前缀替换为实际路径
|
||||
// 处理文件路径,将@package://和@project://前缀替换为实际路径
|
||||
let filePath = roleInfo.file
|
||||
if (filePath.startsWith('@package://')) {
|
||||
filePath = filePath.replace('@package://', '')
|
||||
} else if (filePath.startsWith('@project://')) {
|
||||
// 对于@project://路径,使用当前工作目录作为基础路径
|
||||
const ProjectProtocol = require('../../resource/protocols/ProjectProtocol')
|
||||
const projectProtocol = new ProjectProtocol()
|
||||
const relativePath = filePath.replace('@project://', '')
|
||||
filePath = path.join(process.cwd(), relativePath)
|
||||
}
|
||||
|
||||
// 读取角色文件内容
|
||||
|
||||
@ -26,23 +26,25 @@ class HelloCommand extends BasePouchCommand {
|
||||
}
|
||||
|
||||
try {
|
||||
// 从ResourceManager获取统一注册表
|
||||
// 使用新的ResourceManager架构
|
||||
const ResourceManager = require('../../resource/resourceManager')
|
||||
const resourceManager = new ResourceManager()
|
||||
await resourceManager.initialize() // 确保初始化完成
|
||||
|
||||
let registeredRoles = {}
|
||||
if (resourceManager.registry && resourceManager.registry.protocols && resourceManager.registry.protocols.role && resourceManager.registry.protocols.role.registry) {
|
||||
registeredRoles = resourceManager.registry.protocols.role.registry
|
||||
// 加载统一注册表(包含系统+用户资源)
|
||||
const unifiedRegistry = await resourceManager.loadUnifiedRegistry()
|
||||
|
||||
// 提取角色数据
|
||||
const roleData = unifiedRegistry.role || {}
|
||||
|
||||
// 转换为HelloCommand期望的格式
|
||||
this.roleRegistry = {}
|
||||
for (const [roleId, roleInfo] of Object.entries(roleData)) {
|
||||
this.roleRegistry[roleId] = {
|
||||
file: roleInfo.file,
|
||||
name: roleInfo.name || roleId,
|
||||
description: this.extractDescription(roleInfo) || `${roleInfo.name || roleId}专业角色`,
|
||||
source: roleInfo.source || 'unknown'
|
||||
}
|
||||
|
||||
// 动态发现本地角色并合并
|
||||
const discoveredRoles = await this.discoverLocalRoles()
|
||||
|
||||
// 合并注册表中的角色和动态发现的角色
|
||||
this.roleRegistry = {
|
||||
...registeredRoles,
|
||||
...discoveredRoles
|
||||
}
|
||||
|
||||
// 如果没有任何角色,使用基础角色
|
||||
@ -51,31 +53,21 @@ class HelloCommand extends BasePouchCommand {
|
||||
assistant: {
|
||||
file: '@package://prompt/domain/assistant/assistant.role.md',
|
||||
name: '🙋 智能助手',
|
||||
description: '通用助理角色,提供基础的助理服务和记忆支持'
|
||||
description: '通用助理角色,提供基础的助理服务和记忆支持',
|
||||
source: 'fallback'
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('角色注册表加载失败,尝试动态发现:', error.message)
|
||||
console.warn('角色注册表加载失败,使用基础角色:', error.message)
|
||||
|
||||
// fallback到动态发现
|
||||
try {
|
||||
const discoveredRoles = await this.discoverLocalRoles()
|
||||
this.roleRegistry = Object.keys(discoveredRoles).length > 0 ? discoveredRoles : {
|
||||
assistant: {
|
||||
file: '@package://prompt/domain/assistant/assistant.role.md',
|
||||
name: '🙋 智能助手',
|
||||
description: '通用助理角色,提供基础的助理服务和记忆支持'
|
||||
}
|
||||
}
|
||||
} catch (discoveryError) {
|
||||
console.warn('动态角色发现也失败了:', discoveryError.message)
|
||||
// 使用基础角色作为fallback
|
||||
this.roleRegistry = {
|
||||
assistant: {
|
||||
file: '@package://prompt/domain/assistant/assistant.role.md',
|
||||
name: '🙋 智能助手',
|
||||
description: '通用助理角色,提供基础的助理服务和记忆支持'
|
||||
}
|
||||
description: '通用助理角色,提供基础的助理服务和记忆支持',
|
||||
source: 'fallback'
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -83,6 +75,21 @@ class HelloCommand extends BasePouchCommand {
|
||||
return this.roleRegistry
|
||||
}
|
||||
|
||||
/**
|
||||
* 从角色信息中提取描述
|
||||
* @param {Object} roleInfo - 角色信息对象
|
||||
* @returns {string} 角色描述
|
||||
*/
|
||||
extractDescription(roleInfo) {
|
||||
// 尝试从不同字段提取描述
|
||||
if (roleInfo.description) {
|
||||
return roleInfo.description
|
||||
}
|
||||
|
||||
// 如果有更多元数据,可以在这里扩展提取逻辑
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有角色列表(转换为数组格式)
|
||||
*/
|
||||
@ -92,10 +99,29 @@ class HelloCommand extends BasePouchCommand {
|
||||
id,
|
||||
name: roleInfo.name,
|
||||
description: roleInfo.description,
|
||||
file: roleInfo.file
|
||||
file: roleInfo.file,
|
||||
source: roleInfo.source
|
||||
}))
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取来源标签
|
||||
* @param {string} source - 资源来源
|
||||
* @returns {string} 来源标签
|
||||
*/
|
||||
getSourceLabel(source) {
|
||||
switch (source) {
|
||||
case 'user-generated':
|
||||
return '(用户生成)'
|
||||
case 'system':
|
||||
return '(系统角色)'
|
||||
case 'fallback':
|
||||
return '(默认角色)'
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
async getContent (args) {
|
||||
await this.loadRoleRegistry()
|
||||
const allRoles = await this.getAllRoles()
|
||||
@ -111,7 +137,8 @@ class HelloCommand extends BasePouchCommand {
|
||||
|
||||
// 清楚显示角色ID和激活命令
|
||||
allRoles.forEach((role, index) => {
|
||||
content += `### ${index + 1}. ${role.name}
|
||||
const sourceLabel = this.getSourceLabel(role.source)
|
||||
content += `### ${index + 1}. ${role.name} ${sourceLabel}
|
||||
**角色ID**: \`${role.id}\`
|
||||
**专业能力**: ${role.description}
|
||||
**激活命令**: \`${buildCommand.action(role.id)}\`
|
||||
|
||||
@ -50,6 +50,10 @@ class ExecutionProtocol extends ResourceProtocol {
|
||||
// 处理 @package:// 前缀
|
||||
if (resolvedPath.startsWith('@package://')) {
|
||||
resolvedPath = resolvedPath.replace('@package://', '')
|
||||
} else if (resolvedPath.startsWith('@project://')) {
|
||||
// 处理 @project:// 前缀,转换为绝对路径
|
||||
const relativePath = resolvedPath.replace('@project://', '')
|
||||
resolvedPath = path.join(process.cwd(), relativePath)
|
||||
}
|
||||
|
||||
return resolvedPath
|
||||
|
||||
@ -49,6 +49,10 @@ class ThoughtProtocol extends ResourceProtocol {
|
||||
// 处理 @package:// 前缀
|
||||
if (resolvedPath.startsWith('@package://')) {
|
||||
resolvedPath = resolvedPath.replace('@package://', '')
|
||||
} else if (resolvedPath.startsWith('@project://')) {
|
||||
// 处理 @project:// 前缀,转换为绝对路径
|
||||
const relativePath = resolvedPath.replace('@project://', '')
|
||||
resolvedPath = path.join(process.cwd(), relativePath)
|
||||
}
|
||||
|
||||
return resolvedPath
|
||||
|
||||
@ -11,6 +11,16 @@ const ProjectProtocol = require('./protocols/ProjectProtocol')
|
||||
const UserProtocol = require('./protocols/UserProtocol')
|
||||
const PromptProtocol = require('./protocols/PromptProtocol')
|
||||
|
||||
// 常量定义
|
||||
const USER_RESOURCE_DIR = '.promptx'
|
||||
const RESOURCE_DOMAIN_PATH = ['resource', 'domain']
|
||||
const SUPPORTED_RESOURCE_TYPES = ['role', 'thought', 'execution']
|
||||
const DPML_TAGS = {
|
||||
role: { start: '<role>', end: '</role>' },
|
||||
thought: { start: '<thought>', end: '</thought>' },
|
||||
execution: { start: '<execution>', end: '</execution>' }
|
||||
}
|
||||
|
||||
/**
|
||||
* 资源管理器 - 统一管理各种协议的资源加载
|
||||
*/
|
||||
@ -41,17 +51,101 @@ class ResourceManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载统一资源注册表
|
||||
* 加载统一资源注册表(合并系统和用户资源)
|
||||
*/
|
||||
async loadUnifiedRegistry () {
|
||||
try {
|
||||
// 加载系统资源注册表
|
||||
const registryPath = path.resolve(__dirname, '../../../resource.registry.json')
|
||||
|
||||
if (!await fs.pathExists(registryPath)) {
|
||||
throw new Error(`统一资源注册表文件不存在: ${registryPath}`)
|
||||
}
|
||||
|
||||
const registryContent = await fs.readJSON(registryPath)
|
||||
this.registry = registryContent
|
||||
const systemRegistry = await fs.readJSON(registryPath)
|
||||
|
||||
// 发现用户资源
|
||||
const userResources = await this.discoverUserResources()
|
||||
|
||||
// 从系统注册表中提取资源数据
|
||||
const extractedSystemResources = {}
|
||||
for (const resourceType of SUPPORTED_RESOURCE_TYPES) {
|
||||
const protocolConfig = systemRegistry.protocols[resourceType]
|
||||
if (protocolConfig && protocolConfig.registry) {
|
||||
extractedSystemResources[resourceType] = protocolConfig.registry
|
||||
}
|
||||
}
|
||||
|
||||
// 合并资源,用户资源覆盖系统资源
|
||||
const mergedRegistry = { ...systemRegistry }
|
||||
|
||||
// 合并各种资源类型
|
||||
for (const resourceType of SUPPORTED_RESOURCE_TYPES) {
|
||||
// 确保有基础结构
|
||||
if (!mergedRegistry[resourceType]) {
|
||||
mergedRegistry[resourceType] = {}
|
||||
}
|
||||
|
||||
// 先添加系统资源
|
||||
if (extractedSystemResources[resourceType]) {
|
||||
if (!mergedRegistry[resourceType]) mergedRegistry[resourceType] = {}
|
||||
for (const [id, resourceInfo] of Object.entries(extractedSystemResources[resourceType])) {
|
||||
// 对于role资源,resourceInfo是对象;对于thought/execution,resourceInfo是字符串
|
||||
if (resourceType === 'role') {
|
||||
mergedRegistry[resourceType][id] = {
|
||||
...resourceInfo,
|
||||
source: 'system'
|
||||
}
|
||||
} else {
|
||||
// 对于thought和execution,resourceInfo直接是路径字符串
|
||||
mergedRegistry[resourceType][id] = resourceInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 再添加用户资源(覆盖同名的系统资源)
|
||||
if (userResources[resourceType]) {
|
||||
for (const [id, resourceInfo] of Object.entries(userResources[resourceType])) {
|
||||
let filePath = resourceInfo.file || resourceInfo
|
||||
|
||||
// 将绝对路径转换为@project://相对路径格式
|
||||
if (path.isAbsolute(filePath)) {
|
||||
// 简单的路径转换:去掉项目根目录前缀
|
||||
const projectRoot = process.cwd()
|
||||
if (filePath.startsWith(projectRoot)) {
|
||||
const relativePath = path.relative(projectRoot, filePath)
|
||||
filePath = `@project://${relativePath}`
|
||||
}
|
||||
}
|
||||
|
||||
// 对于role资源类型,需要保持对象格式以包含name和description
|
||||
if (resourceType === 'role') {
|
||||
mergedRegistry[resourceType][id] = {
|
||||
file: filePath,
|
||||
name: resourceInfo.name || id,
|
||||
description: resourceInfo.description || `${resourceInfo.name || id}专业角色`,
|
||||
source: 'user-generated',
|
||||
format: resourceInfo.format,
|
||||
type: resourceInfo.type
|
||||
}
|
||||
} else {
|
||||
// 对于thought和execution,协议处理器期望的是文件路径字符串
|
||||
if (!mergedRegistry[resourceType]) mergedRegistry[resourceType] = {}
|
||||
mergedRegistry[resourceType][id] = filePath
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.registry = mergedRegistry
|
||||
return mergedRegistry
|
||||
} catch (error) {
|
||||
// 如果加载失败,至少返回一个基本结构
|
||||
logger.warn(`加载统一注册表失败: ${error.message}`)
|
||||
const fallbackRegistry = { role: {} }
|
||||
this.registry = fallbackRegistry
|
||||
return fallbackRegistry
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -73,10 +167,17 @@ class ResourceManager {
|
||||
const protocolHandler = new ProtocolClass()
|
||||
|
||||
// 从统一注册表获取协议配置
|
||||
const protocolConfig = this.registry.protocols[protocolName]
|
||||
// 对于基础协议(thought, execution等),直接从registry中获取
|
||||
const protocolRegistry = this.registry[protocolName]
|
||||
if (protocolRegistry) {
|
||||
protocolHandler.setRegistry(protocolRegistry)
|
||||
} else {
|
||||
// 对于复杂协议配置,从protocols配置中获取
|
||||
const protocolConfig = this.registry.protocols && this.registry.protocols[protocolName]
|
||||
if (protocolConfig && protocolConfig.registry) {
|
||||
protocolHandler.setRegistry(protocolConfig.registry)
|
||||
}
|
||||
}
|
||||
|
||||
handlers.set(protocolName, protocolHandler)
|
||||
}
|
||||
@ -204,6 +305,178 @@ class ResourceManager {
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* 发现用户资源
|
||||
* @returns {Promise<Object>} 用户资源注册表
|
||||
*/
|
||||
async discoverUserResources() {
|
||||
try {
|
||||
const PackageProtocol = require('./protocols/PackageProtocol')
|
||||
const packageProtocol = new PackageProtocol()
|
||||
const packageRoot = await packageProtocol.getPackageRoot()
|
||||
|
||||
const userResourcePath = path.join(packageRoot, USER_RESOURCE_DIR, ...RESOURCE_DOMAIN_PATH)
|
||||
|
||||
// 检查用户资源目录是否存在
|
||||
if (!await fs.pathExists(userResourcePath)) {
|
||||
return {}
|
||||
}
|
||||
|
||||
return await this.scanResourceDirectory(userResourcePath)
|
||||
} catch (error) {
|
||||
// 出错时返回空对象,不抛出异常
|
||||
logger.warn(`用户资源发现失败: ${error.message}`)
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描资源目录
|
||||
* @param {string} basePath - 基础路径
|
||||
* @returns {Promise<Object>} 发现的资源
|
||||
*/
|
||||
async scanResourceDirectory(basePath) {
|
||||
const resources = {}
|
||||
|
||||
try {
|
||||
const directories = await fs.readdir(basePath)
|
||||
|
||||
for (const roleDir of directories) {
|
||||
const rolePath = path.join(basePath, roleDir)
|
||||
|
||||
try {
|
||||
const stat = await fs.stat(rolePath)
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
// 扫描角色文件
|
||||
await this.scanRoleResources(rolePath, roleDir, resources)
|
||||
|
||||
// 扫描其他资源类型(thought, execution)
|
||||
await this.scanOtherResources(rolePath, roleDir, resources)
|
||||
}
|
||||
} catch (dirError) {
|
||||
// 跳过无法访问的目录
|
||||
logger.debug(`跳过目录 ${roleDir}: ${dirError.message}`)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn(`扫描资源目录失败 ${basePath}: ${error.message}`)
|
||||
}
|
||||
|
||||
return resources
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描角色资源
|
||||
* @param {string} rolePath - 角色目录路径
|
||||
* @param {string} roleId - 角色ID
|
||||
* @param {Object} resources - 资源容器
|
||||
*/
|
||||
async scanRoleResources(rolePath, roleId, resources) {
|
||||
const roleFile = path.join(rolePath, `${roleId}.role.md`)
|
||||
|
||||
if (await fs.pathExists(roleFile)) {
|
||||
try {
|
||||
const content = await fs.readFile(roleFile, 'utf8')
|
||||
|
||||
// 验证DPML格式
|
||||
if (this.validateDPMLFormat(content, 'role')) {
|
||||
const name = this.extractRoleName(content)
|
||||
|
||||
if (!resources.role) resources.role = {}
|
||||
resources.role[roleId] = {
|
||||
file: roleFile,
|
||||
name: name || roleId,
|
||||
source: 'user-generated',
|
||||
format: 'dpml',
|
||||
type: 'role'
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// 忽略单个文件的错误
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描其他资源类型
|
||||
* @param {string} rolePath - 角色目录路径
|
||||
* @param {string} roleId - 角色ID
|
||||
* @param {Object} resources - 资源容器
|
||||
*/
|
||||
async scanOtherResources(rolePath, roleId, resources) {
|
||||
for (const resourceType of SUPPORTED_RESOURCE_TYPES.filter(type => type !== 'role')) {
|
||||
const resourceDir = path.join(rolePath, resourceType)
|
||||
|
||||
if (await fs.pathExists(resourceDir)) {
|
||||
try {
|
||||
const files = await fs.readdir(resourceDir)
|
||||
|
||||
for (const file of files) {
|
||||
if (file.endsWith(`.${resourceType}.md`)) {
|
||||
const resourceName = file.replace(`.${resourceType}.md`, '')
|
||||
const filePath = path.join(resourceDir, file)
|
||||
const content = await fs.readFile(filePath, 'utf8')
|
||||
|
||||
if (this.validateDPMLFormat(content, resourceType)) {
|
||||
if (!resources[resourceType]) resources[resourceType] = {}
|
||||
resources[resourceType][resourceName] = {
|
||||
file: filePath,
|
||||
name: resourceName,
|
||||
source: 'user-generated',
|
||||
format: 'dpml',
|
||||
type: resourceType
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.debug(`扫描${resourceType}资源失败: ${error.message}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证DPML格式
|
||||
* @param {string} content - 文件内容
|
||||
* @param {string} type - 资源类型
|
||||
* @returns {boolean} 是否为有效格式
|
||||
*/
|
||||
validateDPMLFormat(content, type) {
|
||||
const tags = DPML_TAGS[type]
|
||||
if (!tags) {
|
||||
return false
|
||||
}
|
||||
|
||||
return content.includes(tags.start) && content.includes(tags.end)
|
||||
}
|
||||
|
||||
/**
|
||||
* 从角色内容中提取名称
|
||||
* @param {string} content - 角色文件内容
|
||||
* @returns {string} 角色名称
|
||||
*/
|
||||
extractRoleName(content) {
|
||||
// 简单的名称提取逻辑
|
||||
const match = content.match(/#\s*([^\n]+)/)
|
||||
return match ? match[1].trim() : null
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载系统资源注册表(兼容现有方法)
|
||||
* @returns {Promise<Object>} 系统资源注册表
|
||||
*/
|
||||
async loadSystemRegistry() {
|
||||
const registryPath = path.resolve(__dirname, '../../../resource.registry.json')
|
||||
|
||||
if (!await fs.pathExists(registryPath)) {
|
||||
throw new Error(`统一资源注册表文件不存在: ${registryPath}`)
|
||||
}
|
||||
|
||||
return await fs.readJSON(registryPath)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ResourceManager
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
"assistant": "@package://prompt/domain/assistant/thought/assistant.thought.md",
|
||||
"remember": "@package://prompt/core/thought/remember.thought.md",
|
||||
"recall": "@package://prompt/core/thought/recall.thought.md",
|
||||
"role-creation": "@package://prompt/core/thought/role-creation.thought.md",
|
||||
"product-manager": "@package://prompt/domain/product-manager/thought/product-manager.thought.md",
|
||||
"java-backend-developer": "@package://prompt/domain/java-backend-developer/thought/java-backend-developer.thought.md"
|
||||
}
|
||||
@ -33,7 +34,14 @@
|
||||
"system-architecture": "@package://prompt/domain/java-backend-developer/execution/system-architecture.execution.md",
|
||||
"spring-ecosystem": "@package://prompt/domain/java-backend-developer/execution/spring-ecosystem.execution.md",
|
||||
"code-quality": "@package://prompt/domain/java-backend-developer/execution/code-quality.execution.md",
|
||||
"database-design": "@package://prompt/domain/java-backend-developer/execution/database-design.execution.md"
|
||||
"database-design": "@package://prompt/domain/java-backend-developer/execution/database-design.execution.md",
|
||||
"role-generation": "@package://prompt/core/execution/role-generation.execution.md",
|
||||
"execution-authoring": "@package://prompt/core/execution/execution-authoring.execution.md",
|
||||
"thought-authoring": "@package://prompt/core/execution/thought-authoring.execution.md",
|
||||
"role-authoring": "@package://prompt/core/execution/role-authoring.execution.md",
|
||||
"resource-authoring": "@package://prompt/core/execution/resource-authoring.execution.md",
|
||||
|
||||
"wechat-miniprogram-development": "@package://prompt/domain/frontend-developer/execution/wechat-miniprogram-development.execution.md"
|
||||
}
|
||||
},
|
||||
"memory": {
|
||||
@ -78,6 +86,11 @@
|
||||
"name": "☕ Java后端开发者",
|
||||
"description": "专业Java后端开发专家,精通Spring生态系统、微服务架构和系统设计"
|
||||
},
|
||||
"nuwa": {
|
||||
"file": "@package://prompt/core/nuwa/nuwa.role.md",
|
||||
"name": "🎨 女娲",
|
||||
"description": "专业角色创造顾问,通过对话收集需求,为用户量身定制AI助手角色"
|
||||
},
|
||||
"test-role": {
|
||||
"file": "@package://prompt/domain/test-role/test-role.role.md",
|
||||
"name": "🧪 测试角色",
|
||||
|
||||
221
src/tests/commands/CrossPlatformDiscovery.unit.test.js
Normal file
221
src/tests/commands/CrossPlatformDiscovery.unit.test.js
Normal file
@ -0,0 +1,221 @@
|
||||
const path = require('path')
|
||||
const fs = require('fs-extra')
|
||||
const os = require('os')
|
||||
|
||||
describe('跨平台角色发现兼容性测试', () => {
|
||||
let tempDir
|
||||
let projectDir
|
||||
|
||||
beforeEach(async () => {
|
||||
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'cross-platform-test-'))
|
||||
projectDir = path.join(tempDir, 'test-project')
|
||||
|
||||
await fs.ensureDir(path.join(projectDir, 'prompt', 'domain'))
|
||||
await fs.ensureDir(path.join(projectDir, '.promptx', 'user-roles'))
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
if (tempDir) {
|
||||
await fs.remove(tempDir)
|
||||
}
|
||||
})
|
||||
|
||||
describe('Node.js 原生 API 替代 glob', () => {
|
||||
test('应该能使用 fs.readdir 代替 glob.sync', async () => {
|
||||
// 创建测试角色文件
|
||||
const roleDir = path.join(projectDir, 'prompt', 'domain', 'test-role')
|
||||
await fs.ensureDir(roleDir)
|
||||
await fs.writeFile(
|
||||
path.join(roleDir, 'test-role.role.md'),
|
||||
'<role><personality>测试</personality></role>'
|
||||
)
|
||||
|
||||
// 使用Node.js原生API实现角色发现(替代glob)
|
||||
async function discoverRolesWithNativeAPI(scanPath) {
|
||||
const discoveredRoles = {}
|
||||
|
||||
try {
|
||||
if (await fs.pathExists(scanPath)) {
|
||||
const domains = await fs.readdir(scanPath)
|
||||
|
||||
for (const domain of domains) {
|
||||
const domainDir = path.join(scanPath, domain)
|
||||
const stat = await fs.stat(domainDir)
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
const roleFile = path.join(domainDir, `${domain}.role.md`)
|
||||
if (await fs.pathExists(roleFile)) {
|
||||
const content = await fs.readFile(roleFile, 'utf-8')
|
||||
|
||||
discoveredRoles[domain] = {
|
||||
file: roleFile,
|
||||
name: `🎭 ${domain}`,
|
||||
description: '原生API发现的角色',
|
||||
source: 'native-api'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return discoveredRoles
|
||||
} catch (error) {
|
||||
console.warn('原生API角色发现失败:', error.message)
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
const domainPath = path.join(projectDir, 'prompt', 'domain')
|
||||
const discoveredRoles = await discoverRolesWithNativeAPI(domainPath)
|
||||
|
||||
expect(discoveredRoles).toHaveProperty('test-role')
|
||||
expect(discoveredRoles['test-role'].source).toBe('native-api')
|
||||
})
|
||||
|
||||
test('应该能处理不同平台的路径分隔符', () => {
|
||||
const unixPath = 'prompt/domain/role/role.role.md'
|
||||
const windowsPath = 'prompt\\domain\\role\\role.role.md'
|
||||
|
||||
// 使用path.join确保跨平台兼容性
|
||||
const normalizedPath = path.join('prompt', 'domain', 'role', 'role.role.md')
|
||||
|
||||
// 在当前平台上验证路径处理
|
||||
if (process.platform === 'win32') {
|
||||
expect(normalizedPath).toContain('\\')
|
||||
} else {
|
||||
expect(normalizedPath).toContain('/')
|
||||
}
|
||||
|
||||
// path.relative应该也能正常工作
|
||||
const relativePath = path.relative(projectDir, path.join(projectDir, normalizedPath))
|
||||
expect(relativePath).toBe(normalizedPath)
|
||||
})
|
||||
|
||||
test('应该处理路径中的特殊字符', async () => {
|
||||
// 创建包含特殊字符的角色名(但符合文件系统要求)
|
||||
const specialRoleName = 'role-with_special.chars'
|
||||
const roleDir = path.join(projectDir, 'prompt', 'domain', specialRoleName)
|
||||
await fs.ensureDir(roleDir)
|
||||
|
||||
const roleFile = path.join(roleDir, `${specialRoleName}.role.md`)
|
||||
await fs.writeFile(roleFile, '<role><personality>特殊角色</personality></role>')
|
||||
|
||||
// 验证能正确处理特殊字符的文件名
|
||||
expect(await fs.pathExists(roleFile)).toBe(true)
|
||||
|
||||
const content = await fs.readFile(roleFile, 'utf-8')
|
||||
expect(content).toContain('特殊角色')
|
||||
})
|
||||
})
|
||||
|
||||
describe('文件系统权限处理', () => {
|
||||
test('应该优雅处理无权限访问的目录', async () => {
|
||||
if (process.platform === 'win32') {
|
||||
// Windows权限测试较为复杂,跳过
|
||||
expect(true).toBe(true)
|
||||
return
|
||||
}
|
||||
|
||||
const restrictedDir = path.join(projectDir, 'restricted')
|
||||
await fs.ensureDir(restrictedDir)
|
||||
|
||||
// 移除读权限
|
||||
await fs.chmod(restrictedDir, 0o000)
|
||||
|
||||
// 角色发现应该不会因为权限问题而崩溃
|
||||
async function safeDiscoverRoles(scanPath) {
|
||||
try {
|
||||
if (await fs.pathExists(scanPath)) {
|
||||
const domains = await fs.readdir(scanPath)
|
||||
return domains
|
||||
}
|
||||
return []
|
||||
} catch (error) {
|
||||
// 应该优雅处理权限错误
|
||||
console.warn('权限不足,跳过目录:', scanPath)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
const result = await safeDiscoverRoles(restrictedDir)
|
||||
expect(Array.isArray(result)).toBe(true)
|
||||
|
||||
// 恢复权限以便清理
|
||||
await fs.chmod(restrictedDir, 0o755)
|
||||
})
|
||||
})
|
||||
|
||||
describe('错误恢复机制', () => {
|
||||
test('应该在部分文件失败时继续处理其他文件', async () => {
|
||||
// 创建多个角色,其中一个有问题
|
||||
const goodRoleDir = path.join(projectDir, 'prompt', 'domain', 'good-role')
|
||||
await fs.ensureDir(goodRoleDir)
|
||||
await fs.writeFile(
|
||||
path.join(goodRoleDir, 'good-role.role.md'),
|
||||
'<role><personality>正常角色</personality></role>'
|
||||
)
|
||||
|
||||
const badRoleDir = path.join(projectDir, 'prompt', 'domain', 'bad-role')
|
||||
await fs.ensureDir(badRoleDir)
|
||||
await fs.writeFile(
|
||||
path.join(badRoleDir, 'bad-role.role.md'),
|
||||
'无效内容'
|
||||
)
|
||||
|
||||
// 模拟容错的角色发现实现
|
||||
async function resilientDiscoverRoles(scanPath) {
|
||||
const discoveredRoles = {}
|
||||
const errors = []
|
||||
|
||||
try {
|
||||
if (await fs.pathExists(scanPath)) {
|
||||
const domains = await fs.readdir(scanPath)
|
||||
|
||||
for (const domain of domains) {
|
||||
try {
|
||||
const domainDir = path.join(scanPath, domain)
|
||||
const stat = await fs.stat(domainDir)
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
const roleFile = path.join(domainDir, `${domain}.role.md`)
|
||||
if (await fs.pathExists(roleFile)) {
|
||||
const content = await fs.readFile(roleFile, 'utf-8')
|
||||
|
||||
// 简单验证内容
|
||||
if (content.includes('<role>')) {
|
||||
discoveredRoles[domain] = {
|
||||
file: roleFile,
|
||||
name: `🎭 ${domain}`,
|
||||
description: '容错发现的角色',
|
||||
source: 'resilient-discovery'
|
||||
}
|
||||
} else {
|
||||
throw new Error('无效的角色文件格式')
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// 记录错误但继续处理其他文件
|
||||
errors.push({ domain, error: error.message })
|
||||
console.warn(`跳过无效角色 ${domain}:`, error.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('角色发现过程中出错:', error.message)
|
||||
}
|
||||
|
||||
return { discoveredRoles, errors }
|
||||
}
|
||||
|
||||
const domainPath = path.join(projectDir, 'prompt', 'domain')
|
||||
const result = await resilientDiscoverRoles(domainPath)
|
||||
|
||||
// 应该发现正常角色,跳过问题角色
|
||||
expect(result.discoveredRoles).toHaveProperty('good-role')
|
||||
expect(result.discoveredRoles).not.toHaveProperty('bad-role')
|
||||
expect(result.errors).toHaveLength(1)
|
||||
expect(result.errors[0].domain).toBe('bad-role')
|
||||
})
|
||||
})
|
||||
})
|
||||
231
src/tests/commands/HelloCommand.integration.test.js
Normal file
231
src/tests/commands/HelloCommand.integration.test.js
Normal file
@ -0,0 +1,231 @@
|
||||
const HelloCommand = require('../../lib/core/pouch/commands/HelloCommand')
|
||||
const ResourceManager = require('../../lib/core/resource/resourceManager')
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const os = require('os')
|
||||
|
||||
describe('HelloCommand - ResourceManager集成', () => {
|
||||
let helloCommand
|
||||
let tempDir
|
||||
let mockPackageRoot
|
||||
|
||||
beforeEach(async () => {
|
||||
// 创建临时测试目录
|
||||
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'promptx-hello-test-'))
|
||||
mockPackageRoot = tempDir
|
||||
|
||||
// 模拟用户资源目录结构
|
||||
await fs.ensureDir(path.join(tempDir, '.promptx', 'resource', 'domain'))
|
||||
|
||||
helloCommand = new HelloCommand()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
// 清理临时目录
|
||||
await fs.remove(tempDir)
|
||||
jest.restoreAllMocks()
|
||||
})
|
||||
|
||||
describe('用户角色发现集成', () => {
|
||||
it('应该显示用户创建的角色', async () => {
|
||||
// 创建测试用户角色
|
||||
const roleDir = path.join(tempDir, '.promptx', 'resource', 'domain', 'sales-expert')
|
||||
await fs.ensureDir(roleDir)
|
||||
|
||||
const roleContent = `<role>
|
||||
<personality>
|
||||
# 销售专家思维模式
|
||||
## 核心特征
|
||||
- **客户导向思维**:始终以客户需求为出发点
|
||||
</personality>
|
||||
|
||||
<principle>
|
||||
# 销售专家行为原则
|
||||
## 核心原则
|
||||
- **诚信为本**:建立长期客户关系
|
||||
</principle>
|
||||
|
||||
<knowledge>
|
||||
# 销售专业知识体系
|
||||
## 销售技巧
|
||||
- **需求挖掘**:深度了解客户真实需求
|
||||
</knowledge>
|
||||
</role>`
|
||||
|
||||
await fs.writeFile(path.join(roleDir, 'sales-expert.role.md'), roleContent)
|
||||
|
||||
// Mock ResourceManager的loadUnifiedRegistry方法
|
||||
jest.spyOn(ResourceManager.prototype, 'loadUnifiedRegistry')
|
||||
.mockResolvedValue({
|
||||
role: {
|
||||
'assistant': {
|
||||
file: '@package://prompt/domain/assistant/assistant.role.md',
|
||||
name: '🙋 智能助手',
|
||||
source: 'system',
|
||||
format: 'dpml',
|
||||
type: 'role'
|
||||
},
|
||||
'sales-expert': {
|
||||
file: path.join(roleDir, 'sales-expert.role.md'),
|
||||
name: '销售专家思维模式',
|
||||
source: 'user-generated',
|
||||
format: 'dpml',
|
||||
type: 'role'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 模拟执行hello命令
|
||||
const result = await helloCommand.execute([])
|
||||
|
||||
// 验证用户角色在输出中显示
|
||||
const allOutput = result.content || ''
|
||||
|
||||
expect(allOutput).toContain('sales-expert')
|
||||
expect(allOutput).toContain('销售专家')
|
||||
expect(allOutput).toContain('(用户生成)')
|
||||
})
|
||||
|
||||
it('应该允许用户角色覆盖系统角色', async () => {
|
||||
// 创建与系统角色同名的用户角色
|
||||
const roleDir = path.join(tempDir, '.promptx', 'resource', 'domain', 'assistant')
|
||||
await fs.ensureDir(roleDir)
|
||||
|
||||
const customAssistantContent = `<role>
|
||||
<personality>
|
||||
# 定制智能助手
|
||||
## 个性化特征
|
||||
- **专业导向**:专注于技术问题解决
|
||||
</personality>
|
||||
|
||||
<principle>
|
||||
# 定制助手原则
|
||||
## 核心原则
|
||||
- **精准回答**:提供准确的技术解决方案
|
||||
</principle>
|
||||
|
||||
<knowledge>
|
||||
# 定制助手知识体系
|
||||
## 技术领域
|
||||
- **编程语言**:多种编程语言的深度理解
|
||||
</knowledge>
|
||||
</role>`
|
||||
|
||||
await fs.writeFile(path.join(roleDir, 'assistant.role.md'), customAssistantContent)
|
||||
|
||||
// Mock ResourceManager返回用户覆盖的角色
|
||||
jest.spyOn(ResourceManager.prototype, 'loadUnifiedRegistry')
|
||||
.mockResolvedValue({
|
||||
role: {
|
||||
'assistant': {
|
||||
file: path.join(roleDir, 'assistant.role.md'),
|
||||
name: '定制智能助手',
|
||||
source: 'user-generated',
|
||||
format: 'dpml',
|
||||
type: 'role'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const result = await helloCommand.execute([])
|
||||
|
||||
const allOutput = result.content || ''
|
||||
|
||||
// 验证显示的是用户版本
|
||||
expect(allOutput).toContain('定制智能助手')
|
||||
expect(allOutput).toContain('(用户生成)')
|
||||
expect(allOutput).not.toContain('🙋 智能助手')
|
||||
})
|
||||
|
||||
it('应该同时显示系统角色和用户角色', async () => {
|
||||
// 创建用户角色
|
||||
const userRoleDir = path.join(tempDir, '.promptx', 'resource', 'domain', 'data-analyst')
|
||||
await fs.ensureDir(userRoleDir)
|
||||
|
||||
const userRoleContent = `<role>
|
||||
<personality>
|
||||
# 数据分析师
|
||||
## 分析思维
|
||||
- **逻辑思维**:系统性分析数据模式
|
||||
</personality>
|
||||
|
||||
<principle>
|
||||
# 分析原则
|
||||
## 核心原则
|
||||
- **数据驱动**:基于数据做决策
|
||||
</principle>
|
||||
|
||||
<knowledge>
|
||||
# 分析知识
|
||||
## 统计学
|
||||
- **描述统计**:数据的基本特征分析
|
||||
</knowledge>
|
||||
</role>`
|
||||
|
||||
await fs.writeFile(path.join(userRoleDir, 'data-analyst.role.md'), userRoleContent)
|
||||
|
||||
// Mock ResourceManager返回系统和用户角色
|
||||
jest.spyOn(ResourceManager.prototype, 'loadUnifiedRegistry')
|
||||
.mockResolvedValue({
|
||||
role: {
|
||||
'assistant': {
|
||||
file: '@package://prompt/domain/assistant/assistant.role.md',
|
||||
name: '🙋 智能助手',
|
||||
source: 'system',
|
||||
format: 'dpml',
|
||||
type: 'role'
|
||||
},
|
||||
'java-backend-developer': {
|
||||
file: '@package://prompt/domain/java-backend-developer/java-backend-developer.role.md',
|
||||
name: '☕ Java后端开发专家',
|
||||
source: 'system',
|
||||
format: 'dpml',
|
||||
type: 'role'
|
||||
},
|
||||
'data-analyst': {
|
||||
file: path.join(userRoleDir, 'data-analyst.role.md'),
|
||||
name: '数据分析师',
|
||||
source: 'user-generated',
|
||||
format: 'dpml',
|
||||
type: 'role'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const result = await helloCommand.execute([])
|
||||
|
||||
const allOutput = result.content || ''
|
||||
|
||||
// 验证系统角色和用户角色都显示
|
||||
expect(allOutput).toContain('智能助手')
|
||||
expect(allOutput).toContain('Java后端开发专家')
|
||||
expect(allOutput).toContain('数据分析师')
|
||||
expect(allOutput).toContain('data-analyst')
|
||||
})
|
||||
})
|
||||
|
||||
describe('错误处理', () => {
|
||||
it('应该优雅处理资源发现失败', async () => {
|
||||
// 模拟ResourceManager错误
|
||||
jest.spyOn(ResourceManager.prototype, 'loadUnifiedRegistry')
|
||||
.mockRejectedValue(new Error('资源发现失败'))
|
||||
|
||||
// 应该不抛出异常
|
||||
const result = await helloCommand.execute([])
|
||||
|
||||
// 应该显示基础角色(fallback)
|
||||
expect(result.content).toContain('智能助手')
|
||||
})
|
||||
|
||||
it('应该处理空的资源注册表', async () => {
|
||||
// Mock空的资源注册表
|
||||
jest.spyOn(ResourceManager.prototype, 'loadUnifiedRegistry')
|
||||
.mockResolvedValue({ role: {} })
|
||||
|
||||
const result = await helloCommand.execute([])
|
||||
|
||||
// 应该显示基础角色(fallback)
|
||||
expect(result.content).toContain('智能助手')
|
||||
})
|
||||
})
|
||||
})
|
||||
323
src/tests/commands/HelloCommand.unit.test.js
Normal file
323
src/tests/commands/HelloCommand.unit.test.js
Normal file
@ -0,0 +1,323 @@
|
||||
const path = require('path')
|
||||
const fs = require('fs-extra')
|
||||
const os = require('os')
|
||||
const HelloCommand = require('../../lib/core/pouch/commands/HelloCommand')
|
||||
|
||||
describe('HelloCommand 单元测试', () => {
|
||||
let helloCommand
|
||||
let tempDir
|
||||
let tempProjectDir
|
||||
|
||||
beforeEach(async () => {
|
||||
helloCommand = new HelloCommand()
|
||||
|
||||
// 创建临时目录模拟项目结构
|
||||
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'hello-command-test-'))
|
||||
tempProjectDir = path.join(tempDir, 'test-project')
|
||||
|
||||
// 创建基础目录结构
|
||||
await fs.ensureDir(path.join(tempProjectDir, 'prompt', 'domain'))
|
||||
await fs.ensureDir(path.join(tempProjectDir, '.promptx', 'user-roles'))
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
if (tempDir) {
|
||||
await fs.remove(tempDir)
|
||||
}
|
||||
// 清理缓存
|
||||
if (helloCommand.roleRegistry) {
|
||||
helloCommand.roleRegistry = null
|
||||
}
|
||||
})
|
||||
|
||||
describe('基础功能测试', () => {
|
||||
test('应该能实例化HelloCommand', () => {
|
||||
expect(helloCommand).toBeInstanceOf(HelloCommand)
|
||||
expect(typeof helloCommand.discoverLocalRoles).toBe('function')
|
||||
expect(typeof helloCommand.loadRoleRegistry).toBe('function')
|
||||
})
|
||||
|
||||
test('getPurpose应该返回正确的目的描述', () => {
|
||||
const purpose = helloCommand.getPurpose()
|
||||
expect(purpose).toContain('AI')
|
||||
expect(purpose).toContain('角色')
|
||||
})
|
||||
})
|
||||
|
||||
describe('discoverLocalRoles 功能测试', () => {
|
||||
test('应该能发现系统内置角色', async () => {
|
||||
// 创建模拟的系统角色文件
|
||||
const assistantDir = path.join(tempProjectDir, 'prompt', 'domain', 'assistant')
|
||||
await fs.ensureDir(assistantDir)
|
||||
|
||||
const roleFileContent = `<!--
|
||||
name: 🙋 智能助手
|
||||
description: 通用助理角色,提供基础的助理服务和记忆支持
|
||||
-->
|
||||
|
||||
<role>
|
||||
<personality>
|
||||
@!thought://remember
|
||||
@!thought://recall
|
||||
@!thought://assistant
|
||||
</personality>
|
||||
|
||||
<principle>
|
||||
@!execution://assistant
|
||||
</principle>
|
||||
</role>`
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(assistantDir, 'assistant.role.md'),
|
||||
roleFileContent
|
||||
)
|
||||
|
||||
// Mock PackageProtocol.getPackageRoot 返回临时目录
|
||||
const originalRequire = require
|
||||
jest.doMock('../../lib/core/resource/protocols/PackageProtocol', () => {
|
||||
return class MockPackageProtocol {
|
||||
async getPackageRoot() {
|
||||
return tempProjectDir
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 重新加载HelloCommand使用mock
|
||||
delete require.cache[require.resolve('../../lib/core/pouch/commands/HelloCommand')]
|
||||
const MockedHelloCommand = require('../../lib/core/pouch/commands/HelloCommand')
|
||||
const mockedCommand = new MockedHelloCommand()
|
||||
|
||||
const discoveredRoles = await mockedCommand.discoverLocalRoles()
|
||||
|
||||
expect(discoveredRoles).toHaveProperty('assistant')
|
||||
expect(discoveredRoles.assistant.name).toContain('智能助手')
|
||||
expect(discoveredRoles.assistant.description).toContain('通用助理角色')
|
||||
expect(discoveredRoles.assistant.source).toBe('local-discovery')
|
||||
|
||||
// 恢复原始require
|
||||
jest.unmock('../../lib/core/resource/protocols/PackageProtocol')
|
||||
})
|
||||
|
||||
test('应该处理空的角色目录', async () => {
|
||||
// Mock PackageProtocol.getPackageRoot 返回空目录
|
||||
jest.doMock('../../lib/core/resource/protocols/PackageProtocol', () => {
|
||||
return class MockPackageProtocol {
|
||||
async getPackageRoot() {
|
||||
return tempProjectDir
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
delete require.cache[require.resolve('../../lib/core/pouch/commands/HelloCommand')]
|
||||
const MockedHelloCommand = require('../../lib/core/pouch/commands/HelloCommand')
|
||||
const mockedCommand = new MockedHelloCommand()
|
||||
|
||||
const discoveredRoles = await mockedCommand.discoverLocalRoles()
|
||||
expect(discoveredRoles).toEqual({})
|
||||
|
||||
jest.unmock('../../lib/core/resource/protocols/PackageProtocol')
|
||||
})
|
||||
|
||||
test('应该优雅处理文件读取错误', async () => {
|
||||
// 创建无效的角色文件(权限问题)
|
||||
const invalidRoleDir = path.join(tempProjectDir, 'prompt', 'domain', 'invalid')
|
||||
await fs.ensureDir(invalidRoleDir)
|
||||
|
||||
const invalidRoleFile = path.join(invalidRoleDir, 'invalid.role.md')
|
||||
await fs.writeFile(invalidRoleFile, 'invalid content')
|
||||
|
||||
// 修改文件权限使其不可读(仅在Unix系统上有效)
|
||||
if (process.platform !== 'win32') {
|
||||
await fs.chmod(invalidRoleFile, 0o000)
|
||||
}
|
||||
|
||||
jest.doMock('../../lib/core/resource/protocols/PackageProtocol', () => {
|
||||
return class MockPackageProtocol {
|
||||
async getPackageRoot() {
|
||||
return tempProjectDir
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
delete require.cache[require.resolve('../../lib/core/pouch/commands/HelloCommand')]
|
||||
const MockedHelloCommand = require('../../lib/core/pouch/commands/HelloCommand')
|
||||
const mockedCommand = new MockedHelloCommand()
|
||||
|
||||
// 应该不抛出异常,而是记录警告并跳过无效文件
|
||||
const discoveredRoles = await mockedCommand.discoverLocalRoles()
|
||||
expect(typeof discoveredRoles).toBe('object')
|
||||
|
||||
// 恢复文件权限
|
||||
if (process.platform !== 'win32') {
|
||||
await fs.chmod(invalidRoleFile, 0o644)
|
||||
}
|
||||
|
||||
jest.unmock('../../lib/core/resource/protocols/PackageProtocol')
|
||||
})
|
||||
})
|
||||
|
||||
describe('元数据提取测试', () => {
|
||||
test('应该正确提取角色名称和描述', async () => {
|
||||
const testRoleDir = path.join(tempProjectDir, 'prompt', 'domain', 'test-role')
|
||||
await fs.ensureDir(testRoleDir)
|
||||
|
||||
const roleContent = `<!--
|
||||
name: 🧪 测试角色
|
||||
description: 这是一个测试用的角色
|
||||
-->
|
||||
|
||||
<role>
|
||||
<personality>
|
||||
测试思维模式
|
||||
</personality>
|
||||
|
||||
<principle>
|
||||
测试行为原则
|
||||
</principle>
|
||||
</role>`
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(testRoleDir, 'test-role.role.md'),
|
||||
roleContent
|
||||
)
|
||||
|
||||
jest.doMock('../../lib/core/resource/protocols/PackageProtocol', () => {
|
||||
return class MockPackageProtocol {
|
||||
async getPackageRoot() {
|
||||
return tempProjectDir
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
delete require.cache[require.resolve('../../lib/core/pouch/commands/HelloCommand')]
|
||||
const MockedHelloCommand = require('../../lib/core/pouch/commands/HelloCommand')
|
||||
const mockedCommand = new MockedHelloCommand()
|
||||
|
||||
const discoveredRoles = await mockedCommand.discoverLocalRoles()
|
||||
|
||||
expect(discoveredRoles).toHaveProperty('test-role')
|
||||
expect(discoveredRoles['test-role'].name).toBe('🧪 测试角色')
|
||||
expect(discoveredRoles['test-role'].description).toBe('这是一个测试用的角色')
|
||||
|
||||
jest.unmock('../../lib/core/resource/protocols/PackageProtocol')
|
||||
})
|
||||
|
||||
test('应该处理缺少元数据的角色文件', async () => {
|
||||
const testRoleDir = path.join(tempProjectDir, 'prompt', 'domain', 'no-meta')
|
||||
await fs.ensureDir(testRoleDir)
|
||||
|
||||
const roleContent = `<role>
|
||||
<personality>
|
||||
基础角色内容
|
||||
</personality>
|
||||
</role>`
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(testRoleDir, 'no-meta.role.md'),
|
||||
roleContent
|
||||
)
|
||||
|
||||
jest.doMock('../../lib/core/resource/protocols/PackageProtocol', () => {
|
||||
return class MockPackageProtocol {
|
||||
async getPackageRoot() {
|
||||
return tempProjectDir
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
delete require.cache[require.resolve('../../lib/core/pouch/commands/HelloCommand')]
|
||||
const MockedHelloCommand = require('../../lib/core/pouch/commands/HelloCommand')
|
||||
const mockedCommand = new MockedHelloCommand()
|
||||
|
||||
const discoveredRoles = await mockedCommand.discoverLocalRoles()
|
||||
|
||||
expect(discoveredRoles).toHaveProperty('no-meta')
|
||||
expect(discoveredRoles['no-meta'].name).toBe('🎭 no-meta') // 默认格式
|
||||
expect(discoveredRoles['no-meta'].description).toBe('本地发现的角色') // 默认描述
|
||||
|
||||
jest.unmock('../../lib/core/resource/protocols/PackageProtocol')
|
||||
})
|
||||
})
|
||||
|
||||
describe('角色注册表加载测试', () => {
|
||||
test('应该能加载角色注册表', async () => {
|
||||
const result = await helloCommand.loadRoleRegistry()
|
||||
|
||||
expect(typeof result).toBe('object')
|
||||
expect(helloCommand.roleRegistry).toBe(result)
|
||||
})
|
||||
|
||||
test('应该在失败时返回默认assistant角色', async () => {
|
||||
// Mock ResourceManager抛出异常
|
||||
jest.doMock('../../lib/core/resource/resourceManager', () => {
|
||||
return class MockResourceManager {
|
||||
async initialize() {
|
||||
throw new Error('Mock initialization failure')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Mock discoverLocalRoles也失败
|
||||
jest.spyOn(helloCommand, 'discoverLocalRoles').mockRejectedValue(new Error('Mock discovery failure'))
|
||||
|
||||
delete require.cache[require.resolve('../../lib/core/pouch/commands/HelloCommand')]
|
||||
const MockedHelloCommand = require('../../lib/core/pouch/commands/HelloCommand')
|
||||
const mockedCommand = new MockedHelloCommand()
|
||||
|
||||
const result = await mockedCommand.loadRoleRegistry()
|
||||
|
||||
expect(result).toHaveProperty('assistant')
|
||||
expect(result.assistant.name).toContain('智能助手')
|
||||
|
||||
jest.unmock('../../lib/core/resource/resourceManager')
|
||||
helloCommand.discoverLocalRoles.mockRestore()
|
||||
})
|
||||
})
|
||||
|
||||
describe('角色信息获取测试', () => {
|
||||
test('getRoleInfo应该返回正确的角色信息', async () => {
|
||||
// Mock注册表
|
||||
helloCommand.roleRegistry = {
|
||||
'test-role': {
|
||||
name: '测试角色',
|
||||
description: '测试描述',
|
||||
file: '@package://test/path'
|
||||
}
|
||||
}
|
||||
|
||||
const roleInfo = await helloCommand.getRoleInfo('test-role')
|
||||
|
||||
expect(roleInfo).toEqual({
|
||||
id: 'test-role',
|
||||
name: '测试角色',
|
||||
description: '测试描述',
|
||||
file: '@package://test/path'
|
||||
})
|
||||
})
|
||||
|
||||
test('getRoleInfo对不存在的角色应该返回null', async () => {
|
||||
helloCommand.roleRegistry = {}
|
||||
|
||||
const roleInfo = await helloCommand.getRoleInfo('non-existent')
|
||||
expect(roleInfo).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('getAllRoles测试', () => {
|
||||
test('应该返回角色数组格式', async () => {
|
||||
helloCommand.roleRegistry = {
|
||||
'role1': { name: '角色1', description: '描述1', file: 'file1' },
|
||||
'role2': { name: '角色2', description: '描述2', file: 'file2' }
|
||||
}
|
||||
|
||||
const allRoles = await helloCommand.getAllRoles()
|
||||
|
||||
expect(Array.isArray(allRoles)).toBe(true)
|
||||
expect(allRoles).toHaveLength(2)
|
||||
expect(allRoles[0]).toHaveProperty('id')
|
||||
expect(allRoles[0]).toHaveProperty('name')
|
||||
expect(allRoles[0]).toHaveProperty('description')
|
||||
expect(allRoles[0]).toHaveProperty('file')
|
||||
})
|
||||
})
|
||||
})
|
||||
525
src/tests/commands/UserRoleDiscovery.integration.test.js
Normal file
525
src/tests/commands/UserRoleDiscovery.integration.test.js
Normal file
@ -0,0 +1,525 @@
|
||||
const path = require('path')
|
||||
const fs = require('fs-extra')
|
||||
const os = require('os')
|
||||
const HelloCommand = require('../../lib/core/pouch/commands/HelloCommand')
|
||||
|
||||
describe('用户角色发现机制 集成测试', () => {
|
||||
let tempDir
|
||||
let projectDir
|
||||
let helloCommand
|
||||
|
||||
beforeEach(async () => {
|
||||
// 创建临时项目目录
|
||||
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'user-role-discovery-'))
|
||||
projectDir = path.join(tempDir, 'test-project')
|
||||
|
||||
// 创建完整的项目结构
|
||||
await fs.ensureDir(path.join(projectDir, 'prompt', 'domain'))
|
||||
await fs.ensureDir(path.join(projectDir, '.promptx', 'user-roles'))
|
||||
|
||||
helloCommand = new HelloCommand()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
if (tempDir) {
|
||||
await fs.remove(tempDir)
|
||||
}
|
||||
if (helloCommand.roleRegistry) {
|
||||
helloCommand.roleRegistry = null
|
||||
}
|
||||
})
|
||||
|
||||
describe('用户角色路径扫描', () => {
|
||||
test('应该能扫描 .promptx/user-roles 目录', async () => {
|
||||
// 创建用户自定义角色
|
||||
const userRoleDir = path.join(projectDir, '.promptx', 'user-roles', 'custom-analyst')
|
||||
await fs.ensureDir(userRoleDir)
|
||||
|
||||
const userRoleContent = `<!--
|
||||
name: 📊 自定义分析师
|
||||
description: 用户定制的数据分析专家
|
||||
-->
|
||||
|
||||
<role>
|
||||
<personality>
|
||||
# 数据分析思维
|
||||
我是一个专注于数据洞察的分析师,善于从复杂数据中发现业务价值。
|
||||
</personality>
|
||||
|
||||
<principle>
|
||||
# 分析原则
|
||||
- 数据驱动决策
|
||||
- 业务价值导向
|
||||
- 简洁清晰表达
|
||||
</principle>
|
||||
|
||||
<knowledge>
|
||||
# 专业技能
|
||||
- 统计分析方法
|
||||
- 数据可视化技能
|
||||
- 业务理解能力
|
||||
</knowledge>
|
||||
</role>`
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(userRoleDir, 'custom-analyst.role.md'),
|
||||
userRoleContent
|
||||
)
|
||||
|
||||
// 这个测试假设我们已经实现了用户角色发现功能
|
||||
// 实际实现时,discoverLocalRoles会被扩展以支持用户角色路径
|
||||
|
||||
// 验证文件创建成功
|
||||
expect(await fs.pathExists(path.join(userRoleDir, 'custom-analyst.role.md'))).toBe(true)
|
||||
})
|
||||
|
||||
test('应该同时支持系统角色和用户角色', async () => {
|
||||
// 创建系统角色
|
||||
const systemRoleDir = path.join(projectDir, 'prompt', 'domain', 'assistant')
|
||||
await fs.ensureDir(systemRoleDir)
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(systemRoleDir, 'assistant.role.md'),
|
||||
`<!--
|
||||
name: 🤖 系统助手
|
||||
description: 系统内置助手
|
||||
-->
|
||||
|
||||
<role>
|
||||
<personality>系统助手思维</personality>
|
||||
</role>`
|
||||
)
|
||||
|
||||
// 创建用户角色
|
||||
const userRoleDir = path.join(projectDir, '.promptx', 'user-roles', 'my-role')
|
||||
await fs.ensureDir(userRoleDir)
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(userRoleDir, 'my-role.role.md'),
|
||||
`<!--
|
||||
name: 👤 我的角色
|
||||
description: 用户自定义角色
|
||||
-->
|
||||
|
||||
<role>
|
||||
<personality>用户自定义思维</personality>
|
||||
</role>`
|
||||
)
|
||||
|
||||
jest.doMock('../../lib/core/resource/protocols/PackageProtocol', () => {
|
||||
return class MockPackageProtocol {
|
||||
async getPackageRoot() {
|
||||
return projectDir
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
delete require.cache[require.resolve('../../lib/core/pouch/commands/HelloCommand')]
|
||||
const MockedHelloCommand = require('../../lib/core/pouch/commands/HelloCommand')
|
||||
const mockedCommand = new MockedHelloCommand()
|
||||
|
||||
// 模拟双路径扫描实现
|
||||
mockedCommand.discoverLocalRoles = async function() {
|
||||
const PackageProtocol = require('../../lib/core/resource/protocols/PackageProtocol')
|
||||
const packageProtocol = new PackageProtocol()
|
||||
const glob = require('glob')
|
||||
const discoveredRoles = {}
|
||||
|
||||
try {
|
||||
const packageRoot = await packageProtocol.getPackageRoot()
|
||||
|
||||
// 扫描路径:系统角色 + 用户角色
|
||||
const scanPaths = [
|
||||
path.join(packageRoot, 'prompt', 'domain'), // 系统角色
|
||||
path.join(packageRoot, '.promptx', 'user-roles') // 用户角色
|
||||
]
|
||||
|
||||
for (const scanPath of scanPaths) {
|
||||
if (await fs.pathExists(scanPath)) {
|
||||
const domains = await fs.readdir(scanPath)
|
||||
|
||||
for (const domain of domains) {
|
||||
const domainDir = path.join(scanPath, domain)
|
||||
const stat = await fs.stat(domainDir)
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
const roleFile = path.join(domainDir, `${domain}.role.md`)
|
||||
if (await fs.pathExists(roleFile)) {
|
||||
const content = await fs.readFile(roleFile, 'utf-8')
|
||||
const relativePath = path.relative(packageRoot, roleFile)
|
||||
|
||||
let name = `🎭 ${domain}`
|
||||
let description = '本地发现的角色'
|
||||
let source = 'local-discovery'
|
||||
|
||||
// 区分系统角色和用户角色
|
||||
if (scanPath.includes('.promptx')) {
|
||||
source = 'user-generated'
|
||||
description = '用户自定义角色'
|
||||
}
|
||||
|
||||
const nameMatch = content.match(/name:\s*(.+?)(?:\n|$)/i)
|
||||
if (nameMatch) {
|
||||
name = nameMatch[1].trim()
|
||||
}
|
||||
|
||||
const descMatch = content.match(/description:\s*(.+?)(?:\n|$)/i)
|
||||
if (descMatch) {
|
||||
description = descMatch[1].trim()
|
||||
}
|
||||
|
||||
discoveredRoles[domain] = {
|
||||
file: scanPath.includes('.promptx')
|
||||
? `@project://${relativePath}`
|
||||
: `@package://${relativePath}`,
|
||||
name,
|
||||
description,
|
||||
source
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return discoveredRoles
|
||||
} catch (error) {
|
||||
console.warn('角色发现失败:', error.message)
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
const discoveredRoles = await mockedCommand.discoverLocalRoles()
|
||||
|
||||
// 验证同时发现了系统角色和用户角色
|
||||
expect(discoveredRoles).toHaveProperty('assistant')
|
||||
expect(discoveredRoles).toHaveProperty('my-role')
|
||||
|
||||
expect(discoveredRoles.assistant.source).toBe('local-discovery')
|
||||
expect(discoveredRoles.assistant.file).toContain('@package://')
|
||||
|
||||
expect(discoveredRoles['my-role'].source).toBe('user-generated')
|
||||
expect(discoveredRoles['my-role'].file).toContain('@project://')
|
||||
|
||||
jest.unmock('../../lib/core/resource/protocols/PackageProtocol')
|
||||
})
|
||||
})
|
||||
|
||||
describe('DPML格式元数据提取', () => {
|
||||
test('应该能从DPML格式中提取元数据', async () => {
|
||||
const userRoleDir = path.join(projectDir, '.promptx', 'user-roles', 'dpml-role')
|
||||
await fs.ensureDir(userRoleDir)
|
||||
|
||||
// DPML格式的角色文件(根据文档设计的格式)
|
||||
const dpmlRoleContent = `<role>
|
||||
<personality>
|
||||
# 数据分析师思维模式
|
||||
|
||||
## 核心思维特征
|
||||
- **数据敏感性思维**:善于从数字中发现故事和趋势模式
|
||||
- **逻辑分析思维**:系统性地分解复杂数据问题,追求因果关系
|
||||
- **结果导向思维**:专注于为业务决策提供可行洞察和建议
|
||||
</personality>
|
||||
|
||||
<principle>
|
||||
# 数据分析师行为原则
|
||||
|
||||
## 核心工作原则
|
||||
- **数据驱动决策**:所有分析建议必须有可靠数据支撑
|
||||
- **简洁清晰表达**:复杂分析结果要用简单易懂的方式呈现
|
||||
- **业务价值优先**:分析要紧密围绕业务目标和价值创造
|
||||
</principle>
|
||||
|
||||
<knowledge>
|
||||
# 数据分析专业知识体系
|
||||
|
||||
## 数据处理技能
|
||||
- **数据清洗方法**:缺失值处理、异常值识别、数据标准化
|
||||
- **数据整合技巧**:多源数据合并、关联分析、数据建模
|
||||
- **质量控制流程**:数据校验、一致性检查、完整性验证
|
||||
|
||||
## 分析方法论
|
||||
- **描述性分析**:趋势分析、对比分析、分布分析
|
||||
- **诊断性分析**:钻取分析、根因分析、相关性分析
|
||||
</knowledge>
|
||||
</role>`
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(userRoleDir, 'dpml-role.role.md'),
|
||||
dpmlRoleContent
|
||||
)
|
||||
|
||||
jest.doMock('../../lib/core/resource/protocols/PackageProtocol', () => {
|
||||
return class MockPackageProtocol {
|
||||
async getPackageRoot() {
|
||||
return projectDir
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
delete require.cache[require.resolve('../../lib/core/pouch/commands/HelloCommand')]
|
||||
const MockedHelloCommand = require('../../lib/core/pouch/commands/HelloCommand')
|
||||
const mockedCommand = new MockedHelloCommand()
|
||||
|
||||
// 实现DPML元数据提取逻辑(这是我们要实现的功能)
|
||||
function extractDPMLMetadata(content, roleId) {
|
||||
// 从<personality>标签中提取角色名称
|
||||
const personalityMatch = content.match(/<personality[^>]*>([\s\S]*?)<\/personality>/i)
|
||||
const roleNameFromPersonality = personalityMatch
|
||||
? personalityMatch[1].split('\n')[0].replace(/^#\s*/, '').trim()
|
||||
: null
|
||||
|
||||
// 从<knowledge>标签中提取专业能力描述
|
||||
const knowledgeMatch = content.match(/<knowledge[^>]*>([\s\S]*?)<\/knowledge>/i)
|
||||
const roleDescription = knowledgeMatch
|
||||
? knowledgeMatch[1].split('\n').slice(0, 3).join(' ').replace(/[#\-\*]/g, '').trim()
|
||||
: null
|
||||
|
||||
return {
|
||||
file: `@project://.promptx/user-roles/${roleId}/${roleId}.role.md`,
|
||||
name: roleNameFromPersonality || `🎭 ${roleId}`,
|
||||
description: roleDescription || '用户自定义DPML角色',
|
||||
source: 'user-generated',
|
||||
format: 'dpml'
|
||||
}
|
||||
}
|
||||
|
||||
mockedCommand.discoverLocalRoles = async function() {
|
||||
const PackageProtocol = require('../../lib/core/resource/protocols/PackageProtocol')
|
||||
const packageProtocol = new PackageProtocol()
|
||||
const discoveredRoles = {}
|
||||
|
||||
try {
|
||||
const packageRoot = await packageProtocol.getPackageRoot()
|
||||
const userRolesPath = path.join(packageRoot, '.promptx', 'user-roles')
|
||||
|
||||
if (await fs.pathExists(userRolesPath)) {
|
||||
const userRoleDirs = await fs.readdir(userRolesPath)
|
||||
|
||||
for (const roleId of userRoleDirs) {
|
||||
const roleDir = path.join(userRolesPath, roleId)
|
||||
const stat = await fs.stat(roleDir)
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
const roleFile = path.join(roleDir, `${roleId}.role.md`)
|
||||
if (await fs.pathExists(roleFile)) {
|
||||
const content = await fs.readFile(roleFile, 'utf-8')
|
||||
|
||||
// 使用DPML元数据提取
|
||||
const roleInfo = extractDPMLMetadata(content, roleId)
|
||||
discoveredRoles[roleId] = roleInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return discoveredRoles
|
||||
} catch (error) {
|
||||
console.warn('DPML角色发现失败:', error.message)
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
const discoveredRoles = await mockedCommand.discoverLocalRoles()
|
||||
|
||||
// 验证DPML元数据提取
|
||||
expect(discoveredRoles).toHaveProperty('dpml-role')
|
||||
expect(discoveredRoles['dpml-role'].name).toBe('数据分析师思维模式')
|
||||
expect(discoveredRoles['dpml-role'].description).toContain('数据分析专业知识体系')
|
||||
expect(discoveredRoles['dpml-role'].format).toBe('dpml')
|
||||
expect(discoveredRoles['dpml-role'].source).toBe('user-generated')
|
||||
|
||||
jest.unmock('../../lib/core/resource/protocols/PackageProtocol')
|
||||
})
|
||||
})
|
||||
|
||||
describe('错误处理和边界情况', () => {
|
||||
test('应该处理不存在的用户角色目录', async () => {
|
||||
// 只创建系统角色目录,不创建用户角色目录
|
||||
const systemRoleDir = path.join(projectDir, 'prompt', 'domain', 'assistant')
|
||||
await fs.ensureDir(systemRoleDir)
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(systemRoleDir, 'assistant.role.md'),
|
||||
`<role><personality>助手</personality></role>`
|
||||
)
|
||||
|
||||
jest.doMock('../../lib/core/resource/protocols/PackageProtocol', () => {
|
||||
return class MockPackageProtocol {
|
||||
async getPackageRoot() {
|
||||
return projectDir
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
delete require.cache[require.resolve('../../lib/core/pouch/commands/HelloCommand')]
|
||||
const MockedHelloCommand = require('../../lib/core/pouch/commands/HelloCommand')
|
||||
const mockedCommand = new MockedHelloCommand()
|
||||
|
||||
// 模拟处理不存在目录的逻辑
|
||||
mockedCommand.discoverLocalRoles = async function() {
|
||||
const PackageProtocol = require('../../lib/core/resource/protocols/PackageProtocol')
|
||||
const packageProtocol = new PackageProtocol()
|
||||
const discoveredRoles = {}
|
||||
|
||||
try {
|
||||
const packageRoot = await packageProtocol.getPackageRoot()
|
||||
|
||||
const scanPaths = [
|
||||
{ path: path.join(packageRoot, 'prompt', 'domain'), prefix: '@package://' },
|
||||
{ path: path.join(packageRoot, '.promptx', 'user-roles'), prefix: '@project://' }
|
||||
]
|
||||
|
||||
for (const { path: scanPath, prefix } of scanPaths) {
|
||||
if (await fs.pathExists(scanPath)) {
|
||||
const domains = await fs.readdir(scanPath)
|
||||
|
||||
for (const domain of domains) {
|
||||
const domainDir = path.join(scanPath, domain)
|
||||
const stat = await fs.stat(domainDir)
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
const roleFile = path.join(domainDir, `${domain}.role.md`)
|
||||
if (await fs.pathExists(roleFile)) {
|
||||
const content = await fs.readFile(roleFile, 'utf-8')
|
||||
const relativePath = path.relative(packageRoot, roleFile)
|
||||
|
||||
discoveredRoles[domain] = {
|
||||
file: `${prefix}${relativePath}`,
|
||||
name: `🎭 ${domain}`,
|
||||
description: '本地发现的角色',
|
||||
source: prefix.includes('project') ? 'user-generated' : 'local-discovery'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return discoveredRoles
|
||||
} catch (error) {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
const discoveredRoles = await mockedCommand.discoverLocalRoles()
|
||||
|
||||
// 应该只发现系统角色,不会因为用户角色目录不存在而出错
|
||||
expect(discoveredRoles).toHaveProperty('assistant')
|
||||
expect(Object.keys(discoveredRoles)).toHaveLength(1)
|
||||
|
||||
jest.unmock('../../lib/core/resource/protocols/PackageProtocol')
|
||||
})
|
||||
|
||||
test('应该处理用户角色ID冲突', async () => {
|
||||
// 创建同名的系统角色和用户角色
|
||||
const systemRoleDir = path.join(projectDir, 'prompt', 'domain', 'analyst')
|
||||
await fs.ensureDir(systemRoleDir)
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(systemRoleDir, 'analyst.role.md'),
|
||||
`<!--
|
||||
name: 📊 系统分析师
|
||||
description: 系统内置分析师
|
||||
-->
|
||||
<role><personality>系统分析师</personality></role>`
|
||||
)
|
||||
|
||||
const userRoleDir = path.join(projectDir, '.promptx', 'user-roles', 'analyst')
|
||||
await fs.ensureDir(userRoleDir)
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(userRoleDir, 'analyst.role.md'),
|
||||
`<!--
|
||||
name: 👤 用户分析师
|
||||
description: 用户自定义分析师
|
||||
-->
|
||||
<role><personality>用户分析师</personality></role>`
|
||||
)
|
||||
|
||||
jest.doMock('../../lib/core/resource/protocols/PackageProtocol', () => {
|
||||
return class MockPackageProtocol {
|
||||
async getPackageRoot() {
|
||||
return projectDir
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
delete require.cache[require.resolve('../../lib/core/pouch/commands/HelloCommand')]
|
||||
const MockedHelloCommand = require('../../lib/core/pouch/commands/HelloCommand')
|
||||
const mockedCommand = new MockedHelloCommand()
|
||||
|
||||
// 模拟冲突处理逻辑(用户角色优先)
|
||||
mockedCommand.discoverLocalRoles = async function() {
|
||||
const PackageProtocol = require('../../lib/core/resource/protocols/PackageProtocol')
|
||||
const packageProtocol = new PackageProtocol()
|
||||
const discoveredRoles = {}
|
||||
|
||||
try {
|
||||
const packageRoot = await packageProtocol.getPackageRoot()
|
||||
|
||||
// 先扫描系统角色,再扫描用户角色(用户角色会覆盖同名系统角色)
|
||||
const scanPaths = [
|
||||
{ path: path.join(packageRoot, 'prompt', 'domain'), prefix: '@package://', source: 'local-discovery' },
|
||||
{ path: path.join(packageRoot, '.promptx', 'user-roles'), prefix: '@project://', source: 'user-generated' }
|
||||
]
|
||||
|
||||
for (const { path: scanPath, prefix, source } of scanPaths) {
|
||||
if (await fs.pathExists(scanPath)) {
|
||||
const domains = await fs.readdir(scanPath)
|
||||
|
||||
for (const domain of domains) {
|
||||
const domainDir = path.join(scanPath, domain)
|
||||
const stat = await fs.stat(domainDir)
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
const roleFile = path.join(domainDir, `${domain}.role.md`)
|
||||
if (await fs.pathExists(roleFile)) {
|
||||
const content = await fs.readFile(roleFile, 'utf-8')
|
||||
const relativePath = path.relative(packageRoot, roleFile)
|
||||
|
||||
let name = `🎭 ${domain}`
|
||||
let description = '本地发现的角色'
|
||||
|
||||
const nameMatch = content.match(/name:\s*(.+?)(?:\n|$)/i)
|
||||
if (nameMatch) {
|
||||
name = nameMatch[1].trim()
|
||||
}
|
||||
|
||||
const descMatch = content.match(/description:\s*(.+?)(?:\n|$)/i)
|
||||
if (descMatch) {
|
||||
description = descMatch[1].trim()
|
||||
}
|
||||
|
||||
// 用户角色会覆盖系统角色
|
||||
discoveredRoles[domain] = {
|
||||
file: `${prefix}${relativePath}`,
|
||||
name,
|
||||
description,
|
||||
source
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return discoveredRoles
|
||||
} catch (error) {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
const discoveredRoles = await mockedCommand.discoverLocalRoles()
|
||||
|
||||
// 验证用户角色优先级更高
|
||||
expect(discoveredRoles).toHaveProperty('analyst')
|
||||
expect(discoveredRoles.analyst.name).toBe('👤 用户分析师')
|
||||
expect(discoveredRoles.analyst.description).toBe('用户自定义分析师')
|
||||
expect(discoveredRoles.analyst.source).toBe('user-generated')
|
||||
expect(discoveredRoles.analyst.file).toContain('@project://')
|
||||
|
||||
jest.unmock('../../lib/core/resource/protocols/PackageProtocol')
|
||||
})
|
||||
})
|
||||
})
|
||||
220
src/tests/core/resource/ResourceManager.unit.test.js
Normal file
220
src/tests/core/resource/ResourceManager.unit.test.js
Normal file
@ -0,0 +1,220 @@
|
||||
const ResourceManager = require('../../../lib/core/resource/resourceManager')
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const os = require('os')
|
||||
|
||||
describe('ResourceManager - 用户资源发现', () => {
|
||||
let resourceManager
|
||||
let tempDir
|
||||
let mockPackageRoot
|
||||
|
||||
beforeEach(async () => {
|
||||
// 创建临时测试目录
|
||||
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'promptx-test-'))
|
||||
mockPackageRoot = tempDir
|
||||
|
||||
// 模拟用户资源目录结构
|
||||
await fs.ensureDir(path.join(tempDir, '.promptx', 'resource', 'domain'))
|
||||
|
||||
resourceManager = new ResourceManager()
|
||||
|
||||
// Mock packageProtocol module
|
||||
jest.doMock('../../../lib/core/resource/protocols/PackageProtocol', () => {
|
||||
return class MockPackageProtocol {
|
||||
async getPackageRoot() {
|
||||
return mockPackageRoot
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
// 清理临时目录
|
||||
await fs.remove(tempDir)
|
||||
jest.restoreAllMocks()
|
||||
})
|
||||
|
||||
describe('discoverUserResources', () => {
|
||||
it('应该返回空对象当用户资源目录不存在时', async () => {
|
||||
// 删除用户资源目录
|
||||
await fs.remove(path.join(tempDir, '.promptx'))
|
||||
|
||||
const result = await resourceManager.discoverUserResources()
|
||||
|
||||
expect(result).toEqual({})
|
||||
})
|
||||
|
||||
it('应该发现用户创建的角色文件', async () => {
|
||||
// 创建测试角色文件
|
||||
const roleDir = path.join(tempDir, '.promptx', 'resource', 'domain', 'test-sales-analyst')
|
||||
await fs.ensureDir(roleDir)
|
||||
|
||||
const roleContent = `<role>
|
||||
<personality>
|
||||
# 销售数据分析师思维模式
|
||||
## 核心思维特征
|
||||
- **数据敏感性思维**:善于从数字中发现故事和趋势模式
|
||||
</personality>
|
||||
|
||||
<principle>
|
||||
# 销售数据分析师行为原则
|
||||
## 核心工作原则
|
||||
- **数据驱动决策**:所有分析建议必须有可靠数据支撑
|
||||
</principle>
|
||||
|
||||
<knowledge>
|
||||
# 销售数据分析专业知识体系
|
||||
## 数据处理技能
|
||||
- **数据清洗方法**:缺失值处理、异常值识别
|
||||
</knowledge>
|
||||
</role>`
|
||||
|
||||
await fs.writeFile(path.join(roleDir, 'test-sales-analyst.role.md'), roleContent)
|
||||
|
||||
const result = await resourceManager.discoverUserResources()
|
||||
|
||||
expect(result).toHaveProperty('role')
|
||||
expect(result.role).toHaveProperty('test-sales-analyst')
|
||||
expect(result.role['test-sales-analyst']).toMatchObject({
|
||||
file: expect.stringContaining('test-sales-analyst.role.md'),
|
||||
name: expect.stringContaining('销售数据分析师'),
|
||||
source: 'user-generated',
|
||||
format: 'dpml',
|
||||
type: 'role'
|
||||
})
|
||||
})
|
||||
|
||||
it('应该支持多种资源类型发现', async () => {
|
||||
// 创建角色和相关资源
|
||||
const roleDir = path.join(tempDir, '.promptx', 'resource', 'domain', 'test-role')
|
||||
await fs.ensureDir(roleDir)
|
||||
await fs.ensureDir(path.join(roleDir, 'thought'))
|
||||
await fs.ensureDir(path.join(roleDir, 'execution'))
|
||||
|
||||
// 创建角色文件
|
||||
await fs.writeFile(path.join(roleDir, 'test-role.role.md'), '<role><personality>Test</personality><principle>Test</principle><knowledge>Test</knowledge></role>')
|
||||
|
||||
// 创建思维文件
|
||||
await fs.writeFile(path.join(roleDir, 'thought', 'test.thought.md'), '<thought><exploration>Test exploration</exploration><reasoning>Test reasoning</reasoning></thought>')
|
||||
|
||||
// 创建执行文件
|
||||
await fs.writeFile(path.join(roleDir, 'execution', 'test.execution.md'), '<execution><constraint>Test constraint</constraint></execution>')
|
||||
|
||||
const result = await resourceManager.discoverUserResources()
|
||||
|
||||
expect(result).toHaveProperty('role')
|
||||
expect(result).toHaveProperty('thought')
|
||||
expect(result).toHaveProperty('execution')
|
||||
expect(result.role).toHaveProperty('test-role')
|
||||
expect(result.thought).toHaveProperty('test')
|
||||
expect(result.execution).toHaveProperty('test')
|
||||
})
|
||||
|
||||
it('应该处理DPML格式错误的文件', async () => {
|
||||
// 创建格式错误的角色文件
|
||||
const roleDir = path.join(tempDir, '.promptx', 'resource', 'domain', 'invalid-role')
|
||||
await fs.ensureDir(roleDir)
|
||||
|
||||
const invalidContent = `这不是有效的DPML格式`
|
||||
await fs.writeFile(path.join(roleDir, 'invalid-role.role.md'), invalidContent)
|
||||
|
||||
const result = await resourceManager.discoverUserResources()
|
||||
|
||||
// 应该跳过格式错误的文件,但不应该抛出错误
|
||||
expect(result.role || {}).not.toHaveProperty('invalid-role')
|
||||
})
|
||||
|
||||
it('应该跨平台正确处理路径', async () => {
|
||||
// 在不同平台上创建角色文件
|
||||
const roleDir = path.join(tempDir, '.promptx', 'resource', 'domain', 'cross-platform-role')
|
||||
await fs.ensureDir(roleDir)
|
||||
|
||||
const roleContent = '<role><personality>Test</personality><principle>Test</principle><knowledge>Test</knowledge></role>'
|
||||
await fs.writeFile(path.join(roleDir, 'cross-platform-role.role.md'), roleContent)
|
||||
|
||||
const result = await resourceManager.discoverUserResources()
|
||||
|
||||
expect(result.role).toHaveProperty('cross-platform-role')
|
||||
|
||||
// 验证文件路径使用正确的分隔符
|
||||
const roleInfo = result.role['cross-platform-role']
|
||||
expect(roleInfo.file).toBe(path.normalize(roleInfo.file))
|
||||
})
|
||||
})
|
||||
|
||||
describe('loadUnifiedRegistry', () => {
|
||||
it('应该合并系统资源和用户资源', async () => {
|
||||
// 模拟系统资源
|
||||
const mockSystemResources = {
|
||||
role: {
|
||||
'assistant': {
|
||||
file: '@package://prompt/domain/assistant/assistant.role.md',
|
||||
name: '🙋 智能助手',
|
||||
source: 'system',
|
||||
format: 'dpml',
|
||||
type: 'role'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mock fs.readJSON for system registry
|
||||
jest.spyOn(fs, 'readJSON')
|
||||
.mockImplementation((filePath) => {
|
||||
if (filePath.includes('resource.registry.json')) {
|
||||
return Promise.resolve(mockSystemResources)
|
||||
}
|
||||
return Promise.resolve({})
|
||||
})
|
||||
|
||||
// 创建用户资源
|
||||
const roleDir = path.join(tempDir, '.promptx', 'resource', 'domain', 'user-role')
|
||||
await fs.ensureDir(roleDir)
|
||||
await fs.writeFile(
|
||||
path.join(roleDir, 'user-role.role.md'),
|
||||
'<role><personality>User</personality><principle>User</principle><knowledge>User</knowledge></role>'
|
||||
)
|
||||
|
||||
const result = await resourceManager.loadUnifiedRegistry()
|
||||
|
||||
expect(result.role).toHaveProperty('assistant') // 系统资源
|
||||
expect(result.role).toHaveProperty('user-role') // 用户资源
|
||||
})
|
||||
|
||||
it('应该让用户资源覆盖同名系统资源', async () => {
|
||||
// 模拟系统资源
|
||||
const mockSystemResources = {
|
||||
role: {
|
||||
'assistant': {
|
||||
file: '@package://prompt/domain/assistant/assistant.role.md',
|
||||
name: '🙋 智能助手',
|
||||
source: 'system',
|
||||
format: 'dpml',
|
||||
type: 'role'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mock fs.readJSON for system registry
|
||||
jest.spyOn(fs, 'readJSON')
|
||||
.mockImplementation((filePath) => {
|
||||
if (filePath.includes('resource.registry.json')) {
|
||||
return Promise.resolve(mockSystemResources)
|
||||
}
|
||||
return Promise.resolve({})
|
||||
})
|
||||
|
||||
// 创建同名的用户资源
|
||||
const roleDir = path.join(tempDir, '.promptx', 'resource', 'domain', 'assistant')
|
||||
await fs.ensureDir(roleDir)
|
||||
await fs.writeFile(
|
||||
path.join(roleDir, 'assistant.role.md'),
|
||||
'<role><personality># 自定义助手\n用户定制的助手</personality><principle>Custom</principle><knowledge>Custom</knowledge></role>'
|
||||
)
|
||||
|
||||
const result = await resourceManager.loadUnifiedRegistry()
|
||||
|
||||
expect(result.role.assistant.source).toBe('user-generated')
|
||||
expect(result.role.assistant.name).toContain('自定义助手')
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user