@ulpi/cli 0.1.1 → 0.1.3
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/dist/auth-KQCJ43U2.js +118 -0
- package/dist/{chunk-Q4HIY43N.js → chunk-2VYFVYJL.js} +67 -24
- package/dist/{chunk-DBMUNBNB.js → chunk-5J6NLQUN.js} +149 -19
- package/dist/{chunk-4KRVDKGB.js → chunk-F7OXF7Z3.js} +1 -1
- package/dist/chunk-G6SVZ4Q5.js +122 -0
- package/dist/{chunk-NNUWU6CV.js → chunk-JGBXM5NC.js} +42 -0
- package/dist/{chunk-6JCMYYBT.js → chunk-PDR55ZNW.js} +247 -112
- package/dist/{chunk-247GVVKK.js → chunk-ZLYRPD7I.js} +18 -16
- package/dist/ci-QM57ZCBW.js +367 -0
- package/dist/{codemap-RRJIDBQ5.js → codemap-RKSD4MIE.js} +49 -17
- package/dist/{dist-LZKZFPVX.js → dist-CB5D5LMO.js} +6 -3
- package/dist/{dist-7LHZ65GC.js → dist-CS2VKNYS.js} +5 -4
- package/dist/{dist-R5F4MX3I.js → dist-GJYT2OQV.js} +11 -4
- package/dist/{dist-RJGCUS3L.js → dist-QAU3LGJN.js} +3 -1
- package/dist/{dist-W7K4WPAF.js → dist-UKMCJBB2.js} +42 -14
- package/dist/{dist-R5ZJ4LX5.js → dist-YA2BWZB2.js} +1 -1
- package/dist/{history-Q2LDADFW.js → history-NFNA4HE5.js} +13 -7
- package/dist/index.js +50 -21
- package/dist/{init-AY5C2ZAS.js → init-6CH4HV5T.js} +2 -2
- package/dist/{memory-J3G24QHS.js → memory-Y6OZTXJ2.js} +231 -22
- package/dist/{server-MOYPE4SM-N7SE2AN7.js → server-USLHY6GH-AEOJC5ST.js} +2 -2
- package/dist/skills/ulpi-generate-guardian/SKILL.md +246 -7
- package/dist/skills/ulpi-generate-guardian/references/framework-rules.md +161 -4
- package/dist/skills/ulpi-generate-guardian/references/language-rules.md +13 -18
- package/dist/{ui-L7UAWXDY.js → ui-OWXZ3YSR.js} +3 -3
- package/dist/ui.html +112 -112
- package/dist/{update-DJ227LL3.js → update-WUITQX4Z.js} +1 -1
- package/dist/{version-checker-M37KI7DY.js → version-checker-SMAYSN7Y.js} +1 -1
- package/package.json +31 -28
- package/LICENSE +0 -21
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import {
|
|
2
|
+
extractCredentials,
|
|
3
|
+
getCredentialExpiry,
|
|
4
|
+
validateCredentials
|
|
5
|
+
} from "./chunk-G6SVZ4Q5.js";
|
|
6
|
+
import "./chunk-4VNS5WPM.js";
|
|
7
|
+
|
|
8
|
+
// src/commands/auth.ts
|
|
9
|
+
import * as fs from "fs";
|
|
10
|
+
import chalk from "chalk";
|
|
11
|
+
async function runAuth(args) {
|
|
12
|
+
const subcommand = args[0];
|
|
13
|
+
switch (subcommand) {
|
|
14
|
+
case "setup":
|
|
15
|
+
return authSetup();
|
|
16
|
+
case "check":
|
|
17
|
+
return authCheck(args.slice(1));
|
|
18
|
+
case "refresh":
|
|
19
|
+
return authSetup();
|
|
20
|
+
// Same flow as setup
|
|
21
|
+
default:
|
|
22
|
+
console.log(`
|
|
23
|
+
${chalk.bold("ulpi auth")} \u2014 Manage Claude Code credentials for CI workers
|
|
24
|
+
|
|
25
|
+
Usage: ulpi auth <command>
|
|
26
|
+
|
|
27
|
+
Commands:
|
|
28
|
+
setup Extract Claude Code credentials for CI use (interactive)
|
|
29
|
+
check Validate stored credentials
|
|
30
|
+
refresh Re-authenticate and output new credentials
|
|
31
|
+
|
|
32
|
+
How it works:
|
|
33
|
+
1. Run 'ulpi auth setup' on a machine with a browser
|
|
34
|
+
2. It extracts your Claude Code session credentials
|
|
35
|
+
3. Outputs a base64 blob you can store as a Docker secret
|
|
36
|
+
4. Set ULPI_CLAUDE_CREDENTIALS in your orchestrator config
|
|
37
|
+
`.trim());
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async function authSetup() {
|
|
41
|
+
console.log(chalk.blue("Extracting Claude Code credentials..."));
|
|
42
|
+
console.log();
|
|
43
|
+
const claudeDir = process.env.CLAUDE_CONFIG_DIR ?? `${process.env.HOME}/.claude`;
|
|
44
|
+
if (!fs.existsSync(claudeDir)) {
|
|
45
|
+
console.log(chalk.yellow("No Claude Code configuration found."));
|
|
46
|
+
console.log();
|
|
47
|
+
console.log("Please run 'claude login' first to authenticate with Anthropic.");
|
|
48
|
+
console.log("Then run 'ulpi auth setup' again.");
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
const blob = extractCredentials(claudeDir);
|
|
53
|
+
const expiry = getCredentialExpiry(blob);
|
|
54
|
+
console.log(chalk.green("Credentials extracted successfully."));
|
|
55
|
+
console.log();
|
|
56
|
+
if (expiry) {
|
|
57
|
+
console.log(` Expires: ${expiry}`);
|
|
58
|
+
console.log();
|
|
59
|
+
}
|
|
60
|
+
console.log(chalk.bold("Your credentials blob:"));
|
|
61
|
+
console.log();
|
|
62
|
+
console.log(blob);
|
|
63
|
+
console.log();
|
|
64
|
+
console.log(chalk.bold("To use in your orchestrator:"));
|
|
65
|
+
console.log();
|
|
66
|
+
console.log(" 1. Set as environment variable:");
|
|
67
|
+
console.log(chalk.cyan(` export ULPI_CLAUDE_CREDENTIALS="${blob.slice(0, 20)}..."`));
|
|
68
|
+
console.log();
|
|
69
|
+
console.log(" 2. Or add to docker-compose.yml:");
|
|
70
|
+
console.log(chalk.cyan(" environment:"));
|
|
71
|
+
console.log(chalk.cyan(" - ULPI_CLAUDE_CREDENTIALS=${ULPI_CLAUDE_CREDENTIALS}"));
|
|
72
|
+
console.log();
|
|
73
|
+
console.log(" 3. Or store as a Docker secret:");
|
|
74
|
+
console.log(chalk.cyan(` echo "${blob.slice(0, 20)}..." | docker secret create ulpi-claude-creds -`));
|
|
75
|
+
console.log();
|
|
76
|
+
console.log(chalk.yellow("Keep this blob safe \u2014 it contains your Claude Code session credentials."));
|
|
77
|
+
} catch (err) {
|
|
78
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
79
|
+
console.error(chalk.red(`Error: ${message}`));
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async function authCheck(args) {
|
|
84
|
+
const blob = args[0] ?? process.env.ULPI_CLAUDE_CREDENTIALS;
|
|
85
|
+
if (!blob) {
|
|
86
|
+
console.error(chalk.red("Error: No credentials provided."));
|
|
87
|
+
console.log(" Provide as argument: ulpi auth check <blob>");
|
|
88
|
+
console.log(" Or set ULPI_CLAUDE_CREDENTIALS env var");
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
console.log(chalk.blue("Validating credentials..."));
|
|
92
|
+
const valid = validateCredentials(blob);
|
|
93
|
+
const expiry = getCredentialExpiry(blob);
|
|
94
|
+
if (valid) {
|
|
95
|
+
console.log(chalk.green("Credentials are valid."));
|
|
96
|
+
if (expiry) {
|
|
97
|
+
const expiryDate = new Date(expiry);
|
|
98
|
+
const now = /* @__PURE__ */ new Date();
|
|
99
|
+
const hoursLeft = (expiryDate.getTime() - now.getTime()) / (60 * 60 * 1e3);
|
|
100
|
+
if (hoursLeft < 0) {
|
|
101
|
+
console.log(chalk.red(` Expired: ${expiry}`));
|
|
102
|
+
console.log(chalk.yellow(" Run 'ulpi auth refresh' to re-authenticate."));
|
|
103
|
+
} else if (hoursLeft < 24) {
|
|
104
|
+
console.log(chalk.yellow(` Expires soon: ${expiry} (${Math.floor(hoursLeft)}h remaining)`));
|
|
105
|
+
console.log(chalk.yellow(" Consider running 'ulpi auth refresh' soon."));
|
|
106
|
+
} else {
|
|
107
|
+
console.log(chalk.green(` Expires: ${expiry}`));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
console.log(chalk.red("Credentials are invalid or corrupted."));
|
|
112
|
+
console.log("Run 'ulpi auth setup' to generate new credentials.");
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
export {
|
|
117
|
+
runAuth
|
|
118
|
+
};
|
|
@@ -49,7 +49,7 @@ import {
|
|
|
49
49
|
updateEntryEnrichment,
|
|
50
50
|
withWorktree,
|
|
51
51
|
writeAndStage
|
|
52
|
-
} from "./chunk-
|
|
52
|
+
} from "./chunk-JGBXM5NC.js";
|
|
53
53
|
import {
|
|
54
54
|
JsonSessionStore,
|
|
55
55
|
readEvents
|
|
@@ -74,7 +74,7 @@ import {
|
|
|
74
74
|
saveUlpiSettings
|
|
75
75
|
} from "./chunk-7LXY5UVC.js";
|
|
76
76
|
|
|
77
|
-
// ../api/dist/chunk-
|
|
77
|
+
// ../api/dist/chunk-P4BERD2G.js
|
|
78
78
|
import * as http from "http";
|
|
79
79
|
import * as fs12 from "fs";
|
|
80
80
|
import * as path9 from "path";
|
|
@@ -394,33 +394,51 @@ async function putUsernameSettings(ctx) {
|
|
|
394
394
|
resolvedHistoryBranch: getHistoryBranch()
|
|
395
395
|
}, 200, ctx.req);
|
|
396
396
|
}
|
|
397
|
+
function yamlEscapeString(s) {
|
|
398
|
+
return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
399
|
+
}
|
|
397
400
|
function writeYamlFields(obj, lines, indent) {
|
|
398
401
|
const pad = " ".repeat(indent);
|
|
399
402
|
for (const [k, v] of Object.entries(obj)) {
|
|
400
403
|
if (k === "id" || k === "type" || k === "source") continue;
|
|
401
404
|
if (typeof v === "string") {
|
|
402
|
-
lines.push(`${pad}${k}: "${v}"`);
|
|
405
|
+
lines.push(`${pad}${k}: "${yamlEscapeString(v)}"`);
|
|
403
406
|
} else if (typeof v === "boolean" || typeof v === "number") {
|
|
404
407
|
lines.push(`${pad}${k}: ${v}`);
|
|
405
408
|
} else if (Array.isArray(v)) {
|
|
406
409
|
lines.push(`${pad}${k}:`);
|
|
407
410
|
for (const item of v) {
|
|
408
411
|
if (typeof item === "string") {
|
|
409
|
-
lines.push(`${pad} - "${item}"`);
|
|
412
|
+
lines.push(`${pad} - "${yamlEscapeString(item)}"`);
|
|
413
|
+
} else if (item && typeof item === "object") {
|
|
414
|
+
const entries = Object.entries(item);
|
|
415
|
+
if (entries.length > 0) {
|
|
416
|
+
const [firstKey, firstVal] = entries[0];
|
|
417
|
+
lines.push(`${pad} - ${firstKey}: ${formatScalar(firstVal)}`);
|
|
418
|
+
for (let i = 1; i < entries.length; i++) {
|
|
419
|
+
const [ek, ev] = entries[i];
|
|
420
|
+
lines.push(`${pad} ${ek}: ${formatScalar(ev)}`);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
410
423
|
}
|
|
411
424
|
}
|
|
412
425
|
}
|
|
413
426
|
}
|
|
414
427
|
}
|
|
428
|
+
function formatScalar(v) {
|
|
429
|
+
if (typeof v === "string") return `"${yamlEscapeString(v)}"`;
|
|
430
|
+
if (typeof v === "boolean" || typeof v === "number") return String(v);
|
|
431
|
+
return `"${String(v)}"`;
|
|
432
|
+
}
|
|
415
433
|
function writeRulesConfig(rulesPath, config) {
|
|
416
434
|
const lines = [
|
|
417
435
|
"# ULPI \u2014 Rules Configuration",
|
|
418
436
|
"# Generated via Web UI",
|
|
419
437
|
"",
|
|
420
438
|
"project:",
|
|
421
|
-
` name: "${config.project.name}"`,
|
|
422
|
-
` runtime: "${config.project.runtime}"`,
|
|
423
|
-
` package_manager: "${config.project.package_manager}"`,
|
|
439
|
+
` name: "${yamlEscapeString(config.project.name)}"`,
|
|
440
|
+
` runtime: "${yamlEscapeString(config.project.runtime)}"`,
|
|
441
|
+
` package_manager: "${yamlEscapeString(config.project.package_manager)}"`,
|
|
424
442
|
""
|
|
425
443
|
];
|
|
426
444
|
const sections = [
|
|
@@ -2245,7 +2263,7 @@ async function exportObsidian(ctx) {
|
|
|
2245
2263
|
jsonResponse(ctx.res, { format: "obsidian", content: md }, 200, ctx.req);
|
|
2246
2264
|
}
|
|
2247
2265
|
async function getEngine() {
|
|
2248
|
-
return await import("./dist-
|
|
2266
|
+
return await import("./dist-CB5D5LMO.js");
|
|
2249
2267
|
}
|
|
2250
2268
|
var runningPipelines = /* @__PURE__ */ new Map();
|
|
2251
2269
|
var activeWatchers = /* @__PURE__ */ new Map();
|
|
@@ -2551,7 +2569,7 @@ async function codemapActionHandler(ctx) {
|
|
|
2551
2569
|
}
|
|
2552
2570
|
}
|
|
2553
2571
|
async function getEngine2() {
|
|
2554
|
-
return await import("./dist-
|
|
2572
|
+
return await import("./dist-GJYT2OQV.js");
|
|
2555
2573
|
}
|
|
2556
2574
|
async function memoryStatusHandler(ctx) {
|
|
2557
2575
|
try {
|
|
@@ -2559,7 +2577,8 @@ async function memoryStatusHandler(ctx) {
|
|
|
2559
2577
|
const stats = await engine.getMemoryStats(ctx.projectDir);
|
|
2560
2578
|
const config = engine.loadMemoryConfig(ctx.projectDir);
|
|
2561
2579
|
const initialized = engine.isMemoryInitialized(ctx.projectDir);
|
|
2562
|
-
|
|
2580
|
+
const classifyProgress = engine.readClassifyBatchProgress(ctx.projectDir);
|
|
2581
|
+
jsonResponse(ctx.res, { stats, config, initialized, classifyProgress }, 200, ctx.req);
|
|
2563
2582
|
} catch (err) {
|
|
2564
2583
|
const message = err instanceof Error ? err.message : "Failed to get memory status";
|
|
2565
2584
|
jsonResponse(ctx.res, { error: message }, 500, ctx.req);
|
|
@@ -2739,7 +2758,7 @@ async function memoryActionHandler(ctx) {
|
|
|
2739
2758
|
return;
|
|
2740
2759
|
}
|
|
2741
2760
|
const action = data.action;
|
|
2742
|
-
const validActions = ["init", "export", "import", "enable", "disable", "classify", "reindex"];
|
|
2761
|
+
const validActions = ["init", "export", "import", "enable", "disable", "classify", "classify-all", "classify-clear", "reindex"];
|
|
2743
2762
|
if (!validActions.includes(action)) {
|
|
2744
2763
|
jsonResponse(
|
|
2745
2764
|
ctx.res,
|
|
@@ -2765,12 +2784,12 @@ async function memoryActionHandler(ctx) {
|
|
|
2765
2784
|
return;
|
|
2766
2785
|
}
|
|
2767
2786
|
case "export": {
|
|
2768
|
-
const result = engine.exportMemories(ctx.projectDir);
|
|
2787
|
+
const result = await engine.exportMemories(ctx.projectDir);
|
|
2769
2788
|
jsonResponse(ctx.res, { ok: true, action, ...result }, 200, ctx.req);
|
|
2770
2789
|
return;
|
|
2771
2790
|
}
|
|
2772
2791
|
case "import": {
|
|
2773
|
-
const result = engine.importMemories(ctx.projectDir);
|
|
2792
|
+
const result = await engine.importMemories(ctx.projectDir);
|
|
2774
2793
|
jsonResponse(ctx.res, { ok: true, action, ...result }, 200, ctx.req);
|
|
2775
2794
|
return;
|
|
2776
2795
|
}
|
|
@@ -2796,6 +2815,32 @@ async function memoryActionHandler(ctx) {
|
|
|
2796
2815
|
jsonResponse(ctx.res, { ok: true, action, ...result }, 200, ctx.req);
|
|
2797
2816
|
return;
|
|
2798
2817
|
}
|
|
2818
|
+
case "classify-all": {
|
|
2819
|
+
const { spawn: spawn4 } = await import("child_process");
|
|
2820
|
+
const ulpiBin = process.argv[1];
|
|
2821
|
+
const args = ["memory", "classify", "-p", ctx.projectDir];
|
|
2822
|
+
if (typeof data.branch === "string" && data.branch) {
|
|
2823
|
+
args.push("--branch", data.branch);
|
|
2824
|
+
}
|
|
2825
|
+
if (data.export) args.push("--export");
|
|
2826
|
+
const child = spawn4(process.execPath, [ulpiBin, ...args], {
|
|
2827
|
+
detached: true,
|
|
2828
|
+
stdio: "ignore",
|
|
2829
|
+
env: { ...process.env, ULPI_BG_CLASSIFY: "1" }
|
|
2830
|
+
});
|
|
2831
|
+
child.unref();
|
|
2832
|
+
jsonResponse(ctx.res, {
|
|
2833
|
+
ok: true,
|
|
2834
|
+
action,
|
|
2835
|
+
message: "Classification started"
|
|
2836
|
+
}, 200, ctx.req);
|
|
2837
|
+
return;
|
|
2838
|
+
}
|
|
2839
|
+
case "classify-clear": {
|
|
2840
|
+
engine.clearClassifyBatchProgress(ctx.projectDir);
|
|
2841
|
+
jsonResponse(ctx.res, { ok: true, action, message: "Classification progress cleared" }, 200, ctx.req);
|
|
2842
|
+
return;
|
|
2843
|
+
}
|
|
2799
2844
|
case "reindex": {
|
|
2800
2845
|
const result = await engine.reindexMemories(ctx.projectDir);
|
|
2801
2846
|
jsonResponse(ctx.res, { ok: true, action, ...result }, 200, ctx.req);
|
|
@@ -2855,7 +2900,7 @@ async function memoryConfigUpdateHandler(ctx) {
|
|
|
2855
2900
|
}
|
|
2856
2901
|
}
|
|
2857
2902
|
async function getDepgraph() {
|
|
2858
|
-
return await import("./dist-
|
|
2903
|
+
return await import("./dist-YA2BWZB2.js");
|
|
2859
2904
|
}
|
|
2860
2905
|
async function getBranch(projectDir) {
|
|
2861
2906
|
const { getCurrentBranch: getCurrentBranch2 } = await import("./dist-RKOGLK7R.js");
|
|
@@ -3014,11 +3059,7 @@ async function depgraphMetricsHandler(ctx) {
|
|
|
3014
3059
|
jsonResponse(ctx.res, { error: error ?? "Invalid request body" }, 400, ctx.req);
|
|
3015
3060
|
return;
|
|
3016
3061
|
}
|
|
3017
|
-
const modulePath = data.modulePath;
|
|
3018
|
-
if (typeof modulePath !== "string" || !modulePath.trim()) {
|
|
3019
|
-
jsonResponse(ctx.res, { error: "Missing or empty 'modulePath' field" }, 400, ctx.req);
|
|
3020
|
-
return;
|
|
3021
|
-
}
|
|
3062
|
+
const modulePath = typeof data.modulePath === "string" ? data.modulePath : "";
|
|
3022
3063
|
try {
|
|
3023
3064
|
const depgraph = await getDepgraph();
|
|
3024
3065
|
const branch = await getBranch(ctx.projectDir);
|
|
@@ -3461,7 +3502,7 @@ async function historyInitHandler(ctx) {
|
|
|
3461
3502
|
}
|
|
3462
3503
|
let gitHooksResult = { installed: [], skipped: [] };
|
|
3463
3504
|
try {
|
|
3464
|
-
const { installGitHooks } = await import("./dist-
|
|
3505
|
+
const { installGitHooks } = await import("./dist-QAU3LGJN.js");
|
|
3465
3506
|
const binaryPath = getBinaryPath();
|
|
3466
3507
|
gitHooksResult = installGitHooks(projectDir, binaryPath);
|
|
3467
3508
|
} catch {
|
|
@@ -3499,8 +3540,10 @@ async function historyBackfillHandler(ctx) {
|
|
|
3499
3540
|
} catch {
|
|
3500
3541
|
}
|
|
3501
3542
|
const limit = Math.min(options.limit ?? 20, 100);
|
|
3543
|
+
const branchOnly = options.branchOnly ?? false;
|
|
3502
3544
|
const {
|
|
3503
3545
|
listRecentCommits,
|
|
3546
|
+
listBranchOnlyCommits,
|
|
3504
3547
|
getCommitMetadata,
|
|
3505
3548
|
getCommitDiffStats,
|
|
3506
3549
|
getCommitRawDiff,
|
|
@@ -3510,8 +3553,8 @@ async function historyBackfillHandler(ctx) {
|
|
|
3510
3553
|
entryExists,
|
|
3511
3554
|
writeHistoryEntry,
|
|
3512
3555
|
DEFAULT_HISTORY_CONFIG
|
|
3513
|
-
} = await import("./dist-
|
|
3514
|
-
const commits = listRecentCommits(projectDir, limit);
|
|
3556
|
+
} = await import("./dist-QAU3LGJN.js");
|
|
3557
|
+
const commits = branchOnly ? listBranchOnlyCommits(projectDir, limit) : listRecentCommits(projectDir, limit);
|
|
3515
3558
|
if (commits.length === 0) {
|
|
3516
3559
|
jsonResponse(res, { captured: 0, skipped: 0, total: 0 }, 200, req);
|
|
3517
3560
|
return;
|
|
@@ -3834,7 +3877,7 @@ async function historyEntryTagsHandler(ctx) {
|
|
|
3834
3877
|
return;
|
|
3835
3878
|
}
|
|
3836
3879
|
try {
|
|
3837
|
-
const { updateEntryTags } = await import("./dist-
|
|
3880
|
+
const { updateEntryTags } = await import("./dist-QAU3LGJN.js");
|
|
3838
3881
|
await updateEntryTags(projectDir, sha, payload.tags);
|
|
3839
3882
|
jsonResponse(res, { success: true, sha, tags: payload.tags }, 200, req);
|
|
3840
3883
|
} catch (err) {
|
|
@@ -3850,7 +3893,7 @@ async function historyEntryTranscriptHandler(ctx) {
|
|
|
3850
3893
|
jsonResponse(res, { error: "History branch not initialized" }, 400, req);
|
|
3851
3894
|
return;
|
|
3852
3895
|
}
|
|
3853
|
-
const { readEntryTranscript } = await import("./dist-
|
|
3896
|
+
const { readEntryTranscript } = await import("./dist-QAU3LGJN.js");
|
|
3854
3897
|
const transcript = readEntryTranscript(projectDir, sha);
|
|
3855
3898
|
if (!transcript) {
|
|
3856
3899
|
jsonResponse(res, { error: "No transcript for this entry" }, 404, req);
|
|
@@ -4,10 +4,13 @@ import {
|
|
|
4
4
|
historyBranchExists,
|
|
5
5
|
withWorktree,
|
|
6
6
|
writeAndStage
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-JGBXM5NC.js";
|
|
8
8
|
import {
|
|
9
9
|
CodemapConfigSchema
|
|
10
10
|
} from "./chunk-74WVVWJ4.js";
|
|
11
|
+
import {
|
|
12
|
+
extractTagsFromTree
|
|
13
|
+
} from "./chunk-ZLYRPD7I.js";
|
|
11
14
|
import {
|
|
12
15
|
LOGS_DIR,
|
|
13
16
|
codemapBranchDir,
|
|
@@ -527,6 +530,7 @@ function getLanguageId(filePath) {
|
|
|
527
530
|
}
|
|
528
531
|
var ParserCtor = null;
|
|
529
532
|
var LanguageClass = null;
|
|
533
|
+
var QueryCtor = null;
|
|
530
534
|
var initialized = false;
|
|
531
535
|
var languageCache = /* @__PURE__ */ new Map();
|
|
532
536
|
var require2 = createRequire(import.meta.url);
|
|
@@ -535,6 +539,7 @@ async function initTreeSitter() {
|
|
|
535
539
|
const mod = await import("web-tree-sitter");
|
|
536
540
|
ParserCtor = mod.Parser;
|
|
537
541
|
LanguageClass = mod.Language;
|
|
542
|
+
QueryCtor = mod.Query;
|
|
538
543
|
const wasmPath = require2.resolve("web-tree-sitter/web-tree-sitter.wasm");
|
|
539
544
|
const wasmDir = path4.dirname(wasmPath) + "/";
|
|
540
545
|
await ParserCtor.init({
|
|
@@ -556,6 +561,12 @@ async function loadLanguage(langConfig) {
|
|
|
556
561
|
languageCache.set(cacheKey, language);
|
|
557
562
|
return language;
|
|
558
563
|
}
|
|
564
|
+
function createTreeSitterQuery(language, source) {
|
|
565
|
+
if (!QueryCtor) {
|
|
566
|
+
throw new Error("Tree-sitter not initialized. Call initTreeSitter() first.");
|
|
567
|
+
}
|
|
568
|
+
return new QueryCtor(language, source);
|
|
569
|
+
}
|
|
559
570
|
async function parseSource(content, langConfig) {
|
|
560
571
|
if (!ParserCtor) {
|
|
561
572
|
throw new Error("Tree-sitter not initialized. Call initTreeSitter() first.");
|
|
@@ -1532,14 +1543,9 @@ async function runPipelineInner(projectDir, onProgress, branch) {
|
|
|
1532
1543
|
total: scannedFiles.length,
|
|
1533
1544
|
message: "Chunking files..."
|
|
1534
1545
|
});
|
|
1535
|
-
let extractTagsFromTree = null;
|
|
1536
|
-
try {
|
|
1537
|
-
const depgraph = await import("./dist-R5ZJ4LX5.js");
|
|
1538
|
-
extractTagsFromTree = depgraph.extractTagsFromTree;
|
|
1539
|
-
} catch {
|
|
1540
|
-
}
|
|
1541
1546
|
await initTreeSitter();
|
|
1542
1547
|
const allTags = /* @__PURE__ */ new Map();
|
|
1548
|
+
let tagExtractionFailures = 0;
|
|
1543
1549
|
const chunks = [];
|
|
1544
1550
|
const filePaths = scannedFiles.map((f) => f.filePath);
|
|
1545
1551
|
for (let i = 0; i < filePaths.length; i++) {
|
|
@@ -1561,15 +1567,19 @@ async function runPipelineInner(projectDir, onProgress, branch) {
|
|
|
1561
1567
|
const fallbackChunks = await chunkFile(filePath, content, config.chunking);
|
|
1562
1568
|
chunks.push(...fallbackChunks);
|
|
1563
1569
|
}
|
|
1564
|
-
if (
|
|
1570
|
+
if (result.tree && result.language && langId && langConfig.hasTagQuery) {
|
|
1565
1571
|
try {
|
|
1566
1572
|
const lang = result.language;
|
|
1567
|
-
const createQuery = (source) => lang
|
|
1573
|
+
const createQuery = (source) => createTreeSitterQuery(lang, source);
|
|
1568
1574
|
const tags = extractTagsFromTree(createQuery, result.tree.rootNode, filePath, langId);
|
|
1569
1575
|
if (tags.length > 0) {
|
|
1570
1576
|
allTags.set(filePath, tags);
|
|
1571
1577
|
}
|
|
1572
|
-
} catch {
|
|
1578
|
+
} catch (tagErr) {
|
|
1579
|
+
tagExtractionFailures++;
|
|
1580
|
+
if (tagExtractionFailures <= 3) {
|
|
1581
|
+
log("warn", `Tag extraction failed for ${filePath}: ${tagErr instanceof Error ? tagErr.message : String(tagErr)}`);
|
|
1582
|
+
}
|
|
1573
1583
|
}
|
|
1574
1584
|
}
|
|
1575
1585
|
if (result.tree) {
|
|
@@ -1594,6 +1604,13 @@ async function runPipelineInner(projectDir, onProgress, branch) {
|
|
|
1594
1604
|
total: scannedFiles.length,
|
|
1595
1605
|
message: `Created ${chunks.length} chunks`
|
|
1596
1606
|
});
|
|
1607
|
+
log("info", `Tag extraction: ${allTags.size} files tagged, ${tagExtractionFailures} failures out of ${scannedFiles.length} files`);
|
|
1608
|
+
if (tagExtractionFailures > 3) {
|
|
1609
|
+
log("warn", `Tag extraction failed for ${tagExtractionFailures} files (only first 3 logged above)`);
|
|
1610
|
+
}
|
|
1611
|
+
if (allTags.size === 0) {
|
|
1612
|
+
log("warn", "No tags extracted \u2014 depgraph will be skipped. Check tree-sitter language support.");
|
|
1613
|
+
}
|
|
1597
1614
|
assignChunkIds(chunks, config.chunking.version);
|
|
1598
1615
|
onProgress?.({
|
|
1599
1616
|
phase: "finalizing",
|
|
@@ -1646,20 +1663,20 @@ async function runPipelineInner(projectDir, onProgress, branch) {
|
|
|
1646
1663
|
let depgraphResult;
|
|
1647
1664
|
if (allTags.size > 0) {
|
|
1648
1665
|
try {
|
|
1649
|
-
const
|
|
1666
|
+
const { buildReferenceGraph, computePageRank, savePageRank, computeMetrics, saveMetrics, saveGraph } = await import("./dist-YA2BWZB2.js");
|
|
1650
1667
|
onProgress?.({
|
|
1651
1668
|
phase: "graph",
|
|
1652
1669
|
current: 0,
|
|
1653
1670
|
total: 3,
|
|
1654
1671
|
message: "Building dependency graph..."
|
|
1655
1672
|
});
|
|
1656
|
-
const graph =
|
|
1673
|
+
const graph = buildReferenceGraph(allTags, filePaths);
|
|
1657
1674
|
for (const sf of scannedFiles) {
|
|
1658
1675
|
if (graph.nodes[sf.filePath]) {
|
|
1659
1676
|
graph.nodes[sf.filePath].sizeBytes = sf.sizeBytes;
|
|
1660
1677
|
}
|
|
1661
1678
|
}
|
|
1662
|
-
|
|
1679
|
+
saveGraph(graph, projectDir, resolvedBranch);
|
|
1663
1680
|
onProgress?.({
|
|
1664
1681
|
phase: "graph",
|
|
1665
1682
|
current: 1,
|
|
@@ -1672,8 +1689,8 @@ async function runPipelineInner(projectDir, onProgress, branch) {
|
|
|
1672
1689
|
total: 3,
|
|
1673
1690
|
message: "Computing PageRank..."
|
|
1674
1691
|
});
|
|
1675
|
-
const pageRank =
|
|
1676
|
-
|
|
1692
|
+
const pageRank = computePageRank(graph);
|
|
1693
|
+
savePageRank(pageRank, projectDir, resolvedBranch);
|
|
1677
1694
|
onProgress?.({
|
|
1678
1695
|
phase: "ranking",
|
|
1679
1696
|
current: 2,
|
|
@@ -1686,8 +1703,8 @@ async function runPipelineInner(projectDir, onProgress, branch) {
|
|
|
1686
1703
|
total: 3,
|
|
1687
1704
|
message: "Computing metrics..."
|
|
1688
1705
|
});
|
|
1689
|
-
const metrics =
|
|
1690
|
-
|
|
1706
|
+
const metrics = computeMetrics(graph, pageRank);
|
|
1707
|
+
saveMetrics(metrics, projectDir, resolvedBranch);
|
|
1691
1708
|
onProgress?.({
|
|
1692
1709
|
phase: "metrics",
|
|
1693
1710
|
current: 3,
|
|
@@ -1741,6 +1758,98 @@ async function runPipelineInner(projectDir, onProgress, branch) {
|
|
|
1741
1758
|
depgraph: depgraphResult
|
|
1742
1759
|
};
|
|
1743
1760
|
}
|
|
1761
|
+
async function rebuildDepgraph(projectDir, onProgress, branch) {
|
|
1762
|
+
const resolvedBranch = branch ?? getCurrentBranch(projectDir);
|
|
1763
|
+
acquireCodemapLock(projectDir, resolvedBranch);
|
|
1764
|
+
try {
|
|
1765
|
+
return await rebuildDepgraphInner(projectDir, onProgress, resolvedBranch);
|
|
1766
|
+
} finally {
|
|
1767
|
+
releaseCodemapLock(projectDir, resolvedBranch);
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
async function rebuildDepgraphInner(projectDir, onProgress, branch) {
|
|
1771
|
+
const startTime = Date.now();
|
|
1772
|
+
const config = loadCodemapConfig(projectDir);
|
|
1773
|
+
const resolvedBranch = branch ?? getCurrentBranch(projectDir);
|
|
1774
|
+
const codemapDir = codemapBranchDir(projectDir, resolvedBranch);
|
|
1775
|
+
fs9.mkdirSync(codemapDir, { recursive: true });
|
|
1776
|
+
onProgress?.({ phase: "scanning", current: 0, total: 0, message: "Scanning repository..." });
|
|
1777
|
+
const scannedFiles = scanRepository(projectDir, config);
|
|
1778
|
+
const filePaths = scannedFiles.map((f) => f.filePath);
|
|
1779
|
+
onProgress?.({ phase: "scanning", current: scannedFiles.length, total: scannedFiles.length, message: `Found ${scannedFiles.length} files` });
|
|
1780
|
+
if (scannedFiles.length === 0) {
|
|
1781
|
+
return { nodeCount: 0, edgeCount: 0, definitionCount: 0, referenceCount: 0, cycleCount: 0, durationMs: Date.now() - startTime, taggedFiles: 0, totalFiles: 0 };
|
|
1782
|
+
}
|
|
1783
|
+
await initTreeSitter();
|
|
1784
|
+
const allTags = /* @__PURE__ */ new Map();
|
|
1785
|
+
let tagFailures = 0;
|
|
1786
|
+
for (let i = 0; i < filePaths.length; i++) {
|
|
1787
|
+
const filePath = filePaths[i];
|
|
1788
|
+
const absPath = path11.join(projectDir, filePath);
|
|
1789
|
+
let content;
|
|
1790
|
+
try {
|
|
1791
|
+
content = fs9.readFileSync(absPath, "utf-8");
|
|
1792
|
+
} catch {
|
|
1793
|
+
continue;
|
|
1794
|
+
}
|
|
1795
|
+
const langConfig = getLanguageConfig(filePath);
|
|
1796
|
+
const langId = getLanguageId(filePath);
|
|
1797
|
+
if (langConfig && langConfig.hasTagQuery && langId) {
|
|
1798
|
+
const result = await parseAndChunkFile(filePath, content, config.chunking);
|
|
1799
|
+
if (result.tree && result.language) {
|
|
1800
|
+
try {
|
|
1801
|
+
const lang = result.language;
|
|
1802
|
+
const createQuery = (source) => createTreeSitterQuery(lang, source);
|
|
1803
|
+
const tags = extractTagsFromTree(createQuery, result.tree.rootNode, filePath, langId);
|
|
1804
|
+
if (tags.length > 0) {
|
|
1805
|
+
allTags.set(filePath, tags);
|
|
1806
|
+
}
|
|
1807
|
+
} catch {
|
|
1808
|
+
tagFailures++;
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
if (result.tree) result.tree.delete();
|
|
1812
|
+
}
|
|
1813
|
+
if ((i + 1) % 100 === 0 || i === filePaths.length - 1) {
|
|
1814
|
+
onProgress?.({ phase: "chunking", current: i + 1, total: scannedFiles.length, message: `Parsed ${i + 1}/${scannedFiles.length} files (${allTags.size} tagged)` });
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
log("info", `DepGraph rebuild: ${allTags.size} files tagged, ${tagFailures} failures out of ${scannedFiles.length} files`);
|
|
1818
|
+
if (allTags.size === 0) {
|
|
1819
|
+
log("warn", "No tags extracted \u2014 cannot build depgraph");
|
|
1820
|
+
return { nodeCount: 0, edgeCount: 0, definitionCount: 0, referenceCount: 0, cycleCount: 0, durationMs: Date.now() - startTime, taggedFiles: 0, totalFiles: scannedFiles.length };
|
|
1821
|
+
}
|
|
1822
|
+
const { buildReferenceGraph, computePageRank, savePageRank, computeMetrics, saveMetrics, saveGraph } = await import("./dist-YA2BWZB2.js");
|
|
1823
|
+
onProgress?.({ phase: "graph", current: 0, total: 3, message: "Building dependency graph..." });
|
|
1824
|
+
const graph = buildReferenceGraph(allTags, filePaths);
|
|
1825
|
+
for (const sf of scannedFiles) {
|
|
1826
|
+
if (graph.nodes[sf.filePath]) {
|
|
1827
|
+
graph.nodes[sf.filePath].sizeBytes = sf.sizeBytes;
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
saveGraph(graph, projectDir, resolvedBranch);
|
|
1831
|
+
onProgress?.({ phase: "graph", current: 1, total: 3, message: `Graph: ${Object.keys(graph.nodes).length} nodes, ${graph.edges.length} edges` });
|
|
1832
|
+
onProgress?.({ phase: "ranking", current: 1, total: 3, message: "Computing PageRank..." });
|
|
1833
|
+
const pageRank = computePageRank(graph);
|
|
1834
|
+
savePageRank(pageRank, projectDir, resolvedBranch);
|
|
1835
|
+
onProgress?.({ phase: "ranking", current: 2, total: 3, message: `PageRank: ${pageRank.iterations} iterations, converged=${pageRank.converged}` });
|
|
1836
|
+
onProgress?.({ phase: "metrics", current: 2, total: 3, message: "Computing metrics..." });
|
|
1837
|
+
const metrics = computeMetrics(graph, pageRank);
|
|
1838
|
+
saveMetrics(metrics, projectDir, resolvedBranch);
|
|
1839
|
+
onProgress?.({ phase: "metrics", current: 3, total: 3, message: `Metrics: ${metrics.cycles.length} cycles, ${metrics.totalDefinitions} defs, ${metrics.totalReferences} refs` });
|
|
1840
|
+
const durationMs = Date.now() - startTime;
|
|
1841
|
+
log("info", `DepGraph rebuild: ${metrics.totalFiles} nodes, ${metrics.totalEdges} edges, ${metrics.totalDefinitions} defs, ${metrics.totalReferences} refs, ${metrics.cycles.length} cycles in ${(durationMs / 1e3).toFixed(1)}s`);
|
|
1842
|
+
return {
|
|
1843
|
+
nodeCount: metrics.totalFiles,
|
|
1844
|
+
edgeCount: metrics.totalEdges,
|
|
1845
|
+
definitionCount: metrics.totalDefinitions,
|
|
1846
|
+
referenceCount: metrics.totalReferences,
|
|
1847
|
+
cycleCount: metrics.cycles.length,
|
|
1848
|
+
durationMs,
|
|
1849
|
+
taggedFiles: allTags.size,
|
|
1850
|
+
totalFiles: scannedFiles.length
|
|
1851
|
+
};
|
|
1852
|
+
}
|
|
1744
1853
|
var STORE_BATCH_SIZE = 5e3;
|
|
1745
1854
|
async function embedAndStore(embedder, texts, chunks, store, batchConfig, onProgress) {
|
|
1746
1855
|
if (texts.length === 0) return;
|
|
@@ -1981,7 +2090,7 @@ async function searchCode(projectDir, query, options = {}) {
|
|
|
1981
2090
|
let graphRanks;
|
|
1982
2091
|
if (weights.graphRank && weights.graphRank > 0) {
|
|
1983
2092
|
try {
|
|
1984
|
-
const { loadPageRankMap } = await import("./dist-
|
|
2093
|
+
const { loadPageRankMap } = await import("./dist-YA2BWZB2.js");
|
|
1985
2094
|
graphRanks = loadPageRankMap(projectDir, resolvedBranch) ?? void 0;
|
|
1986
2095
|
} catch {
|
|
1987
2096
|
}
|
|
@@ -2511,7 +2620,7 @@ var GITHUB_MAX_FILE_BYTES = 100 * 1024 * 1024;
|
|
|
2511
2620
|
var GITHUB_WARN_FILE_BYTES = 50 * 1024 * 1024;
|
|
2512
2621
|
function ensureBranch(projectDir, branchName) {
|
|
2513
2622
|
if (historyBranchExists(projectDir, branchName)) return;
|
|
2514
|
-
const emptyBlob = execFileSync2("git", ["hash-object", "-t", "blob", "--stdin"], {
|
|
2623
|
+
const emptyBlob = execFileSync2("git", ["hash-object", "-w", "-t", "blob", "--stdin"], {
|
|
2515
2624
|
cwd: projectDir,
|
|
2516
2625
|
input: "",
|
|
2517
2626
|
encoding: "utf-8",
|
|
@@ -2591,6 +2700,26 @@ async function exportIndex(projectDir, branch) {
|
|
|
2591
2700
|
};
|
|
2592
2701
|
writeAndStage(worktreeDir, "export-meta.json", JSON.stringify(exportMeta, null, 2) + "\n");
|
|
2593
2702
|
filesExported++;
|
|
2703
|
+
const readme = [
|
|
2704
|
+
"# ULPI CodeMap Index",
|
|
2705
|
+
"",
|
|
2706
|
+
"This branch stores the semantic code search index for this repository.",
|
|
2707
|
+
"It is maintained automatically by [ULPI](https://github.com/ulpi-io/ulpi).",
|
|
2708
|
+
"",
|
|
2709
|
+
"## Contents",
|
|
2710
|
+
"",
|
|
2711
|
+
"- `schema.json` \u2014 Index schema (embedding provider, model, dimensions)",
|
|
2712
|
+
"- `manifest.json` \u2014 File/chunk manifest for incremental updates",
|
|
2713
|
+
"- `config.json` \u2014 CodeMap configuration",
|
|
2714
|
+
"- `stats.json` \u2014 Index statistics",
|
|
2715
|
+
"- `index/lance/` \u2014 LanceDB vector index",
|
|
2716
|
+
"- `index/metadata/` \u2014 BM25 text index + symbol index",
|
|
2717
|
+
"- `export-meta.json` \u2014 Export metadata",
|
|
2718
|
+
"",
|
|
2719
|
+
`_Exported: ${exportMeta.exportedAt}_`
|
|
2720
|
+
].join("\n") + "\n";
|
|
2721
|
+
writeAndStage(worktreeDir, "README.md", readme);
|
|
2722
|
+
filesExported++;
|
|
2594
2723
|
return commitInWorktree(worktreeDir, `codemap: export index (${filesExported} files)`);
|
|
2595
2724
|
});
|
|
2596
2725
|
const commitMessage = `codemap: export index (${filesExported} files)`;
|
|
@@ -3027,6 +3156,7 @@ export {
|
|
|
3027
3156
|
isCodemapLocked,
|
|
3028
3157
|
buildEmbeddingTexts,
|
|
3029
3158
|
runInitPipeline,
|
|
3159
|
+
rebuildDepgraph,
|
|
3030
3160
|
DEFAULT_HYBRID_WEIGHTS,
|
|
3031
3161
|
normalizeScores,
|
|
3032
3162
|
computePathBoost,
|