@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.
- package/README.md +143 -214
- 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-P2RESJRN.js → chunk-EWLYVXQ4.js} +2 -2
- 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-PO4NUZUU.js +147 -0
- 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-WED4LM5N.js +322 -0
- package/dist/chunk-WVOZE25N.js +6757 -0
- package/dist/{chunk-5SCG7UYM.js → chunk-XKF4DPUM.js} +7 -7
- package/dist/{chunk-74WVVWJ4.js → chunk-YOKL7RB5.js} +184 -15
- package/dist/chunk-Z53CAR7G.js +298 -0
- package/dist/{ci-JQ56YIKC.js → ci-COZRTPGQ.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-FKYSIHER.js +345 -0
- package/dist/{export-import-4A5MWLIA.js → export-import-JFQH4KSJ.js} +1 -1
- package/dist/{history-RNUWO4JZ.js → history-UMGQNQQ7.js} +7 -7
- package/dist/{hooks-installer-K2JXEBNN.js → hooks-installer-YEYTYA6Q.js} +2 -2
- package/dist/index.js +398 -622
- package/dist/{init-NQWFZPKO.js → init-TJYW5ROZ.js} +78 -12
- 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-U3MSWBRH.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-RQE4KMAV.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-U7PQ6FTS-MG4MJPTS.js +20 -0
- package/dist/{skills-GY2CTPWN.js → skills-QEYU2N27.js} +4 -2
- package/dist/start-IJKY5RVT.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-BX6FOV77.js} +3 -3
- package/dist/{update-QYZA4D23.js → update-AQKTHFVQ.js} +3 -3
- package/dist/{version-checker-MVB74DEX.js → version-checker-5L5PUOEX.js} +2 -2
- package/package.json +13 -4
- 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/ui-4SM2SUI6.js +0 -167
- package/dist/ui.html +0 -698
- /package/dist/skills/{ulpi-generate-guardian → ulpi-generate-guards}/SKILL.md +0 -0
- /package/dist/skills/{ulpi-generate-guardian → ulpi-generate-guards}/references/framework-rules.md +0 -0
- /package/dist/skills/{ulpi-generate-guardian → ulpi-generate-guards}/references/language-rules.md +0 -0
|
@@ -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-YEYTYA6Q.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.7" : "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 = "";
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import {
|
|
2
|
+
loadCloudGatewayConfig,
|
|
3
|
+
loadUlpiSettings,
|
|
4
|
+
removeCloudGatewayConfig,
|
|
5
|
+
saveCloudGatewayConfig
|
|
6
|
+
} from "./chunk-C7CLUQI6.js";
|
|
7
|
+
import "./chunk-4VNS5WPM.js";
|
|
8
|
+
|
|
9
|
+
// src/commands/cloud.ts
|
|
10
|
+
import * as fs from "fs";
|
|
11
|
+
import * as path from "path";
|
|
12
|
+
import * as os from "os";
|
|
13
|
+
import * as https from "https";
|
|
14
|
+
import * as http from "http";
|
|
15
|
+
import chalk from "chalk";
|
|
16
|
+
function getFlag(args, flag) {
|
|
17
|
+
const prefix = `--${flag}=`;
|
|
18
|
+
const withEq = args.find((a) => a.startsWith(prefix));
|
|
19
|
+
if (withEq) return withEq.slice(prefix.length);
|
|
20
|
+
const idx = args.indexOf(`--${flag}`);
|
|
21
|
+
if (idx !== -1 && args[idx + 1] && !args[idx + 1].startsWith("--")) return args[idx + 1];
|
|
22
|
+
return void 0;
|
|
23
|
+
}
|
|
24
|
+
function fetchJson(url, apiKey) {
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
const parsedUrl = new URL(url);
|
|
27
|
+
const mod = parsedUrl.protocol === "https:" ? https : http;
|
|
28
|
+
const req = mod.request(
|
|
29
|
+
{
|
|
30
|
+
hostname: parsedUrl.hostname,
|
|
31
|
+
port: parsedUrl.port || (parsedUrl.protocol === "https:" ? 443 : 80),
|
|
32
|
+
path: parsedUrl.pathname + parsedUrl.search,
|
|
33
|
+
method: "GET",
|
|
34
|
+
headers: {
|
|
35
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
36
|
+
"Accept": "application/json"
|
|
37
|
+
},
|
|
38
|
+
timeout: 1e4
|
|
39
|
+
},
|
|
40
|
+
(res) => {
|
|
41
|
+
let data = "";
|
|
42
|
+
res.on("data", (chunk) => {
|
|
43
|
+
data += chunk;
|
|
44
|
+
});
|
|
45
|
+
res.on("end", () => {
|
|
46
|
+
try {
|
|
47
|
+
if ((res.statusCode ?? 0) >= 400) {
|
|
48
|
+
reject(new Error(`HTTP ${res.statusCode}: ${data}`));
|
|
49
|
+
} else {
|
|
50
|
+
resolve(JSON.parse(data));
|
|
51
|
+
}
|
|
52
|
+
} catch {
|
|
53
|
+
reject(new Error(`Invalid JSON response: ${data}`));
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
req.on("error", reject);
|
|
59
|
+
req.on("timeout", () => {
|
|
60
|
+
req.destroy();
|
|
61
|
+
reject(new Error("Request timed out"));
|
|
62
|
+
});
|
|
63
|
+
req.end();
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function mcpJsonPath(projectDir) {
|
|
67
|
+
return path.join(projectDir, ".mcp.json");
|
|
68
|
+
}
|
|
69
|
+
function readMcpJson(projectDir) {
|
|
70
|
+
const filePath = mcpJsonPath(projectDir);
|
|
71
|
+
if (fs.existsSync(filePath)) {
|
|
72
|
+
try {
|
|
73
|
+
const data = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
74
|
+
return { mcpServers: data.mcpServers ?? {} };
|
|
75
|
+
} catch {
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return { mcpServers: {} };
|
|
79
|
+
}
|
|
80
|
+
function writeMcpJson(projectDir, mcpServers) {
|
|
81
|
+
const filePath = mcpJsonPath(projectDir);
|
|
82
|
+
fs.writeFileSync(filePath, JSON.stringify({ mcpServers }, null, 2) + "\n", "utf-8");
|
|
83
|
+
}
|
|
84
|
+
var CLOUD_MCP_KEY = "ulpi-cloud";
|
|
85
|
+
function readJsonConfig(filePath) {
|
|
86
|
+
if (fs.existsSync(filePath)) {
|
|
87
|
+
try {
|
|
88
|
+
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
89
|
+
} catch {
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return {};
|
|
93
|
+
}
|
|
94
|
+
function writeJsonConfig(filePath, config) {
|
|
95
|
+
const dir = path.dirname(filePath);
|
|
96
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
97
|
+
fs.writeFileSync(filePath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
98
|
+
}
|
|
99
|
+
function getAgentConfigTargets() {
|
|
100
|
+
const home = os.homedir();
|
|
101
|
+
const targets = [];
|
|
102
|
+
const claudePath = path.join(home, ".claude", "settings.json");
|
|
103
|
+
targets.push({ name: "Claude Code", configPath: claudePath });
|
|
104
|
+
return targets;
|
|
105
|
+
}
|
|
106
|
+
function writeCloudMcpToAgentConfig(configPath, entry) {
|
|
107
|
+
const config = readJsonConfig(configPath);
|
|
108
|
+
const mcpServers = config.mcpServers ?? {};
|
|
109
|
+
if (mcpServers[CLOUD_MCP_KEY]) return false;
|
|
110
|
+
mcpServers[CLOUD_MCP_KEY] = entry;
|
|
111
|
+
config.mcpServers = mcpServers;
|
|
112
|
+
writeJsonConfig(configPath, config);
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
function removeCloudMcpFromAgentConfig(configPath) {
|
|
116
|
+
if (!fs.existsSync(configPath)) return false;
|
|
117
|
+
const config = readJsonConfig(configPath);
|
|
118
|
+
const mcpServers = config.mcpServers ?? {};
|
|
119
|
+
if (!mcpServers[CLOUD_MCP_KEY]) return false;
|
|
120
|
+
delete mcpServers[CLOUD_MCP_KEY];
|
|
121
|
+
config.mcpServers = mcpServers;
|
|
122
|
+
writeJsonConfig(configPath, config);
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
async function runConnect(args, projectDir) {
|
|
126
|
+
const settings = loadUlpiSettings();
|
|
127
|
+
const cloudApiUrl = getFlag(args, "url") ?? settings.ulpiUrl ?? process.env.ULPI_CLOUD_URL ?? "https://api.ulpi.io";
|
|
128
|
+
const apiKey = getFlag(args, "key") ?? settings.apiKeys?.ulpi ?? process.env.ULPI_API_KEY;
|
|
129
|
+
if (!apiKey) {
|
|
130
|
+
console.error(chalk.red("Error: API key required."));
|
|
131
|
+
console.error(chalk.dim("Provide via --key=<apiKey>, settings.json apiKeys.ulpi, or ULPI_API_KEY env var."));
|
|
132
|
+
console.error(chalk.dim("Run: ulpi config set apiKeys.ulpi <your-key>"));
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
const orgId = getFlag(args, "org");
|
|
136
|
+
if (!orgId) {
|
|
137
|
+
console.error(chalk.red("Error: Organization ID required."));
|
|
138
|
+
console.error(chalk.dim("Provide via --org=<orgId>"));
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
const connectionUrl = `${cloudApiUrl}/api/orgs/${encodeURIComponent(orgId)}/gateway/connection`;
|
|
142
|
+
console.log(chalk.dim(`Fetching gateway info from ${connectionUrl}...`));
|
|
143
|
+
let gatewayInfo;
|
|
144
|
+
try {
|
|
145
|
+
const response = await fetchJson(connectionUrl, apiKey);
|
|
146
|
+
const conn = response.connection;
|
|
147
|
+
if (!conn || typeof conn.url !== "string" || typeof conn.transport !== "string") {
|
|
148
|
+
console.error(chalk.red("Error: Invalid gateway response from cloud API."));
|
|
149
|
+
console.error(chalk.dim(`Response: ${JSON.stringify(response)}`));
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
gatewayInfo = { url: conn.url, transport: conn.transport };
|
|
153
|
+
} catch (err) {
|
|
154
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
155
|
+
console.error(chalk.red(`Error: Failed to fetch gateway connection info.`));
|
|
156
|
+
console.error(chalk.dim(message));
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
const { mcpServers } = readMcpJson(projectDir);
|
|
160
|
+
mcpServers[CLOUD_MCP_KEY] = {
|
|
161
|
+
url: gatewayInfo.url,
|
|
162
|
+
headers: {
|
|
163
|
+
Authorization: `Bearer ${apiKey}`
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
writeMcpJson(projectDir, mcpServers);
|
|
167
|
+
console.log(chalk.green(" Written to .mcp.json"));
|
|
168
|
+
const cloudEntry = {
|
|
169
|
+
url: gatewayInfo.url,
|
|
170
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
171
|
+
};
|
|
172
|
+
const agentTargets = getAgentConfigTargets();
|
|
173
|
+
for (const target of agentTargets) {
|
|
174
|
+
const written = writeCloudMcpToAgentConfig(target.configPath, cloudEntry);
|
|
175
|
+
if (written) {
|
|
176
|
+
console.log(chalk.green(` Written to ${target.name} (${chalk.dim(target.configPath)})`));
|
|
177
|
+
} else {
|
|
178
|
+
console.log(chalk.dim(` ${target.name}: already configured`));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
saveCloudGatewayConfig({
|
|
182
|
+
orgId,
|
|
183
|
+
url: gatewayInfo.url,
|
|
184
|
+
transport: gatewayInfo.transport,
|
|
185
|
+
cloudApiUrl,
|
|
186
|
+
connectedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
187
|
+
});
|
|
188
|
+
console.log("");
|
|
189
|
+
console.log(chalk.green("Connected to Cloud MCP Gateway."));
|
|
190
|
+
console.log(` Org: ${chalk.cyan(orgId)}`);
|
|
191
|
+
console.log(` Gateway: ${chalk.cyan(gatewayInfo.url)}`);
|
|
192
|
+
console.log(` Transport: ${chalk.cyan(gatewayInfo.transport)}`);
|
|
193
|
+
console.log("");
|
|
194
|
+
console.log(chalk.dim("Restart your agent session for the new MCP server to take effect."));
|
|
195
|
+
}
|
|
196
|
+
function runStatus() {
|
|
197
|
+
const config = loadCloudGatewayConfig();
|
|
198
|
+
if (!config) {
|
|
199
|
+
console.log(chalk.yellow("Not connected to any Cloud MCP Gateway."));
|
|
200
|
+
console.log(chalk.dim("Run: ulpi cloud connect --org=<orgId> --key=<apiKey>"));
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
console.log(chalk.bold("Cloud MCP Gateway Status\n"));
|
|
204
|
+
console.log(` Org: ${chalk.cyan(config.orgId)}`);
|
|
205
|
+
console.log(` Gateway URL: ${chalk.cyan(config.url)}`);
|
|
206
|
+
console.log(` Transport: ${chalk.cyan(config.transport)}`);
|
|
207
|
+
console.log(` Cloud API: ${chalk.dim(config.cloudApiUrl)}`);
|
|
208
|
+
console.log(` Connected: ${chalk.dim(config.connectedAt)}`);
|
|
209
|
+
}
|
|
210
|
+
function runDisconnect(projectDir) {
|
|
211
|
+
let removed = false;
|
|
212
|
+
const { mcpServers } = readMcpJson(projectDir);
|
|
213
|
+
if (mcpServers[CLOUD_MCP_KEY]) {
|
|
214
|
+
delete mcpServers[CLOUD_MCP_KEY];
|
|
215
|
+
writeMcpJson(projectDir, mcpServers);
|
|
216
|
+
console.log(chalk.green(" Removed from .mcp.json"));
|
|
217
|
+
removed = true;
|
|
218
|
+
}
|
|
219
|
+
const agentTargets = getAgentConfigTargets();
|
|
220
|
+
for (const target of agentTargets) {
|
|
221
|
+
const wasRemoved = removeCloudMcpFromAgentConfig(target.configPath);
|
|
222
|
+
if (wasRemoved) {
|
|
223
|
+
console.log(chalk.green(` Removed from ${target.name} (${chalk.dim(target.configPath)})`));
|
|
224
|
+
removed = true;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const configRemoved = removeCloudGatewayConfig();
|
|
228
|
+
if (configRemoved) {
|
|
229
|
+
removed = true;
|
|
230
|
+
}
|
|
231
|
+
if (removed) {
|
|
232
|
+
console.log("");
|
|
233
|
+
console.log(chalk.green("Disconnected from Cloud MCP Gateway."));
|
|
234
|
+
console.log(chalk.dim("Restart your agent session for changes to take effect."));
|
|
235
|
+
} else {
|
|
236
|
+
console.log(chalk.yellow("No cloud gateway connection found."));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
function printUsage() {
|
|
240
|
+
console.log(`
|
|
241
|
+
Usage: ulpi cloud <subcommand> [options]
|
|
242
|
+
|
|
243
|
+
Subcommands:
|
|
244
|
+
connect Connect local agent to cloud MCP gateway
|
|
245
|
+
status Show current cloud connection status
|
|
246
|
+
disconnect Remove cloud gateway from agent configs
|
|
247
|
+
|
|
248
|
+
Connect Options:
|
|
249
|
+
--url=<url> Cloud API URL (default: https://api.ulpi.io)
|
|
250
|
+
--org=<orgId> Organization ID (required)
|
|
251
|
+
--key=<key> API key (or set via ULPI_API_KEY env / ulpi config)
|
|
252
|
+
|
|
253
|
+
Examples:
|
|
254
|
+
ulpi cloud connect --org=org-abc123 --key=sk-...
|
|
255
|
+
ulpi cloud status
|
|
256
|
+
ulpi cloud disconnect
|
|
257
|
+
`.trim());
|
|
258
|
+
}
|
|
259
|
+
async function runCloud(args, projectDir) {
|
|
260
|
+
const sub = args[0];
|
|
261
|
+
switch (sub) {
|
|
262
|
+
case "connect":
|
|
263
|
+
return runConnect(args.slice(1), projectDir);
|
|
264
|
+
case "status":
|
|
265
|
+
return runStatus();
|
|
266
|
+
case "disconnect":
|
|
267
|
+
return runDisconnect(projectDir);
|
|
268
|
+
default:
|
|
269
|
+
printUsage();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
export {
|
|
273
|
+
runCloud
|
|
274
|
+
};
|