@wrongstack/core 0.313.0 → 0.313.1

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 (182) hide show
  1. package/dist/chronicle/index.js +84 -6391
  2. package/dist/chronicle/project-server.js +84 -4868
  3. package/dist/chunk-2BUUIMQB.js +225 -0
  4. package/dist/chunk-2FK4WTFF.js +207 -0
  5. package/dist/chunk-2FQL4PDC.js +292 -0
  6. package/dist/chunk-2KKODIQU.js +2256 -0
  7. package/dist/chunk-2YU4WVBW.js +134 -0
  8. package/dist/chunk-3E244UDA.js +356 -0
  9. package/dist/chunk-3XPVB7BA.js +135 -0
  10. package/dist/chunk-4LJJYBAU.js +388 -0
  11. package/dist/chunk-4VWO3ZXL.js +342 -0
  12. package/dist/chunk-4XV6IR37.js +187 -0
  13. package/dist/chunk-4ZHTKJHM.js +183 -0
  14. package/dist/chunk-52RTVH4M.js +361 -0
  15. package/dist/chunk-57GEWK5X.js +26 -0
  16. package/dist/chunk-5DSMEVA4.js +931 -0
  17. package/dist/chunk-5RP3QTRL.js +1223 -0
  18. package/dist/chunk-5S437PRC.js +50 -0
  19. package/dist/chunk-5XDUGTKN.js +511 -0
  20. package/dist/chunk-62V2AFVG.js +422 -0
  21. package/dist/chunk-65RDIOR2.js +2470 -0
  22. package/dist/chunk-6AKOTGLM.js +597 -0
  23. package/dist/chunk-7ZHZE5EO.js +28 -0
  24. package/dist/chunk-A455ATZB.js +140 -0
  25. package/dist/chunk-AD7ZFZCS.js +46 -0
  26. package/dist/chunk-AI4LLQ2U.js +856 -0
  27. package/dist/chunk-ASX6AW36.js +789 -0
  28. package/dist/chunk-AT4V4XGI.js +3522 -0
  29. package/dist/chunk-AWNQ6ED6.js +311 -0
  30. package/dist/chunk-B4AMLZPB.js +9 -0
  31. package/dist/chunk-BAKVJLEV.js +417 -0
  32. package/dist/chunk-BJHVULHO.js +55 -0
  33. package/dist/chunk-CB2HFKTP.js +9 -0
  34. package/dist/chunk-CE4ZOCOI.js +1640 -0
  35. package/dist/chunk-CQQL4ZOQ.js +151 -0
  36. package/dist/chunk-DEIQMBD2.js +65 -0
  37. package/dist/chunk-E6I5RXVK.js +58 -0
  38. package/dist/chunk-EFEHDQGY.js +1589 -0
  39. package/dist/chunk-EODDVJBM.js +82 -0
  40. package/dist/chunk-EPC3GZD4.js +308 -0
  41. package/dist/chunk-FAYRX2AD.js +13 -0
  42. package/dist/chunk-FFSX2QDQ.js +362 -0
  43. package/dist/chunk-FHAMQS7Q.js +5856 -0
  44. package/dist/chunk-FIV4IJL7.js +2263 -0
  45. package/dist/chunk-FNXVT7HF.js +52 -0
  46. package/dist/chunk-FSWCWSTS.js +395 -0
  47. package/dist/chunk-FW5LDDMJ.js +16464 -0
  48. package/dist/chunk-FXQLIX3K.js +211 -0
  49. package/dist/chunk-G5AAKEEZ.js +228 -0
  50. package/dist/chunk-G5S3S5ZN.js +38 -0
  51. package/dist/chunk-GAZEWQEZ.js +231 -0
  52. package/dist/chunk-GFJOMSHG.js +125 -0
  53. package/dist/chunk-GFXZTHNI.js +158 -0
  54. package/dist/chunk-GM54GJBU.js +960 -0
  55. package/dist/chunk-GPQ3NJPR.js +900 -0
  56. package/dist/chunk-GTCU6DZ3.js +1 -0
  57. package/dist/chunk-H5E77KUJ.js +15 -0
  58. package/dist/chunk-H72H4XDU.js +1023 -0
  59. package/dist/chunk-HA4ZYA6T.js +178 -0
  60. package/dist/chunk-I45KAW2N.js +5020 -0
  61. package/dist/chunk-IQ35HMQL.js +1396 -0
  62. package/dist/chunk-IYFBFX34.js +86 -0
  63. package/dist/chunk-JJPMWPB6.js +109 -0
  64. package/dist/chunk-JTHKOYWU.js +663 -0
  65. package/dist/chunk-JVGPR3TS.js +365 -0
  66. package/dist/chunk-JVKB2TBL.js +48 -0
  67. package/dist/chunk-JX7VBZDK.js +31 -0
  68. package/dist/chunk-K3G6JBUC.js +5232 -0
  69. package/dist/chunk-K4YUNKU7.js +589 -0
  70. package/dist/chunk-KBZGE6LN.js +710 -0
  71. package/dist/chunk-KDQJCT2J.js +45 -0
  72. package/dist/chunk-KI4L2DUL.js +560 -0
  73. package/dist/chunk-KJ2ZTQSL.js +47 -0
  74. package/dist/chunk-KSJ5UTP7.js +154 -0
  75. package/dist/chunk-LHOA5DWA.js +28 -0
  76. package/dist/chunk-LX5QWLBL.js +27 -0
  77. package/dist/chunk-LZDDLNCZ.js +279 -0
  78. package/dist/chunk-M2WF6AXG.js +115 -0
  79. package/dist/chunk-M7BS2K6U.js +1 -0
  80. package/dist/chunk-NDBIPFNY.js +102 -0
  81. package/dist/chunk-NEMIWEXC.js +1199 -0
  82. package/dist/chunk-NFAYHPRH.js +323 -0
  83. package/dist/chunk-O37BQ65S.js +21 -0
  84. package/dist/chunk-O74MJHXU.js +187 -0
  85. package/dist/chunk-OJZEX73F.js +112 -0
  86. package/dist/chunk-PAAWLLZX.js +74 -0
  87. package/dist/chunk-PFXFQQRT.js +339 -0
  88. package/dist/chunk-PFZO3C6H.js +14 -0
  89. package/dist/chunk-PHH7LD45.js +113 -0
  90. package/dist/chunk-QRTG4XTI.js +188 -0
  91. package/dist/chunk-QVOVMXJR.js +1351 -0
  92. package/dist/chunk-RCQYT2I4.js +757 -0
  93. package/dist/chunk-RD6DEE36.js +1178 -0
  94. package/dist/chunk-RJIZEJ42.js +30 -0
  95. package/dist/chunk-RPOA7IAE.js +150 -0
  96. package/dist/chunk-SN2DQRL6.js +1172 -0
  97. package/dist/chunk-SQJVFEBV.js +41 -0
  98. package/dist/chunk-SQP64RCS.js +5391 -0
  99. package/dist/chunk-SVT4RNTR.js +49 -0
  100. package/dist/chunk-SWUFN7XM.js +43 -0
  101. package/dist/chunk-SXMVYC5H.js +174 -0
  102. package/dist/chunk-SZ6AYUIZ.js +23 -0
  103. package/dist/chunk-T3KGZBU5.js +3171 -0
  104. package/dist/chunk-TBRL2RCC.js +9 -0
  105. package/dist/chunk-TRT2HRWZ.js +18 -0
  106. package/dist/chunk-TYW2GI5I.js +1077 -0
  107. package/dist/chunk-UIWONWVG.js +4959 -0
  108. package/dist/chunk-VB3H5WWB.js +151 -0
  109. package/dist/chunk-W4A6QA3D.js +258 -0
  110. package/dist/chunk-WBPVKEEE.js +227 -0
  111. package/dist/chunk-WEBMSQP2.js +123 -0
  112. package/dist/chunk-WOEZV3Q4.js +333 -0
  113. package/dist/chunk-WQQON3SV.js +473 -0
  114. package/dist/chunk-WQTOVMGV.js +119 -0
  115. package/dist/chunk-WZO23AWO.js +2779 -0
  116. package/dist/chunk-XBDP4QPW.js +7 -0
  117. package/dist/chunk-XEBHSJLT.js +103 -0
  118. package/dist/chunk-XQ2ECNK3.js +1 -0
  119. package/dist/chunk-XQ2XGIBF.js +2086 -0
  120. package/dist/chunk-Y2GE5TSI.js +355 -0
  121. package/dist/chunk-YBUJCZ57.js +351 -0
  122. package/dist/chunk-YNCVSFGF.js +149 -0
  123. package/dist/chunk-YUPMXOGP.js +282 -0
  124. package/dist/chunk-YY6APYUM.js +49 -0
  125. package/dist/chunk-Z2IJUO45.js +691 -0
  126. package/dist/chunk-ZEHUCJHY.js +129 -0
  127. package/dist/chunk-ZYU2YOJ6.js +1496 -0
  128. package/dist/chunk-ZZ4V3WIZ.js +163 -0
  129. package/dist/coordination/agents/index.js +145 -6260
  130. package/dist/coordination/index.js +365 -34324
  131. package/dist/coordination/mailbox-project-server.js +48 -3276
  132. package/dist/coordination/provider-status-tracker.d.ts +4 -0
  133. package/dist/core/agent.d.ts +14 -1
  134. package/dist/core/index.js +267 -12480
  135. package/dist/core/model-ref.js +5 -23
  136. package/dist/core/provider-runner.d.ts +36 -1
  137. package/dist/design/index.js +50 -1087
  138. package/dist/execution/index.d.ts +1 -1
  139. package/dist/execution/index.js +1599 -21888
  140. package/dist/execution/prompt-enhancer.js +16 -535
  141. package/dist/execution/retry-policy.d.ts +31 -1
  142. package/dist/extension/index.js +7 -311
  143. package/dist/goal/index.js +33 -3435
  144. package/dist/hooks/index.js +14 -1138
  145. package/dist/hq/index.js +112 -5139
  146. package/dist/hq/protocol.js +21 -668
  147. package/dist/index.js +1410 -92656
  148. package/dist/infrastructure/index.js +64 -4811
  149. package/dist/kernel/events/provider-events.d.ts +15 -0
  150. package/dist/kernel/index.js +14 -992
  151. package/dist/models/index.js +40 -1117
  152. package/dist/notifications/index.js +3 -108
  153. package/dist/observability/index.js +17 -279
  154. package/dist/plugin/index.js +584 -14835
  155. package/dist/prompts/index.js +14 -948
  156. package/dist/registry/index.js +9 -822
  157. package/dist/registry-R23UBE7H.js +14 -0
  158. package/dist/replay/index.js +6 -84
  159. package/dist/review-finding-store-DKAV3AS6.js +21 -0
  160. package/dist/review-report-store-OWY3EC3N.js +19 -0
  161. package/dist/security/index.js +114 -2202
  162. package/dist/session-catalog/index.js +36 -2103
  163. package/dist/session-catalog/project-server.js +42 -1435
  164. package/dist/skills/index.js +35 -1578
  165. package/dist/storage/index.js +182 -15600
  166. package/dist/tasking/index.js +10 -676
  167. package/dist/tools/index.js +52 -9590
  168. package/dist/types/config/autonomy.d.ts +5 -0
  169. package/dist/types/config/ui.d.ts +1 -1
  170. package/dist/types/errors.d.ts +18 -0
  171. package/dist/types/index.js +121 -1342
  172. package/dist/types/provider-runner.d.ts +8 -0
  173. package/dist/types/session-markers.js +8 -144
  174. package/dist/utils/error.js +3 -4
  175. package/dist/utils/expect-defined.js +3 -9
  176. package/dist/utils/heap-watchdog.js +6 -706
  177. package/dist/utils/index.js +269 -5685
  178. package/dist/utils/sage-output-block.js +5 -42
  179. package/dist/wiring/proxy-rewrite.d.ts +18 -0
  180. package/dist/wiring/proxy-rewrite.js +18 -121
  181. package/dist/worktree/index.js +6 -887
  182. package/package.json +4 -4
