@ulpi/cli 0.1.4 → 0.1.6

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 (112) hide show
  1. package/LICENSE +21 -0
  2. package/dist/{auth-PN7TMQHV-2W4ICG64.js → auth-FWM7MM4Q-VZC3U2XZ.js} +1 -1
  3. package/dist/{auth-ECQ3IB4E.js → auth-HDK7ECJL.js} +2 -1
  4. package/dist/{chunk-3SBPZRB5.js → chunk-3BCW6ABU.js} +402 -142
  5. package/dist/{chunk-JGBXM5NC.js → chunk-3WB5CXH4.js} +180 -5
  6. package/dist/{chunk-2HEE5OKX.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-76D3BYJD.js +221 -0
  12. package/dist/{chunk-ZLYRPD7I.js → chunk-AWOSRA5F.js} +1 -1
  13. package/dist/{chunk-PDR55ZNW.js → chunk-BFEKZZHM.js} +274 -57
  14. package/dist/chunk-C7CLUQI6.js +1286 -0
  15. package/dist/{chunk-7AL4DOEJ.js → chunk-E3B5NROU.js} +7 -7
  16. package/dist/chunk-EJ7TW77N.js +1418 -0
  17. package/dist/{chunk-5J6NLQUN.js → chunk-IV6MWETF.js} +383 -168
  18. package/dist/chunk-IZPJHSPX.js +1478 -0
  19. package/dist/chunk-JLHNLM3C.js +228 -0
  20. package/dist/{chunk-BZL5H4YQ.js → chunk-KYYI23AQ.js} +2 -2
  21. package/dist/{chunk-2CLNOKPA.js → chunk-RSFJ6QSR.js} +18 -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-JCJ273T6.js → chunk-TSLDGT5O.js} +73 -35
  26. package/dist/{chunk-SPOI23SB.js → chunk-UXHCHOWQ.js} +83 -62
  27. package/dist/chunk-V2H5D6Y3.js +146 -0
  28. package/dist/{chunk-QJ5GSMEC.js → chunk-VVEDXI7E.js} +2 -1
  29. package/dist/chunk-VXH5Y4FO.js +6761 -0
  30. package/dist/chunk-WED4LM5N.js +322 -0
  31. package/dist/{chunk-74WVVWJ4.js → chunk-YOKL7RB5.js} +184 -15
  32. package/dist/chunk-Z53CAR7G.js +298 -0
  33. package/dist/ci-X3U2W4HC.js +854 -0
  34. package/dist/cloud-2F3NLVHN.js +274 -0
  35. package/dist/{codemap-RKSD4MIE.js → codemap-XNGMAF3F.js} +37 -37
  36. package/dist/codex-MB5YTMRT.js +132 -0
  37. package/dist/{config-EGAXXCGL.js → config-OOELBYTH.js} +1 -1
  38. package/dist/dist-2BJYR5EI.js +59 -0
  39. package/dist/dist-2K7IEVTA.js +43 -0
  40. package/dist/dist-3EIQTZHT.js +1380 -0
  41. package/dist/{dist-YA2BWZB2.js → dist-4U5L2X2C.js} +2 -2
  42. package/dist/{dist-UKMCJBB2.js → dist-54KAMNLO.js} +16 -15
  43. package/dist/dist-6M4MZWZW.js +58 -0
  44. package/dist/dist-6X576SU2.js +27 -0
  45. package/dist/dist-7QOEYLFX.js +103 -0
  46. package/dist/dist-AYBGHEDY.js +2541 -0
  47. package/dist/dist-EK45QNEM.js +45 -0
  48. package/dist/{dist-CS2VKNYS.js → dist-FKFEJRPX.js} +16 -15
  49. package/dist/dist-GTEJUBBT.js +66 -0
  50. package/dist/dist-HA74OKJZ.js +40 -0
  51. package/dist/dist-HU5RZAON.js +48 -0
  52. package/dist/dist-IYE3OBRB.js +374 -0
  53. package/dist/{dist-GJYT2OQV.js → dist-JLU26AB6.js} +12 -9
  54. package/dist/{dist-6G7JC2RA.js → dist-KUCI6JFE.js} +49 -9
  55. package/dist/dist-NUEMFZFL.js +33 -0
  56. package/dist/{dist-RKOGLK7R.js → dist-NUXMDXZ3.js} +31 -3
  57. package/dist/{dist-QAU3LGJN.js → dist-YCNWHSLN.js} +15 -5
  58. package/dist/{dist-CB5D5LMO.js → dist-YFFG2ZD6.js} +9 -16
  59. package/dist/dist-ZG4OKCSR.js +15 -0
  60. package/dist/doctor-SI4LLLDZ.js +345 -0
  61. package/dist/{export-import-4A5MWLIA.js → export-import-JFQH4KSJ.js} +1 -1
  62. package/dist/{history-3MOBX4MA.js → history-5NE46ZAH.js} +7 -7
  63. package/dist/hooks-installer-UN5JZLDQ.js +19 -0
  64. package/dist/index.js +395 -619
  65. package/dist/{init-6CH4HV5T.js → init-5FK3VKRT.js} +79 -13
  66. package/dist/job-HIDMAFW2.js +376 -0
  67. package/dist/jobs.memory-PLMMSFHB-VBECCTHN.js +33 -0
  68. package/dist/kiro-VMUHDFGK.js +153 -0
  69. package/dist/{launchd-LF2QMSKZ.js → launchd-6AWT54HR.js} +9 -17
  70. package/dist/mcp-PDUD7SGP.js +249 -0
  71. package/dist/mcp-installer-PQU3XOGO.js +259 -0
  72. package/dist/mcp-setup-OA7IB3H3.js +263 -0
  73. package/dist/{memory-Y6OZTXJ2.js → memory-ZNAEAK3B.js} +17 -17
  74. package/dist/{ollama-3XCUZMZT-FYKHW4TZ.js → ollama-3XCUZMZT-4JMH6B7P.js} +1 -1
  75. package/dist/{openai-E7G2YAHU-UYY4ZWON.js → openai-E7G2YAHU-T3HMBPH7.js} +2 -2
  76. package/dist/portal-JYWVHXDU.js +210 -0
  77. package/dist/prd-Q4J5NVAR.js +408 -0
  78. package/dist/repos-WWZXNN3P.js +271 -0
  79. package/dist/review-integration-5WHEJU2A.js +14 -0
  80. package/dist/{rules-E427DKYJ.js → rules-Y4VSOY5Y.js} +3 -3
  81. package/dist/run-VPNXEIBY.js +687 -0
  82. package/dist/server-COL4AXKU-P7S7NNF6.js +11 -0
  83. package/dist/server-KKSETHDV-XSSLEENT.js +20 -0
  84. package/dist/{skills-CX73O3IV.js → skills-QEYU2N27.js} +4 -2
  85. package/dist/start-JYOEL7AJ.js +303 -0
  86. package/dist/{status-4DFHDJMN.js → status-BHQYYGAL.js} +2 -2
  87. package/dist/{templates-U7T6MARD.js → templates-CBRUJ66V.js} +4 -3
  88. package/dist/tui-DP7736EX.js +61 -0
  89. package/dist/ulpi-5EN6JCAS-LFE3WSL4.js +10 -0
  90. package/dist/{uninstall-6SW35IK4.js → uninstall-ICUV6DDV.js} +3 -3
  91. package/dist/{update-M6IBJNYP.js → update-7ZMAYRBH.js} +3 -3
  92. package/dist/{version-checker-Q6YTYAGP.js → version-checker-4ZFMZA7Y.js} +2 -2
  93. package/package.json +39 -31
  94. package/dist/chunk-2MZER6ND.js +0 -415
  95. package/dist/chunk-2VYFVYJL.js +0 -4273
  96. package/dist/chunk-6OCEY7JY.js +0 -422
  97. package/dist/chunk-7LXY5UVC.js +0 -330
  98. package/dist/chunk-B55DDP24.js +0 -136
  99. package/dist/chunk-JWUUVXIV.js +0 -13694
  100. package/dist/chunk-MIAQVCFW.js +0 -39
  101. package/dist/chunk-YM2HV4IA.js +0 -505
  102. package/dist/ci-STSL2LSP.js +0 -370
  103. package/dist/mcp-installer-NQCGKQ23.js +0 -124
  104. package/dist/projects-ATHDD3D6.js +0 -271
  105. package/dist/review-ADUPV3PN.js +0 -152
  106. package/dist/server-USLHY6GH-AEOJC5ST.js +0 -18
  107. package/dist/server-X5P6WH2M-7K2RY34N.js +0 -11
  108. package/dist/skills/ulpi-generate-guardian/SKILL.md +0 -750
  109. package/dist/skills/ulpi-generate-guardian/references/framework-rules.md +0 -849
  110. package/dist/skills/ulpi-generate-guardian/references/language-rules.md +0 -591
  111. package/dist/ui-OWXZ3YSR.js +0 -167
  112. package/dist/ui.html +0 -698
