@ulpi/cli 0.1.5 → 0.1.7

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 (109) hide show
  1. package/README.md +143 -214
  2. package/dist/{auth-PN7TMQHV-2W4ICG64.js → auth-FWM7MM4Q-VZC3U2XZ.js} +1 -1
  3. package/dist/{auth-BFFBUJUC.js → auth-HDK7ECJL.js} +2 -1
  4. package/dist/{chunk-RJIRWQJD.js → chunk-3BCW6ABU.js} +402 -142
  5. package/dist/{chunk-L3PWNHSA.js → chunk-3WB5CXH4.js} +180 -5
  6. package/dist/{chunk-K4OVPFY2.js → chunk-4UCJIAOU.js} +2 -2
  7. package/dist/chunk-4XTHZVDS.js +109 -0
  8. package/dist/chunk-4ZPOZULQ.js +6522 -0
  9. package/dist/{chunk-SIAQVRKG.js → chunk-5MI5GIXM.js} +48 -2
  10. package/dist/{chunk-KLEASXUR.js → chunk-6ZL6NXMV.js} +1 -1
  11. package/dist/{chunk-AV5RB3N2.js → chunk-76D3BYJD.js} +48 -0
  12. package/dist/{chunk-DOIKS6C5.js → chunk-AWOSRA5F.js} +1 -1
  13. package/dist/{chunk-UCMT5OKP.js → chunk-BFEKZZHM.js} +274 -57
  14. package/dist/chunk-C7CLUQI6.js +1286 -0
  15. package/dist/{chunk-ELTGWMDE.js → chunk-E3B5NROU.js} +7 -7
  16. package/dist/chunk-EJ7TW77N.js +1418 -0
  17. package/dist/{chunk-P2RESJRN.js → chunk-EWLYVXQ4.js} +2 -2
  18. package/dist/{chunk-6OURRFP7.js → chunk-IV6MWETF.js} +383 -168
  19. package/dist/chunk-IZPJHSPX.js +1478 -0
  20. package/dist/chunk-JLHNLM3C.js +228 -0
  21. package/dist/chunk-PO4NUZUU.js +147 -0
  22. package/dist/chunk-S6ANCSYO.js +1271 -0
  23. package/dist/chunk-SEU7WWNQ.js +1251 -0
  24. package/dist/chunk-SNQ7NAIS.js +453 -0
  25. package/dist/{ulpi-RMMCUAGP-EWYUE7RU.js → chunk-TSLDGT5O.js} +73 -35
  26. package/dist/{chunk-EIWYSP3A.js → chunk-UXHCHOWQ.js} +83 -62
  27. package/dist/chunk-WED4LM5N.js +322 -0
  28. package/dist/chunk-WVOZE25N.js +6757 -0
  29. package/dist/{chunk-5SCG7UYM.js → chunk-XKF4DPUM.js} +7 -7
  30. package/dist/{chunk-74WVVWJ4.js → chunk-YOKL7RB5.js} +184 -15
  31. package/dist/chunk-Z53CAR7G.js +298 -0
  32. package/dist/{ci-JQ56YIKC.js → ci-COZRTPGQ.js} +124 -26
  33. package/dist/cloud-2F3NLVHN.js +274 -0
  34. package/dist/{codemap-HMYBXJL2.js → codemap-XNGMAF3F.js} +37 -37
  35. package/dist/codex-MB5YTMRT.js +132 -0
  36. package/dist/{config-YYWEN7U2.js → config-OOELBYTH.js} +1 -1
  37. package/dist/dist-2BJYR5EI.js +59 -0
  38. package/dist/dist-3EIQTZHT.js +1380 -0
  39. package/dist/{dist-WAMAQVPK.js → dist-4U5L2X2C.js} +2 -2
  40. package/dist/{dist-4XTJ6HLM.js → dist-54KAMNLO.js} +16 -15
  41. package/dist/dist-6M4MZWZW.js +58 -0
  42. package/dist/dist-6X576SU2.js +27 -0
  43. package/dist/dist-7QOEYLFX.js +103 -0
  44. package/dist/dist-AYBGHEDY.js +2541 -0
  45. package/dist/dist-EK45QNEM.js +45 -0
  46. package/dist/{dist-U7ZIJMZD.js → dist-FKFEJRPX.js} +16 -15
  47. package/dist/dist-GTEJUBBT.js +66 -0
  48. package/dist/dist-HA74OKJZ.js +40 -0
  49. package/dist/{dist-XG2GG5SD.js → dist-HU5RZAON.js} +14 -2
  50. package/dist/dist-IYE3OBRB.js +374 -0
  51. package/dist/{dist-7WLLPWWB.js → dist-JLU26AB6.js} +12 -9
  52. package/dist/{dist-6G7JC2RA.js → dist-KUCI6JFE.js} +49 -9
  53. package/dist/dist-NUEMFZFL.js +33 -0
  54. package/dist/{dist-GWGTAHNM.js → dist-NUXMDXZ3.js} +31 -3
  55. package/dist/{dist-5R4RYNQO.js → dist-YCNWHSLN.js} +15 -5
  56. package/dist/{dist-6MFVWIFF.js → dist-YFFG2ZD6.js} +9 -16
  57. package/dist/dist-ZG4OKCSR.js +15 -0
  58. package/dist/doctor-FKYSIHER.js +345 -0
  59. package/dist/{export-import-4A5MWLIA.js → export-import-JFQH4KSJ.js} +1 -1
  60. package/dist/{history-RNUWO4JZ.js → history-UMGQNQQ7.js} +7 -7
  61. package/dist/{hooks-installer-K2JXEBNN.js → hooks-installer-YEYTYA6Q.js} +2 -2
  62. package/dist/index.js +398 -622
  63. package/dist/{init-NQWFZPKO.js → init-TJYW5ROZ.js} +78 -12
  64. package/dist/job-HIDMAFW2.js +376 -0
  65. package/dist/jobs.memory-PLMMSFHB-VBECCTHN.js +33 -0
  66. package/dist/kiro-VMUHDFGK.js +153 -0
  67. package/dist/{launchd-OYXUAVW6.js → launchd-U3MSWBRH.js} +9 -17
  68. package/dist/mcp-PDUD7SGP.js +249 -0
  69. package/dist/mcp-installer-PQU3XOGO.js +259 -0
  70. package/dist/mcp-setup-OA7IB3H3.js +263 -0
  71. package/dist/{memory-D6ZFFCI2.js → memory-ZNAEAK3B.js} +17 -17
  72. package/dist/{ollama-3XCUZMZT-FYKHW4TZ.js → ollama-3XCUZMZT-4JMH6B7P.js} +1 -1
  73. package/dist/{openai-E7G2YAHU-IG33BFYF.js → openai-E7G2YAHU-T3HMBPH7.js} +2 -2
  74. package/dist/portal-JYWVHXDU.js +210 -0
  75. package/dist/prd-Q4J5NVAR.js +408 -0
  76. package/dist/repos-WWZXNN3P.js +271 -0
  77. package/dist/review-integration-RQE4KMAV.js +14 -0
  78. package/dist/{rules-3OFGWHP4.js → rules-Y4VSOY5Y.js} +3 -3
  79. package/dist/run-VPNXEIBY.js +687 -0
  80. package/dist/server-COL4AXKU-P7S7NNF6.js +11 -0
  81. package/dist/server-U7PQ6FTS-MG4MJPTS.js +20 -0
  82. package/dist/{skills-GY2CTPWN.js → skills-QEYU2N27.js} +4 -2
  83. package/dist/start-IJKY5RVT.js +303 -0
  84. package/dist/{status-SE43TIFJ.js → status-BHQYYGAL.js} +2 -2
  85. package/dist/{templates-O2XDKB5R.js → templates-CBRUJ66V.js} +6 -5
  86. package/dist/tui-DP7736EX.js +61 -0
  87. package/dist/ulpi-5EN6JCAS-LFE3WSL4.js +10 -0
  88. package/dist/{uninstall-KWGSGZTI.js → uninstall-BX6FOV77.js} +3 -3
  89. package/dist/{update-QYZA4D23.js → update-AQKTHFVQ.js} +3 -3
  90. package/dist/{version-checker-MVB74DEX.js → version-checker-5L5PUOEX.js} +2 -2
  91. package/package.json +13 -4
  92. package/dist/chunk-26LLDX2T.js +0 -553
  93. package/dist/chunk-DDRLI6JU.js +0 -331
  94. package/dist/chunk-IFATANHR.js +0 -453
  95. package/dist/chunk-JWUUVXIV.js +0 -13694
  96. package/dist/chunk-LD52XG3X.js +0 -4273
  97. package/dist/chunk-MIAQVCFW.js +0 -39
  98. package/dist/chunk-YYZOFYS6.js +0 -415
  99. package/dist/dist-XD4YI27T.js +0 -26
  100. package/dist/mcp-installer-TOYDP77X.js +0 -124
  101. package/dist/projects-COUJP4ZC.js +0 -271
  102. package/dist/review-KMGP2S25.js +0 -152
  103. package/dist/server-USLHY6GH-F4JSXCWA.js +0 -18
  104. package/dist/server-X5P6WH2M-ULZF5WHZ.js +0 -11
  105. package/dist/ui-4SM2SUI6.js +0 -167
  106. package/dist/ui.html +0 -698
  107. /package/dist/skills/{ulpi-generate-guardian → ulpi-generate-guards}/SKILL.md +0 -0
  108. /package/dist/skills/{ulpi-generate-guardian → ulpi-generate-guards}/references/framework-rules.md +0 -0
  109. /package/dist/skills/{ulpi-generate-guardian → ulpi-generate-guards}/references/language-rules.md +0 -0