@@ -1,952 +1,18 @@
1
- // src/types/prompt-registry.ts
2
- var SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
3
- var CHECKSUM_RE = /^[a-f0-9]{64}$/;
4
- var MAX_STR = 4096;
5
- function validateRegistryManifest(raw) {
6
- const errors = [];
7
- if (!raw || typeof raw !== "object") return { ok: false, errors: ["manifest is not an object"] };
8
- const m = raw;
9
- if (m["registryVersion"] !== 1) errors.push("registryVersion must be 1");
10
- if (typeof m["source"] !== "string" || !m["source"])
11
- errors.push("source must be a non-empty string");
12
- if (typeof m["generatedAt"] !== "string") errors.push("generatedAt must be a string");
13
- if (!Array.isArray(m["prompts"])) {
14
- errors.push("prompts must be an array");
15
- return { ok: false, errors };
16
- }
17
- const seen = /* @__PURE__ */ new Set();
18
- const refs = [];
19
- m["prompts"].forEach((p, i) => {
20
- if (!p || typeof p !== "object") {
21
- errors.push(`prompts[${i}] is not an object`);
22
- return;
23
- }
24
- const r = p;
25
- const slug = r["slug"];
26
- if (typeof slug !== "string" || !SLUG_RE.test(slug)) {
27
- errors.push(`prompts[${i}].slug invalid (must be kebab-case)`);
28
- return;
29
- }
30
- if (seen.has(slug)) {
31
- errors.push(`prompts[${i}].slug "${slug}" duplicated`);
32
- return;
33
- }
34
- seen.add(slug);
35
- if (typeof r["checksum"] !== "string" || !CHECKSUM_RE.test(r["checksum"])) {
36
- errors.push(`prompts[${i}].checksum must be a 64-char sha256 hex`);
37
- return;
38
- }
39
- for (const field of ["id", "title", "description", "category"]) {
40
- const v = r[field];
41
- if (typeof v !== "string" || v.length === 0 || v.length > MAX_STR) {
42
- errors.push(`prompts[${i}].${field} must be a non-empty string under ${MAX_STR} chars`);
43
- return;
44
- }
45
- }
46
- const tags = Array.isArray(r["tags"]) ? r["tags"].filter((t) => typeof t === "string") : [];
47
- refs.push({
48
- id: r["id"],
49
- slug,
50
- title: r["title"],
51
- description: r["description"],
52
- category: r["category"],
53
- tags,
54
- checksum: r["checksum"],
55
- version: typeof r["version"] === "string" ? r["version"] : void 0,
56
- license: typeof r["license"] === "string" ? r["license"] : void 0,
57
- url: typeof r["url"] === "string" ? r["url"] : void 0
58
- });
59
- });
60
- if (errors.length > 0) return { ok: false, errors };
61
- return {
62
- ok: true,
63
- manifest: {
64
- registryVersion: 1,
65
- source: m["source"],
66
- generatedAt: m["generatedAt"],
67
- prompts: refs
68
- }
69
- };
70
- }
71
- function diffRegistry(local, manifest) {
72
- const localBySlug = new Map(local.map((e) => [e.slug, e.checksum]));
73
- const diff = { added: [], updated: [], unchanged: [] };
74
- for (const ref of manifest.prompts) {
75
- if (!localBySlug.has(ref.slug)) diff.added.push(ref);
76
- else if (localBySlug.get(ref.slug) !== ref.checksum) diff.updated.push(ref);
77
- else diff.unchanged.push(ref);
78
- }
79
- return diff;
80
- }
81
-
82
- // src/types/errors.ts
83
- var ERROR_CODES = {
84
- // Provider
85
- PROVIDER_RATE_LIMITED: "PROVIDER_RATE_LIMITED",
86
- PROVIDER_AUTH_FAILED: "PROVIDER_AUTH_FAILED",
87
- PROVIDER_OVERLOADED: "PROVIDER_OVERLOADED",
88
- PROVIDER_INVALID_REQUEST: "PROVIDER_INVALID_REQUEST",
89
- PROVIDER_SERVER_ERROR: "PROVIDER_SERVER_ERROR",
90
- PROVIDER_NETWORK_ERROR: "PROVIDER_NETWORK_ERROR",
91
- PROVIDER_CONTEXT_OVERFLOW: "PROVIDER_CONTEXT_OVERFLOW",
92
- // Tool
93
- TOOL_NOT_FOUND: "TOOL_NOT_FOUND",
94
- TOOL_PERMISSION_DENIED: "TOOL_PERMISSION_DENIED",
95
- TOOL_EXECUTION_FAILED: "TOOL_EXECUTION_FAILED",
96
- TOOL_TIMEOUT: "TOOL_TIMEOUT",
97
- TOOL_INPUT_INVALID: "TOOL_INPUT_INVALID",
98
- // Config
99
- CONFIG_INVALID: "CONFIG_INVALID",
100
- CONFIG_NOT_FOUND: "CONFIG_NOT_FOUND",
101
- CONFIG_PARSE_FAILED: "CONFIG_PARSE_FAILED",
102
- CONFIG_MIGRATION_NEEDED: "CONFIG_MIGRATION_NEEDED",
103
- // Plugin
104
- PLUGIN_LOAD_FAILED: "PLUGIN_LOAD_FAILED",
105
- PLUGIN_API_MISMATCH: "PLUGIN_API_MISMATCH",
106
- PLUGIN_MISSING_DEPENDENCY: "PLUGIN_MISSING_DEPENDENCY",
107
- // Agent
108
- AGENT_ITERATION_LIMIT: "AGENT_ITERATION_LIMIT",
109
- AGENT_CONTEXT_OVERFLOW: "AGENT_CONTEXT_OVERFLOW",
110
- AGENT_ABORTED: "AGENT_ABORTED",
111
- AGENT_RUN_FAILED: "AGENT_RUN_FAILED",
112
- // Session
113
- SESSION_NOT_FOUND: "SESSION_NOT_FOUND",
114
- SESSION_CORRUPTED: "SESSION_CORRUPTED",
115
- SESSION_WRITE_FAILED: "SESSION_WRITE_FAILED",
116
- // Container / Registry
117
- CONTAINER_TOKEN_ALREADY_BOUND: "CONTAINER_TOKEN_ALREADY_BOUND",
118
- CONTAINER_TOKEN_NOT_BOUND: "CONTAINER_TOKEN_NOT_BOUND",
119
- CONTAINER_CIRCULAR_DEPENDENCY: "CONTAINER_CIRCULAR_DEPENDENCY",
120
- REGISTRY_DUPLICATE: "REGISTRY_DUPLICATE",
121
- REGISTRY_NOT_FOUND: "REGISTRY_NOT_FOUND",
122
- REGISTRY_INVALID: "REGISTRY_INVALID",
123
- // File system
124
- FS_READ_FAILED: "FS_READ_FAILED",
125
- FS_WRITE_FAILED: "FS_WRITE_FAILED",
126
- FS_MKDIR_FAILED: "FS_MKDIR_FAILED",
127
- FS_DELETE_FAILED: "FS_DELETE_FAILED",
128
- FS_ATOMIC_WRITE_FAILED: "FS_ATOMIC_WRITE_FAILED",
129
- // SDD (Spec-Driven Development)
130
- SDD_VALIDATION_FAILED: "SDD_VALIDATION_FAILED",
131
- SDD_PARSE_FAILED: "SDD_PARSE_FAILED",
132
- SDD_INVALID_STATE: "SDD_INVALID_STATE",
133
- SDD_NOT_READY: "SDD_NOT_READY",
134
- // General
135
- VALIDATION_ERROR: "VALIDATION_ERROR",
136
- PARSE_FAILED: "PARSE_FAILED",
137
- UNKNOWN: "UNKNOWN"
138
- };
139
- var WrongStackError = class extends Error {
140
- code;
141
- subsystem;
142
- severity;
143
- recoverable;
144
- context;
145
- constructor(opts) {
146
- super(opts.message, { cause: opts.cause });
147
- this.name = "WrongStackError";
148
- this.code = opts.code;
149
- this.subsystem = opts.subsystem;
150
- this.severity = opts.severity ?? "error";
151
- this.recoverable = opts.recoverable ?? false;
152
- this.context = opts.context;
153
- }
154
- /**
155
- * Render a one-line user-facing description.
156
- * Subclasses should override for domain-specific formatting.
157
- */
158
- describe() {
159
- const ctx = this.context ? ` ${formatContext(this.context)}` : "";
160
- return `${this.code}: ${this.message}${ctx}`;
161
- }
162
- };
163
- function formatContext(ctx) {
164
- const parts = Object.entries(ctx).filter(([, v]) => v !== void 0).slice(0, 3).map(([k, v]) => `${k}=${String(v)}`);
165
- return parts.length > 0 ? `[${parts.join(" ")}]` : "";
166
- }
167
- var FsError = class extends WrongStackError {
168
- path;
169
- constructor(opts) {
170
- super({
171
- message: opts.message,
172
- code: opts.code,
173
- subsystem: "fs",
174
- severity: "error",
175
- recoverable: opts.code !== ERROR_CODES.FS_READ_FAILED,
176
- context: { path: opts.path, ...opts.context },
177
- cause: opts.cause
178
- });
179
- this.name = "FsError";
180
- this.path = opts.path;
181
- }
182
- };
183
- var FetchError = class extends WrongStackError {
184
- status;
185
- constructor(opts) {
186
- super({
187
- message: opts.message,
188
- code: ERROR_CODES.VALIDATION_ERROR,
189
- subsystem: "general",
190
- severity: "error",
191
- recoverable: opts.status === 429 || opts.status >= 500,
192
- context: { status: opts.status, ...opts.context },
193
- cause: opts.cause
194
- });
195
- this.name = "FetchError";
196
- this.status = opts.status;
197
- }
198
- };
199
- var ParseError = class extends WrongStackError {
200
- source;
201
- constructor(opts) {
202
- super({
203
- message: opts.message,
204
- code: ERROR_CODES.PARSE_FAILED,
205
- subsystem: "general",
206
- severity: "error",
207
- recoverable: false,
208
- context: { source: opts.source, ...opts.context },
209
- cause: opts.cause
210
- });
211
- this.name = "ParseError";
212
- this.source = opts.source;
213
- }
214
- };
215
-
216
- // src/prompts/prompt-installer.ts
217
- var defaultFetcher = async (url) => {
218
- const res = await fetch(url);
219
- if (!res.ok) {
220
- throw new FetchError({
221
- message: `registry fetch failed: ${res.status} ${res.statusText}`,
222
- status: res.status,
223
- context: { op: "fetchManifest" }
224
- });
225
- }
226
- return res.json();
227
- };
228
- var PromptInstaller = class {
229
- fetcher;
230
- constructor(opts = {}) {
231
- this.fetcher = opts.fetcher ?? defaultFetcher;
232
- }
233
- /**
234
- * Fetch + validate a manifest and report what a sync WOULD change. Read-only.
235
- * @param manifestUrl URL to the registry's `registry.json`.
236
- * @param local The prompts the caller already has (slug + checksum).
237
- */
238
- async pull(manifestUrl, local) {
239
- const raw = await this.fetcher(manifestUrl);
240
- const validated = validateRegistryManifest(raw);
241
- if (!validated.ok) {
242
- throw new ParseError({
243
- message: `Invalid prompt registry manifest:
244
- - ${validated.errors.join("\n - ")}`,
245
- source: "prompt-registry-manifest"
246
- });
247
- }
248
- const diff = diffRegistry(local, validated.manifest);
249
- return { manifest: validated.manifest, diff, dryRun: true };
250
- }
251
- /**
252
- * EXTENSION POINT (not yet wired): download the prompt bodies for the given
253
- * registry refs and write them into the user layer as `source:'synced'`,
254
- * recording each in the installed-prompts manifest. Intentionally unimplemented
255
- * — pulling real content over the network is gated behind a future change so
256
- * the format/diff path can ship and be reviewed first.
257
- */
258
- async install() {
259
- throw new Error(
260
- "Prompt sync is not implemented yet. `pull()` reports the diff; installing remote prompt bodies will land in a follow-up."
261
- );
262
- }
263
- };
264
-
265
- // src/prompts/prompt-manifest-store.ts
266
- import * as fs from "node:fs/promises";
267
- import * as path from "node:path";
268
-
269
- // src/utils/atomic-write.ts
270
1
  import {
271
- createPersistencePrimitives
272
- } from "@wrongstack/persistence";
273
- var primitives = createPersistencePrimitives({
274
- createLockTimeoutError: ({ targetPath, timeoutMs }) => new FsError({
275
- message: `Timed out waiting for file lock: ${targetPath}`,
276
- code: "FS_ATOMIC_WRITE_FAILED",
277
- path: targetPath,
278
- context: { timeoutMs }
279
- })
280
- });
281
- var atomicWrite = primitives.atomicWrite;
282
- var atomicReplaceWithWriter = primitives.atomicReplaceWithWriter;
283
- var ensureDir = primitives.ensureDir;
284
- var withFileLock = primitives.withFileLock;
285
-
286
- // src/prompts/prompt-manifest-store.ts
287
- var PromptManifestStore = class {
288
- constructor(manifestPath) {
289
- this.manifestPath = manifestPath;
290
- }
291
- manifestPath;
292
- async load() {
293
- try {
294
- const raw = JSON.parse(await fs.readFile(this.manifestPath, "utf8"));
295
- if (raw && typeof raw === "object" && Array.isArray(raw.entries)) {
296
- return { version: 1, entries: raw.entries };
297
- }
298
- } catch {
299
- }
300
- return { version: 1, entries: [] };
301
- }
302
- async save(data) {
303
- await ensureDir(path.dirname(this.manifestPath));
304
- await atomicWrite(this.manifestPath, JSON.stringify(data, null, 2));
305
- }
306
- /** Upsert one entry keyed by slug. */
307
- async record(entry) {
308
- const data = await this.load();
309
- const idx = data.entries.findIndex((e) => e.slug === entry.slug);
310
- if (idx === -1) data.entries.push(entry);
311
- else data.entries[idx] = entry;
312
- await this.save(data);
313
- }
314
- async remove(slug) {
315
- const data = await this.load();
316
- const next = data.entries.filter((e) => e.slug !== slug);
317
- if (next.length === data.entries.length) return false;
318
- await this.save({ version: 1, entries: next });
319
- return true;
320
- }
321
- async list() {
322
- return (await this.load()).entries;
323
- }
324
- };
325
-
326
- // src/prompts/prompt-journal.ts
327
- import * as fs2 from "node:fs/promises";
328
- import * as path2 from "node:path";
329
-
330
- // src/security/secret-scrubber.ts
331
- var JSON_CREDENTIAL_KEY_ANCHORS = [
332
- 'Key"',
333
- 'key"',
334
- 'KEY"',
335
- 'token"',
336
- 'Token"',
337
- 'TOKEN"',
338
- 'secret"',
339
- 'Secret"',
340
- 'SECRET"',
341
- 'password"',
342
- 'Password"',
343
- 'PASSWORD"',
344
- 'authorization"',
345
- 'Authorization"',
346
- 'bearer"',
347
- 'Bearer"'
348
- ];
349
- var PATTERNS = [
350
- // Anchored at the start where possible so partial matches inside larger
351
- // strings don't trigger false positives.
352
- {
353
- type: "anthropic_key",
354
- regex: /(?<![A-Za-z0-9])sk-ant-api\d+-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9])/g,
355
- anchor: "sk-ant-"
356
- },
357
- { type: "openai_key", regex: /(?<![A-Za-z0-9])sk-(?:proj-)?[A-Za-z0-9_-]{20,}(?![A-Za-z0-9])/g, anchor: "sk-" },
358
- {
359
- // `xai` is a first-class provider in this codebase, but its key shape was
360
- // absent here — so the one credential format WrongStack itself hands users
361
- // was the one the scrubber could not recognize (audit 2026-08-20).
362
- type: "xai_key",
363
- regex: /(?<![A-Za-z0-9])xai-[A-Za-z0-9]{20,}(?![A-Za-z0-9])/g,
364
- anchor: "xai-"
365
- },
366
- { type: "github_pat", regex: /(?<![A-Za-z0-9])ghp_[A-Za-z0-9]{36,}(?![A-Za-z0-9])/g, anchor: "ghp_" },
367
- { type: "github_pat_v2", regex: /(?<![A-Za-z0-9])github_pat_[A-Za-z0-9_]{50,}(?![A-Za-z0-9])/g, anchor: "github_pat_" },
368
- { type: "aws_access_key", regex: /(?<![A-Za-z0-9])AKIA[0-9A-Z]{16}(?![A-Za-z0-9])/g, anchor: "AKIA" },
369
- { type: "gcp_key", regex: /(?<![A-Za-z0-9])AIza[0-9A-Za-z_-]{35}(?![A-Za-z0-9])/g, anchor: "AIza" },
370
- { type: "slack_token", regex: /(?<![A-Za-z0-9-])xox[abpos]-[A-Za-z0-9-]{10,}(?![A-Za-z0-9-])/g, anchor: "xox" },
371
- {
372
- type: "stripe_key",
373
- regex: /(?<![A-Za-z0-9])sk_(?:live|test)_[A-Za-z0-9]{24,}(?![A-Za-z0-9])/g,
374
- anchor: "sk_"
375
- },
376
- {
377
- type: "twilio_sid",
378
- regex: /(?<![A-Za-z0-9])AC[a-f0-9]{32}(?![A-Za-z0-9])/g,
379
- anchor: "AC"
380
- },
381
- {
382
- type: "telegram_bot_token",
383
- // Telegram tokens are of the form bot<digits>:<alphanum> in URL paths
384
- regex: /\/bot\d+:[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
385
- anchor: "/bot"
386
- },
387
- {
388
- type: "jwt",
389
- // Anchored: look for literal "eyJ" which is unambiguous for JWT header
390
- regex: /(?<![A-Za-z0-9/+=])eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}(?![A-Za-z0-9/+=])/g,
391
- anchor: "eyJ"
392
- },
393
- {
394
- type: "private_key",
395
- // Anchored: start must be BEGIN, end must be END with no extra dashes after END
396
- regex: /(?:^|\n)-----BEGIN (?:RSA|EC|OPENSSH|DSA|PGP)? ?PRIVATE KEY-----[\s\S]*?-----END[^-]*-----(?:\n|$)/g,
397
- anchor: "-----BEGIN"
398
- },
399
- { type: "mongodb_uri", regex: /mongodb(?:\+srv)?:\/\/[^\s"'`]+/g, anchor: "mongodb" },
400
- { type: "postgres_uri", regex: /postgres(?:ql)?:\/\/[^\s"'`]+/g, anchor: "postgres" },
401
- { type: "mysql_uri", regex: /mysql:\/\/[^\s"'`]+/g, anchor: "mysql://" },
402
- { type: "redis_uri", regex: /redis:\/\/[^\s"'`]+/g, anchor: "redis://" },
403
- // AI/ML provider keys — modern LLM services with well-known prefixes
404
- {
405
- type: "huggingface_token",
406
- // HuggingFace tokens: hf_ followed by 34 alphanumeric chars
407
- regex: /(?<![A-Za-z0-9])hf_[A-Za-z0-9]{34}(?![A-Za-z0-9])/g,
408
- anchor: "hf_"
409
- },
410
- {
411
- type: "replicate_token",
412
- // Replicate tokens: r8_ followed by 40+ alphanumeric chars
413
- regex: /(?<![A-Za-z0-9])r8_[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g,
414
- anchor: "r8_"
415
- },
416
- {
417
- type: "perplexity_key",
418
- // Perplexity API keys: pplx- followed by 40+ alphanumeric chars
419
- regex: /(?<![A-Za-z0-9])pplx-[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g,
420
- anchor: "pplx-"
421
- },
422
- {
423
- type: "groq_key",
424
- // Groq API keys: gsk_ followed by 40+ alphanumeric chars
425
- regex: /(?<![A-Za-z0-9])gsk_[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g,
426
- anchor: "gsk_"
427
- },
428
- {
429
- type: "bearer_token",
430
- // Anchored with alternation instead of negative lookahead — avoids V8
431
- // backtracking risk on adversarial input. Bounded at 512 chars.
432
- // Min 12 chars: some OAuth providers issue shorter-lived tokens (< 20
433
- // chars). A 12-char base64 string has ~71 bits of entropy — above the
434
- // threshold where random strings are unlikely to produce false matches.
435
- // The trailing boundary is a NON-consuming lookahead: two adjacent bearer
436
- // tokens sharing a single delimiter (`Bearer a… Bearer b…`) must both be
437
- // redacted. A consuming trailing delimiter would eat the separator the
438
- // next match needs for its leading anchor, leaking the second token.
439
- regex: /(?:^|[^A-Za-z0-9_.~+/-])Bearer\s+[A-Za-z0-9._~+/-]{12,512}=*(?=$|[^A-Za-z0-9_.~+/-])/g,
440
- anchor: "Bearer"
441
- },
442
- {
443
- type: "high_entropy_env",
444
- // Anchored with alternation instead of lookbehind to avoid backtracking.
445
- // Value bounded at 512 chars.
446
- // The trailing boundary is a NON-consuming lookahead so two secrets
447
- // separated by a single delimiter (one space OR one newline, e.g.
448
- // `printenv` / `.env` dumps: `API_KEY=… \n SESSION_TOKEN=…`) are BOTH
449
- // redacted. A consuming trailing `\s` would swallow the separator the
450
- // next match needs for its leading anchor, so every other secret would
451
- // leak in plaintext.
452
- // The leading delimiter is CAPTURED (group 1) and re-emitted by the
453
- // replacement so the separator between adjacent secrets is preserved
454
- // rather than collapsed. Capture groups are therefore: 1=leading
455
- // delimiter, 2=key name, 3=value.
456
- regex: /(^|\s)([A-Z_]{4,}(?:KEY|TOKEN|SECRET|PASSWORD|PWD|PASSPHRASE))\s*[:=]\s*['"]?([A-Za-z0-9_/+=-]{20,512})['"]?(?=\s|$)/g,
457
- anchor: ["KEY", "TOKEN", "SECRET", "PASSWORD", "PWD", "PASSPHRASE"]
458
- },
459
- {
460
- type: "json_credential_key",
461
- // The JSON counterpart to `high_entropy_env`, and the pattern that the
462
- // now-deleted `JSON_KEY_ANCHORS` list was written for. Without it those
463
- // anchors only widened the cheap pre-scan — `hasCredentialAnchors` said
464
- // "this text may hold a secret", every pattern then declined to match, and
465
- // the value went out verbatim. `high_entropy_env` cannot cover these: it
466
- // requires an UPPERCASE unquoted key (`API_KEY=…`), so `{"apiKey":"…"}`
467
- // never matched.
468
- //
469
- // Tool results are routinely serialised as JSON, and a credential with no
470
- // recognisable prefix (Azure, self-hosted gateways, Anthropic/Codex OAuth)
471
- // has no other pattern that can catch it — this is the only thing standing
472
- // between such a value and the session JSONL, chronicle, HQ broadcast and
473
- // the model's own context.
474
- //
475
- // The key may carry a prefix (`"anthropicApiKey"`), but the credential word
476
- // must END the key: `"tokenCount"` and `"maxTokens"` do not match, because
477
- // the closing quote has to follow the word immediately.
478
- // Value floor of 8 chars keeps enum-ish values (`"authorization":"none"`)
479
- // out. Capture groups: 1=key + punctuation, 2=value, 3=closing quote.
480
- regex: /("[A-Za-z0-9_]*(?:apiKey|api_key|token|secret|password|authorization|bearer|private_key|access_token|refresh_token|client_secret)"\s*:\s*")([^"\\]{8,512})(")/gi,
481
- anchor: JSON_CREDENTIAL_KEY_ANCHORS
482
- },
483
- // ── Ported from packages/plugins credential-patterns.ts (WS-034) ─────────
484
- // The plugin runtime carried 37 patterns while this scrubber — the one that
485
- // guards session JSONL, chronicle, HQ broadcast, WebUI events and the auth
486
- // audit — carried 22. The plugin side already had a parity test; it just did
487
- // not cover core. Most consequential: WrongStack mints `gho_` tokens itself
488
- // in the Copilot OAuth flow, and `gh[ousr]_` was absent here.
489
- {
490
- type: "github_oauth_token",
491
- regex: /(?<![A-Za-z0-9])gh[ousr]_[A-Za-z0-9]{36,}(?![A-Za-z0-9])/g,
492
- anchor: ["gho_", "ghu_", "ghs_", "ghr_"]
493
- },
494
- {
495
- type: "gitlab_pat",
496
- regex: /(?<![A-Za-z0-9])glpat-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
497
- anchor: "glpat-"
498
- },
499
- {
500
- type: "gitlab_runner_token",
501
- regex: /(?<![A-Za-z0-9])glrt-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
502
- anchor: "glrt-"
503
- },
504
- {
505
- type: "npm_token",
506
- regex: /(?<![A-Za-z0-9])npm_[A-Za-z0-9]{36}(?![A-Za-z0-9])/g,
507
- anchor: "npm_"
508
- },
509
- {
510
- type: "slack_app_token",
511
- regex: /(?<![A-Za-z0-9-])xapp-\d-[A-Za-z0-9-]{10,}(?![A-Za-z0-9-])/g,
512
- anchor: "xapp-"
513
- },
514
- {
515
- type: "slack_webhook",
516
- regex: /https:\/\/hooks\.slack\.com\/services\/T[A-Za-z0-9_-]+\/B[A-Za-z0-9_-]+\/[A-Za-z0-9]{16,}/g,
517
- anchor: "hooks.slack.com"
518
- },
519
- {
520
- type: "sendgrid_key",
521
- regex: /(?<![A-Za-z0-9])SG\.[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
522
- anchor: "SG."
523
- },
524
- {
525
- type: "digitalocean_token",
526
- regex: /(?<![A-Za-z0-9])dop_v1_[a-f0-9]{64}(?![A-Za-z0-9])/g,
527
- anchor: "dop_v1_"
528
- },
529
- {
530
- type: "doppler_token",
531
- regex: /(?<![A-Za-z0-9])dp\.(?:pt|st|sa|scim|audit)\.[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g,
532
- anchor: "dp."
533
- },
534
- {
535
- type: "shopify_token",
536
- regex: /(?<![A-Za-z0-9])shp(?:at|ca|pa|ss)_[a-fA-F0-9]{32}(?![A-Za-z0-9])/g,
537
- anchor: "shp"
538
- },
539
- {
540
- type: "docker_pat",
541
- regex: /(?<![A-Za-z0-9])dckr_pat_[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
542
- anchor: "dckr_pat_"
543
- },
544
- {
545
- type: "linear_key",
546
- regex: /(?<![A-Za-z0-9])lin_api_[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g,
547
- anchor: "lin_api_"
548
- },
549
- {
550
- type: "atlassian_token",
551
- regex: /(?<![A-Za-z0-9])ATATT3[A-Za-z0-9_\-=]{40,}(?![A-Za-z0-9_\-=])/g,
552
- anchor: "ATATT3"
553
- },
554
- {
555
- type: "square_token",
556
- regex: /(?<![A-Za-z0-9])(?:sq0(?:atp|csp)-|EAAA)[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
557
- anchor: ["sq0atp-", "sq0csp-", "EAAA"]
558
- },
559
- {
560
- type: "google_oauth_client_secret",
561
- regex: /(?<![A-Za-z0-9_-])GOCSPX-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
562
- anchor: "GOCSPX-"
563
- }
564
- ];
565
- var SIMPLE_PATTERNS = PATTERNS.filter(
566
- (p) => p.type !== "high_entropy_env" && p.type !== "json_credential_key"
567
- );
568
- var COMBINED_REGEX = new RegExp(SIMPLE_PATTERNS.map((p) => `(${p.regex.source})`).join("|"), "g");
569
- var HIGH_ENTROPY_REGEX = PATTERNS.find((p) => p.type === "high_entropy_env").regex;
570
- var JSON_CREDENTIAL_REGEX = PATTERNS.find((p) => p.type === "json_credential_key").regex;
571
- var COMBINED_REPLACEMENTS = SIMPLE_PATTERNS.map((p) => `[REDACTED:${p.type}]`);
572
- var SCRUB_CHUNK_BYTES = 64 * 1024;
573
- var SCRUB_OVERLAP_BYTES = 1024;
574
- var PEM_PRIVATE_KEY_BEGIN_RE = /-----BEGIN (?:RSA|EC|OPENSSH|DSA|PGP)? ?PRIVATE KEY-----/;
575
- var PEM_END_MARKER = "-----END";
576
- var MAX_PEM_BLOCK_BYTES = 64 * 1024;
577
- var PEM_END_LINE_TOLERANCE = 64;
578
- function extendChunkBoundaryPastPem(text, chunkStart, proposedEnd) {
579
- const head = text.slice(chunkStart, proposedEnd);
580
- const lastBegin = head.lastIndexOf("-----BEGIN ");
581
- if (lastBegin === -1) return proposedEnd;
582
- const fromBegin = text.slice(chunkStart + lastBegin);
583
- const marker = PEM_PRIVATE_KEY_BEGIN_RE.exec(fromBegin);
584
- if (!marker || marker.index !== 0) return proposedEnd;
585
- const bodyStart = marker[0].length;
586
- const cap = Math.min(text.length, chunkStart + lastBegin + MAX_PEM_BLOCK_BYTES);
587
- const closeIdx = fromBegin.indexOf(PEM_END_MARKER, bodyStart);
588
- if (closeIdx === -1 || chunkStart + lastBegin + closeIdx >= cap + PEM_END_LINE_TOLERANCE) {
589
- return proposedEnd;
590
- }
591
- const lineEnd = fromBegin.indexOf("\n", closeIdx);
592
- const end = lineEnd === -1 ? text.length : chunkStart + lastBegin + lineEnd + 1;
593
- return Math.max(proposedEnd, end);
594
- }
595
- var PATTERN_ANCHORS = [
596
- ...new Set(
597
- PATTERNS.flatMap(
598
- (pattern) => typeof pattern.anchor === "string" ? [pattern.anchor] : [...pattern.anchor]
599
- )
600
- )
601
- ];
602
- var ALL_ANCHORS = PATTERN_ANCHORS;
603
- function hasCredentialAnchors(text) {
604
- for (const anchor of ALL_ANCHORS) {
605
- if (text.includes(anchor)) return true;
606
- }
607
- return false;
608
- }
609
- var DefaultSecretScrubber = class {
610
- scrub(text) {
611
- if (!text) return text;
612
- if (!hasCredentialAnchors(text)) return text;
613
- if (text.length <= SCRUB_CHUNK_BYTES) {
614
- return this.scrubOne(text);
615
- }
616
- const out = [];
617
- let i = 0;
618
- while (i < text.length) {
619
- let end = Math.min(i + SCRUB_CHUNK_BYTES, text.length);
620
- if (end < text.length) {
621
- const limit = Math.min(end + SCRUB_OVERLAP_BYTES, text.length);
622
- let safe = -1;
623
- for (let j = end; j < limit; j++) {
624
- const ch = text.charCodeAt(j);
625
- if (ch === 32 || ch === 9 || ch === 10 || ch === 13) {
626
- safe = j;
627
- break;
628
- }
629
- }
630
- end = safe === -1 ? end : safe + 1;
631
- end = extendChunkBoundaryPastPem(text, i, end);
632
- }
633
- out.push(this.scrubOne(text.slice(i, end)));
634
- i = end;
635
- }
636
- return out.join("");
637
- }
638
- scrubOne(text) {
639
- if (!hasCredentialAnchors(text)) return text;
640
- let out = text.replace(
641
- COMBINED_REGEX,
642
- (match, ...groups) => {
643
- const idx = groups.findIndex((g) => g !== void 0);
644
- if (idx < 0) return match;
645
- const replacement = COMBINED_REPLACEMENTS[idx];
646
- return replacement !== void 0 ? replacement : match;
647
- }
648
- );
649
- out = out.replace(HIGH_ENTROPY_REGEX, (_match, lead, key, _value) => {
650
- return `${lead}${key}=[REDACTED:high_entropy_env]`;
651
- });
652
- out = out.replace(JSON_CREDENTIAL_REGEX, (_match, keyPrefix, _value, closingQuote) => {
653
- return `${keyPrefix}[REDACTED:json_credential_key]${closingQuote}`;
654
- });
655
- return out;
656
- }
657
- /**
658
- * Recursively scrub every string value in an object/array graph. Secrets can
659
- * appear under any key — a URL query param, an `authorization` header, an
660
- * arbitrarily-named nested field — so we don't gate recursion on key names.
661
- * The per-string `scrub()` fast-path (anchor pre-scan) keeps this cheap: any
662
- * value without a credential anchor returns immediately without regex work.
663
- */
664
- scrubObject(obj) {
665
- const seen = /* @__PURE__ */ new WeakSet();
666
- const visit = (v) => {
667
- if (typeof v === "string") return this.scrub(v);
668
- if (v === null || typeof v !== "object") return v;
669
- if (seen.has(v)) return v;
670
- seen.add(v);
671
- if (Array.isArray(v)) return v.map(visit);
672
- const out = {};
673
- for (const [k, val] of Object.entries(v)) {
674
- out[k] = visit(val);
675
- }
676
- return out;
677
- };
678
- return visit(obj);
679
- }
680
- };
681
-
682
- // src/prompts/prompt-journal.ts
683
- var defaultScrubber = new DefaultSecretScrubber();
684
- var PROMPT_JOURNAL_RAW_MARKER = "promptJournal.raw";
685
- async function ensureGitignore(projectRoot) {
686
- const gitignorePath = path2.join(projectRoot, ".gitignore");
687
- try {
688
- let content = "";
689
- try {
690
- content = await fs2.readFile(gitignorePath, "utf8");
691
- } catch {
692
- content = "";
693
- }
694
- if (!content.includes(".wrongstack") && !content.includes(".wrongstack/")) {
695
- const addition = content.endsWith("\n") || content.length === 0 ? ".wrongstack/\n" : "\n.wrongstack/\n";
696
- await fs2.writeFile(gitignorePath, content + addition, "utf8");
697
- }
698
- } catch {
699
- }
700
- }
701
- function sessionFileId(sessionId) {
702
- const leaf = sessionId.split(/[\\/]/u).pop();
703
- return leaf && leaf.trim().length > 0 ? leaf : "general";
704
- }
705
- async function recordPromptJournalEntry(opts) {
706
- const now = /* @__PURE__ */ new Date();
707
- const timestamp = now.toISOString();
708
- const dateStr = timestamp.slice(0, 10);
709
- const monthStr = dateStr.slice(0, 7);
710
- const sessionId = opts.sessionId && opts.sessionId.trim() ? opts.sessionId.trim() : "general";
711
- const id = `pmt_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
712
- const content = defaultScrubber.scrub(opts.content ?? "");
713
- const rawContent = typeof opts.rawContent === "string" && opts.rawContent.length > 0 ? defaultScrubber.scrub(opts.rawContent) : opts.rawContent;
714
- const decisionReason = typeof opts.decisionReason === "string" && opts.decisionReason.length > 0 ? defaultScrubber.scrub(opts.decisionReason) : opts.decisionReason;
715
- const lines = content.split("\n");
716
- const characterCount = content.length;
717
- const lineCount = lines.length;
718
- const tokenEstimate = Math.ceil(characterCount / 4);
719
- const entry = {
720
- id,
721
- timestamp,
722
- sessionId,
723
- projectRoot: opts.projectRoot,
724
- role: opts.role ?? (opts.category === "system_prompt" ? "system" : "user"),
725
- category: opts.category,
726
- content,
727
- rawContent,
728
- metadata: {
729
- model: opts.model,
730
- provider: opts.provider,
731
- iterationIndex: opts.iterationIndex,
732
- tokenEstimate,
733
- characterCount,
734
- lineCount,
735
- activeTools: opts.activeTools,
736
- contextFiles: opts.contextFiles,
737
- durationMs: opts.durationMs,
738
- decisionReason,
739
- tags: opts.tags
740
- }
741
- };
742
- const basePromptsDir = path2.join(opts.projectRoot, ".wrongstack", "prompts");
743
- const dayDir = path2.join(basePromptsDir, monthStr, dateStr);
744
- try {
745
- await fs2.mkdir(dayDir, { recursive: true });
746
- await ensureGitignore(opts.projectRoot);
747
- const sessionJsonlFile = path2.join(dayDir, `session-${sessionFileId(sessionId)}.jsonl`);
748
- await fs2.appendFile(sessionJsonlFile, JSON.stringify(entry) + "\n", "utf8");
749
- const sessionMdFile = path2.join(dayDir, `session-${sessionFileId(sessionId)}.md`);
750
- const mdSection = formatEntryMarkdown(entry);
751
- await fs2.appendFile(sessionMdFile, mdSection, "utf8");
752
- const dailySummaryFile = path2.join(dayDir, "daily-summary.md");
753
- await updateDailySummary(dailySummaryFile, dateStr, entry);
754
- await updateRootCatalog(basePromptsDir, monthStr, dateStr, sessionId, entry);
755
- } catch (err) {
756
- console.error?.(`Failed to write hierarchical prompt journal: ${err}`);
757
- }
758
- return entry;
759
- }
760
- function formatEntryMarkdown(entry) {
761
- const tagList = [
762
- `**Category:** \`${entry.category}\``,
763
- `**Role:** \`${entry.role}\``,
764
- entry.metadata.model ? `**Model:** \`${entry.metadata.model}\`` : null,
765
- `**Tokens (est):** ~${entry.metadata.tokenEstimate}`,
766
- entry.metadata.iterationIndex !== void 0 ? `**Iteration:** #${entry.metadata.iterationIndex}` : null,
767
- `**Session:** \`${entry.sessionId}\``
768
- ].filter(Boolean).join(" | ");
769
- let md = `
770
- ### \u{1F4DD} [${entry.timestamp}] \`${entry.id}\`
771
- ${tagList}
772
-
773
- `;
774
- if (entry.metadata.decisionReason) {
775
- md += `> **Rationale:** ${entry.metadata.decisionReason}
776
-
777
- `;
778
- }
779
- if (entry.metadata.activeTools && entry.metadata.activeTools.length > 0) {
780
- md += `*Active Tools:* \`${entry.metadata.activeTools.join("`, `")}\`
781
-
782
- `;
783
- }
784
- if (entry.rawContent && entry.rawContent !== entry.content) {
785
- md += `**Raw Input:**
786
- \`\`\`text
787
- ${entry.rawContent.trim()}
788
- \`\`\`
789
-
790
- `;
791
- md += `**Refined / Injected Prompt:**
792
- \`\`\`text
793
- ${entry.content.trim()}
794
- \`\`\`
795
-
796
- `;
797
- } else {
798
- md += `**Prompt Content:**
799
- \`\`\`text
800
- ${entry.content.trim()}
801
- \`\`\`
802
-
803
- `;
804
- }
805
- md += `---
806
- `;
807
- return md;
808
- }
809
- async function updateDailySummary(summaryFile, dateStr, entry) {
810
- try {
811
- let content = "";
812
- try {
813
- content = await fs2.readFile(summaryFile, "utf8");
814
- } catch {
815
- content = `# \u{1F4C5} Daily Prompt Summary \u2014 ${dateStr}
816
-
817
- | Time | ID | Session | Category | Model | Tokens | Rationale |
818
- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
819
- `;
820
- }
821
- const time = entry.timestamp.slice(11, 19);
822
- const model = entry.metadata.model ?? "-";
823
- const reason = entry.metadata.decisionReason ? entry.metadata.decisionReason.slice(0, 40) : "-";
824
- const row = `| ${time} | [\`${entry.id}\`](session-${sessionFileId(entry.sessionId)}.md) | \`${entry.sessionId}\` | \`${entry.category}\` | ${model} | ~${entry.metadata.tokenEstimate} | ${reason} |
825
- `;
826
- await fs2.writeFile(summaryFile, content + row, "utf8");
827
- } catch {
828
- }
829
- }
830
- async function updateRootCatalog(baseDir, monthStr, dateStr, sessionId, entry) {
831
- const indexJsonFile = path2.join(baseDir, "index.json");
832
- const indexMdFile = path2.join(baseDir, "index.md");
833
- let catalog;
834
- try {
835
- const raw = await fs2.readFile(indexJsonFile, "utf8");
836
- catalog = JSON.parse(raw);
837
- } catch {
838
- catalog = {
839
- updatedAt: entry.timestamp,
840
- totalPrompts: 0,
841
- totalTokensEstimated: 0,
842
- months: {}
843
- };
844
- }
845
- catalog.updatedAt = entry.timestamp;
846
- catalog.totalPrompts += 1;
847
- catalog.totalTokensEstimated += entry.metadata.tokenEstimate;
848
- if (!catalog.months[monthStr]) {
849
- catalog.months[monthStr] = { days: {} };
850
- }
851
- const monthData = catalog.months[monthStr];
852
- if (!monthData.days[dateStr]) {
853
- monthData.days[dateStr] = { sessions: {} };
854
- }
855
- const dayData = monthData.days[dateStr];
856
- if (!dayData.sessions[sessionId]) {
857
- dayData.sessions[sessionId] = {
858
- promptCount: 0,
859
- tokenEstimate: 0,
860
- lastTimestamp: entry.timestamp,
861
- categories: {}
862
- };
863
- }
864
- const sessionData = dayData.sessions[sessionId];
865
- sessionData.promptCount += 1;
866
- sessionData.tokenEstimate += entry.metadata.tokenEstimate;
867
- sessionData.lastTimestamp = entry.timestamp;
868
- sessionData.categories[entry.category] = (sessionData.categories[entry.category] ?? 0) + 1;
869
- try {
870
- await fs2.writeFile(indexJsonFile, JSON.stringify(catalog, null, 2), "utf8");
871
- let md = `# \u{1F5C2}\uFE0F Prompt Journal Navigation Index
872
-
873
- `;
874
- md += `* **Total Prompts Logged:** ${catalog.totalPrompts}
875
- `;
876
- md += `* **Total Tokens (est):** ~${catalog.totalTokensEstimated.toLocaleString()}
877
- `;
878
- md += `* **Last Recorded Activity:** ${catalog.updatedAt}
879
-
880
- `;
881
- md += `## \u{1F4C5} Recorded Dates & Sessions
882
-
883
- `;
884
- md += `| Date | Session | Prompts | Tokens (est) | Daily Log | Session Log |
885
- `;
886
- md += `| :--- | :--- | :--- | :--- | :--- | :--- |
887
- `;
888
- for (const [m, mObj] of Object.entries(catalog.months).sort().reverse()) {
889
- for (const [d, dObj] of Object.entries(mObj.days).sort().reverse()) {
890
- for (const [sId, sData] of Object.entries(dObj.sessions)) {
891
- const dailyLink = `[daily-summary.md](./${m}/${d}/daily-summary.md)`;
892
- const sessionLink = `[session-${sessionFileId(sId)}.md](./${m}/${d}/session-${sessionFileId(sId)}.md)`;
893
- md += `| **${d}** | \`${sId}\` | ${sData.promptCount} | ~${sData.tokenEstimate.toLocaleString()} | ${dailyLink} | ${sessionLink} |
894
- `;
895
- }
896
- }
897
- }
898
- await fs2.writeFile(indexMdFile, md, "utf8");
899
- } catch {
900
- }
901
- }
902
- async function getPromptJournalEntries(projectRoot, filter = {}) {
903
- const basePromptsDir = path2.join(projectRoot, ".wrongstack", "prompts");
904
- const results = [];
905
- try {
906
- const months = await fs2.readdir(basePromptsDir);
907
- for (const month of months) {
908
- if (!/^\d{4}-\d{2}$/.test(month)) continue;
909
- if (filter.month && filter.month !== month) continue;
910
- const monthDir = path2.join(basePromptsDir, month);
911
- const days = await fs2.readdir(monthDir);
912
- for (const day of days) {
913
- if (!/^\d{4}-\d{2}-\d{2}$/.test(day)) continue;
914
- if (filter.date && filter.date !== day) continue;
915
- const dayDir = path2.join(monthDir, day);
916
- const files = await fs2.readdir(dayDir);
917
- const jsonlFiles = files.filter((f) => f.startsWith("session-") && f.endsWith(".jsonl"));
918
- for (const jsonlFile of jsonlFiles) {
919
- const sessionMatch = jsonlFile.match(/^session-(.+)\.jsonl$/);
920
- const sId = sessionMatch ? sessionMatch[1] : void 0;
921
- if (filter.sessionId && sId !== sessionFileId(filter.sessionId)) continue;
922
- const filePath = path2.join(dayDir, jsonlFile);
923
- const content = await fs2.readFile(filePath, "utf8");
924
- const lines = content.split("\n");
925
- for (const line of lines) {
926
- if (!line.trim()) continue;
927
- try {
928
- const entry = JSON.parse(line);
929
- if (filter.sessionId && sessionFileId(entry.sessionId) !== sessionFileId(filter.sessionId)) {
930
- continue;
931
- }
932
- if (filter.category && entry.category !== filter.category) continue;
933
- if (filter.since && entry.timestamp < filter.since) continue;
934
- results.push(entry);
935
- } catch {
936
- }
937
- }
938
- }
939
- }
940
- }
941
- } catch {
942
- return [];
943
- }
944
- results.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
945
- if (filter.limit && results.length > filter.limit) {
946
- return results.slice(-filter.limit);
947
- }
948
- return results;
949
- }
2
+ PromptInstaller,
3
+ PromptManifestStore
4
+ } from "../chunk-JJPMWPB6.js";
5
+ import {
6
+ PROMPT_JOURNAL_RAW_MARKER,
7
+ ensureGitignore,
8
+ getPromptJournalEntries,
9
+ recordPromptJournalEntry
10
+ } from "../chunk-YUPMXOGP.js";
11
+ import "../chunk-3E244UDA.js";
12
+ import "../chunk-IYFBFX34.js";
13
+ import "../chunk-7ZHZE5EO.js";
14
+ import "../chunk-WOEZV3Q4.js";
15
+ import "../chunk-TBRL2RCC.js";
950
16
  export {
951
17
  PROMPT_JOURNAL_RAW_MARKER,
952
18
  PromptInstaller,