@@ -0,0 +1,1478 @@
1
+ import {
2
+ evaluateRules,
3
+ isDangerousCommand,
4
+ matchesFilePattern
5
+ } from "./chunk-5MI5GIXM.js";
6
+
7
+ // ../../packages/agent-plugins/dist/index.js
8
+ import { spawn } from "child_process";
9
+ import { accessSync } from "fs";
10
+ import { platform } from "os";
11
+ import { isAbsolute } from "path";
12
+ import { randomUUID } from "crypto";
13
+ import { execFileSync } from "child_process";
14
+ import { execFileSync as execFileSync2 } from "child_process";
15
+ import * as fs from "fs";
16
+ import * as path from "path";
17
+ import * as os from "os";
18
+ var DEFAULT_ENV_EXCLUDE_PATTERNS = [
19
+ "*_API_KEY",
20
+ "*_SECRET_KEY",
21
+ "*_SECRET"
22
+ ];
23
+ function globMatch(pattern, str) {
24
+ const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&");
25
+ const regex = new RegExp(
26
+ "^" + escaped.replace(/\*/g, ".*").replace(/\?/g, ".") + "$"
27
+ );
28
+ return regex.test(str);
29
+ }
30
+ function filterEnv(env, excludePatterns, passthroughPatterns) {
31
+ if (!excludePatterns || excludePatterns.length === 0) {
32
+ return env;
33
+ }
34
+ const filtered = {};
35
+ for (const [key, value] of Object.entries(env)) {
36
+ const matchesExclude = excludePatterns.some(
37
+ (pattern) => globMatch(pattern, key)
38
+ );
39
+ if (!matchesExclude) {
40
+ filtered[key] = value;
41
+ } else if (passthroughPatterns.length > 0 && passthroughPatterns.some((pattern) => globMatch(pattern, key))) {
42
+ filtered[key] = value;
43
+ }
44
+ }
45
+ return filtered;
46
+ }
47
+ function getEnvExclusionReport(env = process.env, passthroughPatterns = [], additionalExclude = []) {
48
+ const excludePatterns = [...DEFAULT_ENV_EXCLUDE_PATTERNS, ...additionalExclude];
49
+ const blocked = [];
50
+ const allowed = [];
51
+ for (const key of Object.keys(env)) {
52
+ const matchesExclude = excludePatterns.some(
53
+ (pattern) => globMatch(pattern, key)
54
+ );
55
+ if (!matchesExclude) {
56
+ continue;
57
+ }
58
+ const matchesPassthrough = passthroughPatterns.length > 0 && passthroughPatterns.some((pattern) => globMatch(pattern, key));
59
+ if (matchesPassthrough) {
60
+ allowed.push(key);
61
+ } else {
62
+ blocked.push(key);
63
+ }
64
+ }
65
+ return { blocked: blocked.sort(), allowed: allowed.sort() };
66
+ }
67
+ function findCommandPath(command) {
68
+ return new Promise((resolve) => {
69
+ const isWindows = platform() === "win32";
70
+ const trimmed = command.trim();
71
+ const normalized = trimmed.startsWith('"') && trimmed.endsWith('"') ? trimmed.slice(1, -1) : trimmed;
72
+ const isPathLike = isAbsolute(normalized) || normalized.includes("/") || normalized.includes("\\");
73
+ if (isPathLike) {
74
+ try {
75
+ accessSync(normalized);
76
+ return resolve({ found: true, path: normalized });
77
+ } catch {
78
+ return resolve({ found: false, path: "" });
79
+ }
80
+ }
81
+ const whichCmd = isWindows ? "where" : "which";
82
+ const proc = spawn(whichCmd, [command], {
83
+ stdio: ["ignore", "pipe", "pipe"],
84
+ shell: isWindows
85
+ });
86
+ let stdout = "";
87
+ proc.stdout?.on("data", (data) => {
88
+ stdout += data.toString();
89
+ });
90
+ proc.on("error", () => {
91
+ resolve({ found: false, path: "" });
92
+ });
93
+ proc.on("close", (code) => {
94
+ if (code === 0 && stdout.trim()) {
95
+ const firstPath = stdout.trim().split(/\r?\n/)[0] ?? "";
96
+ resolve({ found: true, path: firstPath.trim() });
97
+ } else {
98
+ resolve({ found: false, path: "" });
99
+ }
100
+ });
101
+ setTimeout(() => {
102
+ proc.kill();
103
+ resolve({ found: false, path: "" });
104
+ }, 15e3);
105
+ });
106
+ }
107
+ var MAX_STREAM_CHARS = 2e6;
108
+ function appendCapped(current, chunk, max) {
109
+ const combined = current + chunk;
110
+ if (combined.length <= max) {
111
+ return combined;
112
+ }
113
+ return "[...truncated...]\n" + combined.slice(combined.length - max);
114
+ }
115
+ var BaseAgentPlugin = class {
116
+ config = {};
117
+ ready = false;
118
+ commandPath;
119
+ defaultFlags = [];
120
+ defaultTimeout = 0;
121
+ envExclude = [];
122
+ envPassthrough = [];
123
+ executions = /* @__PURE__ */ new Map();
124
+ currentExecutionId;
125
+ async initialize(config) {
126
+ this.config = config;
127
+ if (typeof config.command === "string") {
128
+ this.commandPath = config.command;
129
+ }
130
+ if (Array.isArray(config.defaultFlags)) {
131
+ this.defaultFlags = config.defaultFlags.filter(
132
+ (f) => typeof f === "string"
133
+ );
134
+ }
135
+ if (typeof config.timeout === "number" && config.timeout > 0) {
136
+ this.defaultTimeout = config.timeout;
137
+ }
138
+ if (Array.isArray(config.envExclude)) {
139
+ this.envExclude = config.envExclude.filter(
140
+ (p) => typeof p === "string" && p.length > 0
141
+ );
142
+ }
143
+ if (Array.isArray(config.envPassthrough)) {
144
+ this.envPassthrough = config.envPassthrough.filter(
145
+ (p) => typeof p === "string" && p.length > 0
146
+ );
147
+ }
148
+ this.ready = true;
149
+ }
150
+ async isReady() {
151
+ return this.ready;
152
+ }
153
+ async detect() {
154
+ const command = this.commandPath ?? this.meta.cliCommand;
155
+ const result = await findCommandPath(command);
156
+ if (!result.found) {
157
+ return {
158
+ available: false,
159
+ error: `${this.meta.name} CLI not found in PATH`
160
+ };
161
+ }
162
+ this.commandPath = result.path;
163
+ return {
164
+ available: true,
165
+ executablePath: result.path
166
+ };
167
+ }
168
+ /**
169
+ * Get input to write to stdin after spawning the process.
170
+ * Override in subclasses to provide stdin input (e.g., prompt content).
171
+ */
172
+ getStdinInput(prompt, options) {
173
+ return void 0;
174
+ }
175
+ execute(prompt, options) {
176
+ const executionId = randomUUID();
177
+ const command = this.commandPath ?? this.meta.cliCommand;
178
+ const args = this.buildArgs(prompt, options);
179
+ const startedAt = /* @__PURE__ */ new Date();
180
+ const timeout = options?.timeout ?? this.defaultTimeout;
181
+ const effectiveExclude = [
182
+ ...DEFAULT_ENV_EXCLUDE_PATTERNS,
183
+ ...this.envExclude
184
+ ];
185
+ const baseEnv = filterEnv(process.env, effectiveExclude, this.envPassthrough);
186
+ const env = { ...baseEnv, ...options?.env };
187
+ const allArgs = [...this.defaultFlags, ...options?.flags ?? [], ...args];
188
+ let resolvePromise;
189
+ let rejectPromise;
190
+ const promise = new Promise((resolve, reject) => {
191
+ resolvePromise = resolve;
192
+ rejectPromise = reject;
193
+ });
194
+ const isWindows = platform() === "win32";
195
+ const proc = spawn(command, allArgs, {
196
+ cwd: options?.cwd ?? process.cwd(),
197
+ env,
198
+ stdio: ["pipe", "pipe", "pipe"],
199
+ shell: isWindows
200
+ });
201
+ const stdinInput = this.getStdinInput(prompt, options);
202
+ if (stdinInput !== void 0 && proc.stdin) {
203
+ proc.stdin.write(stdinInput);
204
+ proc.stdin.end();
205
+ } else if (proc.stdin) {
206
+ proc.stdin.end();
207
+ }
208
+ const execution = {
209
+ executionId,
210
+ process: proc,
211
+ startedAt,
212
+ stdout: "",
213
+ stderr: "",
214
+ interrupted: false,
215
+ resolve: resolvePromise,
216
+ reject: rejectPromise,
217
+ options
218
+ };
219
+ this.executions.set(executionId, execution);
220
+ this.currentExecutionId = executionId;
221
+ options?.onStart?.(executionId);
222
+ proc.stdout?.on("data", (data) => {
223
+ const text = data.toString();
224
+ execution.stdout = appendCapped(
225
+ execution.stdout,
226
+ text,
227
+ MAX_STREAM_CHARS
228
+ );
229
+ options?.onStdout?.(text);
230
+ });
231
+ proc.stderr?.on("data", (data) => {
232
+ const text = data.toString();
233
+ execution.stderr = appendCapped(
234
+ execution.stderr,
235
+ text,
236
+ MAX_STREAM_CHARS
237
+ );
238
+ options?.onStderr?.(text);
239
+ });
240
+ proc.on("error", (error) => {
241
+ this.completeExecution(executionId, "failed", void 0, error.message);
242
+ });
243
+ proc.on("close", (code, signal) => {
244
+ let status;
245
+ if (execution.interrupted) {
246
+ status = "interrupted";
247
+ } else if (signal === "SIGTERM" || signal === "SIGKILL") {
248
+ status = execution.timeoutId ? "timeout" : "interrupted";
249
+ } else if (code === 0) {
250
+ status = "completed";
251
+ } else {
252
+ status = "failed";
253
+ }
254
+ this.completeExecution(executionId, status, code ?? void 0);
255
+ });
256
+ if (timeout > 0) {
257
+ execution.timeoutId = setTimeout(() => {
258
+ if (this.executions.has(executionId)) {
259
+ proc.kill("SIGTERM");
260
+ setTimeout(() => {
261
+ if (this.executions.has(executionId)) {
262
+ proc.kill("SIGKILL");
263
+ }
264
+ }, 5e3);
265
+ }
266
+ }, timeout);
267
+ }
268
+ return {
269
+ executionId,
270
+ promise,
271
+ interrupt: () => this.interrupt(executionId),
272
+ isRunning: () => this.executions.has(executionId)
273
+ };
274
+ }
275
+ completeExecution(executionId, status, exitCode, error) {
276
+ const execution = this.executions.get(executionId);
277
+ if (!execution) return;
278
+ if (execution.timeoutId) {
279
+ clearTimeout(execution.timeoutId);
280
+ }
281
+ const endedAt = /* @__PURE__ */ new Date();
282
+ const durationMs = endedAt.getTime() - execution.startedAt.getTime();
283
+ const result = {
284
+ executionId,
285
+ status,
286
+ exitCode,
287
+ stdout: execution.stdout,
288
+ stderr: execution.stderr,
289
+ durationMs,
290
+ error,
291
+ interrupted: execution.interrupted,
292
+ startedAt: execution.startedAt.toISOString(),
293
+ endedAt: endedAt.toISOString()
294
+ };
295
+ this.executions.delete(executionId);
296
+ if (this.currentExecutionId === executionId) {
297
+ this.currentExecutionId = void 0;
298
+ }
299
+ if (execution.options?.onEnd) {
300
+ try {
301
+ execution.options.onEnd(result);
302
+ } catch {
303
+ }
304
+ }
305
+ execution.resolve(result);
306
+ }
307
+ interrupt(executionId) {
308
+ const execution = this.executions.get(executionId);
309
+ if (!execution) return false;
310
+ execution.interrupted = true;
311
+ execution.process.kill("SIGTERM");
312
+ setTimeout(() => {
313
+ if (this.executions.has(executionId)) {
314
+ execution.process.kill("SIGKILL");
315
+ }
316
+ }, 5e3);
317
+ return true;
318
+ }
319
+ interruptAll() {
320
+ for (const executionId of this.executions.keys()) {
321
+ this.interrupt(executionId);
322
+ }
323
+ }
324
+ getCurrentExecution() {
325
+ if (!this.currentExecutionId) return void 0;
326
+ const execution = this.executions.get(this.currentExecutionId);
327
+ if (!execution) return void 0;
328
+ const executionId = this.currentExecutionId;
329
+ return {
330
+ executionId,
331
+ promise: new Promise((resolve, reject) => {
332
+ execution.resolve = resolve;
333
+ execution.reject = reject;
334
+ }),
335
+ interrupt: () => this.interrupt(executionId),
336
+ isRunning: () => this.executions.has(executionId)
337
+ };
338
+ }
339
+ async preflight(options) {
340
+ const startTime = Date.now();
341
+ const timeout = options?.timeout ?? 15e3;
342
+ try {
343
+ const detection = await this.detect();
344
+ if (!detection.available) {
345
+ return {
346
+ success: false,
347
+ error: detection.error ?? "Agent not available",
348
+ suggestion: `Make sure ${this.meta.name} is installed and accessible`,
349
+ durationMs: Date.now() - startTime
350
+ };
351
+ }
352
+ const testPrompt = "Respond with exactly: PREFLIGHT_OK";
353
+ let stdoutCapture = "";
354
+ const handle = this.execute(testPrompt, {
355
+ timeout,
356
+ onStdout: (data) => {
357
+ stdoutCapture += data;
358
+ }
359
+ });
360
+ const result = await handle.promise;
361
+ const durationMs = Date.now() - startTime;
362
+ if (result.status === "completed" && stdoutCapture.length > 0) {
363
+ return { success: true, durationMs };
364
+ }
365
+ return {
366
+ success: false,
367
+ error: result.error ?? `Agent ${result.status} without output`,
368
+ suggestion: `Verify ${this.meta.name} is properly configured`,
369
+ durationMs
370
+ };
371
+ } catch (error) {
372
+ return {
373
+ success: false,
374
+ error: error instanceof Error ? error.message : String(error),
375
+ durationMs: Date.now() - startTime
376
+ };
377
+ }
378
+ }
379
+ getSandboxRequirements() {
380
+ return null;
381
+ }
382
+ async dispose() {
383
+ this.interruptAll();
384
+ this.ready = false;
385
+ }
386
+ };
387
+ var ClaudeAgentPlugin = class extends BaseAgentPlugin {
388
+ meta = {
389
+ name: "claude",
390
+ version: "1.0.0",
391
+ description: "Anthropic Claude Code CLI for AI-assisted coding",
392
+ cliCommand: "claude",
393
+ supportsHooks: true,
394
+ supportsSkills: true,
395
+ supportsStreaming: true,
396
+ supportsSubagentTracing: true
397
+ };
398
+ model;
399
+ agent;
400
+ skipPermissions = true;
401
+ async initialize(config) {
402
+ await super.initialize(config);
403
+ if (typeof config.model === "string" && config.model.length > 0) {
404
+ this.model = config.model;
405
+ }
406
+ if (typeof config.agent === "string" && config.agent.length > 0) {
407
+ this.agent = config.agent;
408
+ }
409
+ if (typeof config.skipPermissions === "boolean") {
410
+ this.skipPermissions = config.skipPermissions;
411
+ }
412
+ if (typeof config.sandbox === "string" && config.sandbox.length > 0) {
413
+ this.skipPermissions = config.sandbox === "danger-full-access";
414
+ }
415
+ }
416
+ async detect() {
417
+ const command = this.commandPath ?? this.meta.cliCommand;
418
+ const findResult = await findCommandPath(command);
419
+ if (!findResult.found) {
420
+ return {
421
+ available: false,
422
+ error: "Claude CLI not found in PATH. Install with: npm install -g @anthropic-ai/claude-code"
423
+ };
424
+ }
425
+ this.commandPath = findResult.path;
426
+ return {
427
+ available: true,
428
+ executablePath: findResult.path
429
+ };
430
+ }
431
+ buildArgs(prompt, options) {
432
+ const args = ["--print"];
433
+ args.push("--verbose", "--output-format", "stream-json");
434
+ if (this.model) {
435
+ args.push("--model", this.model);
436
+ }
437
+ if (this.skipPermissions) {
438
+ args.push("--dangerously-skip-permissions");
439
+ }
440
+ if (this.agent) {
441
+ args.push("--agent", this.agent);
442
+ }
443
+ return args;
444
+ }
445
+ getStdinInput(prompt, options) {
446
+ return prompt;
447
+ }
448
+ };
449
+ var createClaudeAgent = () => new ClaudeAgentPlugin();
450
+ var claude_default = createClaudeAgent;
451
+ var GeminiAgentPlugin = class extends BaseAgentPlugin {
452
+ meta = {
453
+ name: "gemini",
454
+ version: "1.0.0",
455
+ description: "Google Gemini CLI for AI-assisted coding",
456
+ cliCommand: "gemini",
457
+ supportsHooks: false,
458
+ supportsSkills: true,
459
+ supportsStreaming: true,
460
+ supportsSubagentTracing: true
461
+ };
462
+ model;
463
+ yoloMode = true;
464
+ async initialize(config) {
465
+ await super.initialize(config);
466
+ if (typeof config.model === "string" && config.model.length > 0) {
467
+ this.model = config.model;
468
+ }
469
+ if (typeof config.yoloMode === "boolean") {
470
+ this.yoloMode = config.yoloMode;
471
+ }
472
+ if (typeof config.sandbox === "string" && config.sandbox.length > 0) {
473
+ this.yoloMode = config.sandbox === "danger-full-access";
474
+ }
475
+ }
476
+ async detect() {
477
+ const command = this.commandPath ?? this.meta.cliCommand;
478
+ const findResult = await findCommandPath(command);
479
+ if (!findResult.found) {
480
+ return {
481
+ available: false,
482
+ error: "Gemini CLI not found in PATH. Install from: https://github.com/google-gemini/gemini-cli"
483
+ };
484
+ }
485
+ this.commandPath = findResult.path;
486
+ return {
487
+ available: true,
488
+ executablePath: findResult.path
489
+ };
490
+ }
491
+ buildArgs(prompt, options) {
492
+ const args = [];
493
+ args.push("--output-format", "stream-json");
494
+ if (this.model) {
495
+ args.push("-m", this.model);
496
+ }
497
+ if (this.yoloMode) {
498
+ args.push("--yolo");
499
+ }
500
+ return args;
501
+ }
502
+ getStdinInput(prompt, options) {
503
+ return prompt;
504
+ }
505
+ };
506
+ var createGeminiAgent = () => new GeminiAgentPlugin();
507
+ var gemini_default = createGeminiAgent;
508
+ var OpenCodeAgentPlugin = class extends BaseAgentPlugin {
509
+ meta = {
510
+ name: "opencode",
511
+ version: "1.0.0",
512
+ description: "OpenCode AI coding assistant CLI",
513
+ cliCommand: "opencode",
514
+ supportsHooks: true,
515
+ supportsSkills: true,
516
+ supportsStreaming: true,
517
+ supportsSubagentTracing: true
518
+ };
519
+ provider;
520
+ model;
521
+ agent = "general";
522
+ async initialize(config) {
523
+ await super.initialize(config);
524
+ if (typeof config.provider === "string" && config.provider.length > 0) {
525
+ this.provider = config.provider;
526
+ }
527
+ if (typeof config.model === "string" && config.model.length > 0) {
528
+ this.model = config.model;
529
+ }
530
+ if (typeof config.agent === "string" && ["general", "build", "plan"].includes(config.agent)) {
531
+ this.agent = config.agent;
532
+ }
533
+ }
534
+ async detect() {
535
+ const command = this.commandPath ?? this.meta.cliCommand;
536
+ const findResult = await findCommandPath(command);
537
+ if (!findResult.found) {
538
+ return {
539
+ available: false,
540
+ error: "OpenCode CLI not found in PATH. Install with: curl -fsSL https://opencode.ai/install | bash"
541
+ };
542
+ }
543
+ this.commandPath = findResult.path;
544
+ return {
545
+ available: true,
546
+ executablePath: findResult.path
547
+ };
548
+ }
549
+ buildArgs(prompt, options) {
550
+ const args = ["run"];
551
+ if (this.agent !== "general") {
552
+ args.push("--agent", this.agent);
553
+ }
554
+ const modelStr = this.buildModelString();
555
+ if (modelStr) {
556
+ args.push("--model", modelStr);
557
+ }
558
+ args.push("--format", "json");
559
+ return args;
560
+ }
561
+ getStdinInput(prompt, options) {
562
+ return prompt;
563
+ }
564
+ buildModelString() {
565
+ if (this.provider && this.model) {
566
+ return `${this.provider}/${this.model}`;
567
+ }
568
+ if (this.model) {
569
+ return this.model;
570
+ }
571
+ return void 0;
572
+ }
573
+ };
574
+ var createOpenCodeAgent = () => new OpenCodeAgentPlugin();
575
+ var opencode_default = createOpenCodeAgent;
576
+ var CodexAgentPlugin = class extends BaseAgentPlugin {
577
+ meta = {
578
+ name: "codex",
579
+ version: "1.0.0",
580
+ description: "OpenAI Codex CLI for AI-assisted coding",
581
+ cliCommand: "codex",
582
+ supportsHooks: false,
583
+ supportsSkills: true,
584
+ supportsStreaming: true,
585
+ supportsSubagentTracing: true
586
+ };
587
+ model;
588
+ agent;
589
+ fullAuto = true;
590
+ sandbox = "workspace-write";
591
+ async initialize(config) {
592
+ await super.initialize(config);
593
+ if (typeof config.model === "string" && config.model.length > 0) {
594
+ this.model = config.model;
595
+ }
596
+ if (typeof config.agent === "string" && config.agent.length > 0) {
597
+ this.agent = config.agent;
598
+ }
599
+ if (typeof config.fullAuto === "boolean") {
600
+ this.fullAuto = config.fullAuto;
601
+ }
602
+ if (typeof config.sandbox === "string" && ["read-only", "workspace-write", "danger-full-access"].includes(
603
+ config.sandbox
604
+ )) {
605
+ this.sandbox = config.sandbox;
606
+ }
607
+ }
608
+ async detect() {
609
+ const command = this.commandPath ?? this.meta.cliCommand;
610
+ const findResult = await findCommandPath(command);
611
+ if (!findResult.found) {
612
+ return {
613
+ available: false,
614
+ error: "Codex CLI not found in PATH. Install from: https://github.com/openai/codex"
615
+ };
616
+ }
617
+ this.commandPath = findResult.path;
618
+ return {
619
+ available: true,
620
+ executablePath: findResult.path
621
+ };
622
+ }
623
+ buildArgs(prompt, options) {
624
+ const preArgs = [];
625
+ const args = [];
626
+ if (this.fullAuto && this.sandbox !== "workspace-write") {
627
+ preArgs.push("-a", "on-request");
628
+ }
629
+ args.push("exec");
630
+ if (this.fullAuto && this.sandbox === "workspace-write") {
631
+ args.push("--full-auto");
632
+ }
633
+ args.push("--json");
634
+ if (this.model) {
635
+ args.push("--model", this.model);
636
+ }
637
+ args.push("--sandbox", this.sandbox);
638
+ if (this.agent) {
639
+ args.push("--profile", this.agent);
640
+ }
641
+ args.push("-");
642
+ return [...preArgs, ...args];
643
+ }
644
+ getStdinInput(prompt, options) {
645
+ return prompt;
646
+ }
647
+ };
648
+ var createCodexAgent = () => new CodexAgentPlugin();
649
+ var codex_default = createCodexAgent;
650
+ var KiroAgentPlugin = class extends BaseAgentPlugin {
651
+ meta = {
652
+ name: "kiro",
653
+ version: "1.0.0",
654
+ description: "AWS Kiro CLI for AI-assisted coding",
655
+ cliCommand: "kiro-cli",
656
+ supportsHooks: true,
657
+ supportsSkills: true,
658
+ supportsStreaming: true,
659
+ supportsSubagentTracing: false
660
+ };
661
+ trustAllTools = true;
662
+ agent;
663
+ model;
664
+ async initialize(config) {
665
+ await super.initialize(config);
666
+ if (typeof config.trustAllTools === "boolean") {
667
+ this.trustAllTools = config.trustAllTools;
668
+ }
669
+ if (typeof config.agent === "string" && config.agent.length > 0) {
670
+ this.agent = config.agent;
671
+ }
672
+ if (typeof config.model === "string" && config.model.length > 0) {
673
+ this.model = config.model;
674
+ }
675
+ if (typeof config.sandbox === "string" && config.sandbox.length > 0) {
676
+ this.trustAllTools = config.sandbox === "danger-full-access";
677
+ }
678
+ }
679
+ async detect() {
680
+ const command = this.commandPath ?? this.meta.cliCommand;
681
+ const findResult = await findCommandPath(command);
682
+ if (!findResult.found) {
683
+ return {
684
+ available: false,
685
+ error: "Kiro CLI not found in PATH. Install from: https://kiro.dev"
686
+ };
687
+ }
688
+ this.commandPath = findResult.path;
689
+ return {
690
+ available: true,
691
+ executablePath: findResult.path
692
+ };
693
+ }
694
+ buildArgs(prompt, options) {
695
+ const args = ["chat", "--no-interactive"];
696
+ if (this.trustAllTools) {
697
+ args.push("--trust-all-tools");
698
+ }
699
+ if (this.agent) {
700
+ args.push("--agent", this.agent);
701
+ }
702
+ if (this.model) {
703
+ args.push("--model", this.model);
704
+ }
705
+ return args;
706
+ }
707
+ getStdinInput(prompt, options) {
708
+ return prompt;
709
+ }
710
+ };
711
+ var createKiroAgent = () => new KiroAgentPlugin();
712
+ var kiro_default = createKiroAgent;
713
+ var FactoryDroidAgentPlugin = class extends BaseAgentPlugin {
714
+ meta = {
715
+ name: "factory-droid",
716
+ version: "1.0.0",
717
+ description: "Factory Droid CLI for AI-assisted coding",
718
+ cliCommand: "droid",
719
+ supportsHooks: false,
720
+ supportsSkills: false,
721
+ supportsStreaming: false,
722
+ supportsSubagentTracing: false
723
+ };
724
+ async detect() {
725
+ const command = this.commandPath ?? this.meta.cliCommand;
726
+ const findResult = await findCommandPath(command);
727
+ if (!findResult.found) {
728
+ return {
729
+ available: false,
730
+ error: "Droid CLI not found in PATH. Install from: https://docs.factory.ai"
731
+ };
732
+ }
733
+ this.commandPath = findResult.path;
734
+ return {
735
+ available: true,
736
+ executablePath: findResult.path
737
+ };
738
+ }
739
+ buildArgs(prompt, options) {
740
+ const args = ["exec"];
741
+ return args;
742
+ }
743
+ getStdinInput(prompt, options) {
744
+ return prompt;
745
+ }
746
+ };
747
+ var createFactoryDroidAgent = () => new FactoryDroidAgentPlugin();
748
+ var factory_droid_default = createFactoryDroidAgent;
749
+ var AgentRegistry = class _AgentRegistry {
750
+ static instance = null;
751
+ plugins = /* @__PURE__ */ new Map();
752
+ loadedInstances = /* @__PURE__ */ new Map();
753
+ initialized = false;
754
+ constructor() {
755
+ }
756
+ static getInstance() {
757
+ if (!_AgentRegistry.instance) {
758
+ _AgentRegistry.instance = new _AgentRegistry();
759
+ }
760
+ return _AgentRegistry.instance;
761
+ }
762
+ static resetInstance() {
763
+ if (_AgentRegistry.instance) {
764
+ for (const instance of _AgentRegistry.instance.loadedInstances.values()) {
765
+ instance.dispose().catch(() => {
766
+ });
767
+ }
768
+ _AgentRegistry.instance.plugins.clear();
769
+ _AgentRegistry.instance.loadedInstances.clear();
770
+ _AgentRegistry.instance.initialized = false;
771
+ }
772
+ _AgentRegistry.instance = null;
773
+ }
774
+ registerBuiltin(factory) {
775
+ const instance = factory();
776
+ const { meta } = instance;
777
+ this.plugins.set(meta.name, { factory, meta, builtin: true });
778
+ instance.dispose().catch(() => {
779
+ });
780
+ }
781
+ getRegisteredPlugins() {
782
+ return Array.from(this.plugins.values()).map((p) => p.meta);
783
+ }
784
+ getPluginMeta(pluginName) {
785
+ return this.plugins.get(pluginName)?.meta;
786
+ }
787
+ hasPlugin(pluginName) {
788
+ return this.plugins.has(pluginName);
789
+ }
790
+ isBuiltin(pluginName) {
791
+ return this.plugins.get(pluginName)?.builtin ?? false;
792
+ }
793
+ createInstance(pluginName) {
794
+ const registered = this.plugins.get(pluginName);
795
+ if (!registered) return void 0;
796
+ return registered.factory();
797
+ }
798
+ async getInstance(config) {
799
+ const cacheKey = config.name;
800
+ const cached = this.loadedInstances.get(cacheKey);
801
+ if (cached) return cached;
802
+ const instance = this.createInstance(config.plugin);
803
+ if (!instance) {
804
+ throw new Error(`Unknown agent plugin: ${config.plugin}`);
805
+ }
806
+ const initConfig = {
807
+ ...config.options,
808
+ command: config.command,
809
+ defaultFlags: config.defaultFlags,
810
+ timeout: config.timeout,
811
+ envExclude: config.envExclude,
812
+ envPassthrough: config.envPassthrough
813
+ };
814
+ await instance.initialize(initConfig);
815
+ this.loadedInstances.set(cacheKey, instance);
816
+ return instance;
817
+ }
818
+ async disposeInstance(configName) {
819
+ const instance = this.loadedInstances.get(configName);
820
+ if (instance) {
821
+ await instance.dispose();
822
+ this.loadedInstances.delete(configName);
823
+ }
824
+ }
825
+ async disposeAll() {
826
+ const disposals = Array.from(this.loadedInstances.values()).map(
827
+ (instance) => instance.dispose()
828
+ );
829
+ await Promise.all(disposals);
830
+ this.loadedInstances.clear();
831
+ }
832
+ async initialize() {
833
+ if (this.initialized) return;
834
+ this.initialized = true;
835
+ const builtins = [
836
+ claude_default,
837
+ gemini_default,
838
+ opencode_default,
839
+ codex_default,
840
+ kiro_default,
841
+ factory_droid_default
842
+ ];
843
+ for (const factory of builtins) {
844
+ this.registerBuiltin(factory);
845
+ }
846
+ }
847
+ };
848
+ function getAgentRegistry() {
849
+ return AgentRegistry.getInstance();
850
+ }
851
+ function translateToHookInput(event, defaults) {
852
+ return {
853
+ session_id: event.sessionId ?? defaults.sessionId,
854
+ transcript_path: defaults.transcriptPath ?? "",
855
+ cwd: event.cwd ?? defaults.cwd,
856
+ permission_mode: defaults.permissionMode ?? "default",
857
+ hook_event_name: event.eventName,
858
+ tool_name: event.toolName,
859
+ tool_input: event.toolInput,
860
+ tool_response: event.toolResponse
861
+ };
862
+ }
863
+ function evaluateHookEvent(event, rules, state, projectDir, defaults) {
864
+ const input = translateToHookInput(event, defaults);
865
+ return evaluateRules(input, rules, state, projectDir);
866
+ }
867
+ function fromClaudeHookEvent(input) {
868
+ return {
869
+ eventName: input.hook_event_name,
870
+ toolName: input.tool_name,
871
+ toolInput: input.tool_input,
872
+ toolResponse: input.tool_response,
873
+ sessionId: input.session_id,
874
+ cwd: input.cwd
875
+ };
876
+ }
877
+ function toClaudeHookInput(event, defaults) {
878
+ return {
879
+ session_id: event.sessionId ?? "",
880
+ transcript_path: defaults?.transcriptPath ?? "",
881
+ cwd: event.cwd ?? "",
882
+ permission_mode: defaults?.permissionMode ?? "default",
883
+ hook_event_name: event.eventName,
884
+ tool_name: event.toolName,
885
+ tool_input: event.toolInput,
886
+ tool_response: event.toolResponse
887
+ };
888
+ }
889
+ var EVENT_MAP = {
890
+ "tool.execute.before": "PreToolUse",
891
+ "tool.execute.after": "PostToolUse",
892
+ "permission.request": "PermissionRequest",
893
+ "session.start": "SessionStart",
894
+ "session.end": "SessionEnd"
895
+ };
896
+ function fromOpenCodeEvent(event) {
897
+ const hookEvent = EVENT_MAP[event.type];
898
+ return {
899
+ eventName: hookEvent ?? event.type,
900
+ toolName: event.tool?.name,
901
+ toolInput: event.tool?.input,
902
+ toolResponse: event.tool?.output ? { output: event.tool.output } : void 0,
903
+ sessionId: event.session?.id,
904
+ cwd: event.session?.cwd
905
+ };
906
+ }
907
+ function toOpenCodeResponse(decision, reason) {
908
+ return {
909
+ action: decision === "allow" ? "proceed" : decision === "deny" ? "block" : "ask",
910
+ message: reason
911
+ };
912
+ }
913
+ var EVENT_MAP2 = {
914
+ BeforeTool: "PreToolUse",
915
+ AfterTool: "PostToolUse",
916
+ BeforePermission: "PermissionRequest",
917
+ SessionStart: "SessionStart",
918
+ SessionEnd: "SessionEnd"
919
+ };
920
+ function fromGeminiEvent(event) {
921
+ const hookEvent = EVENT_MAP2[event.type];
922
+ return {
923
+ eventName: hookEvent ?? event.type,
924
+ toolName: event.toolName,
925
+ toolInput: event.toolInput,
926
+ toolResponse: event.toolOutput ? { output: event.toolOutput } : void 0,
927
+ sessionId: event.sessionId,
928
+ cwd: event.workingDir
929
+ };
930
+ }
931
+ function toGeminiResponse(decision, reason) {
932
+ return {
933
+ allowed: decision === "allow",
934
+ reason
935
+ };
936
+ }
937
+ var EVENT_MAP3 = {
938
+ preToolUse: "PreToolUse",
939
+ postToolUse: "PostToolUse",
940
+ permissionRequest: "PermissionRequest",
941
+ agentSpawn: "SessionStart",
942
+ sessionStart: "SessionStart",
943
+ sessionEnd: "SessionEnd",
944
+ userPromptSubmit: "UserPromptSubmit",
945
+ stop: "Stop"
946
+ };
947
+ var TOOL_MAP = {
948
+ fs_read: "Read",
949
+ read: "Read",
950
+ fs_write: "Write",
951
+ write: "Write",
952
+ fs_edit: "Edit",
953
+ edit: "Edit",
954
+ execute_bash: "Bash",
955
+ shell: "Bash",
956
+ fs_glob: "Glob",
957
+ glob: "Glob",
958
+ fs_grep: "Grep",
959
+ grep: "Grep",
960
+ web_fetch: "WebFetch",
961
+ web_search: "WebSearch"
962
+ };
963
+ function fromKiroEvent(event) {
964
+ const hookEvent = EVENT_MAP3[event.hook_event_name];
965
+ const toolName = event.tool_name ? TOOL_MAP[event.tool_name.toLowerCase()] ?? event.tool_name : void 0;
966
+ return {
967
+ eventName: hookEvent ?? event.hook_event_name,
968
+ toolName,
969
+ toolInput: event.tool_input,
970
+ toolResponse: event.tool_response,
971
+ sessionId: event.session_id,
972
+ cwd: event.cwd
973
+ };
974
+ }
975
+ function toKiroResponse(decision, reason) {
976
+ return {
977
+ exitCode: decision === "allow" ? 0 : 2,
978
+ message: reason
979
+ };
980
+ }
981
+ function mapCodexEvent(eventType, itemType) {
982
+ if (eventType === "thread.started") return "SessionStart";
983
+ if (eventType === "turn.failed") return "Stop";
984
+ if (eventType === "item.started") {
985
+ if (itemType === "command_execution") return "PreToolUse";
986
+ if (itemType === "file_change") return "PreToolUse";
987
+ if (itemType === "mcp_tool_call") return "PreToolUse";
988
+ if (itemType === "web_search") return "PreToolUse";
989
+ }
990
+ if (eventType === "item.completed") {
991
+ if (itemType === "command_execution") return "PostToolUse";
992
+ if (itemType === "file_change") return "PostToolUse";
993
+ if (itemType === "mcp_tool_call") return "PostToolUse";
994
+ if (itemType === "web_search") return "PostToolUse";
995
+ }
996
+ return null;
997
+ }
998
+ function mapToolName(item) {
999
+ if (!item) return void 0;
1000
+ switch (item.type) {
1001
+ case "command_execution":
1002
+ return "Bash";
1003
+ case "file_change":
1004
+ return item.status === "in_progress" ? "Write" : "Write";
1005
+ case "mcp_tool_call":
1006
+ return item.tool_name ?? "McpTool";
1007
+ case "web_search":
1008
+ return "WebSearch";
1009
+ case "agent_message":
1010
+ return "AgentMessage";
1011
+ case "reasoning":
1012
+ return "Reasoning";
1013
+ case "plan_update":
1014
+ return "PlanUpdate";
1015
+ default:
1016
+ return item.type;
1017
+ }
1018
+ }
1019
+ function buildToolInput(item) {
1020
+ if (!item) return void 0;
1021
+ switch (item.type) {
1022
+ case "command_execution":
1023
+ return { command: item.command };
1024
+ case "file_change":
1025
+ return {
1026
+ file_path: item.file_path,
1027
+ content: item.content
1028
+ };
1029
+ case "mcp_tool_call":
1030
+ return {
1031
+ tool_name: item.tool_name,
1032
+ arguments: item.arguments
1033
+ };
1034
+ case "agent_message":
1035
+ return { text: item.text };
1036
+ case "reasoning":
1037
+ return { text: item.text };
1038
+ default:
1039
+ return void 0;
1040
+ }
1041
+ }
1042
+ function buildToolResponse(item) {
1043
+ if (!item) return void 0;
1044
+ const response = {};
1045
+ if (item.aggregated_output !== void 0) {
1046
+ response.output = item.aggregated_output;
1047
+ }
1048
+ if (item.exit_code !== void 0 && item.exit_code !== null) {
1049
+ response.exit_code = item.exit_code;
1050
+ }
1051
+ if (item.text !== void 0) {
1052
+ response.output = item.text;
1053
+ }
1054
+ return Object.keys(response).length > 0 ? response : void 0;
1055
+ }
1056
+ function fromCodexEvent(event, sessionId, cwd) {
1057
+ const hookEvent = mapCodexEvent(event.type, event.item?.type);
1058
+ if (!hookEvent) return null;
1059
+ return {
1060
+ eventName: hookEvent,
1061
+ toolName: mapToolName(event.item),
1062
+ toolInput: buildToolInput(event.item),
1063
+ toolResponse: buildToolResponse(event.item),
1064
+ sessionId: sessionId ?? event.thread_id,
1065
+ cwd
1066
+ };
1067
+ }
1068
+ function parseCodexLine(line) {
1069
+ const trimmed = line.trim();
1070
+ if (!trimmed) return null;
1071
+ try {
1072
+ return JSON.parse(trimmed);
1073
+ } catch {
1074
+ return null;
1075
+ }
1076
+ }
1077
+ function isToolEvent(event) {
1078
+ if (event.type !== "item.started" && event.type !== "item.completed") {
1079
+ return false;
1080
+ }
1081
+ const itemType = event.item?.type;
1082
+ return itemType === "command_execution" || itemType === "file_change" || itemType === "mcp_tool_call" || itemType === "web_search";
1083
+ }
1084
+ function rulesToPromptInstructions(rules) {
1085
+ const sections = [];
1086
+ sections.push("## ULPI Governance Rules\n");
1087
+ sections.push(
1088
+ "You MUST follow these rules during this session. Violations will be caught and rolled back.\n"
1089
+ );
1090
+ sections.push("### Forbidden Actions");
1091
+ sections.push("- NEVER run `rm -rf /` or any recursive force-delete on root");
1092
+ sections.push("- NEVER run `git push --force` to main or master");
1093
+ sections.push("- NEVER run `git reset --hard` without explicit user approval");
1094
+ sections.push("- NEVER modify `.env` files containing secrets\n");
1095
+ const permissions = Object.entries(rules.permissions ?? {});
1096
+ if (permissions.length > 0) {
1097
+ sections.push("### Permission Rules");
1098
+ for (const [name, rule] of permissions) {
1099
+ if (!rule.enabled) continue;
1100
+ const r = rule;
1101
+ const action = r.decision === "allow" ? "Auto-approved" : r.decision === "deny" ? "BLOCKED" : "Requires approval";
1102
+ const scope = r.command_pattern ? ` (command: ${r.command_pattern})` : r.file_pattern ? ` (files: ${r.file_pattern})` : "";
1103
+ sections.push(`- **${name}**: ${action} for ${r.matcher}${scope}`);
1104
+ if (r.message) {
1105
+ sections.push(` Reason: ${r.message}`);
1106
+ }
1107
+ }
1108
+ sections.push("");
1109
+ }
1110
+ const preconditions = Object.entries(rules.preconditions ?? {});
1111
+ if (preconditions.length > 0) {
1112
+ sections.push("### Precondition Rules");
1113
+ for (const [name, rule] of preconditions) {
1114
+ if (!rule.enabled) continue;
1115
+ const r = rule;
1116
+ const conditions = [];
1117
+ if (r.requires_read) {
1118
+ conditions.push("read the file first");
1119
+ }
1120
+ if (r.requires?.length) {
1121
+ conditions.push(`require flags: ${r.requires.join(", ")}`);
1122
+ }
1123
+ sections.push(
1124
+ `- **${name}**: Before using ${r.matcher}, you must ${conditions.join(" and ")}`
1125
+ );
1126
+ if (r.message) {
1127
+ sections.push(` ${r.message}`);
1128
+ }
1129
+ }
1130
+ sections.push("");
1131
+ }
1132
+ return sections.join("\n");
1133
+ }
1134
+ function injectGovernance(prompt, rules) {
1135
+ const instructions = rulesToPromptInstructions(rules);
1136
+ return `${instructions}
1137
+ ---
1138
+
1139
+ ${prompt}`;
1140
+ }
1141
+ function createOutputMonitor(config) {
1142
+ const violations = [];
1143
+ let buffer = "";
1144
+ function processLine(line) {
1145
+ const trimmed = line.trim();
1146
+ if (!trimmed) return;
1147
+ const commandMatch = trimmed.match(
1148
+ /(?:^\$\s+|Running:\s+|Executing:\s+|Command:\s+)(.+)/
1149
+ );
1150
+ if (commandMatch) {
1151
+ const cmd = commandMatch[1];
1152
+ const result = isDangerousCommand(cmd);
1153
+ if (result.dangerous) {
1154
+ const violation = {
1155
+ type: "dangerous_command",
1156
+ message: result.reason ?? "Dangerous command detected",
1157
+ line: trimmed,
1158
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1159
+ };
1160
+ violations.push(violation);
1161
+ config.onViolation(violation);
1162
+ }
1163
+ }
1164
+ if (config.blockedFilePatterns) {
1165
+ for (const pattern of config.blockedFilePatterns) {
1166
+ const pathMatch = trimmed.match(
1167
+ /(?:Writing|Creating|Modifying|Editing|Deleting)\s+(.+)/i
1168
+ );
1169
+ if (pathMatch) {
1170
+ const filePath = pathMatch[1].trim();
1171
+ if (matchesFilePattern(pattern, filePath, config.projectDir)) {
1172
+ const violation = {
1173
+ type: "file_access",
1174
+ message: `File access blocked by pattern: ${pattern}`,
1175
+ line: trimmed,
1176
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1177
+ };
1178
+ violations.push(violation);
1179
+ config.onViolation(violation);
1180
+ }
1181
+ }
1182
+ }
1183
+ }
1184
+ }
1185
+ function processData(data) {
1186
+ buffer += data;
1187
+ const lines = buffer.split("\n");
1188
+ buffer = lines.pop() ?? "";
1189
+ for (const line of lines) {
1190
+ processLine(line);
1191
+ }
1192
+ }
1193
+ return {
1194
+ pushStdout: processData,
1195
+ pushStderr: processData,
1196
+ getViolations: () => [...violations]
1197
+ };
1198
+ }
1199
+ function getChangedFiles(projectDir) {
1200
+ try {
1201
+ const output = execFileSync("git", ["diff", "--name-only"], {
1202
+ cwd: projectDir,
1203
+ encoding: "utf-8",
1204
+ timeout: 1e4
1205
+ });
1206
+ return output.split("\n").map((f) => f.trim()).filter(Boolean);
1207
+ } catch {
1208
+ return [];
1209
+ }
1210
+ }
1211
+ function getUntrackedFiles(projectDir) {
1212
+ try {
1213
+ const output = execFileSync(
1214
+ "git",
1215
+ ["ls-files", "--others", "--exclude-standard"],
1216
+ {
1217
+ cwd: projectDir,
1218
+ encoding: "utf-8",
1219
+ timeout: 1e4
1220
+ }
1221
+ );
1222
+ return output.split("\n").map((f) => f.trim()).filter(Boolean);
1223
+ } catch {
1224
+ return [];
1225
+ }
1226
+ }
1227
+ function validatePostExecution(rules, projectDir) {
1228
+ const violations = [];
1229
+ const changedFiles = [...getChangedFiles(projectDir), ...getUntrackedFiles(projectDir)];
1230
+ const denyRules = [];
1231
+ for (const [name, rule] of Object.entries(rules.permissions ?? {})) {
1232
+ if (!rule.enabled) continue;
1233
+ const r = rule;
1234
+ if (r.decision === "deny" && r.file_pattern) {
1235
+ denyRules.push({ name, rule: r });
1236
+ }
1237
+ }
1238
+ for (const filePath of changedFiles) {
1239
+ for (const { name, rule } of denyRules) {
1240
+ if (rule.file_pattern && matchesFilePattern(rule.file_pattern, filePath, projectDir)) {
1241
+ violations.push({
1242
+ type: "unauthorized_file_change",
1243
+ filePath,
1244
+ ruleName: name,
1245
+ message: rule.message ?? `File change denied by rule: ${name}`
1246
+ });
1247
+ }
1248
+ }
1249
+ }
1250
+ return {
1251
+ valid: violations.length === 0,
1252
+ violations
1253
+ };
1254
+ }
1255
+ function rollbackFiles(projectDir, filePaths) {
1256
+ const rolledBack = [];
1257
+ const errors = [];
1258
+ for (const filePath of filePaths) {
1259
+ try {
1260
+ execFileSync2("git", ["checkout", "--", filePath], {
1261
+ cwd: projectDir,
1262
+ encoding: "utf-8",
1263
+ timeout: 1e4
1264
+ });
1265
+ rolledBack.push(filePath);
1266
+ } catch (err) {
1267
+ const message = err instanceof Error ? err.message : String(err);
1268
+ errors.push(`Failed to rollback ${filePath}: ${message}`);
1269
+ }
1270
+ }
1271
+ return {
1272
+ success: errors.length === 0,
1273
+ filesRolledBack: rolledBack,
1274
+ error: errors.length > 0 ? errors.join("; ") : void 0
1275
+ };
1276
+ }
1277
+ function removeUntrackedFiles(projectDir, filePaths) {
1278
+ const rolledBack = [];
1279
+ const errors = [];
1280
+ for (const filePath of filePaths) {
1281
+ try {
1282
+ execFileSync2("git", ["clean", "-f", "--", filePath], {
1283
+ cwd: projectDir,
1284
+ encoding: "utf-8",
1285
+ timeout: 1e4
1286
+ });
1287
+ rolledBack.push(filePath);
1288
+ } catch (err) {
1289
+ const message = err instanceof Error ? err.message : String(err);
1290
+ errors.push(`Failed to clean ${filePath}: ${message}`);
1291
+ }
1292
+ }
1293
+ return {
1294
+ success: errors.length === 0,
1295
+ filesRolledBack: rolledBack,
1296
+ error: errors.length > 0 ? errors.join("; ") : void 0
1297
+ };
1298
+ }
1299
+ function fullRollback(projectDir, reason) {
1300
+ try {
1301
+ const stashMessage = `ulpi-rollback: ${reason} (${(/* @__PURE__ */ new Date()).toISOString()})`;
1302
+ execFileSync2("git", ["stash", "push", "-u", "-m", stashMessage], {
1303
+ cwd: projectDir,
1304
+ encoding: "utf-8",
1305
+ timeout: 3e4
1306
+ });
1307
+ let filesStashed = [];
1308
+ try {
1309
+ const diffOutput = execFileSync2(
1310
+ "git",
1311
+ ["stash", "show", "--name-only", "stash@{0}"],
1312
+ {
1313
+ cwd: projectDir,
1314
+ encoding: "utf-8",
1315
+ timeout: 1e4
1316
+ }
1317
+ );
1318
+ filesStashed = diffOutput.split("\n").map((f) => f.trim()).filter(Boolean);
1319
+ } catch {
1320
+ }
1321
+ return {
1322
+ success: true,
1323
+ filesRolledBack: filesStashed
1324
+ };
1325
+ } catch (err) {
1326
+ const message = err instanceof Error ? err.message : String(err);
1327
+ return {
1328
+ success: false,
1329
+ filesRolledBack: [],
1330
+ error: `Full rollback failed: ${message}`
1331
+ };
1332
+ }
1333
+ }
1334
+ function renderSkillForClaude(skill) {
1335
+ const sections = [];
1336
+ sections.push(`# ${skill.name}
1337
+ `);
1338
+ if (skill.description) {
1339
+ sections.push(`${skill.description}
1340
+ `);
1341
+ }
1342
+ sections.push("## Instructions\n");
1343
+ sections.push("Follow the instructions in this skill when triggered.\n");
1344
+ return sections.join("\n");
1345
+ }
1346
+ function getClaudeSkillPath(skillName, scope) {
1347
+ if (scope === "personal") {
1348
+ return `~/.claude/skills/${skillName}.md`;
1349
+ }
1350
+ return `.claude/skills/${skillName}.md`;
1351
+ }
1352
+ function renderSkillForOpenCode(skill) {
1353
+ const sections = [];
1354
+ sections.push(`# ${skill.name}
1355
+ `);
1356
+ if (skill.description) {
1357
+ sections.push(`> ${skill.description}
1358
+ `);
1359
+ }
1360
+ sections.push("## Instructions\n");
1361
+ sections.push("Follow the instructions in this skill when triggered.\n");
1362
+ return sections.join("\n");
1363
+ }
1364
+ function getOpenCodeSkillPath(skillName, scope) {
1365
+ if (scope === "personal") {
1366
+ return `~/.config/opencode/skills/${skillName}.md`;
1367
+ }
1368
+ return `.opencode/skills/${skillName}.md`;
1369
+ }
1370
+ function renderSkillForKiro(skill) {
1371
+ const safeName = `ulpi-${skill.name}`;
1372
+ const description = skill.description ?? `ULPI ${skill.name} skill`;
1373
+ const sections = [];
1374
+ sections.push("---");
1375
+ sections.push(`name: ${safeName}`);
1376
+ sections.push(`description: ${description}`);
1377
+ sections.push("---");
1378
+ sections.push("");
1379
+ sections.push(`# ${skill.name}`);
1380
+ sections.push("");
1381
+ if (skill.description) {
1382
+ sections.push(skill.description);
1383
+ sections.push("");
1384
+ }
1385
+ sections.push("## Instructions");
1386
+ sections.push("");
1387
+ sections.push("Follow the instructions in this skill when triggered.");
1388
+ sections.push("");
1389
+ return sections.join("\n");
1390
+ }
1391
+ function getKiroSkillPath(skillName, scope) {
1392
+ const dirName = skillName.startsWith("ulpi-") ? skillName : `ulpi-${skillName}`;
1393
+ if (scope === "personal") {
1394
+ return path.join("~", ".kiro", "skills", dirName, "SKILL.md");
1395
+ }
1396
+ return path.join(".kiro", "skills", dirName, "SKILL.md");
1397
+ }
1398
+ function installKiroSkill(skill, projectDir, scope) {
1399
+ const content = renderSkillForKiro(skill);
1400
+ const dirName = `ulpi-${skill.name}`;
1401
+ const dir = scope === "personal" ? path.join(os.homedir(), ".kiro", "skills", dirName) : path.join(projectDir, ".kiro", "skills", dirName);
1402
+ fs.mkdirSync(dir, { recursive: true });
1403
+ const skillPath = path.join(dir, "SKILL.md");
1404
+ fs.writeFileSync(skillPath, content, "utf-8");
1405
+ return skillPath;
1406
+ }
1407
+ function renderSkillAsPrompt(skill) {
1408
+ const sections = [];
1409
+ sections.push(`[Skill: ${skill.name}]`);
1410
+ if (skill.description) {
1411
+ sections.push(skill.description);
1412
+ }
1413
+ sections.push("");
1414
+ sections.push("You must follow the instructions in this skill.");
1415
+ sections.push(`[End Skill: ${skill.name}]`);
1416
+ return sections.join("\n");
1417
+ }
1418
+ function renderSkillsAsPrompt(skills) {
1419
+ if (skills.length === 0) return "";
1420
+ const sections = [];
1421
+ sections.push("## Active Skills\n");
1422
+ for (const skill of skills) {
1423
+ sections.push(renderSkillAsPrompt(skill));
1424
+ sections.push("");
1425
+ }
1426
+ return sections.join("\n");
1427
+ }
1428
+
1429
+ export {
1430
+ DEFAULT_ENV_EXCLUDE_PATTERNS,
1431
+ filterEnv,
1432
+ getEnvExclusionReport,
1433
+ findCommandPath,
1434
+ BaseAgentPlugin,
1435
+ ClaudeAgentPlugin,
1436
+ claude_default,
1437
+ GeminiAgentPlugin,
1438
+ gemini_default,
1439
+ OpenCodeAgentPlugin,
1440
+ opencode_default,
1441
+ CodexAgentPlugin,
1442
+ codex_default,
1443
+ KiroAgentPlugin,
1444
+ kiro_default,
1445
+ FactoryDroidAgentPlugin,
1446
+ factory_droid_default,
1447
+ AgentRegistry,
1448
+ getAgentRegistry,
1449
+ translateToHookInput,
1450
+ evaluateHookEvent,
1451
+ fromClaudeHookEvent,
1452
+ toClaudeHookInput,
1453
+ fromOpenCodeEvent,
1454
+ toOpenCodeResponse,
1455
+ fromGeminiEvent,
1456
+ toGeminiResponse,
1457
+ fromKiroEvent,
1458
+ toKiroResponse,
1459
+ fromCodexEvent,
1460
+ parseCodexLine,
1461
+ isToolEvent,
1462
+ rulesToPromptInstructions,
1463
+ injectGovernance,
1464
+ createOutputMonitor,
1465
+ validatePostExecution,
1466
+ rollbackFiles,
1467
+ removeUntrackedFiles,
1468
+ fullRollback,
1469
+ renderSkillForClaude,
1470
+ getClaudeSkillPath,
1471
+ renderSkillForOpenCode,
1472
+ getOpenCodeSkillPath,
1473
+ renderSkillForKiro,
1474
+ getKiroSkillPath,
1475
+ installKiroSkill,
1476
+ renderSkillAsPrompt,
1477
+ renderSkillsAsPrompt
1478
+ };