@zihanw/pi-forge 0.3.2 → 0.4.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 (231) hide show
  1. package/CHANGELOG.md +196 -0
  2. package/PUBLIC_API.md +5 -0
  3. package/README.md +93 -478
  4. package/README.zh-CN.md +88 -461
  5. package/SUBAGENT_ADAPTER_CONTRACT.md +5 -0
  6. package/dist/agent-profile.d.ts +71 -0
  7. package/dist/agent-profile.d.ts.map +1 -0
  8. package/dist/agent-profile.js +303 -0
  9. package/dist/agent-profile.js.map +1 -0
  10. package/dist/forge-config.d.ts +88 -0
  11. package/dist/forge-config.d.ts.map +1 -0
  12. package/dist/forge-config.js +290 -0
  13. package/dist/forge-config.js.map +1 -0
  14. package/dist/forge-extensions.d.ts.map +1 -1
  15. package/dist/forge-extensions.js +19 -3
  16. package/dist/forge-extensions.js.map +1 -1
  17. package/dist/index.d.ts +4 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +97 -358
  20. package/dist/index.js.map +1 -1
  21. package/dist/lifecycle.d.ts +11 -3
  22. package/dist/lifecycle.d.ts.map +1 -1
  23. package/dist/lifecycle.js +90 -4
  24. package/dist/lifecycle.js.map +1 -1
  25. package/dist/loader.d.ts.map +1 -1
  26. package/dist/loader.js +116 -5
  27. package/dist/loader.js.map +1 -1
  28. package/dist/payload-capture.d.ts.map +1 -1
  29. package/dist/payload-capture.js +27 -0
  30. package/dist/payload-capture.js.map +1 -1
  31. package/dist/payload-command.d.ts +3 -3
  32. package/dist/payload-command.d.ts.map +1 -1
  33. package/dist/payload-command.js.map +1 -1
  34. package/dist/preview.d.ts +2 -2
  35. package/dist/preview.d.ts.map +1 -1
  36. package/dist/preview.js +2 -3
  37. package/dist/preview.js.map +1 -1
  38. package/dist/profile-command.d.ts +12 -0
  39. package/dist/profile-command.d.ts.map +1 -0
  40. package/dist/profile-command.js +291 -0
  41. package/dist/profile-command.js.map +1 -0
  42. package/dist/profile-service.d.ts +103 -0
  43. package/dist/profile-service.d.ts.map +1 -0
  44. package/dist/profile-service.js +220 -0
  45. package/dist/profile-service.js.map +1 -0
  46. package/dist/runtime/profile-runtime.d.ts +13 -0
  47. package/dist/runtime/profile-runtime.d.ts.map +1 -0
  48. package/dist/runtime/profile-runtime.js +47 -0
  49. package/dist/runtime/profile-runtime.js.map +1 -0
  50. package/dist/runtime/prompt-stack-runtime.d.ts +22 -0
  51. package/dist/runtime/prompt-stack-runtime.d.ts.map +1 -0
  52. package/dist/runtime/prompt-stack-runtime.js +104 -0
  53. package/dist/runtime/prompt-stack-runtime.js.map +1 -0
  54. package/dist/runtime/subagent-runtime.d.ts +45 -0
  55. package/dist/runtime/subagent-runtime.d.ts.map +1 -0
  56. package/dist/runtime/subagent-runtime.js +335 -0
  57. package/dist/runtime/subagent-runtime.js.map +1 -0
  58. package/dist/runtime/tool-policy-runtime.d.ts +15 -0
  59. package/dist/runtime/tool-policy-runtime.d.ts.map +1 -0
  60. package/dist/runtime/tool-policy-runtime.js +170 -0
  61. package/dist/runtime/tool-policy-runtime.js.map +1 -0
  62. package/dist/runtime/web-editor-runtime.d.ts +9 -0
  63. package/dist/runtime/web-editor-runtime.d.ts.map +1 -0
  64. package/dist/runtime/web-editor-runtime.js +131 -0
  65. package/dist/runtime/web-editor-runtime.js.map +1 -0
  66. package/dist/runtime-state.d.ts +4 -0
  67. package/dist/runtime-state.d.ts.map +1 -1
  68. package/dist/runtime-state.js +2 -0
  69. package/dist/runtime-state.js.map +1 -1
  70. package/dist/storage.d.ts +4 -0
  71. package/dist/storage.d.ts.map +1 -1
  72. package/dist/storage.js +24 -1
  73. package/dist/storage.js.map +1 -1
  74. package/dist/subagent/canonical.d.ts +22 -0
  75. package/dist/subagent/canonical.d.ts.map +1 -0
  76. package/dist/subagent/canonical.js +24 -0
  77. package/dist/subagent/canonical.js.map +1 -0
  78. package/dist/subagent/context.d.ts +8 -0
  79. package/dist/subagent/context.d.ts.map +1 -0
  80. package/dist/subagent/context.js +125 -0
  81. package/dist/subagent/context.js.map +1 -0
  82. package/dist/subagent/contract.d.ts +10 -0
  83. package/dist/subagent/contract.d.ts.map +1 -0
  84. package/dist/subagent/contract.js +10 -0
  85. package/dist/subagent/contract.js.map +1 -0
  86. package/dist/subagent/index.d.ts +14 -0
  87. package/dist/subagent/index.d.ts.map +1 -0
  88. package/dist/subagent/index.js +14 -0
  89. package/dist/subagent/index.js.map +1 -0
  90. package/dist/subagent/plan.d.ts +18 -0
  91. package/dist/subagent/plan.d.ts.map +1 -0
  92. package/dist/subagent/plan.js +157 -0
  93. package/dist/subagent/plan.js.map +1 -0
  94. package/dist/subagent/preflight.d.ts +4 -0
  95. package/dist/subagent/preflight.d.ts.map +1 -0
  96. package/dist/subagent/preflight.js +108 -0
  97. package/dist/subagent/preflight.js.map +1 -0
  98. package/dist/subagent/request.d.ts +4 -0
  99. package/dist/subagent/request.d.ts.map +1 -0
  100. package/dist/subagent/request.js +122 -0
  101. package/dist/subagent/request.js.map +1 -0
  102. package/dist/subagent/response.d.ts +8 -0
  103. package/dist/subagent/response.d.ts.map +1 -0
  104. package/dist/subagent/response.js +155 -0
  105. package/dist/subagent/response.js.map +1 -0
  106. package/dist/subagent/tools.d.ts +4 -0
  107. package/dist/subagent/tools.d.ts.map +1 -0
  108. package/dist/subagent/tools.js +42 -0
  109. package/dist/subagent/tools.js.map +1 -0
  110. package/dist/subagent/types.d.ts +268 -0
  111. package/dist/subagent/types.d.ts.map +1 -0
  112. package/dist/subagent/types.js +3 -0
  113. package/dist/subagent/types.js.map +1 -0
  114. package/dist/subagent/validation.d.ts +35 -0
  115. package/dist/subagent/validation.d.ts.map +1 -0
  116. package/dist/subagent/validation.js +314 -0
  117. package/dist/subagent/validation.js.map +1 -0
  118. package/dist/subagent-command.d.ts +4 -0
  119. package/dist/subagent-command.d.ts.map +1 -0
  120. package/dist/subagent-command.js +246 -0
  121. package/dist/subagent-command.js.map +1 -0
  122. package/dist/subagent-host.d.ts +44 -0
  123. package/dist/subagent-host.d.ts.map +1 -0
  124. package/dist/subagent-host.js +292 -0
  125. package/dist/subagent-host.js.map +1 -0
  126. package/dist/subagent-profile-tool.d.ts +49 -0
  127. package/dist/subagent-profile-tool.d.ts.map +1 -0
  128. package/dist/subagent-profile-tool.js +124 -0
  129. package/dist/subagent-profile-tool.js.map +1 -0
  130. package/dist/subagent-tool.d.ts +53 -0
  131. package/dist/subagent-tool.d.ts.map +1 -0
  132. package/dist/subagent-tool.js +456 -0
  133. package/dist/subagent-tool.js.map +1 -0
  134. package/dist/web-editor/client-script.d.ts +2 -0
  135. package/dist/web-editor/client-script.d.ts.map +1 -0
  136. package/dist/web-editor/client-script.generated.d.ts +2 -0
  137. package/dist/web-editor/client-script.generated.d.ts.map +1 -0
  138. package/dist/web-editor/client-script.generated.js +3 -0
  139. package/dist/web-editor/client-script.generated.js.map +1 -0
  140. package/dist/web-editor/client-script.js +2 -0
  141. package/dist/web-editor/client-script.js.map +1 -0
  142. package/dist/web-editor/client-styles.d.ts +2 -0
  143. package/dist/web-editor/client-styles.d.ts.map +1 -0
  144. package/dist/web-editor/client-styles.generated.d.ts +2 -0
  145. package/dist/web-editor/client-styles.generated.d.ts.map +1 -0
  146. package/dist/web-editor/client-styles.generated.js +3 -0
  147. package/dist/web-editor/client-styles.generated.js.map +1 -0
  148. package/dist/web-editor/client-styles.js +2 -0
  149. package/dist/web-editor/client-styles.js.map +1 -0
  150. package/dist/web-editor/page.d.ts +2 -0
  151. package/dist/web-editor/page.d.ts.map +1 -1
  152. package/dist/web-editor/page.js +14 -3319
  153. package/dist/web-editor/page.js.map +1 -1
  154. package/dist/web-editor/server.d.ts.map +1 -1
  155. package/dist/web-editor/server.js +148 -0
  156. package/dist/web-editor/server.js.map +1 -1
  157. package/dist/web-editor/styles.d.ts +2 -0
  158. package/dist/web-editor/styles.d.ts.map +1 -0
  159. package/dist/web-editor/styles.js +1053 -0
  160. package/dist/web-editor/styles.js.map +1 -0
  161. package/dist/web-editor/types.d.ts +79 -0
  162. package/dist/web-editor/types.d.ts.map +1 -1
  163. package/dist/web-host.d.ts +16 -5
  164. package/dist/web-host.d.ts.map +1 -1
  165. package/dist/web-host.js +307 -0
  166. package/dist/web-host.js.map +1 -1
  167. package/docs/README.md +41 -0
  168. package/docs/concepts/agent-profiles.md +60 -0
  169. package/docs/concepts/prompt-stacks.md +90 -0
  170. package/docs/design/README.md +17 -0
  171. package/docs/design/roadmap-0.4-archive.md +216 -0
  172. package/docs/design/subagents/design-review.md +220 -0
  173. package/docs/design/subagents/interface-design.md +274 -0
  174. package/docs/design/subagents/sdk-spike-findings.md +117 -0
  175. package/docs/development/complexity-review.md +86 -0
  176. package/docs/development/release.md +31 -0
  177. package/docs/development/roadmap.md +42 -0
  178. package/docs/development/setup.md +75 -0
  179. package/docs/getting-started.md +93 -0
  180. package/docs/guides/custom-macros-and-slots.md +68 -0
  181. package/docs/guides/debugging.md +39 -0
  182. package/docs/guides/delegation.md +99 -0
  183. package/docs/guides/sillytavern-import.md +47 -0
  184. package/docs/guides/use-cases.md +65 -0
  185. package/docs/guides/web-editor.md +75 -0
  186. package/docs/reference/commands.md +60 -0
  187. package/docs/reference/configuration.md +64 -0
  188. package/docs/reference/features.md +279 -0
  189. package/docs/reference/macros-and-slots.md +82 -0
  190. package/docs/reference/public-api.md +28 -0
  191. package/docs/reference/stack-schema.md +167 -0
  192. package/docs/reference/subagent-adapter.md +204 -0
  193. package/docs/zh-CN/README.md +37 -0
  194. package/docs/zh-CN/concepts/agent-profiles.md +44 -0
  195. package/docs/zh-CN/concepts/prompt-stacks.md +40 -0
  196. package/docs/zh-CN/getting-started.md +79 -0
  197. package/docs/zh-CN/guides/delegation.md +66 -0
  198. package/docs/zh-CN/guides/web-editor.md +45 -0
  199. package/docs/zh-CN/reference/commands.md +58 -0
  200. package/package.json +55 -13
  201. package/src/compiler.ts +0 -578
  202. package/src/extension-registry.ts +0 -33
  203. package/src/forge-extensions.ts +0 -223
  204. package/src/index.ts +0 -445
  205. package/src/lifecycle.ts +0 -171
  206. package/src/loader.ts +0 -394
  207. package/src/macro-engine.ts +0 -358
  208. package/src/payload-capture.ts +0 -85
  209. package/src/payload-command.ts +0 -138
  210. package/src/policy.ts +0 -42
  211. package/src/preset-command.ts +0 -280
  212. package/src/preview.ts +0 -226
  213. package/src/regex.ts +0 -500
  214. package/src/render-helpers.ts +0 -169
  215. package/src/runtime-state.ts +0 -40
  216. package/src/sillytavern-importer/items.ts +0 -98
  217. package/src/sillytavern-importer/macros.ts +0 -159
  218. package/src/sillytavern-importer/prompt-order.ts +0 -54
  219. package/src/sillytavern-importer/regex.ts +0 -270
  220. package/src/sillytavern-importer/report.ts +0 -202
  221. package/src/sillytavern-importer/types.ts +0 -120
  222. package/src/sillytavern-importer.ts +0 -152
  223. package/src/slot-renderers.ts +0 -414
  224. package/src/stack-migration.ts +0 -159
  225. package/src/storage.ts +0 -45
  226. package/src/types.ts +0 -209
  227. package/src/web-editor/index.ts +0 -2
  228. package/src/web-editor/page.ts +0 -3330
  229. package/src/web-editor/server.ts +0 -294
  230. package/src/web-editor/types.ts +0 -98
  231. package/src/web-host.ts +0 -232