@@ -1,553 +0,0 @@
1
- import {
2
- SESSIONS_DIR
3
- } from "./chunk-DDRLI6JU.js";
4
-
5
- // ../../packages/session-engine/dist/index.js
6
- import * as fs from "fs";
7
- import * as path from "path";
8
- import * as path2 from "path";
9
- import * as fs2 from "fs";
10
- import * as path3 from "path";
11
- import { execFileSync } from "child_process";
12
- function projectDirToSlug(dir) {
13
- const slug = dir.replace(/^\//, "").replace(/\//g, "-").replace(/\./g, "_").replace(/-{2,}/g, "-").replace(/^-|-$/g, "").toLowerCase();
14
- return slug || "unknown-project";
15
- }
16
- function validateSessionId(sessionId) {
17
- if (!sessionId || sessionId.includes("..") || sessionId.includes("/") || sessionId.includes("\\")) {
18
- throw new Error("Invalid session ID");
19
- }
20
- if (sessionId.length > 200) {
21
- throw new Error("Session ID too long");
22
- }
23
- if (!/^[a-zA-Z0-9_-]+$/.test(sessionId)) {
24
- throw new Error("Invalid session ID characters");
25
- }
26
- }
27
- var JsonSessionStore = class {
28
- baseDir;
29
- projectSlug;
30
- constructor(baseDir, projectDir) {
31
- this.baseDir = baseDir ?? path.dirname(SESSIONS_DIR);
32
- this.projectSlug = projectDir ? projectDirToSlug(projectDir) : null;
33
- }
34
- /**
35
- * Load session state from disk.
36
- * Returns `null` if the file does not exist or contains invalid JSON.
37
- */
38
- load(sessionId) {
39
- validateSessionId(sessionId);
40
- try {
41
- const raw = fs.readFileSync(this.sessionPath(sessionId), "utf-8");
42
- return JSON.parse(raw);
43
- } catch {
44
- }
45
- if (this.projectSlug) {
46
- try {
47
- const flatPath = path.join(this.baseDir, "sessions", `${sessionId}.json`);
48
- const raw = fs.readFileSync(flatPath, "utf-8");
49
- return JSON.parse(raw);
50
- } catch {
51
- }
52
- }
53
- if (!this.projectSlug) {
54
- try {
55
- const sessionsDir = path.join(this.baseDir, "sessions");
56
- const entries = fs.readdirSync(sessionsDir, { withFileTypes: true });
57
- for (const entry of entries) {
58
- if (!entry.isDirectory()) continue;
59
- try {
60
- const subPath = path.join(sessionsDir, entry.name, `${sessionId}.json`);
61
- const raw = fs.readFileSync(subPath, "utf-8");
62
- return JSON.parse(raw);
63
- } catch {
64
- }
65
- }
66
- } catch {
67
- }
68
- }
69
- return null;
70
- }
71
- /**
72
- * Persist session state atomically.
73
- *
74
- * The directory tree is created on demand. Data is first written to a
75
- * temporary file adjacent to the target, then renamed so the operation
76
- * is atomic on POSIX file-systems.
77
- */
78
- save(state) {
79
- validateSessionId(state.sessionId);
80
- const target = this.sessionPath(state.sessionId);
81
- const dir = path.dirname(target);
82
- fs.mkdirSync(dir, { recursive: true });
83
- const tmp = `${target}.tmp`;
84
- fs.writeFileSync(tmp, JSON.stringify(state, null, 2), "utf-8");
85
- fs.renameSync(tmp, target);
86
- }
87
- /**
88
- * Delete a session's state file. Silently ignores missing files.
89
- */
90
- delete(sessionId) {
91
- validateSessionId(sessionId);
92
- try {
93
- fs.unlinkSync(this.sessionPath(sessionId));
94
- return;
95
- } catch {
96
- }
97
- if (this.projectSlug) {
98
- try {
99
- fs.unlinkSync(path.join(this.baseDir, "sessions", `${sessionId}.json`));
100
- } catch (err) {
101
- if (err.code !== "ENOENT") throw err;
102
- }
103
- }
104
- }
105
- /**
106
- * List all persisted session IDs by scanning the sessions directory.
107
- */
108
- list() {
109
- if (this.projectSlug) {
110
- return this.scanJsonFiles(path.join(this.baseDir, "sessions", this.projectSlug));
111
- }
112
- const base = path.join(this.baseDir, "sessions");
113
- return [.../* @__PURE__ */ new Set([...this.scanJsonFiles(base), ...this.scanSubdirs(base)])];
114
- }
115
- /** Resolve the on-disk path for a given session ID. */
116
- sessionPath(sessionId) {
117
- if (this.projectSlug) {
118
- return path.join(this.baseDir, "sessions", this.projectSlug, `${sessionId}.json`);
119
- }
120
- return path.join(this.baseDir, "sessions", `${sessionId}.json`);
121
- }
122
- /**
123
- * List session IDs for a specific project directory.
124
- * Returns sessions where state.projectDir matches the given path.
125
- */
126
- listByProject(projectDir) {
127
- const slug = projectDirToSlug(projectDir);
128
- const results = this.scanJsonFiles(path.join(this.baseDir, "sessions", slug));
129
- const flatDir = path.join(this.baseDir, "sessions");
130
- const normalizedTarget = path.resolve(projectDir);
131
- for (const id of this.scanJsonFiles(flatDir)) {
132
- if (results.includes(id)) continue;
133
- try {
134
- const raw = fs.readFileSync(path.join(flatDir, `${id}.json`), "utf-8");
135
- const state = JSON.parse(raw);
136
- if (path.resolve(state.projectDir) === normalizedTarget) {
137
- results.push(id);
138
- }
139
- } catch {
140
- }
141
- }
142
- return results;
143
- }
144
- /**
145
- * Get the most recent session for a specific project directory.
146
- * Prefers sessions with actual activity over empty sessions.
147
- * Returns null if no sessions found for the project.
148
- */
149
- getLatestForProject(projectDir) {
150
- const sessionIds = this.listByProject(projectDir);
151
- if (sessionIds.length === 0) {
152
- return null;
153
- }
154
- let latest = null;
155
- let latestTime = -Infinity;
156
- let latestActive = null;
157
- let latestActiveTime = -Infinity;
158
- for (const id of sessionIds) {
159
- const s = this.load(id);
160
- if (!s) continue;
161
- const t = new Date(s.startedAt).getTime();
162
- if (t > latestTime) {
163
- latestTime = t;
164
- latest = s;
165
- }
166
- const hasActivity = s.filesRead.length > 0 || s.filesWritten.length > 0 || s.commandsRun.length > 0 || s.rulesEnforced > 0;
167
- if (hasActivity && t > latestActiveTime) {
168
- latestActiveTime = t;
169
- latestActive = s;
170
- }
171
- }
172
- return latestActive ?? latest;
173
- }
174
- scanJsonFiles(dir) {
175
- try {
176
- return fs.readdirSync(dir).filter((f) => f.endsWith(".json") && !f.endsWith(".tmp")).map((f) => f.replace(/\.json$/, ""));
177
- } catch {
178
- return [];
179
- }
180
- }
181
- scanSubdirs(base) {
182
- try {
183
- const ids = [];
184
- for (const entry of fs.readdirSync(base, { withFileTypes: true })) {
185
- if (entry.isDirectory()) {
186
- ids.push(...this.scanJsonFiles(path.join(base, entry.name)));
187
- }
188
- }
189
- return ids;
190
- } catch {
191
- return [];
192
- }
193
- }
194
- };
195
- function createEmptyState(sessionId, projectDir) {
196
- validateSessionId(sessionId);
197
- if (!projectDir || typeof projectDir !== "string") {
198
- throw new Error("Invalid project directory");
199
- }
200
- return {
201
- sessionId,
202
- startedAt: (/* @__PURE__ */ new Date()).toISOString(),
203
- projectDir,
204
- branch: void 0,
205
- filesRead: [],
206
- filesWritten: [],
207
- filesDeleted: [],
208
- commandsRun: [],
209
- testsRun: false,
210
- testsPassed: null,
211
- lintRun: false,
212
- lintPassed: null,
213
- buildRun: false,
214
- buildPassed: null,
215
- formatterRun: false,
216
- branchCreated: false,
217
- planApproved: false,
218
- rulesEnforced: 0,
219
- actionsBlocked: 0,
220
- autoActionsRun: 0,
221
- consecutiveBlocks: 0,
222
- lastNotifications: {},
223
- stack: void 0,
224
- phase: void 0,
225
- transcriptPath: void 0,
226
- untrackedFilesAtStart: void 0,
227
- workingTreeDirtyAtStart: void 0
228
- };
229
- }
230
- function createInitialState(sessionId, projectDir) {
231
- return createEmptyState(sessionId, projectDir);
232
- }
233
- function updateStateFromInput(state, input, config) {
234
- const next = {
235
- ...state,
236
- filesRead: [...state.filesRead],
237
- filesWritten: [...state.filesWritten],
238
- filesDeleted: [...state.filesDeleted],
239
- commandsRun: [...state.commandsRun],
240
- lastNotifications: { ...state.lastNotifications }
241
- };
242
- const toolName = input.tool_name;
243
- const toolInput = input.tool_input;
244
- const toolResponse = input.tool_response;
245
- if (toolName === "Read" && toolInput?.file_path) {
246
- const norm = normalizePath(toolInput.file_path, next.projectDir);
247
- if (!next.filesRead.includes(norm)) {
248
- next.filesRead.push(norm);
249
- }
250
- }
251
- if ((toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit") && toolInput?.file_path) {
252
- const norm = normalizePath(toolInput.file_path, next.projectDir);
253
- if (!next.filesWritten.includes(norm)) {
254
- next.filesWritten.push(norm);
255
- }
256
- }
257
- if (toolName === "Bash" && toolInput?.command) {
258
- const cmd = toolInput.command;
259
- next.commandsRun.push(cmd);
260
- trackDeletedFiles(cmd, next);
261
- if (isBranchCreationCommand(cmd)) {
262
- next.branchCreated = true;
263
- next.branch = extractBranchName(cmd) ?? next.branch;
264
- }
265
- const passed = didCommandPass(toolResponse);
266
- if (isTestCommand(cmd, config)) {
267
- next.testsRun = true;
268
- if (passed !== null) {
269
- next.testsPassed = passed;
270
- }
271
- }
272
- if (isLintCommand(cmd, config)) {
273
- next.lintRun = true;
274
- if (passed !== null) {
275
- next.lintPassed = passed;
276
- }
277
- }
278
- if (isBuildCommand(cmd, config)) {
279
- next.buildRun = true;
280
- if (passed !== null) {
281
- next.buildPassed = passed;
282
- }
283
- }
284
- if (isFormatCommand(cmd, config)) {
285
- next.formatterRun = true;
286
- }
287
- }
288
- return next;
289
- }
290
- var TEST_PATTERNS = [
291
- /\bjest\b/,
292
- /\bvitest\b/,
293
- /\bpytest\b/,
294
- /\bgo\s+test\b/,
295
- /\bcargo\s+test\b/,
296
- /\bphpunit\b/,
297
- /\bpest\b/,
298
- /\brspec\b/,
299
- /\bmocha\b/,
300
- /\bava\b/,
301
- /\bnpm\s+run\s+test\b/,
302
- /\bpnpm\s+(run\s+)?test\b/,
303
- /\byarn\s+(run\s+)?test\b/,
304
- /\bbun\s+test\b/,
305
- /\bmake\s+test\b/
306
- ];
307
- var LINT_PATTERNS = [
308
- /\beslint\b/,
309
- /\btsc\s+--noEmit\b/,
310
- /\btsc\b.*--noEmit\b/,
311
- /\bruff\s+check\b/,
312
- /\bruff\s+lint\b/,
313
- /\bcargo\s+clippy\b/,
314
- /\bphpstan\b/,
315
- /\bpylint\b/,
316
- /\bflake8\b/,
317
- /\bmypy\b/,
318
- /\bgolangci-lint\b/,
319
- /\brubocop\b/,
320
- /\bnpm\s+run\s+lint\b/,
321
- /\bpnpm\s+(run\s+)?lint\b/,
322
- /\byarn\s+(run\s+)?lint\b/
323
- ];
324
- var BUILD_PATTERNS = [
325
- /\bnpm\s+run\s+build\b/,
326
- /\bpnpm\s+(run\s+)?build\b/,
327
- /\byarn\s+(run\s+)?build\b/,
328
- /\bcargo\s+build\b/,
329
- /\bgo\s+build\b/,
330
- /\bmake\s+build\b/,
331
- /\bmake\b(?!.*test|.*lint|.*format)/,
332
- /\btsc\b(?!.*--noEmit)/,
333
- /\bvite\s+build\b/,
334
- /\bnext\s+build\b/
335
- ];
336
- var FORMAT_PATTERNS = [
337
- /\bprettier\b/,
338
- /\bblack\b/,
339
- /\bruff\s+format\b/,
340
- /\bgofmt\b/,
341
- /\bgoimports\b/,
342
- /\brustfmt\b/,
343
- /\bphp-cs-fixer\b/,
344
- /\bpint\b/,
345
- /\bnpm\s+run\s+format\b/,
346
- /\bpnpm\s+(run\s+)?format\b/,
347
- /\byarn\s+(run\s+)?format\b/
348
- ];
349
- function matchesPatterns(command, patterns, configCommand) {
350
- if (configCommand && command.includes(configCommand)) {
351
- return true;
352
- }
353
- return patterns.some((p) => p.test(command));
354
- }
355
- function isTestCommand(command, config) {
356
- return matchesPatterns(command, TEST_PATTERNS, config?.test_command);
357
- }
358
- function isLintCommand(command, config) {
359
- return matchesPatterns(command, LINT_PATTERNS, config?.lint_command);
360
- }
361
- function isBuildCommand(command, config) {
362
- return matchesPatterns(command, BUILD_PATTERNS, config?.build_command);
363
- }
364
- function isFormatCommand(command, config) {
365
- return matchesPatterns(command, FORMAT_PATTERNS, config?.format_command);
366
- }
367
- var BRANCH_CREATION_RE = /\bgit\s+(?:checkout\s+-b|switch\s+-c|branch)\s+/;
368
- function isBranchCreationCommand(command) {
369
- return BRANCH_CREATION_RE.test(command);
370
- }
371
- function extractBranchName(command) {
372
- const cbMatch = command.match(
373
- /\bgit\s+(?:checkout\s+-b|switch\s+-c)\s+(\S+)/
374
- );
375
- if (cbMatch) return cbMatch[1];
376
- const brMatch = command.match(/\bgit\s+branch\s+(?!-\w)(\S+)/);
377
- if (brMatch) return brMatch[1];
378
- return void 0;
379
- }
380
- function trackDeletedFiles(command, state) {
381
- const rmMatch = command.match(/\brm\s+(.*)/);
382
- if (!rmMatch) return;
383
- const args = rmMatch[1].split(/\s+/).filter((a) => {
384
- return a.length > 0 && !a.startsWith("-");
385
- });
386
- for (const arg of args) {
387
- const norm = normalizePath(arg, state.projectDir);
388
- if (!state.filesDeleted.includes(norm)) {
389
- state.filesDeleted.push(norm);
390
- }
391
- }
392
- }
393
- function normalizePath(filePath, projectDir) {
394
- let normalized = filePath;
395
- if (path2.isAbsolute(normalized)) {
396
- const prefix = projectDir.endsWith(path2.sep) ? projectDir : projectDir + path2.sep;
397
- if (normalized.startsWith(prefix)) {
398
- normalized = normalized.slice(prefix.length);
399
- }
400
- }
401
- normalized = normalized.replace(/^\.\//, "");
402
- return normalized;
403
- }
404
- function didCommandPass(response) {
405
- if (!response) return null;
406
- if (typeof response.exit_code === "number") {
407
- return response.exit_code === 0;
408
- }
409
- if (typeof response.success === "boolean") {
410
- return response.success;
411
- }
412
- return null;
413
- }
414
- var MAX_EVENTS_PER_SESSION = 1e4;
415
- function eventLogPath(sessionId, baseDir, projectDir) {
416
- if (projectDir) {
417
- const slug = projectDirToSlug(projectDir);
418
- return path3.join(baseDir, "sessions", slug, `${sessionId}.events.jsonl`);
419
- }
420
- return path3.join(baseDir, "sessions", `${sessionId}.events.jsonl`);
421
- }
422
- function countFilePath(logPath) {
423
- return logPath + ".count";
424
- }
425
- function readEventCount(logPath) {
426
- const countPath = countFilePath(logPath);
427
- try {
428
- const raw = fs2.readFileSync(countPath, "utf-8").trim();
429
- const count2 = parseInt(raw, 10);
430
- if (!Number.isNaN(count2) && count2 >= 0) {
431
- return count2;
432
- }
433
- } catch {
434
- }
435
- let count = 0;
436
- try {
437
- const existing = fs2.readFileSync(logPath, "utf-8");
438
- count = existing.split("\n").filter((l) => l.trim().length > 0).length;
439
- } catch {
440
- }
441
- try {
442
- fs2.writeFileSync(countPath, String(count), "utf-8");
443
- } catch {
444
- }
445
- return count;
446
- }
447
- function writeEventCount(logPath, count) {
448
- try {
449
- fs2.writeFileSync(countFilePath(logPath), String(count), "utf-8");
450
- } catch {
451
- }
452
- }
453
- function appendEvent(sessionId, event, projectDir, baseDir) {
454
- validateSessionId(sessionId);
455
- const dir = baseDir ?? path3.dirname(SESSIONS_DIR);
456
- const logPath = eventLogPath(sessionId, dir, projectDir);
457
- const currentCount = readEventCount(logPath);
458
- if (currentCount >= MAX_EVENTS_PER_SESSION) {
459
- console.error(
460
- `[ulpi] Session ${sessionId}: event log at capacity (${MAX_EVENTS_PER_SESSION}), dropping new events`
461
- );
462
- return;
463
- }
464
- fs2.mkdirSync(path3.dirname(logPath), { recursive: true });
465
- fs2.appendFileSync(logPath, JSON.stringify(event) + "\n", "utf-8");
466
- writeEventCount(logPath, currentCount + 1);
467
- }
468
- function readEvents(sessionId, projectDir, baseDir) {
469
- validateSessionId(sessionId);
470
- const dir = baseDir ?? path3.dirname(SESSIONS_DIR);
471
- const logPath = eventLogPath(sessionId, dir, projectDir);
472
- let raw;
473
- try {
474
- raw = fs2.readFileSync(logPath, "utf-8");
475
- } catch {
476
- if (projectDir) {
477
- const flatPath = eventLogPath(sessionId, dir);
478
- try {
479
- raw = fs2.readFileSync(flatPath, "utf-8");
480
- } catch {
481
- return [];
482
- }
483
- } else {
484
- return [];
485
- }
486
- }
487
- const events = [];
488
- for (const line of raw.split("\n")) {
489
- const trimmed = line.trim();
490
- if (trimmed.length === 0) continue;
491
- try {
492
- events.push(JSON.parse(trimmed));
493
- } catch {
494
- }
495
- }
496
- return events;
497
- }
498
- function nextPhase(current, hookEvent, context) {
499
- switch (current) {
500
- case "idle":
501
- if (hookEvent === "SessionStart") {
502
- return { phase: "active" };
503
- }
504
- break;
505
- case "active":
506
- if (hookEvent === "PostToolUse" && context?.hasNewCommit) {
507
- return { phase: "active_committed", sideEffect: "capture" };
508
- }
509
- if (hookEvent === "SessionEnd") {
510
- return { phase: "ended" };
511
- }
512
- break;
513
- case "active_committed":
514
- if (hookEvent === "PostToolUse" && context?.hasNewCommit) {
515
- return { phase: "active_committed", sideEffect: "capture" };
516
- }
517
- if (hookEvent === "SessionEnd") {
518
- return { phase: "ended" };
519
- }
520
- break;
521
- case "ended":
522
- break;
523
- }
524
- return { phase: current };
525
- }
526
- function detectNewCommit(projectDir, lastKnownHead) {
527
- try {
528
- const currentHead = execFileSync("git", ["rev-parse", "HEAD"], {
529
- cwd: projectDir,
530
- encoding: "utf-8",
531
- timeout: 3e3
532
- }).toString().trim();
533
- if (currentHead && currentHead !== lastKnownHead) {
534
- return { hasNewCommit: true, newHead: currentHead };
535
- }
536
- return { hasNewCommit: false, newHead: null };
537
- } catch {
538
- return { hasNewCommit: false, newHead: null };
539
- }
540
- }
541
-
542
- export {
543
- projectDirToSlug,
544
- validateSessionId,
545
- JsonSessionStore,
546
- createEmptyState,
547
- createInitialState,
548
- updateStateFromInput,
549
- appendEvent,
550
- readEvents,
551
- nextPhase,
552
- detectNewCommit
553
- };