kevlar-4u 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (238) hide show
  1. package/README.md +357 -0
  2. package/config/mcp-config.json +9 -0
  3. package/dist/__tests__/configureWizard.test.d.ts +2 -0
  4. package/dist/__tests__/configureWizard.test.d.ts.map +1 -0
  5. package/dist/__tests__/configureWizard.test.js +89 -0
  6. package/dist/__tests__/configureWizard.test.js.map +1 -0
  7. package/dist/__tests__/createPersonaTool.test.d.ts +2 -0
  8. package/dist/__tests__/createPersonaTool.test.d.ts.map +1 -0
  9. package/dist/__tests__/createPersonaTool.test.js +292 -0
  10. package/dist/__tests__/createPersonaTool.test.js.map +1 -0
  11. package/dist/__tests__/createPersonaWizard.test.d.ts +2 -0
  12. package/dist/__tests__/createPersonaWizard.test.d.ts.map +1 -0
  13. package/dist/__tests__/createPersonaWizard.test.js +138 -0
  14. package/dist/__tests__/createPersonaWizard.test.js.map +1 -0
  15. package/dist/__tests__/deletePersonaWizard.test.d.ts +2 -0
  16. package/dist/__tests__/deletePersonaWizard.test.d.ts.map +1 -0
  17. package/dist/__tests__/deletePersonaWizard.test.js +78 -0
  18. package/dist/__tests__/deletePersonaWizard.test.js.map +1 -0
  19. package/dist/__tests__/e2e.test.d.ts +2 -0
  20. package/dist/__tests__/e2e.test.d.ts.map +1 -0
  21. package/dist/__tests__/e2e.test.js +121 -0
  22. package/dist/__tests__/e2e.test.js.map +1 -0
  23. package/dist/__tests__/errors.test.d.ts +2 -0
  24. package/dist/__tests__/errors.test.d.ts.map +1 -0
  25. package/dist/__tests__/errors.test.js +86 -0
  26. package/dist/__tests__/errors.test.js.map +1 -0
  27. package/dist/__tests__/execution.test.d.ts +2 -0
  28. package/dist/__tests__/execution.test.d.ts.map +1 -0
  29. package/dist/__tests__/execution.test.js +792 -0
  30. package/dist/__tests__/execution.test.js.map +1 -0
  31. package/dist/__tests__/getModesTool.test.d.ts +2 -0
  32. package/dist/__tests__/getModesTool.test.d.ts.map +1 -0
  33. package/dist/__tests__/getModesTool.test.js +47 -0
  34. package/dist/__tests__/getModesTool.test.js.map +1 -0
  35. package/dist/__tests__/helpTool.test.d.ts +2 -0
  36. package/dist/__tests__/helpTool.test.d.ts.map +1 -0
  37. package/dist/__tests__/helpTool.test.js +18 -0
  38. package/dist/__tests__/helpTool.test.js.map +1 -0
  39. package/dist/__tests__/listPersonasTool.test.d.ts +2 -0
  40. package/dist/__tests__/listPersonasTool.test.d.ts.map +1 -0
  41. package/dist/__tests__/listPersonasTool.test.js +110 -0
  42. package/dist/__tests__/listPersonasTool.test.js.map +1 -0
  43. package/dist/__tests__/logger.test.d.ts +2 -0
  44. package/dist/__tests__/logger.test.d.ts.map +1 -0
  45. package/dist/__tests__/logger.test.js +56 -0
  46. package/dist/__tests__/logger.test.js.map +1 -0
  47. package/dist/__tests__/observability.test.d.ts +2 -0
  48. package/dist/__tests__/observability.test.d.ts.map +1 -0
  49. package/dist/__tests__/observability.test.js +60 -0
  50. package/dist/__tests__/observability.test.js.map +1 -0
  51. package/dist/__tests__/parser.test.d.ts +2 -0
  52. package/dist/__tests__/parser.test.d.ts.map +1 -0
  53. package/dist/__tests__/parser.test.js +259 -0
  54. package/dist/__tests__/parser.test.js.map +1 -0
  55. package/dist/__tests__/persona_creation_debug.test.d.ts +2 -0
  56. package/dist/__tests__/persona_creation_debug.test.d.ts.map +1 -0
  57. package/dist/__tests__/persona_creation_debug.test.js +56 -0
  58. package/dist/__tests__/persona_creation_debug.test.js.map +1 -0
  59. package/dist/__tests__/resetPersonasWizard.test.d.ts +2 -0
  60. package/dist/__tests__/resetPersonasWizard.test.d.ts.map +1 -0
  61. package/dist/__tests__/resetPersonasWizard.test.js +74 -0
  62. package/dist/__tests__/resetPersonasWizard.test.js.map +1 -0
  63. package/dist/__tests__/reviewContentWizard.test.d.ts +2 -0
  64. package/dist/__tests__/reviewContentWizard.test.d.ts.map +1 -0
  65. package/dist/__tests__/reviewContentWizard.test.js +148 -0
  66. package/dist/__tests__/reviewContentWizard.test.js.map +1 -0
  67. package/dist/__tests__/sanitize.test.d.ts +2 -0
  68. package/dist/__tests__/sanitize.test.d.ts.map +1 -0
  69. package/dist/__tests__/sanitize.test.js +138 -0
  70. package/dist/__tests__/sanitize.test.js.map +1 -0
  71. package/dist/__tests__/server.test.d.ts +2 -0
  72. package/dist/__tests__/server.test.d.ts.map +1 -0
  73. package/dist/__tests__/server.test.js +49 -0
  74. package/dist/__tests__/server.test.js.map +1 -0
  75. package/dist/execution/aggregator.d.ts +43 -0
  76. package/dist/execution/aggregator.d.ts.map +1 -0
  77. package/dist/execution/aggregator.js +132 -0
  78. package/dist/execution/aggregator.js.map +1 -0
  79. package/dist/execution/base.d.ts +62 -0
  80. package/dist/execution/base.d.ts.map +1 -0
  81. package/dist/execution/base.js +5 -0
  82. package/dist/execution/base.js.map +1 -0
  83. package/dist/execution/client.d.ts +9 -0
  84. package/dist/execution/client.d.ts.map +1 -0
  85. package/dist/execution/client.js +30 -0
  86. package/dist/execution/client.js.map +1 -0
  87. package/dist/execution/config.d.ts +30 -0
  88. package/dist/execution/config.d.ts.map +1 -0
  89. package/dist/execution/config.js +95 -0
  90. package/dist/execution/config.js.map +1 -0
  91. package/dist/execution/index.d.ts +19 -0
  92. package/dist/execution/index.d.ts.map +1 -0
  93. package/dist/execution/index.js +151 -0
  94. package/dist/execution/index.js.map +1 -0
  95. package/dist/execution/limiter.d.ts +32 -0
  96. package/dist/execution/limiter.d.ts.map +1 -0
  97. package/dist/execution/limiter.js +147 -0
  98. package/dist/execution/limiter.js.map +1 -0
  99. package/dist/execution/lock.d.ts +17 -0
  100. package/dist/execution/lock.d.ts.map +1 -0
  101. package/dist/execution/lock.js +37 -0
  102. package/dist/execution/lock.js.map +1 -0
  103. package/dist/execution/modes/direct_api.d.ts +11 -0
  104. package/dist/execution/modes/direct_api.d.ts.map +1 -0
  105. package/dist/execution/modes/direct_api.js +213 -0
  106. package/dist/execution/modes/direct_api.js.map +1 -0
  107. package/dist/execution/modes/index.d.ts +7 -0
  108. package/dist/execution/modes/index.d.ts.map +1 -0
  109. package/dist/execution/modes/index.js +7 -0
  110. package/dist/execution/modes/index.js.map +1 -0
  111. package/dist/execution/modes/orchestration.d.ts +11 -0
  112. package/dist/execution/modes/orchestration.d.ts.map +1 -0
  113. package/dist/execution/modes/orchestration.js +110 -0
  114. package/dist/execution/modes/orchestration.js.map +1 -0
  115. package/dist/execution/modes/sampling.d.ts +9 -0
  116. package/dist/execution/modes/sampling.d.ts.map +1 -0
  117. package/dist/execution/modes/sampling.js +66 -0
  118. package/dist/execution/modes/sampling.js.map +1 -0
  119. package/dist/execution/parallel.d.ts +16 -0
  120. package/dist/execution/parallel.d.ts.map +1 -0
  121. package/dist/execution/parallel.js +90 -0
  122. package/dist/execution/parallel.js.map +1 -0
  123. package/dist/index.d.ts +3 -0
  124. package/dist/index.d.ts.map +1 -0
  125. package/dist/index.js +17 -0
  126. package/dist/index.js.map +1 -0
  127. package/dist/prompts/reviewDispatcherPrompt.d.ts +2 -0
  128. package/dist/prompts/reviewDispatcherPrompt.d.ts.map +1 -0
  129. package/dist/prompts/reviewDispatcherPrompt.js +67 -0
  130. package/dist/prompts/reviewDispatcherPrompt.js.map +1 -0
  131. package/dist/server.d.ts +3 -0
  132. package/dist/server.d.ts.map +1 -0
  133. package/dist/server.js +156 -0
  134. package/dist/server.js.map +1 -0
  135. package/dist/tools/configureTool.d.ts +17 -0
  136. package/dist/tools/configureTool.d.ts.map +1 -0
  137. package/dist/tools/configureTool.js +104 -0
  138. package/dist/tools/configureTool.js.map +1 -0
  139. package/dist/tools/configureWizardTool.d.ts +11 -0
  140. package/dist/tools/configureWizardTool.d.ts.map +1 -0
  141. package/dist/tools/configureWizardTool.js +205 -0
  142. package/dist/tools/configureWizardTool.js.map +1 -0
  143. package/dist/tools/createPersonaTool.d.ts +37 -0
  144. package/dist/tools/createPersonaTool.d.ts.map +1 -0
  145. package/dist/tools/createPersonaTool.js +353 -0
  146. package/dist/tools/createPersonaTool.js.map +1 -0
  147. package/dist/tools/createPersonaWizardTool.d.ts +13 -0
  148. package/dist/tools/createPersonaWizardTool.d.ts.map +1 -0
  149. package/dist/tools/createPersonaWizardTool.js +713 -0
  150. package/dist/tools/createPersonaWizardTool.js.map +1 -0
  151. package/dist/tools/deletePersonaTool.d.ts +10 -0
  152. package/dist/tools/deletePersonaTool.d.ts.map +1 -0
  153. package/dist/tools/deletePersonaTool.js +75 -0
  154. package/dist/tools/deletePersonaTool.js.map +1 -0
  155. package/dist/tools/deletePersonaWizardTool.d.ts +11 -0
  156. package/dist/tools/deletePersonaWizardTool.d.ts.map +1 -0
  157. package/dist/tools/deletePersonaWizardTool.js +184 -0
  158. package/dist/tools/deletePersonaWizardTool.js.map +1 -0
  159. package/dist/tools/getModesTool.d.ts +12 -0
  160. package/dist/tools/getModesTool.d.ts.map +1 -0
  161. package/dist/tools/getModesTool.js +78 -0
  162. package/dist/tools/getModesTool.js.map +1 -0
  163. package/dist/tools/helpTool.d.ts +7 -0
  164. package/dist/tools/helpTool.d.ts.map +1 -0
  165. package/dist/tools/helpTool.js +65 -0
  166. package/dist/tools/helpTool.js.map +1 -0
  167. package/dist/tools/index.d.ts +9 -0
  168. package/dist/tools/index.d.ts.map +1 -0
  169. package/dist/tools/index.js +30 -0
  170. package/dist/tools/index.js.map +1 -0
  171. package/dist/tools/listPersonasTool.d.ts +7 -0
  172. package/dist/tools/listPersonasTool.d.ts.map +1 -0
  173. package/dist/tools/listPersonasTool.js +118 -0
  174. package/dist/tools/listPersonasTool.js.map +1 -0
  175. package/dist/tools/resetPersonasTool.d.ts +7 -0
  176. package/dist/tools/resetPersonasTool.d.ts.map +1 -0
  177. package/dist/tools/resetPersonasTool.js +447 -0
  178. package/dist/tools/resetPersonasTool.js.map +1 -0
  179. package/dist/tools/resetPersonasWizardTool.d.ts +9 -0
  180. package/dist/tools/resetPersonasWizardTool.d.ts.map +1 -0
  181. package/dist/tools/resetPersonasWizardTool.js +125 -0
  182. package/dist/tools/resetPersonasWizardTool.js.map +1 -0
  183. package/dist/tools/reviewContentWizardTool.d.ts +13 -0
  184. package/dist/tools/reviewContentWizardTool.d.ts.map +1 -0
  185. package/dist/tools/reviewContentWizardTool.js +411 -0
  186. package/dist/tools/reviewContentWizardTool.js.map +1 -0
  187. package/dist/tools/reviewTool.d.ts +10 -0
  188. package/dist/tools/reviewTool.d.ts.map +1 -0
  189. package/dist/tools/reviewTool.js +133 -0
  190. package/dist/tools/reviewTool.js.map +1 -0
  191. package/dist/tools/types.d.ts +14 -0
  192. package/dist/tools/types.d.ts.map +1 -0
  193. package/dist/tools/types.js +2 -0
  194. package/dist/tools/types.js.map +1 -0
  195. package/dist/utils/errors.d.ts +30 -0
  196. package/dist/utils/errors.d.ts.map +1 -0
  197. package/dist/utils/errors.js +47 -0
  198. package/dist/utils/errors.js.map +1 -0
  199. package/dist/utils/logger.d.ts +18 -0
  200. package/dist/utils/logger.d.ts.map +1 -0
  201. package/dist/utils/logger.js +52 -0
  202. package/dist/utils/logger.js.map +1 -0
  203. package/dist/utils/observability.d.ts +61 -0
  204. package/dist/utils/observability.d.ts.map +1 -0
  205. package/dist/utils/observability.js +69 -0
  206. package/dist/utils/observability.js.map +1 -0
  207. package/dist/utils/parser.d.ts +27 -0
  208. package/dist/utils/parser.d.ts.map +1 -0
  209. package/dist/utils/parser.js +178 -0
  210. package/dist/utils/parser.js.map +1 -0
  211. package/dist/utils/personaIdMaps.d.ts +7 -0
  212. package/dist/utils/personaIdMaps.d.ts.map +1 -0
  213. package/dist/utils/personaIdMaps.js +51 -0
  214. package/dist/utils/personaIdMaps.js.map +1 -0
  215. package/dist/utils/sanitize.d.ts +4 -0
  216. package/dist/utils/sanitize.d.ts.map +1 -0
  217. package/dist/utils/sanitize.js +49 -0
  218. package/dist/utils/sanitize.js.map +1 -0
  219. package/dist/utils/types.d.ts +8 -0
  220. package/dist/utils/types.d.ts.map +1 -0
  221. package/dist/utils/types.js +2 -0
  222. package/dist/utils/types.js.map +1 -0
  223. package/package.json +42 -0
  224. package/skills/_template.md +66 -0
  225. package/skills/tmp/wizard-create-cwzrrpim_draft.json +26 -0
  226. package/skills/tmp/wizard-create-cwzrrpim_wizard.json +26 -0
  227. package/skills/tmp/wizard-create-d81intme_draft.json +26 -0
  228. package/skills/tmp/wizard-create-d81intme_wizard.json +26 -0
  229. package/skills/tmp/wizard-create-g50jqzmh_draft.json +8 -0
  230. package/skills/tmp/wizard-create-g50jqzmh_wizard.json +8 -0
  231. package/skills/tmp/wizard-create-onupu9wb_draft.json +8 -0
  232. package/skills/tmp/wizard-create-onupu9wb_wizard.json +8 -0
  233. package/skills/tmp/wizard-review-fwbxe3d2_review_wizard.json +9 -0
  234. package/skills/tmp/wizard-review-sypg5e9d_review_wizard.json +9 -0
  235. package/skills/wechat_official/wechat_official.md +26 -0
  236. package/skills/wechat_official/wechat_official_1.md +31 -0
  237. package/skills/xiaohongshu/xiaohongshu.md +26 -0
  238. package/skills/xiaohongshu/xiaohongshu_1.md +29 -0