@@ -1,202 +0,0 @@
1
- import type { PromptStackItem } from "../types.ts";
2
- import {
3
- displayMacroName,
4
- MACRO_NEEDS_MIGRATION,
5
- type MacroDetection,
6
- NATIVE_MACRO_NOTES,
7
- NATIVE_MACROS,
8
- } from "./macros.ts";
9
- import type { PromptConversionDisabledCounts, RegexScriptReport } from "./types.ts";
10
-
11
- export interface SillyTavernImportReportInput {
12
- fileName: string;
13
- sourcePath: string;
14
- characterId: number;
15
- stackId: string;
16
- styleName: string;
17
- totalPrompts: number;
18
- orderItemCount: number;
19
- items: PromptStackItem[];
20
- macroUsage: MacroDetection;
21
- disabledCount: PromptConversionDisabledCounts;
22
- missingIdentifiers: string[];
23
- regexScripts?: RegexScriptReport;
24
- variables: Record<string, string>;
25
- usesLastUserMessage: boolean;
26
- }
27
-
28
- export function buildSillyTavernImportReport(input: SillyTavernImportReportInput): string {
29
- const reportLines: string[] = [];
30
- reportLines.push(`# SillyTavern Import Report: ${input.fileName}`);
31
- reportLines.push("");
32
- reportLines.push(`- **Source file**: ${input.sourcePath}`);
33
- reportLines.push(`- **Character ID**: ${input.characterId}`);
34
- reportLines.push(`- **Output stack ID**: ${input.stackId}`);
35
- reportLines.push(`- **Names behavior**: ${input.styleName}`);
36
- reportLines.push(`- **Total prompts in source**: ${input.totalPrompts}`);
37
- reportLines.push(`- **Items in prompt_order**: ${input.orderItemCount}`);
38
- reportLines.push(`- **Converted items**: ${input.items.length}`);
39
- reportLines.push("");
40
-
41
- if (input.macroUsage.commentsStripped > 0) {
42
- reportLines.push(`- **Comments stripped**: ${input.macroUsage.commentsStripped} ST comment blocks removed`);
43
- }
44
- if (input.macroUsage.trimStripped > 0) {
45
- reportLines.push(`- **Trim markers removed**: ${input.macroUsage.trimStripped} {{trim}} markers (ST formatting hint)`);
46
- }
47
- reportLines.push("");
48
-
49
- if (input.disabledCount.marker > 0 || input.disabledCount.empty > 0 || input.disabledCount.orderDisable > 0) {
50
- reportLines.push("## Skipped items");
51
- reportLines.push("");
52
- if (input.disabledCount.marker > 0) {
53
- reportLines.push(`- ${input.disabledCount.marker} marker items (charDescription, worldInfo, etc.) — handled by Pi/ST runtime, not needed in prompt stack`);
54
- }
55
- if (input.disabledCount.empty > 0) {
56
- reportLines.push(`- ${input.disabledCount.empty} items with empty or missing content`);
57
- }
58
- if (input.disabledCount.orderDisable > 0) {
59
- reportLines.push(`- ${input.disabledCount.orderDisable} items disabled in prompt_order`);
60
- }
61
- reportLines.push("");
62
- }
63
-
64
- if (input.missingIdentifiers.length > 0) {
65
- reportLines.push("## ⚠️ Missing identifiers in prompts array");
66
- reportLines.push("");
67
- for (const id of input.missingIdentifiers) {
68
- reportLines.push(`- \`${id}\` (referenced in prompt_order but not found in prompts)`);
69
- }
70
- reportLines.push("");
71
- }
72
-
73
- if (input.regexScripts && input.regexScripts.total > 0) {
74
- reportLines.push("## SillyTavern regex scripts");
75
- reportLines.push("");
76
- reportLines.push(`- **Total scripts**: ${input.regexScripts.total}`);
77
- reportLines.push(`- **Enabled scripts**: ${input.regexScripts.enabled}`);
78
- reportLines.push(`- **Disabled scripts**: ${input.regexScripts.disabled}`);
79
- reportLines.push(`- **Prompt-only**: ${input.regexScripts.promptOnly}`);
80
- reportLines.push(`- **Markdown-only / display-only**: ${input.regexScripts.markdownOnly}`);
81
- reportLines.push(`- **Prompt + markdown mixed**: ${input.regexScripts.mixed}`);
82
- if (input.regexScripts.unspecified > 0) {
83
- reportLines.push(`- **Enabled with unspecified mode**: ${input.regexScripts.unspecified}`);
84
- }
85
- reportLines.push(`- **Converted to pi-forge rules**: ${input.regexScripts.converted}`);
86
- reportLines.push("");
87
- reportLines.push("| Script | Enabled | Mode | Converted | Warnings | Find regex | Replacement preview |");
88
- reportLines.push("|--------|---------|------|-----------|----------|------------|---------------------|");
89
- for (const script of input.regexScripts.scripts) {
90
- const converted = script.convertedRuleId ? `yes: ${script.convertedRuleId}` : script.conversionNote ?? "no";
91
- const warnings = script.conversionWarnings.length > 0 ? script.conversionWarnings.join("; ") : "-";
92
- reportLines.push(`| ${markdownTableCell(script.name)} | ${script.enabled ? "yes" : "no"} | ${script.mode} | ${markdownTableCell(converted)} | ${markdownTableCell(warnings)} | ${markdownTableCell(script.findRegex)} | ${markdownTableCell(script.replaceString)} |`);
93
- }
94
- reportLines.push("");
95
- const warningEntries = input.regexScripts.scripts.filter((script) => script.conversionWarnings.length > 0);
96
- if (warningEntries.length > 0) {
97
- reportLines.push("Conversion warnings:");
98
- for (const script of warningEntries) {
99
- for (const warning of script.conversionWarnings) {
100
- reportLines.push(`- ${script.name}: ${warning}`);
101
- }
102
- }
103
- reportLines.push("");
104
- }
105
- reportLines.push("pi-forge does not run SillyTavern markdown rewriting, DOM/browser automation, CSS/HTML decoration, toasts, embedded JavaScript, or UI panel behavior.");
106
- if (input.regexScripts.converted > 0) {
107
- reportLines.push("Enabled and disabled prompt-only deterministic regex scripts were converted to `regex.rules` with `stage: \"history\"`, `effect: \"outgoing\"`, JavaScript replacement syntax, and preserved SillyTavern metadata under `source.sillytavern`. History-stage depth is relative to the filtered chat history inserted at the `chat-history` slot, matching SillyTavern's chat-relative depth, and rules do not touch the compiled system prompt. Use `stage: \"compiled\"` manually for global prompt transforms.");
108
- }
109
- reportLines.push("Markdown-only, mixed prompt/markdown, unspecified, invalid, or unsupported regex scripts remain report-only and require manual review.");
110
- reportLines.push("");
111
- }
112
-
113
- if (Object.keys(input.variables).length > 0) {
114
- reportLines.push("## Auto-populated variables");
115
- reportLines.push("");
116
- reportLines.push("These ST built-in macros were auto-populated as static variables with placeholder values:");
117
- reportLines.push("");
118
- for (const [key, val] of Object.entries(input.variables)) {
119
- reportLines.push(`- \`${key}\` = \`${val}\` — replace with your character/persona name`);
120
- }
121
- reportLines.push("");
122
- reportLines.push("Edit `stack.variables` in the stack JSON or web editor to set real values.");
123
- reportLines.push("");
124
- }
125
-
126
- const migrationEntries = Object.entries(input.macroUsage.migrationNeeded).sort(([, a], [, b]) => b - a);
127
- if (migrationEntries.length > 0) {
128
- reportLines.push("## ⚠️ Macros needing manual migration");
129
- reportLines.push("");
130
- reportLines.push("These ST macros appear in the preset but have no direct pi-forge equivalent:");
131
- reportLines.push("");
132
- for (const [name, count] of migrationEntries) {
133
- const note = MACRO_NEEDS_MIGRATION[name] ?? "no mapping available";
134
- reportLines.push(`- **\`{{${displayMacroName(name)}}}\`** (${count} occurrence${count > 1 ? "s" : ""}) — ${note}`);
135
- }
136
- reportLines.push("");
137
- }
138
-
139
- const nativeDetected = [...input.macroUsage.detected].filter((macro) => NATIVE_MACROS.has(macro));
140
- if (nativeDetected.length > 0) {
141
- reportLines.push("## Handled macros");
142
- reportLines.push("");
143
- for (const name of nativeDetected) {
144
- const displayName = displayMacroName(name);
145
- if (name === "char" || name === "user") {
146
- reportLines.push(`- \`{{${displayName}}}\` → auto-populated as static variable`);
147
- } else if (name === "lastusermessage") {
148
- reportLines.push(`- \`{{${displayName}}}\` → handled by pi-forge runtime (chat-history slot)`);
149
- } else if (NATIVE_MACRO_NOTES[name]) {
150
- reportLines.push(`- \`{{${displayName}}}\` → ${NATIVE_MACRO_NOTES[name]}`);
151
- } else {
152
- reportLines.push(`- \`{{${displayName}}}\` → handled natively by pi-forge`);
153
- }
154
- }
155
- reportLines.push("");
156
- }
157
-
158
- reportLines.push("## Item mapping");
159
- reportLines.push("");
160
- reportLines.push("| # | ST Identifier | ST Name | pi-forge ID | Role | Enabled | Kind |");
161
- reportLines.push("|---|--------------|---------|-------------|------|---------|------|");
162
-
163
- for (const item of input.items) {
164
- const stId = (item.source as Record<string, unknown> | undefined)?.previousId ?? "—";
165
- const stName = item.name ?? "—";
166
- const enabled = item.enabled !== false ? "✓" : "✗";
167
- const kind = item.kind;
168
- reportLines.push(`| ${item.id} | ${stId} | ${stName} | ${item.id} | ${item.role ?? "—"} | ${enabled} | ${kind} |`);
169
- }
170
-
171
- reportLines.push("");
172
- reportLines.push("## General notes");
173
- reportLines.push("");
174
-
175
- if (input.usesLastUserMessage) {
176
- reportLines.push("- Auto-detected `{{lastUserMessage}}` in post-history content. `chat-history` slot set with `includeLastUserMessage: false`.");
177
- }
178
-
179
- if (input.macroUsage.commentsStripped > 0 || input.macroUsage.trimStripped > 0) {
180
- reportLines.push(`- ${input.macroUsage.commentsStripped} ST comment blocks and ${input.macroUsage.trimStripped} TRIM markers were stripped during import.`);
181
- }
182
-
183
- reportLines.push("- All items are assigned sequential numeric IDs. Original SillyTavern identifiers are preserved in `source.previousId`.");
184
- reportLines.push("- Marker items for world info, character description, persona, scenario, and dialogue examples are omitted — these are handled by the SillyTavern frontend and have no direct pi-forge equivalent.");
185
- reportLines.push(`- The stack is set with \`autoActivate: false\`. Use \`/preset use ${input.stackId}\` to activate.`);
186
- reportLines.push("");
187
- reportLines.push("## Suggested next steps");
188
- reportLines.push("");
189
- reportLines.push("1. Set real values for auto-populated variables by editing `stack.variables`.");
190
- reportLines.push("2. Review items with migration-needed macros and rewrite for pi-forge's macro system.");
191
- reportLines.push("3. Consider adding a `variables` slot for template variable visibility.");
192
- reportLines.push(`4. Run \`/preset validate ${input.stackId}\` to check for issues.`);
193
-
194
- return reportLines.join("\n");
195
- }
196
-
197
- function markdownTableCell(value: unknown): string {
198
- if (typeof value !== "string" || !value.trim()) return "-";
199
- const collapsed = value.replace(/\s+/g, " ").trim();
200
- const truncated = collapsed.length > 96 ? `${collapsed.slice(0, 93)}...` : collapsed;
201
- return truncated.replace(/\|/g, "\\|");
202
- }
@@ -1,120 +0,0 @@
1
- import type { PromptRegexRule, PromptStack, PromptStackItem } from "../types.ts";
2
-
3
- export interface StPreset {
4
- prompts?: StPromptDef[];
5
- prompt_order?: StPromptOrderEntry[];
6
- extensions?: {
7
- regex_scripts?: unknown[];
8
- [key: string]: unknown;
9
- };
10
- names_behavior?: number;
11
- preset_name?: string;
12
- name?: string;
13
- [key: string]: unknown;
14
- }
15
-
16
- export interface StPromptDef {
17
- identifier: string;
18
- name?: string;
19
- role?: string;
20
- content?: string;
21
- system_prompt?: boolean;
22
- marker?: boolean;
23
- enabled?: boolean;
24
- }
25
-
26
- export interface StPromptOrderEntry {
27
- character_id: number;
28
- order?: StOrderItem[];
29
- }
30
-
31
- export interface StOrderItem {
32
- identifier: string;
33
- enabled: boolean;
34
- }
35
-
36
- export interface StRegexScript {
37
- script_name?: string;
38
- scriptName?: string;
39
- name?: string;
40
- disabled?: boolean;
41
- promptOnly?: boolean;
42
- markdownOnly?: boolean;
43
- findRegex?: string;
44
- replaceString?: string;
45
- trimStrings?: unknown;
46
- substituteRegex?: unknown;
47
- placement?: unknown;
48
- minDepth?: unknown;
49
- maxDepth?: unknown;
50
- runOnEdit?: unknown;
51
- [key: string]: unknown;
52
- }
53
-
54
- export interface StConversionItem {
55
- def: StPromptDef;
56
- orderEnabled: boolean;
57
- orderIndex: number;
58
- }
59
-
60
- export type RegexScriptMode = "prompt-only" | "markdown-only" | "prompt+markdown" | "unspecified" | "disabled";
61
-
62
- export interface RegexScriptReportEntry {
63
- name: string;
64
- mode: RegexScriptMode;
65
- enabled: boolean;
66
- promptOnly: boolean;
67
- markdownOnly: boolean;
68
- findRegex?: string;
69
- replaceString?: string;
70
- trimStrings?: string[];
71
- placement?: number[];
72
- substituteRegex?: number;
73
- minDepth?: number;
74
- maxDepth?: number;
75
- convertedRuleId?: string;
76
- conversionNote?: string;
77
- conversionWarnings: string[];
78
- }
79
-
80
- export interface RegexScriptReport {
81
- total: number;
82
- enabled: number;
83
- disabled: number;
84
- promptOnly: number;
85
- markdownOnly: number;
86
- mixed: number;
87
- unspecified: number;
88
- converted: number;
89
- scripts: RegexScriptReportEntry[];
90
- rules: PromptRegexRule[];
91
- }
92
-
93
- export interface PromptConversionDisabledCounts {
94
- marker: number;
95
- empty: number;
96
- orderDisable: number;
97
- }
98
-
99
- export interface PromptConversionResult {
100
- items: PromptStackItem[];
101
- usesLastUserMessage: boolean;
102
- disabledCount: PromptConversionDisabledCounts;
103
- }
104
-
105
- export interface SillyTavernImportResult {
106
- stack: PromptStack;
107
- report: string;
108
- }
109
-
110
- export interface SillyTavernImportError {
111
- error: string;
112
- }
113
-
114
- export type SillyTavernImportOutcome = SillyTavernImportResult | SillyTavernImportError;
115
-
116
- export interface SillyTavernConvertOptions {
117
- sourceName?: string;
118
- sourcePath?: string;
119
- characterId?: number;
120
- }
@@ -1,152 +0,0 @@
1
- import { readFileSync } from "node:fs";
2
- import { basename } from "node:path";
3
- import { convertPromptItems } from "./sillytavern-importer/items.ts";
4
- import { detectMacros } from "./sillytavern-importer/macros.ts";
5
- import {
6
- buildConversionItems,
7
- buildPromptMap,
8
- findMissingIdentifiers,
9
- selectCharacterEntry,
10
- } from "./sillytavern-importer/prompt-order.ts";
11
- import { summarizeRegexScripts } from "./sillytavern-importer/regex.ts";
12
- import { buildSillyTavernImportReport } from "./sillytavern-importer/report.ts";
13
- import type {
14
- SillyTavernConvertOptions,
15
- SillyTavernImportOutcome,
16
- StPreset,
17
- } from "./sillytavern-importer/types.ts";
18
- import type { PromptStack } from "./types.ts";
19
-
20
- export type {
21
- SillyTavernConvertOptions,
22
- SillyTavernImportError,
23
- SillyTavernImportOutcome,
24
- SillyTavernImportResult,
25
- } from "./sillytavern-importer/types.ts";
26
-
27
- export function importSillyTavernPreset(
28
- filePath: string,
29
- characterId?: number,
30
- ): SillyTavernImportOutcome {
31
- let raw: unknown;
32
- try {
33
- raw = JSON.parse(readFileSync(filePath, "utf8"));
34
- } catch (err) {
35
- return { error: `Failed to read or parse ${filePath}: ${err instanceof Error ? err.message : String(err)}` };
36
- }
37
-
38
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
39
- return { error: "Preset root must be a JSON object." };
40
- }
41
-
42
- return convertSillyTavernPreset(raw, { sourceName: filePath, sourcePath: filePath, characterId });
43
- }
44
-
45
- export function convertSillyTavernPreset(
46
- raw: unknown,
47
- options: SillyTavernConvertOptions = {},
48
- ): SillyTavernImportOutcome {
49
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
50
- return { error: "Preset root must be a JSON object." };
51
- }
52
-
53
- const preset = raw as StPreset;
54
- const fallbackName = typeof preset.preset_name === "string" && preset.preset_name.trim()
55
- ? preset.preset_name
56
- : typeof preset.name === "string" && preset.name.trim()
57
- ? preset.name
58
- : "imported.json";
59
- const sourceName = options.sourceName ?? fallbackName;
60
- const sourcePath = options.sourcePath ?? sourceName;
61
- const allPrompts = preset.prompts ?? [];
62
- const promptOrder = preset.prompt_order ?? [];
63
- const regexScripts = summarizeRegexScripts(preset);
64
-
65
- if (allPrompts.length === 0) {
66
- return { error: "Preset has no prompts array." };
67
- }
68
-
69
- const selectedEntry = selectCharacterEntry(promptOrder, options.characterId);
70
- if (!selectedEntry) {
71
- const ids = promptOrder.map((entry) => entry.character_id);
72
- if (ids.length === 0) {
73
- return { error: "Preset has no prompt_order entries. Choose a character_id and retry." };
74
- }
75
- return {
76
- error: `Multiple character configs found: [${ids.join(", ")}]. Choose a character_id and retry.`,
77
- };
78
- }
79
-
80
- const promptMap = buildPromptMap(allPrompts);
81
- const conversionItems = buildConversionItems(selectedEntry, promptMap);
82
- const macroUsage = detectMacros(conversionItems);
83
- const { items, disabledCount, usesLastUserMessage: itemUsesLastUserMessage } = convertPromptItems(conversionItems);
84
- const missingIdentifiers = findMissingIdentifiers(selectedEntry, promptMap);
85
- const fileName = basename(sourceName).replace(/\.json$/i, "") || "imported";
86
- const styleName = preset.names_behavior === 1 ? "names" : preset.names_behavior === 2 ? "nonames" : "default";
87
- const stackId = normalizeStackId(fileName);
88
- const variables = buildStaticVariables(macroUsage.detected);
89
- let usesLastUserMessage = itemUsesLastUserMessage;
90
-
91
- const stack: PromptStack = {
92
- schemaVersion: 1,
93
- type: "pi-forge.prompt-stack",
94
- id: stackId,
95
- name: preset.preset_name || preset.name || fileName,
96
- autoActivate: false,
97
- mode: "replace",
98
- variables: Object.keys(variables).length > 0 ? variables : undefined,
99
- context: { allowDuplicateChatHistory: false },
100
- regex: regexScripts && regexScripts.rules.length > 0 ? { schemaVersion: 1, rules: regexScripts.rules } : undefined,
101
- items,
102
- import: {
103
- source: "sillytavern",
104
- sourceFile: fileName,
105
- characterId: selectedEntry.character_id,
106
- convertedAt: new Date().toISOString(),
107
- },
108
- };
109
-
110
- if (macroUsage.detected.has("lastusermessage")) usesLastUserMessage = true;
111
- if (usesLastUserMessage) {
112
- const chatHistoryItem = items.find(
113
- (item) => item.kind === "slot" && item.slot === "chat-history",
114
- );
115
- if (chatHistoryItem && chatHistoryItem.kind === "slot") {
116
- chatHistoryItem.options = { includeLastUserMessage: false };
117
- }
118
- }
119
-
120
- const report = buildSillyTavernImportReport({
121
- fileName,
122
- sourcePath,
123
- characterId: selectedEntry.character_id,
124
- stackId,
125
- styleName: `${styleName} (ST value ${preset.names_behavior ?? "?"})`,
126
- totalPrompts: allPrompts.length,
127
- orderItemCount: selectedEntry.order?.length ?? 0,
128
- items,
129
- macroUsage,
130
- disabledCount,
131
- missingIdentifiers,
132
- regexScripts,
133
- variables,
134
- usesLastUserMessage,
135
- });
136
-
137
- return {
138
- stack,
139
- report,
140
- };
141
- }
142
-
143
- function normalizeStackId(fileName: string): string {
144
- return fileName.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").toLowerCase() || "imported";
145
- }
146
-
147
- function buildStaticVariables(detectedMacros: Set<string>): Record<string, string> {
148
- const variables: Record<string, string> = {};
149
- if (detectedMacros.has("char")) variables.char = "{{char}}";
150
- if (detectedMacros.has("user")) variables.user = "{{user}}";
151
- return variables;
152
- }