@ulpi/cli 0.1.5 → 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.
- package/LICENSE +21 -0
- package/dist/{auth-PN7TMQHV-2W4ICG64.js → auth-FWM7MM4Q-VZC3U2XZ.js} +1 -1
- package/dist/{auth-BFFBUJUC.js → auth-HDK7ECJL.js} +2 -1
- package/dist/{chunk-RJIRWQJD.js → chunk-3BCW6ABU.js} +402 -142
- package/dist/{chunk-L3PWNHSA.js → chunk-3WB5CXH4.js} +180 -5
- package/dist/{chunk-K4OVPFY2.js → chunk-4UCJIAOU.js} +2 -2
- package/dist/chunk-4XTHZVDS.js +109 -0
- package/dist/chunk-4ZPOZULQ.js +6522 -0
- package/dist/{chunk-SIAQVRKG.js → chunk-5MI5GIXM.js} +48 -2
- package/dist/{chunk-KLEASXUR.js → chunk-6ZL6NXMV.js} +1 -1
- package/dist/{chunk-AV5RB3N2.js → chunk-76D3BYJD.js} +48 -0
- package/dist/{chunk-DOIKS6C5.js → chunk-AWOSRA5F.js} +1 -1
- package/dist/{chunk-UCMT5OKP.js → chunk-BFEKZZHM.js} +274 -57
- package/dist/chunk-C7CLUQI6.js +1286 -0
- package/dist/{chunk-ELTGWMDE.js → chunk-E3B5NROU.js} +7 -7
- package/dist/chunk-EJ7TW77N.js +1418 -0
- package/dist/{chunk-6OURRFP7.js → chunk-IV6MWETF.js} +383 -168
- package/dist/chunk-IZPJHSPX.js +1478 -0
- package/dist/chunk-JLHNLM3C.js +228 -0
- package/dist/{chunk-P2RESJRN.js → chunk-KYYI23AQ.js} +2 -2
- package/dist/chunk-S6ANCSYO.js +1271 -0
- package/dist/chunk-SEU7WWNQ.js +1251 -0
- package/dist/chunk-SNQ7NAIS.js +453 -0
- package/dist/{ulpi-RMMCUAGP-EWYUE7RU.js → chunk-TSLDGT5O.js} +73 -35
- package/dist/{chunk-EIWYSP3A.js → chunk-UXHCHOWQ.js} +83 -62
- package/dist/chunk-V2H5D6Y3.js +146 -0
- package/dist/{chunk-5SCG7UYM.js → chunk-VVEDXI7E.js} +1 -1
- package/dist/chunk-VXH5Y4FO.js +6761 -0
- package/dist/chunk-WED4LM5N.js +322 -0
- package/dist/{chunk-74WVVWJ4.js → chunk-YOKL7RB5.js} +184 -15
- package/dist/chunk-Z53CAR7G.js +298 -0
- package/dist/{ci-JQ56YIKC.js → ci-X3U2W4HC.js} +124 -26
- package/dist/cloud-2F3NLVHN.js +274 -0
- package/dist/{codemap-HMYBXJL2.js → codemap-XNGMAF3F.js} +37 -37
- package/dist/codex-MB5YTMRT.js +132 -0
- package/dist/{config-YYWEN7U2.js → config-OOELBYTH.js} +1 -1
- package/dist/dist-2BJYR5EI.js +59 -0
- package/dist/dist-3EIQTZHT.js +1380 -0
- package/dist/{dist-WAMAQVPK.js → dist-4U5L2X2C.js} +2 -2
- package/dist/{dist-4XTJ6HLM.js → dist-54KAMNLO.js} +16 -15
- package/dist/dist-6M4MZWZW.js +58 -0
- package/dist/dist-6X576SU2.js +27 -0
- package/dist/dist-7QOEYLFX.js +103 -0
- package/dist/dist-AYBGHEDY.js +2541 -0
- package/dist/dist-EK45QNEM.js +45 -0
- package/dist/{dist-U7ZIJMZD.js → dist-FKFEJRPX.js} +16 -15
- package/dist/dist-GTEJUBBT.js +66 -0
- package/dist/dist-HA74OKJZ.js +40 -0
- package/dist/{dist-XG2GG5SD.js → dist-HU5RZAON.js} +14 -2
- package/dist/dist-IYE3OBRB.js +374 -0
- package/dist/{dist-7WLLPWWB.js → dist-JLU26AB6.js} +12 -9
- package/dist/{dist-6G7JC2RA.js → dist-KUCI6JFE.js} +49 -9
- package/dist/dist-NUEMFZFL.js +33 -0
- package/dist/{dist-GWGTAHNM.js → dist-NUXMDXZ3.js} +31 -3
- package/dist/{dist-5R4RYNQO.js → dist-YCNWHSLN.js} +15 -5
- package/dist/{dist-6MFVWIFF.js → dist-YFFG2ZD6.js} +9 -16
- package/dist/dist-ZG4OKCSR.js +15 -0
- package/dist/doctor-SI4LLLDZ.js +345 -0
- package/dist/{export-import-4A5MWLIA.js → export-import-JFQH4KSJ.js} +1 -1
- package/dist/{history-RNUWO4JZ.js → history-5NE46ZAH.js} +7 -7
- package/dist/{hooks-installer-K2JXEBNN.js → hooks-installer-UN5JZLDQ.js} +2 -2
- package/dist/index.js +394 -618
- package/dist/{init-NQWFZPKO.js → init-5FK3VKRT.js} +76 -10
- package/dist/job-HIDMAFW2.js +376 -0
- package/dist/jobs.memory-PLMMSFHB-VBECCTHN.js +33 -0
- package/dist/kiro-VMUHDFGK.js +153 -0
- package/dist/{launchd-OYXUAVW6.js → launchd-6AWT54HR.js} +9 -17
- package/dist/mcp-PDUD7SGP.js +249 -0
- package/dist/mcp-installer-PQU3XOGO.js +259 -0
- package/dist/mcp-setup-OA7IB3H3.js +263 -0
- package/dist/{memory-D6ZFFCI2.js → memory-ZNAEAK3B.js} +17 -17
- package/dist/{ollama-3XCUZMZT-FYKHW4TZ.js → ollama-3XCUZMZT-4JMH6B7P.js} +1 -1
- package/dist/{openai-E7G2YAHU-IG33BFYF.js → openai-E7G2YAHU-T3HMBPH7.js} +2 -2
- package/dist/portal-JYWVHXDU.js +210 -0
- package/dist/prd-Q4J5NVAR.js +408 -0
- package/dist/repos-WWZXNN3P.js +271 -0
- package/dist/review-integration-5WHEJU2A.js +14 -0
- package/dist/{rules-3OFGWHP4.js → rules-Y4VSOY5Y.js} +3 -3
- package/dist/run-VPNXEIBY.js +687 -0
- package/dist/server-COL4AXKU-P7S7NNF6.js +11 -0
- package/dist/server-KKSETHDV-XSSLEENT.js +20 -0
- package/dist/{skills-GY2CTPWN.js → skills-QEYU2N27.js} +4 -2
- package/dist/start-JYOEL7AJ.js +303 -0
- package/dist/{status-SE43TIFJ.js → status-BHQYYGAL.js} +2 -2
- package/dist/{templates-O2XDKB5R.js → templates-CBRUJ66V.js} +6 -5
- package/dist/tui-DP7736EX.js +61 -0
- package/dist/ulpi-5EN6JCAS-LFE3WSL4.js +10 -0
- package/dist/{uninstall-KWGSGZTI.js → uninstall-ICUV6DDV.js} +3 -3
- package/dist/{update-QYZA4D23.js → update-7ZMAYRBH.js} +3 -3
- package/dist/{version-checker-MVB74DEX.js → version-checker-4ZFMZA7Y.js} +2 -2
- package/package.json +39 -31
- package/dist/chunk-26LLDX2T.js +0 -553
- package/dist/chunk-DDRLI6JU.js +0 -331
- package/dist/chunk-IFATANHR.js +0 -453
- package/dist/chunk-JWUUVXIV.js +0 -13694
- package/dist/chunk-LD52XG3X.js +0 -4273
- package/dist/chunk-MIAQVCFW.js +0 -39
- package/dist/chunk-YYZOFYS6.js +0 -415
- package/dist/dist-XD4YI27T.js +0 -26
- package/dist/mcp-installer-TOYDP77X.js +0 -124
- package/dist/projects-COUJP4ZC.js +0 -271
- package/dist/review-KMGP2S25.js +0 -152
- package/dist/server-USLHY6GH-F4JSXCWA.js +0 -18
- package/dist/server-X5P6WH2M-ULZF5WHZ.js +0 -11
- package/dist/skills/ulpi-generate-guardian/SKILL.md +0 -750
- package/dist/skills/ulpi-generate-guardian/references/framework-rules.md +0 -849
- package/dist/skills/ulpi-generate-guardian/references/language-rules.md +0 -591
- package/dist/ui-4SM2SUI6.js +0 -167
- package/dist/ui.html +0 -698
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
// ../../packages/http/dist/middleware/auth.js
|
|
2
|
+
import { randomBytes } from "crypto";
|
|
3
|
+
var apiSecret;
|
|
4
|
+
function generateApiSecret() {
|
|
5
|
+
return randomBytes(32).toString("hex");
|
|
6
|
+
}
|
|
7
|
+
function setApiSecret(secret) {
|
|
8
|
+
apiSecret = secret;
|
|
9
|
+
}
|
|
10
|
+
function getApiSecret() {
|
|
11
|
+
return apiSecret;
|
|
12
|
+
}
|
|
13
|
+
var LOOPBACK_ADDRESSES = /* @__PURE__ */ new Set(["127.0.0.1", "::1", "::ffff:127.0.0.1"]);
|
|
14
|
+
function validateLoopback(req) {
|
|
15
|
+
const remoteAddr = req.socket.remoteAddress ?? "";
|
|
16
|
+
return LOOPBACK_ADDRESSES.has(remoteAddr);
|
|
17
|
+
}
|
|
18
|
+
function validateAuth(req) {
|
|
19
|
+
if (!validateLoopback(req))
|
|
20
|
+
return false;
|
|
21
|
+
if (apiSecret) {
|
|
22
|
+
const headerSecret = req.headers["x-ulpi-secret"];
|
|
23
|
+
if (headerSecret === apiSecret)
|
|
24
|
+
return true;
|
|
25
|
+
try {
|
|
26
|
+
const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
|
|
27
|
+
const paramSecret = url.searchParams.get("secret");
|
|
28
|
+
if (paramSecret === apiSecret)
|
|
29
|
+
return true;
|
|
30
|
+
} catch {
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ../../packages/http/dist/router.js
|
|
38
|
+
function createRouter() {
|
|
39
|
+
const routes = [];
|
|
40
|
+
function add(method, pattern, handler) {
|
|
41
|
+
routes.push({ method, pattern, handler });
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
get(pattern, handler) {
|
|
45
|
+
add("GET", pattern, handler);
|
|
46
|
+
},
|
|
47
|
+
post(pattern, handler) {
|
|
48
|
+
add("POST", pattern, handler);
|
|
49
|
+
},
|
|
50
|
+
put(pattern, handler) {
|
|
51
|
+
add("PUT", pattern, handler);
|
|
52
|
+
},
|
|
53
|
+
patch(pattern, handler) {
|
|
54
|
+
add("PATCH", pattern, handler);
|
|
55
|
+
},
|
|
56
|
+
delete(pattern, handler) {
|
|
57
|
+
add("DELETE", pattern, handler);
|
|
58
|
+
},
|
|
59
|
+
/**
|
|
60
|
+
* Try each registered route in order. For string patterns, performs an
|
|
61
|
+
* exact match against `ctx.pathname`. For RegExp patterns, tests the
|
|
62
|
+
* pathname and extracts named groups (or positional captures indexed by
|
|
63
|
+
* their number) into `params`.
|
|
64
|
+
*
|
|
65
|
+
* Returns `true` if a matching route was found (and its handler was
|
|
66
|
+
* invoked), `false` otherwise.
|
|
67
|
+
*/
|
|
68
|
+
async handle(ctx) {
|
|
69
|
+
for (const route of routes) {
|
|
70
|
+
if (route.method !== ctx.method)
|
|
71
|
+
continue;
|
|
72
|
+
let params = {};
|
|
73
|
+
if (typeof route.pattern === "string") {
|
|
74
|
+
if (ctx.pathname !== route.pattern)
|
|
75
|
+
continue;
|
|
76
|
+
} else {
|
|
77
|
+
const match = route.pattern.exec(ctx.pathname);
|
|
78
|
+
if (!match)
|
|
79
|
+
continue;
|
|
80
|
+
if (match.groups) {
|
|
81
|
+
params = { ...match.groups };
|
|
82
|
+
}
|
|
83
|
+
for (let i = 1; i < match.length; i++) {
|
|
84
|
+
if (match[i] !== void 0) {
|
|
85
|
+
params[String(i)] = decodeURIComponent(match[i]);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
await route.handler({ ...ctx, params });
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ../../packages/http/dist/request.js
|
|
98
|
+
function readBody(req, maxSize = 1048576) {
|
|
99
|
+
return new Promise((resolve, reject) => {
|
|
100
|
+
const chunks = [];
|
|
101
|
+
let size = 0;
|
|
102
|
+
req.on("data", (chunk) => {
|
|
103
|
+
size += chunk.length;
|
|
104
|
+
if (size > maxSize) {
|
|
105
|
+
req.destroy();
|
|
106
|
+
reject(new Error("Request body too large"));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
chunks.push(chunk);
|
|
110
|
+
});
|
|
111
|
+
req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
112
|
+
req.on("error", reject);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
function readBodyRaw(req, maxSize = 5 * 1024 * 1024) {
|
|
116
|
+
return new Promise((resolve, reject) => {
|
|
117
|
+
const chunks = [];
|
|
118
|
+
let size = 0;
|
|
119
|
+
req.on("data", (chunk) => {
|
|
120
|
+
size += chunk.length;
|
|
121
|
+
if (size > maxSize) {
|
|
122
|
+
req.destroy();
|
|
123
|
+
reject(new Error("Request body too large"));
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
chunks.push(chunk);
|
|
127
|
+
});
|
|
128
|
+
req.on("end", () => resolve(Buffer.concat(chunks)));
|
|
129
|
+
req.on("error", reject);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
async function parseJsonBody(req) {
|
|
133
|
+
try {
|
|
134
|
+
const raw = await readBody(req);
|
|
135
|
+
const parsed = JSON.parse(raw);
|
|
136
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
137
|
+
return { data: null, error: "Expected a JSON object" };
|
|
138
|
+
}
|
|
139
|
+
return { data: parsed, error: null };
|
|
140
|
+
} catch (err) {
|
|
141
|
+
const message = err instanceof Error ? err.message : "Invalid JSON body";
|
|
142
|
+
return { data: null, error: message };
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function parseMultipartImage(body, boundary) {
|
|
146
|
+
const boundaryBuffer = Buffer.from(`--${boundary}`);
|
|
147
|
+
const start = body.indexOf(boundaryBuffer);
|
|
148
|
+
if (start === -1)
|
|
149
|
+
return null;
|
|
150
|
+
const headerEnd = body.indexOf(Buffer.from("\r\n\r\n"), start);
|
|
151
|
+
if (headerEnd === -1)
|
|
152
|
+
return null;
|
|
153
|
+
const headers = body.subarray(start + boundaryBuffer.length + 2, headerEnd).toString();
|
|
154
|
+
const ctMatch = headers.match(/Content-Type:\s*(.+)/i);
|
|
155
|
+
const contentType = ctMatch ? ctMatch[1].trim() : "image/png";
|
|
156
|
+
const dataStart = headerEnd + 4;
|
|
157
|
+
const nextBoundary = body.indexOf(boundaryBuffer, dataStart);
|
|
158
|
+
const dataEnd = nextBoundary !== -1 ? nextBoundary - 2 : body.length;
|
|
159
|
+
return { data: body.subarray(dataStart, dataEnd), contentType };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ../../packages/http/dist/response.js
|
|
163
|
+
function resolveOrigin(options, req) {
|
|
164
|
+
if (!options)
|
|
165
|
+
return "*";
|
|
166
|
+
if (typeof options.origin === "function")
|
|
167
|
+
return options.origin(req);
|
|
168
|
+
return options.origin;
|
|
169
|
+
}
|
|
170
|
+
function corsHeaders(options, req) {
|
|
171
|
+
const headers = {
|
|
172
|
+
"Access-Control-Allow-Origin": resolveOrigin(options, req),
|
|
173
|
+
"Access-Control-Allow-Methods": options?.methods ?? "GET, POST, PUT, PATCH, DELETE, OPTIONS",
|
|
174
|
+
"Access-Control-Allow-Headers": options?.headers ?? "Content-Type, Authorization"
|
|
175
|
+
};
|
|
176
|
+
if (options?.credentials) {
|
|
177
|
+
headers["Access-Control-Allow-Credentials"] = "true";
|
|
178
|
+
}
|
|
179
|
+
if (options?.maxAge) {
|
|
180
|
+
headers["Access-Control-Max-Age"] = String(options.maxAge);
|
|
181
|
+
}
|
|
182
|
+
return headers;
|
|
183
|
+
}
|
|
184
|
+
function jsonResponse(res, data, status = 200, req, cors) {
|
|
185
|
+
const body = JSON.stringify(data);
|
|
186
|
+
res.writeHead(status, {
|
|
187
|
+
"Content-Type": "application/json",
|
|
188
|
+
...corsHeaders(cors, req)
|
|
189
|
+
});
|
|
190
|
+
res.end(body);
|
|
191
|
+
}
|
|
192
|
+
function notFound(res, message = "Not found", req, cors) {
|
|
193
|
+
jsonResponse(res, { error: message }, 404, req, cors);
|
|
194
|
+
}
|
|
195
|
+
function sseHeaders(res, req, cors) {
|
|
196
|
+
res.writeHead(200, {
|
|
197
|
+
"Content-Type": "text/event-stream",
|
|
198
|
+
"Cache-Control": "no-cache",
|
|
199
|
+
Connection: "keep-alive",
|
|
200
|
+
...corsHeaders(cors, req)
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
function sseEvent(res, eventName, data) {
|
|
204
|
+
res.write(`event: ${eventName}
|
|
205
|
+
data: ${JSON.stringify(data)}
|
|
206
|
+
|
|
207
|
+
`);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ../../packages/http/dist/middleware/rate-limit.js
|
|
211
|
+
var store = /* @__PURE__ */ new Map();
|
|
212
|
+
var CLEANUP_INTERVAL = 6e4;
|
|
213
|
+
setInterval(() => {
|
|
214
|
+
const now = Date.now();
|
|
215
|
+
for (const [key, entry] of store) {
|
|
216
|
+
if (now >= entry.resetAt) {
|
|
217
|
+
store.delete(key);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}, CLEANUP_INTERVAL).unref();
|
|
221
|
+
function checkRateLimit(key, limit = 200, windowMs = 6e4) {
|
|
222
|
+
const now = Date.now();
|
|
223
|
+
const entry = store.get(key);
|
|
224
|
+
if (!entry || now >= entry.resetAt) {
|
|
225
|
+
store.set(key, { count: 1, resetAt: now + windowMs });
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
entry.count++;
|
|
229
|
+
return entry.count <= limit;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ../../packages/http/dist/middleware/cors.js
|
|
233
|
+
function handleCorsPreFlight(req, res, options) {
|
|
234
|
+
if ((req.method ?? "GET") !== "OPTIONS")
|
|
235
|
+
return false;
|
|
236
|
+
res.writeHead(204, corsHeaders(options, req));
|
|
237
|
+
res.end();
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ../../packages/http/dist/websocket.js
|
|
242
|
+
import { WebSocketServer } from "ws";
|
|
243
|
+
|
|
244
|
+
// ../../packages/http/dist/sse.js
|
|
245
|
+
var SSEBroadcaster = class _SSEBroadcaster {
|
|
246
|
+
static MAX_CLIENTS = 100;
|
|
247
|
+
clients = /* @__PURE__ */ new Set();
|
|
248
|
+
/** Check if the broadcaster can accept another client. */
|
|
249
|
+
canAccept() {
|
|
250
|
+
return this.clients.size < _SSEBroadcaster.MAX_CLIENTS;
|
|
251
|
+
}
|
|
252
|
+
/** Register a new SSE client. */
|
|
253
|
+
add(res) {
|
|
254
|
+
this.clients.add(res);
|
|
255
|
+
}
|
|
256
|
+
/** Remove a disconnected SSE client. */
|
|
257
|
+
remove(res) {
|
|
258
|
+
this.clients.delete(res);
|
|
259
|
+
}
|
|
260
|
+
/** Broadcast a named SSE event to every connected client. */
|
|
261
|
+
broadcast(eventName, data) {
|
|
262
|
+
const message = `event: ${eventName}
|
|
263
|
+
data: ${JSON.stringify(data)}
|
|
264
|
+
|
|
265
|
+
`;
|
|
266
|
+
for (const client of this.clients) {
|
|
267
|
+
try {
|
|
268
|
+
client.write(message);
|
|
269
|
+
} catch {
|
|
270
|
+
this.clients.delete(client);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
/** Number of currently connected clients. */
|
|
275
|
+
get size() {
|
|
276
|
+
return this.clients.size;
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
export {
|
|
281
|
+
createRouter,
|
|
282
|
+
readBody,
|
|
283
|
+
readBodyRaw,
|
|
284
|
+
parseJsonBody,
|
|
285
|
+
parseMultipartImage,
|
|
286
|
+
jsonResponse,
|
|
287
|
+
notFound,
|
|
288
|
+
sseHeaders,
|
|
289
|
+
sseEvent,
|
|
290
|
+
generateApiSecret,
|
|
291
|
+
setApiSecret,
|
|
292
|
+
getApiSecret,
|
|
293
|
+
validateLoopback,
|
|
294
|
+
validateAuth,
|
|
295
|
+
checkRateLimit,
|
|
296
|
+
handleCorsPreFlight,
|
|
297
|
+
SSEBroadcaster
|
|
298
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildPrompt
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-76D3BYJD.js";
|
|
4
4
|
import {
|
|
5
5
|
external_exports
|
|
6
6
|
} from "./chunk-KIKPIH6N.js";
|
|
@@ -38,12 +38,13 @@ var JobConfigSchema = external_exports.object({
|
|
|
38
38
|
repoUrl: external_exports.string().url(),
|
|
39
39
|
ref: external_exports.string(),
|
|
40
40
|
baseBranch: external_exports.string(),
|
|
41
|
-
prNumber: external_exports.number().int().positive(),
|
|
41
|
+
prNumber: external_exports.number().int().positive().optional(),
|
|
42
42
|
repoFullName: external_exports.string(),
|
|
43
43
|
instruction: external_exports.string(),
|
|
44
44
|
jiraContext: external_exports.string().optional(),
|
|
45
45
|
timeoutMinutes: external_exports.number().int().positive().default(1440),
|
|
46
|
-
envVars: external_exports.record(external_exports.string()).optional()
|
|
46
|
+
envVars: external_exports.record(external_exports.string()).optional(),
|
|
47
|
+
autoCommitIgnore: external_exports.array(external_exports.string()).default([".pnpm-store/", "node_modules/", ".claude/"])
|
|
47
48
|
});
|
|
48
49
|
var JobResultSchema = external_exports.object({
|
|
49
50
|
jobId: external_exports.string(),
|
|
@@ -69,8 +70,8 @@ var JobSchema = external_exports.object({
|
|
|
69
70
|
startedAt: external_exports.string().datetime().optional(),
|
|
70
71
|
completedAt: external_exports.string().datetime().optional(),
|
|
71
72
|
cancelledBy: external_exports.string().optional(),
|
|
72
|
-
commentId: external_exports.number().int().positive(),
|
|
73
|
-
commentAuthor: external_exports.string()
|
|
73
|
+
commentId: external_exports.number().int().positive().optional(),
|
|
74
|
+
commentAuthor: external_exports.string().optional()
|
|
74
75
|
});
|
|
75
76
|
var GitHubAppAuthSchema = external_exports.object({
|
|
76
77
|
appId: external_exports.string(),
|
|
@@ -157,6 +158,26 @@ var JiraReferenceSchema = external_exports.object({
|
|
|
157
158
|
key: external_exports.string(),
|
|
158
159
|
url: external_exports.string().url()
|
|
159
160
|
});
|
|
161
|
+
var JiraSprintSchema = external_exports.object({
|
|
162
|
+
id: external_exports.number().int(),
|
|
163
|
+
name: external_exports.string(),
|
|
164
|
+
state: external_exports.string(),
|
|
165
|
+
// "active", "closed", "future"
|
|
166
|
+
startDate: external_exports.string().optional(),
|
|
167
|
+
endDate: external_exports.string().optional()
|
|
168
|
+
});
|
|
169
|
+
var SprintImportConfigSchema = external_exports.object({
|
|
170
|
+
jiraBaseUrl: external_exports.string().url(),
|
|
171
|
+
email: external_exports.string().email(),
|
|
172
|
+
apiToken: external_exports.string(),
|
|
173
|
+
sprintId: external_exports.number().int().positive()
|
|
174
|
+
});
|
|
175
|
+
var JiraSprintImportSchema = external_exports.object({
|
|
176
|
+
sprintId: external_exports.number().int(),
|
|
177
|
+
tasks: external_exports.array(external_exports.unknown()),
|
|
178
|
+
// TrackerTask[] — imported at runtime
|
|
179
|
+
warnings: external_exports.array(external_exports.string())
|
|
180
|
+
});
|
|
160
181
|
|
|
161
182
|
// src/commands/ci.ts
|
|
162
183
|
async function runCi(args, projectDir) {
|
|
@@ -185,7 +206,7 @@ async function runCiJob(args) {
|
|
|
185
206
|
let jobId = "";
|
|
186
207
|
let configJson = "";
|
|
187
208
|
let outputPath = "";
|
|
188
|
-
let model = "claude-
|
|
209
|
+
let model = process.env.ULPI_CLAUDE_MODEL ?? "claude-opus-4-6";
|
|
189
210
|
for (let i = 0; i < args.length; i++) {
|
|
190
211
|
switch (args[i]) {
|
|
191
212
|
case "--job-id":
|
|
@@ -250,6 +271,7 @@ async function runCiJob(args) {
|
|
|
250
271
|
process.exit(1);
|
|
251
272
|
}
|
|
252
273
|
console.log(chalk.blue(`[ulpi-ci] Using claude at: ${claudePath}`));
|
|
274
|
+
logClaudeMdFiles(projectDir);
|
|
253
275
|
const claudeArgs = [
|
|
254
276
|
"--print",
|
|
255
277
|
"--verbose",
|
|
@@ -261,8 +283,9 @@ async function runCiJob(args) {
|
|
|
261
283
|
];
|
|
262
284
|
console.log(chalk.blue("[ulpi-ci] Spawning Claude Code..."));
|
|
263
285
|
const result = await spawnClaude(claudePath, claudeArgs, prompt, config.timeoutMinutes * 60 * 1e3);
|
|
264
|
-
commitUnstagedWork(process.cwd());
|
|
286
|
+
commitUnstagedWork(process.cwd(), config.autoCommitIgnore);
|
|
265
287
|
await postSessionFinalize(process.cwd());
|
|
288
|
+
await exportShadowBranches(process.cwd());
|
|
266
289
|
const baseBranch = config.baseBranch;
|
|
267
290
|
let diff = "";
|
|
268
291
|
let filesChanged = [];
|
|
@@ -327,7 +350,7 @@ async function initUlpiSubsystems(projectDir) {
|
|
|
327
350
|
if (fs.existsSync(guardsPath)) {
|
|
328
351
|
console.log(chalk.blue("[ulpi-ci] guards.yml found \u2014 rules will be enforced"));
|
|
329
352
|
} else {
|
|
330
|
-
const { loadBundledTemplates, composeTemplates, resolveTemplate } = await import("./dist-
|
|
353
|
+
const { loadBundledTemplates, composeTemplates, resolveTemplate } = await import("./dist-HU5RZAON.js");
|
|
331
354
|
const stackConfig = {
|
|
332
355
|
name: path.basename(projectDir),
|
|
333
356
|
runtime: stack.runtime?.id ?? "unknown",
|
|
@@ -369,14 +392,14 @@ async function initUlpiSubsystems(projectDir) {
|
|
|
369
392
|
console.log(chalk.yellow(`[ulpi-ci] Warning: Could not detect stack/generate guards.yml: ${err instanceof Error ? err.message : String(err)}`));
|
|
370
393
|
}
|
|
371
394
|
try {
|
|
372
|
-
const { installHooks } = await import("./hooks-installer-
|
|
395
|
+
const { installHooks } = await import("./hooks-installer-UN5JZLDQ.js");
|
|
373
396
|
installHooks(projectDir);
|
|
374
397
|
console.log(chalk.blue("[ulpi-ci] Hooks installed"));
|
|
375
398
|
} catch (err) {
|
|
376
399
|
console.log(chalk.yellow(`[ulpi-ci] Warning: Could not install hooks: ${err instanceof Error ? err.message : String(err)}`));
|
|
377
400
|
}
|
|
378
401
|
try {
|
|
379
|
-
const { installMcpServer, installMemoryMcpServer } = await import("./mcp-installer-
|
|
402
|
+
const { installMcpServer, installMemoryMcpServer } = await import("./mcp-installer-PQU3XOGO.js");
|
|
380
403
|
installMcpServer(projectDir);
|
|
381
404
|
installMemoryMcpServer(projectDir);
|
|
382
405
|
console.log(chalk.blue("[ulpi-ci] MCP servers registered"));
|
|
@@ -389,10 +412,10 @@ async function initUlpiSubsystems(projectDir) {
|
|
|
389
412
|
} catch {
|
|
390
413
|
}
|
|
391
414
|
try {
|
|
392
|
-
const { historyBranchExists, initHistoryBranch } = await import("./dist-
|
|
415
|
+
const { historyBranchExists, initHistoryBranch } = await import("./dist-YCNWHSLN.js");
|
|
393
416
|
if (!historyBranchExists(projectDir)) {
|
|
394
417
|
const projectName = path.basename(projectDir);
|
|
395
|
-
const version = true ? "0.1.
|
|
418
|
+
const version = true ? "0.1.6" : "0.0.0";
|
|
396
419
|
initHistoryBranch(projectDir, projectName, version);
|
|
397
420
|
console.log(chalk.blue("[ulpi-ci] History branch initialized"));
|
|
398
421
|
} else {
|
|
@@ -402,8 +425,8 @@ async function initUlpiSubsystems(projectDir) {
|
|
|
402
425
|
console.log(chalk.yellow(`[ulpi-ci] Warning: Could not init history: ${err instanceof Error ? err.message : String(err)}`));
|
|
403
426
|
}
|
|
404
427
|
try {
|
|
405
|
-
const { isMemoryInitialized, saveMemoryConfig, DEFAULT_MEMORY_CONFIG, initMemoryBranch } = await import("./dist-
|
|
406
|
-
const { projectMemoryDir } = await import("./dist-
|
|
428
|
+
const { isMemoryInitialized, saveMemoryConfig, DEFAULT_MEMORY_CONFIG, initMemoryBranch } = await import("./dist-JLU26AB6.js");
|
|
429
|
+
const { projectMemoryDir } = await import("./dist-NUXMDXZ3.js");
|
|
407
430
|
if (!isMemoryInitialized(projectDir)) {
|
|
408
431
|
const memDir = projectMemoryDir(projectDir);
|
|
409
432
|
fs.mkdirSync(memDir, { recursive: true });
|
|
@@ -418,11 +441,11 @@ async function initUlpiSubsystems(projectDir) {
|
|
|
418
441
|
console.log(chalk.yellow(`[ulpi-ci] Warning: Could not init memory: ${err instanceof Error ? err.message : String(err)}`));
|
|
419
442
|
}
|
|
420
443
|
try {
|
|
421
|
-
const { resolveApiKey } = await import("./dist-
|
|
444
|
+
const { resolveApiKey } = await import("./dist-NUXMDXZ3.js");
|
|
422
445
|
const hasOpenAiKey = !!resolveApiKey("openai");
|
|
423
446
|
const hasUlpiKey = !!resolveApiKey("ulpi");
|
|
424
447
|
if (hasOpenAiKey || hasUlpiKey) {
|
|
425
|
-
const { runInitPipeline } = await import("./dist-
|
|
448
|
+
const { runInitPipeline } = await import("./dist-YFFG2ZD6.js");
|
|
426
449
|
console.log(chalk.blue("[ulpi-ci] Indexing codebase..."));
|
|
427
450
|
await runInitPipeline(projectDir, (progress) => {
|
|
428
451
|
if (progress.message) {
|
|
@@ -490,8 +513,22 @@ function writeYamlFields(obj, lines, indent) {
|
|
|
490
513
|
}
|
|
491
514
|
}
|
|
492
515
|
}
|
|
493
|
-
function commitUnstagedWork(cwd) {
|
|
516
|
+
function commitUnstagedWork(cwd, ignorePatterns = []) {
|
|
494
517
|
try {
|
|
518
|
+
if (ignorePatterns.length > 0) {
|
|
519
|
+
const gitignorePath = path.join(cwd, ".gitignore");
|
|
520
|
+
try {
|
|
521
|
+
const existing = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, "utf-8") : "";
|
|
522
|
+
const missing = ignorePatterns.filter((p) => !existing.includes(p));
|
|
523
|
+
if (missing.length > 0) {
|
|
524
|
+
const separator = existing.endsWith("\n") || existing === "" ? "" : "\n";
|
|
525
|
+
fs.appendFileSync(gitignorePath, `${separator}# Added by ULPI CI worker
|
|
526
|
+
${missing.join("\n")}
|
|
527
|
+
`);
|
|
528
|
+
}
|
|
529
|
+
} catch {
|
|
530
|
+
}
|
|
531
|
+
}
|
|
495
532
|
const status = execFileSync("git", ["status", "--porcelain"], {
|
|
496
533
|
cwd,
|
|
497
534
|
encoding: "utf-8",
|
|
@@ -500,10 +537,11 @@ function commitUnstagedWork(cwd) {
|
|
|
500
537
|
if (!status) return;
|
|
501
538
|
const fileCount = status.split("\n").filter(Boolean).length;
|
|
502
539
|
console.log(chalk.blue(`[ulpi-ci] Committing ${fileCount} uncommitted files...`));
|
|
503
|
-
execFileSync("git", ["add", "-A"], { cwd, timeout:
|
|
504
|
-
execFileSync("git", ["commit", "-m", "wip: auto-commit from ULPI CI worker"], {
|
|
540
|
+
execFileSync("git", ["add", "-A"], { cwd, timeout: 6e5, maxBuffer: 50 * 1024 * 1024 });
|
|
541
|
+
execFileSync("git", ["commit", "--no-verify", "-m", "wip: auto-commit from ULPI CI worker"], {
|
|
505
542
|
cwd,
|
|
506
|
-
timeout:
|
|
543
|
+
timeout: 6e5,
|
|
544
|
+
maxBuffer: 50 * 1024 * 1024
|
|
507
545
|
});
|
|
508
546
|
console.log(chalk.blue("[ulpi-ci] WIP commit created"));
|
|
509
547
|
} catch (err) {
|
|
@@ -522,8 +560,8 @@ async function postSessionFinalize(projectDir) {
|
|
|
522
560
|
writeHistoryEntry,
|
|
523
561
|
readBranchMeta,
|
|
524
562
|
DEFAULT_HISTORY_CONFIG
|
|
525
|
-
} = await import("./dist-
|
|
526
|
-
const { loadActiveGuards } = await import("./dist-
|
|
563
|
+
} = await import("./dist-YCNWHSLN.js");
|
|
564
|
+
const { loadActiveGuards } = await import("./dist-YCNWHSLN.js");
|
|
527
565
|
if (historyBranchExists(projectDir)) {
|
|
528
566
|
const commits = listBranchOnlyCommits(projectDir, 50);
|
|
529
567
|
const meta = readBranchMeta(projectDir);
|
|
@@ -563,9 +601,9 @@ async function postSessionFinalize(projectDir) {
|
|
|
563
601
|
console.log(chalk.yellow(`[ulpi-ci] Warning: History backfill failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
564
602
|
}
|
|
565
603
|
try {
|
|
566
|
-
const { isMemoryInitialized, classifySession } = await import("./dist-
|
|
567
|
-
const { ULPI_GLOBAL_DIR } = await import("./dist-
|
|
568
|
-
const { JsonSessionStore } = await import("./dist-
|
|
604
|
+
const { isMemoryInitialized, classifySession } = await import("./dist-JLU26AB6.js");
|
|
605
|
+
const { ULPI_GLOBAL_DIR } = await import("./dist-NUXMDXZ3.js");
|
|
606
|
+
const { JsonSessionStore } = await import("./dist-6M4MZWZW.js");
|
|
569
607
|
if (isMemoryInitialized(projectDir)) {
|
|
570
608
|
const store = new JsonSessionStore(ULPI_GLOBAL_DIR, projectDir);
|
|
571
609
|
const latest = store.getLatestForProject(projectDir);
|
|
@@ -586,6 +624,66 @@ async function postSessionFinalize(projectDir) {
|
|
|
586
624
|
console.log(chalk.yellow(`[ulpi-ci] Warning: Memory classify failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
587
625
|
}
|
|
588
626
|
}
|
|
627
|
+
async function exportShadowBranches(projectDir) {
|
|
628
|
+
try {
|
|
629
|
+
const { getCodemapStatus, exportIndex } = await import("./dist-YFFG2ZD6.js");
|
|
630
|
+
const { getCurrentBranch } = await import("./dist-NUXMDXZ3.js");
|
|
631
|
+
const branch = getCurrentBranch(projectDir);
|
|
632
|
+
const status = getCodemapStatus(projectDir, branch);
|
|
633
|
+
if (status.initialized) {
|
|
634
|
+
const result = await exportIndex(projectDir, branch);
|
|
635
|
+
console.log(chalk.blue(`[ulpi-ci] Codemap exported to ${result.branchName} (${result.filesExported} files)`));
|
|
636
|
+
}
|
|
637
|
+
} catch (err) {
|
|
638
|
+
console.log(chalk.yellow(`[ulpi-ci] Warning: Codemap export failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
639
|
+
}
|
|
640
|
+
try {
|
|
641
|
+
const { isMemoryInitialized, exportMemories } = await import("./dist-JLU26AB6.js");
|
|
642
|
+
if (isMemoryInitialized(projectDir)) {
|
|
643
|
+
const result = await exportMemories(projectDir);
|
|
644
|
+
console.log(chalk.blue(`[ulpi-ci] Memory exported to ${result.branchName} (${result.memoriesExported} memories, ${result.filesExported} files)`));
|
|
645
|
+
}
|
|
646
|
+
} catch (err) {
|
|
647
|
+
console.log(chalk.yellow(`[ulpi-ci] Warning: Memory export failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
function logClaudeMdFiles(projectDir) {
|
|
651
|
+
const rootMd = path.join(projectDir, "CLAUDE.md");
|
|
652
|
+
if (fs.existsSync(rootMd)) {
|
|
653
|
+
const stat = fs.statSync(rootMd);
|
|
654
|
+
console.log(chalk.blue(`[ulpi-ci] Found CLAUDE.md (${stat.size} bytes)`));
|
|
655
|
+
} else {
|
|
656
|
+
console.log(chalk.yellow("[ulpi-ci] No CLAUDE.md in workspace root"));
|
|
657
|
+
}
|
|
658
|
+
const claudeDir = path.join(projectDir, ".claude");
|
|
659
|
+
if (fs.existsSync(claudeDir)) {
|
|
660
|
+
const items = [];
|
|
661
|
+
try {
|
|
662
|
+
for (const entry of fs.readdirSync(claudeDir, { recursive: true })) {
|
|
663
|
+
if (entry.endsWith(".md") || entry === "settings.local.json") {
|
|
664
|
+
items.push(entry);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
} catch {
|
|
668
|
+
}
|
|
669
|
+
console.log(chalk.blue(`[ulpi-ci] .claude/ contents: ${items.length > 0 ? items.join(", ") : "(empty)"}`));
|
|
670
|
+
} else {
|
|
671
|
+
console.log(chalk.yellow("[ulpi-ci] No .claude/ directory in workspace"));
|
|
672
|
+
}
|
|
673
|
+
try {
|
|
674
|
+
const nested = [];
|
|
675
|
+
for (const entry of fs.readdirSync(projectDir, { withFileTypes: true })) {
|
|
676
|
+
if (entry.isDirectory() && !entry.name.startsWith(".")) {
|
|
677
|
+
const nestedMd = path.join(projectDir, entry.name, "CLAUDE.md");
|
|
678
|
+
if (fs.existsSync(nestedMd)) nested.push(entry.name);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
if (nested.length > 0) {
|
|
682
|
+
console.log(chalk.blue(`[ulpi-ci] Nested CLAUDE.md: ${nested.join(", ")}`));
|
|
683
|
+
}
|
|
684
|
+
} catch {
|
|
685
|
+
}
|
|
686
|
+
}
|
|
589
687
|
function findClaudeBinary() {
|
|
590
688
|
try {
|
|
591
689
|
const result = execFileSync("which", ["claude"], { encoding: "utf-8", timeout: 5e3 });
|
|
@@ -662,7 +760,7 @@ function spawnClaude(claudePath, args, prompt, timeoutMs) {
|
|
|
662
760
|
return new Promise((resolve) => {
|
|
663
761
|
const proc = spawn(claudePath, args, {
|
|
664
762
|
cwd: process.cwd(),
|
|
665
|
-
env: process.env,
|
|
763
|
+
env: { ...process.env, ULPI_RUN_MODE: "ci" },
|
|
666
764
|
stdio: ["pipe", "pipe", "pipe"]
|
|
667
765
|
});
|
|
668
766
|
let stdout = "";
|