@@ -0,0 +1,259 @@
1
+ import { describe, it, before, after, beforeEach, afterEach } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import * as fs from "fs";
4
+ import * as path from "path";
5
+ import * as os from "os";
6
+ import { validateWritePath, parsePersonaFile, writePersonaFile, loadAllPersonas, invalidatePersonasCache, } from "../utils/parser.js";
7
+ import { promises as fsp } from "fs";
8
+ let tmpDir;
9
+ before(() => {
10
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "kevlar-test-"));
11
+ });
12
+ after(() => {
13
+ fs.rmSync(tmpDir, { recursive: true, force: true });
14
+ });
15
+ describe("validateWritePath", () => {
16
+ it("allows paths within baseDir", () => {
17
+ const allowed = path.join(tmpDir, "foo.md");
18
+ assert.ok(validateWritePath(allowed, tmpDir));
19
+ });
20
+ it("allows paths in subdirectories", () => {
21
+ const allowed = path.join(tmpDir, "sub", "foo.md");
22
+ assert.ok(validateWritePath(allowed, tmpDir));
23
+ });
24
+ it("rejects paths outside baseDir", () => {
25
+ const outside = path.join(os.tmpdir(), "outside.md");
26
+ assert.ok(!validateWritePath(outside, tmpDir));
27
+ });
28
+ it("rejects path traversal attempts", () => {
29
+ const traversal = path.join(tmpDir, "..", "escape.md");
30
+ assert.ok(!validateWritePath(traversal, tmpDir));
31
+ });
32
+ });
33
+ describe("parsePersonaFile", () => {
34
+ it("parses a valid persona file", async () => {
35
+ const filePath = path.join(tmpDir, "test_persona.md");
36
+ fs.writeFileSync(filePath, [
37
+ "---",
38
+ "id: test_persona",
39
+ "name: 测试人设",
40
+ "name_en: Test Persona",
41
+ "version: 1.0.0",
42
+ "author: tester",
43
+ "tags:",
44
+ " - test",
45
+ " - demo",
46
+ "description: A test persona",
47
+ "---",
48
+ "You are a test persona.",
49
+ ].join("\n"), "utf-8");
50
+ const persona = await parsePersonaFile(filePath);
51
+ assert.ok(persona !== null);
52
+ assert.equal(persona.meta.id, "test_persona");
53
+ assert.equal(persona.meta.name, "测试人设");
54
+ assert.equal(persona.meta.tags.length, 2);
55
+ assert.equal(persona.systemPrompt, "You are a test persona.");
56
+ });
57
+ it("parses a persona file with inferred metadata fields", async () => {
58
+ const filePath = path.join(tmpDir, "inferred_persona.md");
59
+ fs.writeFileSync(filePath, [
60
+ "---",
61
+ "id: inferred_persona",
62
+ "name: 推断人设",
63
+ "name_en: Inferred Persona",
64
+ "version: 1.0.0",
65
+ "author: tester",
66
+ "tags: []",
67
+ "description: A test inferred persona",
68
+ "culturalContext: Chinese culture",
69
+ "authorRelation: Subscribed",
70
+ "stance: Supportive",
71
+ "blindSpot: Math",
72
+ "---",
73
+ "You are an inferred persona.",
74
+ ].join("\n"), "utf-8");
75
+ const persona = await parsePersonaFile(filePath);
76
+ assert.ok(persona !== null);
77
+ assert.equal(persona.meta.id, "inferred_persona");
78
+ assert.equal(persona.meta.culturalContext, "Chinese culture");
79
+ assert.equal(persona.meta.authorRelation, "Subscribed");
80
+ assert.equal(persona.meta.stance, "Supportive");
81
+ assert.equal(persona.meta.blindSpot, "Math");
82
+ assert.equal(persona.systemPrompt, "You are an inferred persona.");
83
+ });
84
+ // it("skips template files starting with _", async () => {
85
+ // const filePath = path.join(tmpDir, "_template.md");
86
+ // fs.writeFileSync(filePath, "---\nid: template\nname: Template\n---\nContent", "utf-8");
87
+ // const persona = await parsePersonaFile(filePath);
88
+ // assert.equal(persona, null);
89
+ // });
90
+ it("returns null for missing id in frontmatter", async () => {
91
+ const filePath = path.join(tmpDir, "no_id.md");
92
+ fs.writeFileSync(filePath, "---\nname: No ID\n---\nContent", "utf-8");
93
+ const persona = await parsePersonaFile(filePath);
94
+ assert.equal(persona, null);
95
+ });
96
+ it("returns null for non-existent file", async () => {
97
+ const persona = await parsePersonaFile("/nonexistent/path.md");
98
+ assert.equal(persona, null);
99
+ });
100
+ });
101
+ // ─────────────────────────────────────────────────────────────────────────────
102
+ // writePersonaFile Tests
103
+ // ─────────────────────────────────────────────────────────────────────────────
104
+ describe("writePersonaFile", () => {
105
+ beforeEach(() => {
106
+ invalidatePersonasCache();
107
+ });
108
+ it("writes a valid persona file and returns the path", async () => {
109
+ const filePath = await writePersonaFile(tmpDir, {
110
+ id: "test_writer",
111
+ name: "测试写入",
112
+ name_en: "Test Writer",
113
+ version: "1.0.0",
114
+ author: "tester",
115
+ tags: ["test"],
116
+ description: "Test write",
117
+ }, "You are a test persona.");
118
+ assert.ok(filePath.endsWith("test_writer.md"));
119
+ assert.ok(fs.existsSync(filePath));
120
+ const persona = await parsePersonaFile(filePath);
121
+ assert.ok(persona);
122
+ assert.equal(persona.meta.id, "test_writer");
123
+ assert.equal(persona.systemPrompt, "You are a test persona.");
124
+ });
125
+ it("writes inferred metadata fields to frontmatter", async () => {
126
+ const filePath = await writePersonaFile(tmpDir, {
127
+ id: "test_inferred_writer",
128
+ name: "测试推断写入",
129
+ name_en: "Test Inferred Writer",
130
+ version: "1.0.0",
131
+ author: "tester",
132
+ tags: ["test"],
133
+ description: "Test inferred write",
134
+ culturalContext: "Chinese context",
135
+ authorRelation: "Stranger",
136
+ stance: "Skeptical",
137
+ blindSpot: "Visuals",
138
+ }, "Content here");
139
+ const persona = await parsePersonaFile(filePath);
140
+ assert.ok(persona);
141
+ assert.equal(persona.meta.culturalContext, "Chinese context");
142
+ assert.equal(persona.meta.authorRelation, "Stranger");
143
+ assert.equal(persona.meta.stance, "Skeptical");
144
+ assert.equal(persona.meta.blindSpot, "Visuals");
145
+ });
146
+ it("rejects path traversal via id", async () => {
147
+ await assert.rejects(() => writePersonaFile(tmpDir, {
148
+ id: "../escape",
149
+ name: "Escape",
150
+ name_en: "Escape",
151
+ version: "1.0.0",
152
+ author: "tester",
153
+ tags: [],
154
+ description: "Escape attempt",
155
+ }, "content"), /Invalid file path/);
156
+ });
157
+ it("overwrites existing persona file", async () => {
158
+ const filePath = await writePersonaFile(tmpDir, {
159
+ id: "overwrite_test",
160
+ name: "Original",
161
+ name_en: "",
162
+ version: "1.0.0",
163
+ author: "tester",
164
+ tags: [],
165
+ description: "Original",
166
+ }, "Original content");
167
+ const filePath2 = await writePersonaFile(tmpDir, {
168
+ id: "overwrite_test",
169
+ name: "Updated",
170
+ name_en: "",
171
+ version: "2.0.0",
172
+ author: "tester",
173
+ tags: [],
174
+ description: "Updated",
175
+ }, "Updated content");
176
+ assert.equal(filePath, filePath2);
177
+ const persona = await parsePersonaFile(filePath);
178
+ assert.equal(persona?.meta.name, "Updated");
179
+ assert.equal(persona?.systemPrompt, "Updated content");
180
+ });
181
+ it("throws error when file system write fails", async () => {
182
+ const originalWriteFile = fsp.writeFile;
183
+ fsp.writeFile = async () => {
184
+ throw new Error("Simulated I/O Error");
185
+ };
186
+ try {
187
+ await assert.rejects(() => writePersonaFile(tmpDir, {
188
+ id: "write_fail_test",
189
+ name: "Fail Test",
190
+ name_en: "",
191
+ version: "1.0.0",
192
+ author: "tester",
193
+ tags: [],
194
+ description: "Fail attempt",
195
+ }, "content"), /Simulated I\/O Error/);
196
+ }
197
+ finally {
198
+ fsp.writeFile = originalWriteFile;
199
+ }
200
+ });
201
+ });
202
+ // ─────────────────────────────────────────────────────────────────────────────
203
+ // loadAllPersonas Cache Tests
204
+ // ─────────────────────────────────────────────────────────────────────────────
205
+ describe("loadAllPersonas caching", () => {
206
+ let cacheDir;
207
+ beforeEach(() => {
208
+ invalidatePersonasCache();
209
+ cacheDir = fs.mkdtempSync(path.join(os.tmpdir(), "kevlar-cache-test-"));
210
+ });
211
+ afterEach(() => {
212
+ fs.rmSync(cacheDir, { recursive: true, force: true });
213
+ });
214
+ it("returns cached result on subsequent calls", async () => {
215
+ await writePersonaFile(cacheDir, {
216
+ id: "cache_test",
217
+ name: "Cache Test",
218
+ name_en: "",
219
+ version: "1.0.0",
220
+ author: "tester",
221
+ tags: [],
222
+ description: "",
223
+ }, "content");
224
+ const first = await loadAllPersonas(cacheDir);
225
+ assert.equal(first.length, 1);
226
+ const second = await loadAllPersonas(cacheDir);
227
+ assert.equal(second.length, 1);
228
+ });
229
+ it("invalidates cache after writePersonaFile", async () => {
230
+ await writePersonaFile(cacheDir, {
231
+ id: "cache_inval_test",
232
+ name: "Before",
233
+ name_en: "",
234
+ version: "1.0.0",
235
+ author: "tester",
236
+ tags: [],
237
+ description: "",
238
+ }, "before");
239
+ const before = await loadAllPersonas(cacheDir);
240
+ assert.equal(before.length, 1);
241
+ // Write a new persona — should invalidate cache
242
+ await writePersonaFile(cacheDir, {
243
+ id: "cache_inval_test2",
244
+ name: "After",
245
+ name_en: "",
246
+ version: "1.0.0",
247
+ author: "tester",
248
+ tags: [],
249
+ description: "",
250
+ }, "after");
251
+ const after = await loadAllPersonas(cacheDir);
252
+ assert.equal(after.length, 2);
253
+ });
254
+ it("returns empty array when directory does not exist", async () => {
255
+ const personas = await loadAllPersonas(path.join(cacheDir, "nonexistent"));
256
+ assert.deepEqual(personas, []);
257
+ });
258
+ });
259
+ //# sourceMappingURL=parser.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.test.js","sourceRoot":"","sources":["../../src/__tests__/parser.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=persona_creation_debug.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persona_creation_debug.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/persona_creation_debug.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,56 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ import { tmpdir } from "os";
4
+ import { describe, it, before, after } from "node:test";
5
+ import { ok } from "node:assert";
6
+ import { handleCreatePersonaWizard } from "../tools/createPersonaWizardTool.js";
7
+ import { loadAllPersonas } from "../utils/parser.js";
8
+ describe("persona creation debug", () => {
9
+ const skillsDir = path.join(tmpdir(), `kevlar-debug-${Date.now()}`);
10
+ const tmpDir = path.join(tmpdir(), `kevlar-debug-tmp-${Date.now()}`);
11
+ before(() => {
12
+ fs.mkdirSync(skillsDir, { recursive: true });
13
+ fs.mkdirSync(tmpDir, { recursive: true });
14
+ });
15
+ after(() => {
16
+ fs.rmSync(skillsDir, { recursive: true, force: true });
17
+ fs.rmSync(tmpDir, { recursive: true, force: true });
18
+ });
19
+ it("generateIdFromDraft with real wizard data", async () => {
20
+ // Simulate full wizard flow
21
+ const r1 = await handleCreatePersonaWizard(skillsDir, tmpDir, { userMessage: "创建一个美食评论员" });
22
+ const sidMatch = r1.content[0].text.match(/sessionId: (\S+)/);
23
+ const sessionId = sidMatch ? sidMatch[1] : "";
24
+ ok(sessionId, "should have sessionId");
25
+ await handleCreatePersonaWizard(skillsDir, tmpDir, { userMessage: "25-30岁", sessionId });
26
+ await handleCreatePersonaWizard(skillsDir, tmpDir, { userMessage: "美食、旅行、科技", sessionId });
27
+ // Step 4: traits - use "宅男" which is NOT in the trait map (real-world case)
28
+ const r4 = await handleCreatePersonaWizard(skillsDir, tmpDir, { userMessage: "宅男、孤僻", sessionId });
29
+ const r5 = await handleCreatePersonaWizard(skillsDir, tmpDir, { userMessage: "知乎", sessionId });
30
+ // Step 6: author relation - shows final confirm with preview
31
+ const r6 = await handleCreatePersonaWizard(skillsDir, tmpDir, { userMessage: "已关注", sessionId });
32
+ // Print the confirmation message
33
+ console.log("\n=== CONFIRMATION MESSAGE ===");
34
+ console.log(r6.content[0].text);
35
+ console.log("=== END ===\n");
36
+ // Check if preview shows the correct path
37
+ const previewMatch = r6.content[0].text.match(/📄 (\S+)/);
38
+ if (previewMatch) {
39
+ console.log("Preview path:", previewMatch[1]);
40
+ // Should be skills/zhihu/zhihu.md or similar (not persona_xxx)
41
+ ok(!previewMatch[1].includes("persona_"), "Preview should NOT show random persona_ ID");
42
+ }
43
+ // Now confirm
44
+ const r7 = await handleCreatePersonaWizard(skillsDir, tmpDir, { userMessage: "确认创建", sessionId });
45
+ console.log("Creation result:", r7.content[0].text.substring(0, 200));
46
+ // Load personas to check actual file
47
+ const personas = await loadAllPersonas(skillsDir);
48
+ console.log("\nCreated personas:", personas.length);
49
+ for (const p of personas) {
50
+ console.log(` ID: ${p.meta.id}, Name: ${p.meta.name}, File: ${p.filePath}`);
51
+ ok(!p.meta.id.startsWith("persona_"), `ID ${p.meta.id} should NOT be random`);
52
+ ok(p.filePath.includes("zhihu/"), `File ${p.filePath} should be in zhihu/ subdirectory`);
53
+ }
54
+ });
55
+ });
56
+ //# sourceMappingURL=persona_creation_debug.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persona_creation_debug.test.js","sourceRoot":"","sources":["../../src/__tests__/persona_creation_debug.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=resetPersonasWizard.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resetPersonasWizard.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/resetPersonasWizard.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,74 @@
1
+ import { describe, it, beforeEach, afterEach } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import * as fs from "fs";
4
+ import * as path from "path";
5
+ import * as os from "os";
6
+ import { handleResetPersonasWizard } from "../tools/resetPersonasWizardTool.js";
7
+ import { handleResetPersonas } from "../tools/resetPersonasTool.js";
8
+ import { invalidatePersonasCache } from "../utils/parser.js";
9
+ let skillsDir;
10
+ let tmpDir;
11
+ beforeEach(() => {
12
+ skillsDir = fs.mkdtempSync(path.join(os.tmpdir(), "kevlar-reset-wizard-"));
13
+ tmpDir = path.join(skillsDir, "tmp");
14
+ invalidatePersonasCache();
15
+ });
16
+ afterEach(() => {
17
+ fs.rmSync(skillsDir, { recursive: true, force: true });
18
+ invalidatePersonasCache();
19
+ });
20
+ function textOf(result) {
21
+ return result.content.map((c) => c.text).join("\n");
22
+ }
23
+ function extractSessionId(text) {
24
+ const match = text.match(/sessionId:\s*([a-z0-9-]+)/);
25
+ assert.ok(match, `expected sessionId in response: ${text}`);
26
+ return match[1];
27
+ }
28
+ describe("handleResetPersonasWizard", () => {
29
+ it("previews impact and does not restore before explicit confirmation", async () => {
30
+ const result = await handleResetPersonasWizard(skillsDir, tmpDir, {
31
+ userMessage: "恢复默认人设",
32
+ });
33
+ const text = textOf(result);
34
+ assert.ok(text.includes("将恢复系统内置默认评论员"));
35
+ assert.ok(text.includes("currentStep: confirmReset"));
36
+ assert.equal(fs.readdirSync(skillsDir).filter((f) => f.endsWith(".md")).length, 0);
37
+ });
38
+ it("runs reset only after exact confirmation phrase", async () => {
39
+ const started = await handleResetPersonasWizard(skillsDir, tmpDir, {
40
+ userMessage: "恢复默认人设",
41
+ });
42
+ const sessionId = extractSessionId(textOf(started));
43
+ const wrongConfirm = await handleResetPersonasWizard(skillsDir, tmpDir, {
44
+ sessionId,
45
+ userMessage: "确认",
46
+ });
47
+ assert.ok(textOf(wrongConfirm).includes("请回复完整确认语"));
48
+ assert.equal(fs.readdirSync(skillsDir).filter((f) => f.endsWith(".md")).length, 0);
49
+ const restored = await handleResetPersonasWizard(skillsDir, tmpDir, {
50
+ sessionId,
51
+ userMessage: "确认恢复默认评论员",
52
+ });
53
+ const restoredText = textOf(restored);
54
+ assert.ok(restoredText.includes("系统人设恢复完成"));
55
+ assert.ok(fs.readdirSync(skillsDir).some((f) => f.endsWith(".md")));
56
+ });
57
+ });
58
+ describe("handleResetPersonas (direct tool)", () => {
59
+ it("blocks reset when confirm is false", async () => {
60
+ const result = await handleResetPersonas(skillsDir, { confirm: false });
61
+ assert.ok(result.isError);
62
+ assert.ok(result.content[0]?.text.includes("二次确认"));
63
+ assert.equal(fs.readdirSync(skillsDir).filter((f) => f.endsWith(".md")).length, 0);
64
+ });
65
+ it("writes built-in persona files when confirm is true", async () => {
66
+ const result = await handleResetPersonas(skillsDir, { confirm: true });
67
+ assert.ok(!result.isError);
68
+ const files = fs.readdirSync(skillsDir).filter((f) => f.endsWith(".md"));
69
+ assert.ok(files.includes("impatient_passerby.md"));
70
+ assert.ok(files.includes("keyboard_warrior.md"));
71
+ assert.ok(files.includes("first_time_reader.md"));
72
+ });
73
+ });
74
+ //# sourceMappingURL=resetPersonasWizard.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resetPersonasWizard.test.js","sourceRoot":"","sources":["../../src/__tests__/resetPersonasWizard.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=reviewContentWizard.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reviewContentWizard.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/reviewContentWizard.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,148 @@
1
+ import { describe, it, beforeEach, afterEach } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import * as fs from "fs";
4
+ import * as path from "path";
5
+ import * as os from "os";
6
+ import { handleReviewContentWizard } from "../tools/reviewContentWizardTool.js";
7
+ import { invalidatePersonasCache } from "../utils/parser.js";
8
+ let skillsDir;
9
+ let tmpDir;
10
+ let previousApiKey;
11
+ let previousOpenAiKey;
12
+ let previousAnthropicKey;
13
+ beforeEach(() => {
14
+ skillsDir = fs.mkdtempSync(path.join(os.tmpdir(), "kevlar-review-wizard-"));
15
+ tmpDir = path.join(skillsDir, "tmp");
16
+ previousApiKey = process.env.KEVLAR_API_KEY;
17
+ previousOpenAiKey = process.env.OPENAI_API_KEY;
18
+ previousAnthropicKey = process.env.ANTHROPIC_API_KEY;
19
+ delete process.env.KEVLAR_API_KEY;
20
+ delete process.env.OPENAI_API_KEY;
21
+ delete process.env.ANTHROPIC_API_KEY;
22
+ invalidatePersonasCache();
23
+ });
24
+ afterEach(() => {
25
+ fs.rmSync(skillsDir, { recursive: true, force: true });
26
+ if (previousApiKey === undefined)
27
+ delete process.env.KEVLAR_API_KEY;
28
+ else
29
+ process.env.KEVLAR_API_KEY = previousApiKey;
30
+ if (previousOpenAiKey === undefined)
31
+ delete process.env.OPENAI_API_KEY;
32
+ else
33
+ process.env.OPENAI_API_KEY = previousOpenAiKey;
34
+ if (previousAnthropicKey === undefined)
35
+ delete process.env.ANTHROPIC_API_KEY;
36
+ else
37
+ process.env.ANTHROPIC_API_KEY = previousAnthropicKey;
38
+ invalidatePersonasCache();
39
+ });
40
+ function textOf(result) {
41
+ return result.content.map((c) => c.text).join("\n");
42
+ }
43
+ function extractSessionId(text) {
44
+ const match = text.match(/sessionId:\s*([a-z0-9-]+)/);
45
+ assert.ok(match, `expected sessionId in response: ${text}`);
46
+ return match[1];
47
+ }
48
+ function writePersona(id, name, tags) {
49
+ const file = [
50
+ "---",
51
+ `id: ${id}`,
52
+ `name: ${name}`,
53
+ "name_en: Test Persona",
54
+ "version: 1.0.0",
55
+ "author: kevlar-core",
56
+ "tags:",
57
+ ...tags.map((tag) => ` - ${tag}`),
58
+ `description: ${name} 的测试描述`,
59
+ "---",
60
+ "性格特质:直接。常用平台:小红书。盲区:无特定盲区。",
61
+ ].join("\n");
62
+ fs.writeFileSync(path.join(skillsDir, `${id}.md`), file, "utf-8");
63
+ invalidatePersonasCache();
64
+ }
65
+ describe("handleReviewContentWizard state machine", () => {
66
+ it("stores content and asks for persona creation when no personas exist without dumping dispatcher prompt", async () => {
67
+ const result = await handleReviewContentWizard(skillsDir, tmpDir, {
68
+ userMessage: "请评测这篇小红书文案:今天分享一个新品故事。",
69
+ });
70
+ const text = textOf(result);
71
+ assert.ok(text.includes("当前还没有可用评论员"));
72
+ assert.ok(text.includes("currentStep: waitingForPersonaCreation"));
73
+ assert.ok(text.includes("sessionId:"));
74
+ assert.ok(!text.includes("=== SYSTEM_PROMPT 开始 ==="));
75
+ assert.ok(!text.includes("你是一个内容评论调度引擎"));
76
+ const sessionId = extractSessionId(text);
77
+ const state = JSON.parse(fs.readFileSync(path.join(tmpDir, `${sessionId}_review_wizard.json`), "utf-8"));
78
+ assert.ok(state.content.includes("新品故事"));
79
+ });
80
+ it("collects target platform first, filters personas, and executes review after confirmation", async () => {
81
+ writePersona("visual_reader", "视觉读者", ["小红书", "视觉"]);
82
+ writePersona("logic_reader", "逻辑读者", ["知乎", "逻辑"]);
83
+ const started = await handleReviewContentWizard(skillsDir, tmpDir, {
84
+ userMessage: "请评测这篇内容:这是一篇新品发布文案。",
85
+ });
86
+ const startText = textOf(started);
87
+ assert.ok(startText.includes("这份内容准备投放在哪些平台"));
88
+ assert.ok(startText.includes("currentStep: collectPlatforms"));
89
+ assert.ok(!startText.includes("Kevlar 宿主辅助评测任务"));
90
+ const sessionId = extractSessionId(startText);
91
+ // Step 2: specify target platform
92
+ const filtered = await handleReviewContentWizard(skillsDir, tmpDir, {
93
+ sessionId,
94
+ userMessage: "小红书",
95
+ });
96
+ const filteredText = textOf(filtered);
97
+ assert.ok(filteredText.includes("已为你筛选出匹配「小红书」平台的评论员"));
98
+ assert.ok(filteredText.includes("视觉读者"));
99
+ assert.ok(!filteredText.includes("逻辑读者"));
100
+ assert.ok(filteredText.includes("currentStep: confirmSelection"));
101
+ // Step 3: confirm execution
102
+ const executed = await handleReviewContentWizard(skillsDir, tmpDir, {
103
+ sessionId,
104
+ userMessage: "确认",
105
+ });
106
+ const executedText = textOf(executed);
107
+ // Only visual_reader (小红书) was selected; logic_reader (知乎) is excluded
108
+ assert.ok(executedText.includes("Kevlar 宿主辅助评测任务"));
109
+ assert.ok(executedText.includes("视觉读者"));
110
+ });
111
+ it("does not treat '这是什么意思' as an affirmative confirmation", async () => {
112
+ writePersona("visual_reader", "视觉读者", ["小红书", "视觉"]);
113
+ const started = await handleReviewContentWizard(skillsDir, tmpDir, {
114
+ userMessage: "请评测这篇内容:这是一篇新品发布文案。",
115
+ });
116
+ const sessionId = extractSessionId(textOf(started));
117
+ // Select persona
118
+ await handleReviewContentWizard(skillsDir, tmpDir, {
119
+ sessionId,
120
+ userMessage: "视觉读者",
121
+ });
122
+ // Try to confirm with ambiguous word containing "是"
123
+ const executed = await handleReviewContentWizard(skillsDir, tmpDir, {
124
+ sessionId,
125
+ userMessage: "这是什么意思",
126
+ });
127
+ const executedText = textOf(executed);
128
+ // Should NOT execute review.
129
+ assert.ok(!executedText.includes("Kevlar 宿主辅助评测任务"));
130
+ });
131
+ it("does not falsely match short persona names", async () => {
132
+ // Create a persona with a very short name "好"
133
+ writePersona("good_reader", "好", ["小红书", "视觉"]);
134
+ const started = await handleReviewContentWizard(skillsDir, tmpDir, {
135
+ userMessage: "请评测这篇内容:这是一篇新品发布文案。",
136
+ });
137
+ const sessionId = extractSessionId(textOf(started));
138
+ // User message contains "好" but is not exactly "好"
139
+ const selection = await handleReviewContentWizard(skillsDir, tmpDir, {
140
+ sessionId,
141
+ userMessage: "这篇不好",
142
+ });
143
+ const text = textOf(selection);
144
+ // Should NOT say "已选择:好"
145
+ assert.ok(!text.includes("已选择:好"));
146
+ });
147
+ });
148
+ //# sourceMappingURL=reviewContentWizard.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reviewContentWizard.test.js","sourceRoot":"","sources":["../../src/__tests__/reviewContentWizard.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=sanitize.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitize.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/sanitize.test.ts"],"names":[],"mappings":""}