rudel 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +15 -0
  2. package/dist/cli.js +412 -246
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  CLI for uploading [Claude Code](https://docs.anthropic.com/en/docs/claude-code) session transcripts to [Rudel](https://app.rudel.ai) for analytics.
4
4
 
5
+ ## Prerequisites
6
+
7
+ - [Bun](https://bun.sh) runtime installed
8
+
5
9
  ## Installation
6
10
 
7
11
  ```bash
@@ -65,6 +69,17 @@ Show the currently authenticated user.
65
69
 
66
70
  Clear stored credentials.
67
71
 
72
+ ## What Data Is Collected
73
+
74
+ Each uploaded session includes:
75
+
76
+ - Session ID & timestamps (start, last interaction)
77
+ - User ID & organization ID
78
+ - Project path & package name
79
+ - Git context (repository, branch, SHA, remote)
80
+ - Session transcript (full prompt & response content)
81
+ - Sub-agent usage
82
+
68
83
  ## Links
69
84
 
70
85
  - **Web App**: [app.rudel.ai](https://app.rudel.ai)
package/dist/cli.js CHANGED
@@ -1959,7 +1959,7 @@ async function run(app, inputs, context) {
1959
1959
  // package.json
1960
1960
  var package_default = {
1961
1961
  name: "rudel",
1962
- version: "0.1.7",
1962
+ version: "0.1.9",
1963
1963
  type: "module",
1964
1964
  description: "CLI for the Coding Agent Analytics Platform rudel.ai",
1965
1965
  license: "MIT",
@@ -5997,9 +5997,9 @@ var RudelClaudeSessionsRowSchema = exports_external.object({
5997
5997
  session_id: exports_external.string(),
5998
5998
  organization_id: exports_external.string(),
5999
5999
  project_path: exports_external.string(),
6000
- repository: exports_external.string().nullable(),
6001
6000
  git_remote: exports_external.string(),
6002
6001
  package_name: exports_external.string(),
6002
+ package_type: exports_external.string(),
6003
6003
  content: exports_external.string(),
6004
6004
  ingested_at: exports_external.string(),
6005
6005
  user_id: exports_external.string(),
@@ -6014,7 +6014,9 @@ var RudelCodexSessionsRowSchema = exports_external.object({
6014
6014
  session_id: exports_external.string(),
6015
6015
  organization_id: exports_external.string(),
6016
6016
  project_path: exports_external.string(),
6017
- repository: exports_external.string().nullable(),
6017
+ git_remote: exports_external.string(),
6018
+ package_name: exports_external.string(),
6019
+ package_type: exports_external.string(),
6018
6020
  content: exports_external.string(),
6019
6021
  ingested_at: exports_external.string(),
6020
6022
  user_id: exports_external.string(),
@@ -6028,9 +6030,9 @@ var RudelSessionAnalyticsRowSchema = exports_external.object({
6028
6030
  session_id: exports_external.string(),
6029
6031
  organization_id: exports_external.string(),
6030
6032
  project_path: exports_external.string(),
6031
- repository: exports_external.string().nullable(),
6032
6033
  git_remote: exports_external.string(),
6033
6034
  package_name: exports_external.string(),
6035
+ package_type: exports_external.string(),
6034
6036
  content: exports_external.string(),
6035
6037
  subagents: exports_external.record(exports_external.string(), exports_external.string()),
6036
6038
  skills: exports_external.array(exports_external.string()),
@@ -6130,16 +6132,6 @@ function toDisplayPath(absolutePath) {
6130
6132
  const home = homedir();
6131
6133
  return absolutePath.startsWith(home) ? `~${absolutePath.slice(home.length)}` : absolutePath;
6132
6134
  }
6133
- function groupProjectsForCwd(projects, cwd) {
6134
- const current = projects.filter((p) => p.projectPath === cwd);
6135
- const currentSet = new Set(current);
6136
- const subfolders = projects.filter((p) => !currentSet.has(p) && p.projectPath.startsWith(`${cwd}/`));
6137
- const subfoldersSet = new Set(subfolders);
6138
- const others = projects.filter((p) => !currentSet.has(p) && !subfoldersSet.has(p));
6139
- subfolders.sort((a, b) => a.projectPath.localeCompare(b.projectPath));
6140
- others.sort((a, b) => a.displayPath.localeCompare(b.displayPath));
6141
- return { current, subfolders, others };
6142
- }
6143
6135
 
6144
6136
  // ../../packages/agent-adapters/src/adapters/claude-code/settings.ts
6145
6137
  import { execSync } from "child_process";
@@ -6363,9 +6355,9 @@ class ClaudeCodeAdapter {
6363
6355
  source: this.source,
6364
6356
  sessionId: session.sessionId,
6365
6357
  projectPath: session.projectPath,
6366
- repository: context.gitInfo.repository,
6367
6358
  gitRemote: context.gitInfo.gitRemote,
6368
6359
  packageName: context.gitInfo.packageName,
6360
+ packageType: context.gitInfo.packageType,
6369
6361
  gitBranch: context.gitInfo.branch,
6370
6362
  gitSha: context.gitInfo.sha,
6371
6363
  tag: context.tag,
@@ -6418,9 +6410,9 @@ class ClaudeCodeAdapter {
6418
6410
  session_id: input.sessionId,
6419
6411
  organization_id: context.organizationId,
6420
6412
  project_path: input.projectPath,
6421
- repository: input.repository ?? null,
6422
6413
  git_remote: input.gitRemote ?? "",
6423
6414
  package_name: input.packageName ?? "",
6415
+ package_type: input.packageType ?? "",
6424
6416
  content: input.content,
6425
6417
  subagents,
6426
6418
  ingested_at: now,
@@ -7695,7 +7687,6 @@ class CodexAdapter {
7695
7687
  source: this.source,
7696
7688
  sessionId: session.sessionId,
7697
7689
  projectPath: session.projectPath,
7698
- repository: context.gitInfo.repository,
7699
7690
  gitBranch: session.gitBranch ?? context.gitInfo.branch,
7700
7691
  gitSha: session.gitSha ?? context.gitInfo.sha,
7701
7692
  tag: context.tag,
@@ -7741,7 +7732,9 @@ class CodexAdapter {
7741
7732
  session_id: input.sessionId,
7742
7733
  organization_id: context.organizationId,
7743
7734
  project_path: input.projectPath,
7744
- repository: input.repository ?? null,
7735
+ git_remote: input.gitRemote ?? "",
7736
+ package_name: input.packageName ?? "",
7737
+ package_type: input.packageType ?? "",
7745
7738
  content: input.content,
7746
7739
  ingested_at: now,
7747
7740
  user_id: context.userId,
@@ -7775,33 +7768,314 @@ function getAvailableAdapters() {
7775
7768
  registerAdapter(claudeCodeAdapter);
7776
7769
  registerAdapter(codexAdapter);
7777
7770
 
7778
- // src/commands/dev/list-sessions.ts
7779
- async function runListSessions() {
7771
+ // src/lib/project-grouping.ts
7772
+ import { homedir as homedir6 } from "os";
7773
+
7774
+ // src/lib/git-info.ts
7775
+ import { join as join6 } from "path";
7776
+ var {$ } = globalThis.Bun;
7777
+ function normalizeRemoteUrl(url) {
7778
+ return url.replace(/^(https?:\/\/|git@|ssh:\/\/)/, "").replace(/:/, "/").replace(/\.git$/, "");
7779
+ }
7780
+ async function getGitInfo(cwd) {
7781
+ const [remoteUrl, branch, sha, packageInfo] = await Promise.all([
7782
+ getGitRemoteUrl(cwd),
7783
+ getGitBranch(cwd),
7784
+ getGitSha(cwd),
7785
+ getPackageInfo(cwd)
7786
+ ]);
7787
+ const gitRemote = remoteUrl ? normalizeRemoteUrl(remoteUrl) : undefined;
7788
+ return {
7789
+ gitRemote,
7790
+ packageName: packageInfo?.name,
7791
+ packageType: packageInfo?.type,
7792
+ branch: branch ?? undefined,
7793
+ sha: sha ?? undefined
7794
+ };
7795
+ }
7796
+ async function getPackageInfo(cwd) {
7797
+ try {
7798
+ const gitRootResult = await $`git -C ${cwd} rev-parse --show-toplevel`.quiet();
7799
+ const root = gitRootResult.exitCode === 0 ? gitRootResult.text().trim() : cwd;
7800
+ return await getNodePackage(root) ?? await getPythonPackage(root) ?? await getRustPackage(root) ?? await getGoModule(root);
7801
+ } catch {
7802
+ return null;
7803
+ }
7804
+ }
7805
+ async function getNodePackage(root) {
7806
+ try {
7807
+ const file = Bun.file(join6(root, "package.json"));
7808
+ if (!await file.exists())
7809
+ return null;
7810
+ const pkg = await file.json();
7811
+ return pkg.name ? { name: pkg.name, type: "package.json" } : null;
7812
+ } catch {
7813
+ return null;
7814
+ }
7815
+ }
7816
+ async function getPythonPackage(root) {
7817
+ try {
7818
+ const file = Bun.file(join6(root, "pyproject.toml"));
7819
+ if (!await file.exists())
7820
+ return null;
7821
+ const content = await file.text();
7822
+ const name = content.match(/^\s*name\s*=\s*"([^"]+)"/m)?.[1];
7823
+ return name ? { name, type: "pyproject.toml" } : null;
7824
+ } catch {
7825
+ return null;
7826
+ }
7827
+ }
7828
+ async function getRustPackage(root) {
7829
+ try {
7830
+ const file = Bun.file(join6(root, "Cargo.toml"));
7831
+ if (!await file.exists())
7832
+ return null;
7833
+ const content = await file.text();
7834
+ const name = content.match(/^\s*name\s*=\s*"([^"]+)"/m)?.[1];
7835
+ return name ? { name, type: "Cargo.toml" } : null;
7836
+ } catch {
7837
+ return null;
7838
+ }
7839
+ }
7840
+ async function getGoModule(root) {
7841
+ try {
7842
+ const file = Bun.file(join6(root, "go.mod"));
7843
+ if (!await file.exists())
7844
+ return null;
7845
+ const content = await file.text();
7846
+ const name = content.match(/^module\s+(\S+)/m)?.[1];
7847
+ return name ? { name, type: "go.mod" } : null;
7848
+ } catch {
7849
+ return null;
7850
+ }
7851
+ }
7852
+ async function getGitRemoteUrl(cwd) {
7853
+ try {
7854
+ const result = await $`git -C ${cwd} remote get-url origin`.quiet();
7855
+ if (result.exitCode !== 0)
7856
+ return null;
7857
+ return result.text().trim() || null;
7858
+ } catch {
7859
+ return null;
7860
+ }
7861
+ }
7862
+ async function getGitBranch(cwd) {
7863
+ try {
7864
+ const result = await $`git -C ${cwd} rev-parse --abbrev-ref HEAD`.quiet();
7865
+ if (result.exitCode !== 0)
7866
+ return null;
7867
+ return result.text().trim();
7868
+ } catch {
7869
+ return null;
7870
+ }
7871
+ }
7872
+ async function getGitSha(cwd) {
7873
+ try {
7874
+ const result = await $`git -C ${cwd} rev-parse HEAD`.quiet();
7875
+ if (result.exitCode !== 0)
7876
+ return null;
7877
+ return result.text().trim();
7878
+ } catch {
7879
+ return null;
7880
+ }
7881
+ }
7882
+
7883
+ // src/lib/remote-cache.ts
7884
+ import { homedir as homedir5 } from "os";
7885
+ import { join as join7 } from "path";
7886
+ var CACHE_PATH = join7(homedir5(), ".rudel", "remote-cache.json");
7887
+ async function getRemoteCache() {
7888
+ try {
7889
+ const file = Bun.file(CACHE_PATH);
7890
+ if (!await file.exists())
7891
+ return {};
7892
+ return await file.json();
7893
+ } catch {
7894
+ return {};
7895
+ }
7896
+ }
7897
+ function getCachedRemote(cache, encodedDir) {
7898
+ return cache[encodedDir] ?? null;
7899
+ }
7900
+ function cacheRemote(cache, encodedDir, normalizedRemote) {
7901
+ cache[encodedDir] = normalizedRemote;
7902
+ }
7903
+ async function cacheRemotes(cache) {
7904
+ try {
7905
+ const { mkdir } = await import("fs/promises");
7906
+ const { dirname: dirname3 } = await import("path");
7907
+ await mkdir(dirname3(CACHE_PATH), { recursive: true });
7908
+ await Bun.write(CACHE_PATH, JSON.stringify(cache));
7909
+ } catch {}
7910
+ }
7911
+
7912
+ // src/lib/project-grouping.ts
7913
+ async function scanAndGroupProjects(cwd = process.cwd()) {
7780
7914
  const adapters2 = getAvailableAdapters();
7781
- const allProjects = [];
7915
+ const projects = [];
7782
7916
  for (const adapter of adapters2) {
7783
- const projects = await adapter.scanAllSessions();
7784
- allProjects.push(...projects);
7917
+ const scanned = await adapter.scanAllSessions();
7918
+ projects.push(...scanned);
7785
7919
  }
7920
+ const groups = await groupProjectsByRemote(projects, cwd);
7921
+ return { projects, groups };
7922
+ }
7923
+ function extractDisplayName(normalized) {
7924
+ const parts = normalized.split("/");
7925
+ if (parts.length >= 3) {
7926
+ return parts.slice(1).join("/");
7927
+ }
7928
+ return normalized;
7929
+ }
7930
+ function encodeProjectPath2(projectPath) {
7931
+ return projectPath.replace(/\//g, "-");
7932
+ }
7933
+ async function groupProjectsByRemote(projects, cwd) {
7934
+ const cache = await getRemoteCache();
7935
+ let cacheUpdated = false;
7936
+ const remotes = await Promise.all(projects.map((p) => getGitRemoteUrl(p.projectPath)));
7937
+ const grouped = new Map;
7938
+ const ungrouped = [];
7939
+ for (let i = 0;i < projects.length; i++) {
7940
+ const project = projects[i];
7941
+ const remote = remotes[i];
7942
+ if (remote) {
7943
+ const normalized = normalizeRemoteUrl(remote);
7944
+ const encodedDir = encodeProjectPath2(project.projectPath);
7945
+ if (getCachedRemote(cache, encodedDir) !== normalized) {
7946
+ cacheRemote(cache, encodedDir, normalized);
7947
+ cacheUpdated = true;
7948
+ }
7949
+ const existing = grouped.get(normalized);
7950
+ if (existing) {
7951
+ existing.projects.push(project);
7952
+ } else {
7953
+ grouped.set(normalized, { remote: normalized, projects: [project] });
7954
+ }
7955
+ } else {
7956
+ const encodedDir = encodeProjectPath2(project.projectPath);
7957
+ const cached = getCachedRemote(cache, encodedDir);
7958
+ if (cached) {
7959
+ const existing = grouped.get(cached);
7960
+ if (existing) {
7961
+ existing.projects.push(project);
7962
+ } else {
7963
+ grouped.set(cached, { remote: cached, projects: [project] });
7964
+ }
7965
+ } else {
7966
+ ungrouped.push(project);
7967
+ }
7968
+ }
7969
+ }
7970
+ const groups = [];
7971
+ for (const [, entry] of grouped) {
7972
+ const containsCwd = entry.projects.some((p) => cwd === p.projectPath || cwd.startsWith(`${p.projectPath}/`));
7973
+ groups.push({
7974
+ displayName: extractDisplayName(entry.remote),
7975
+ gitRemote: entry.remote,
7976
+ projects: entry.projects,
7977
+ totalSessions: entry.projects.reduce((s, p) => s + p.sessionCount, 0),
7978
+ containsCwd
7979
+ });
7980
+ }
7981
+ const homeSegments = homedir6().split("/").length;
7982
+ const remainingUngrouped = [];
7983
+ for (const project of ungrouped) {
7984
+ const match = findBestGroupByPath(project, groups, homeSegments);
7985
+ if (match) {
7986
+ match.projects.push(project);
7987
+ match.totalSessions += project.sessionCount;
7988
+ if (cwd === project.projectPath || cwd.startsWith(`${project.projectPath}/`)) {
7989
+ match.containsCwd = true;
7990
+ }
7991
+ } else {
7992
+ remainingUngrouped.push(project);
7993
+ }
7994
+ }
7995
+ for (const project of remainingUngrouped) {
7996
+ const containsCwd = cwd === project.projectPath || cwd.startsWith(`${project.projectPath}/`);
7997
+ groups.push({
7998
+ displayName: project.displayPath,
7999
+ gitRemote: null,
8000
+ projects: [project],
8001
+ totalSessions: project.sessionCount,
8002
+ containsCwd
8003
+ });
8004
+ }
8005
+ groups.sort((a, b) => {
8006
+ if (a.containsCwd !== b.containsCwd)
8007
+ return a.containsCwd ? -1 : 1;
8008
+ const aHasRemote = a.gitRemote !== null;
8009
+ const bHasRemote = b.gitRemote !== null;
8010
+ if (aHasRemote !== bHasRemote)
8011
+ return aHasRemote ? -1 : 1;
8012
+ return a.displayName.localeCompare(b.displayName);
8013
+ });
8014
+ if (cacheUpdated) {
8015
+ cacheRemotes(cache);
8016
+ }
8017
+ return groups;
8018
+ }
8019
+ function commonPrefixLength(a, b) {
8020
+ const partsA = a.split("/");
8021
+ const partsB = b.split("/");
8022
+ let count = 0;
8023
+ for (let i = 0;i < Math.min(partsA.length, partsB.length); i++) {
8024
+ if (partsA[i] === partsB[i])
8025
+ count++;
8026
+ else
8027
+ break;
8028
+ }
8029
+ return count;
8030
+ }
8031
+ function findBestGroupByPath(project, groups, homeSegments) {
8032
+ let bestGroup = null;
8033
+ let bestLen = 0;
8034
+ let secondBestLen = 0;
8035
+ for (const group2 of groups) {
8036
+ if (!group2.gitRemote)
8037
+ continue;
8038
+ let groupBest = 0;
8039
+ for (const p of group2.projects) {
8040
+ groupBest = Math.max(groupBest, commonPrefixLength(project.projectPath, p.projectPath));
8041
+ }
8042
+ if (groupBest > bestLen) {
8043
+ secondBestLen = bestLen;
8044
+ bestLen = groupBest;
8045
+ bestGroup = group2;
8046
+ } else if (groupBest > secondBestLen) {
8047
+ secondBestLen = groupBest;
8048
+ }
8049
+ }
8050
+ if (bestGroup && bestLen > homeSegments && bestLen > secondBestLen) {
8051
+ return bestGroup;
8052
+ }
8053
+ return null;
8054
+ }
8055
+
8056
+ // src/commands/dev/list-sessions.ts
8057
+ async function runListSessions() {
8058
+ const { projects: allProjects, groups } = await scanAndGroupProjects();
7786
8059
  if (allProjects.length === 0) {
7787
8060
  console.log("No projects with sessions found.");
7788
8061
  return;
7789
8062
  }
7790
- const cwd = process.cwd();
7791
- const grouped = groupProjectsForCwd(allProjects, cwd);
7792
8063
  const lines = [];
7793
- for (const proj of grouped.current) {
7794
- const name = getAdapter(proj.source).name;
7795
- lines.push(`[${name}] ${proj.displayPath} (${proj.sessionCount} sessions) [current]`);
7796
- }
7797
- for (const sub of grouped.subfolders) {
7798
- const name = getAdapter(sub.source).name;
7799
- const relative = sub.projectPath.slice(cwd.length + 1);
7800
- lines.push(` [${name}] ${relative} (${sub.sessionCount} sessions)`);
7801
- }
7802
- for (const other of grouped.others) {
7803
- const name = getAdapter(other.source).name;
7804
- lines.push(`[${name}] ${other.displayPath} (${other.sessionCount} sessions)`);
8064
+ for (const group2 of groups) {
8065
+ const isCurrent = group2.containsCwd ? " [current]" : "";
8066
+ if (group2.projects.length === 1) {
8067
+ const proj = group2.projects[0];
8068
+ const name = getAdapter(proj.source).name;
8069
+ lines.push(`[${name}] ${proj.displayPath} (${proj.sessionCount} sessions)${isCurrent}`);
8070
+ continue;
8071
+ }
8072
+ const totalSessions2 = group2.projects.reduce((s, p) => s + p.sessionCount, 0);
8073
+ lines.push(`${group2.displayName} (${group2.projects.length} projects, ${totalSessions2} sessions)${isCurrent}`);
8074
+ for (const proj of group2.projects) {
8075
+ const name = getAdapter(proj.source).name;
8076
+ const cwdMarker = proj.projectPath === process.cwd() ? " [cwd]" : "";
8077
+ lines.push(` [${name}] ${proj.displayPath} (${proj.sessionCount} sessions)${cwdMarker}`);
8078
+ }
7805
8079
  }
7806
8080
  const totalSessions = allProjects.reduce((s, p) => s + p.sessionCount, 0);
7807
8081
  console.log(`${allProjects.length} projects, ${totalSessions} sessions
@@ -7860,8 +8134,8 @@ var X = (t, e = {}, s = {}) => {
7860
8134
  const ut = t.slice(C, w) || t.slice(h, o);
7861
8135
  v = 0;
7862
8136
  for (const Y of ut.replaceAll(ct, "")) {
7863
- const $ = Y.codePointAt(0) || 0;
7864
- if (lt($) ? f = m : ht($) ? f = V : E !== A && at($) ? f = E : f = A, c + f > b && (d = Math.min(d, Math.max(C, h) + v)), c + f > i) {
8137
+ const $2 = Y.codePointAt(0) || 0;
8138
+ if (lt($2) ? f = m : ht($2) ? f = V : E !== A && at($2) ? f = E : f = A, c + f > b && (d = Math.min(d, Math.max(C, h) + v)), c + f > i) {
7865
8139
  F = true;
7866
8140
  break t;
7867
8141
  }
@@ -8498,15 +8772,15 @@ var Fe = /\p{M}+/gu;
8498
8772
  var ye = { limit: 1 / 0, ellipsis: "" };
8499
8773
  var jt = (t, r = {}, s = {}) => {
8500
8774
  const i = r.limit ?? 1 / 0, a = r.ellipsis ?? "", o = r?.ellipsisWidth ?? (a ? jt(a, ye, s).width : 0), u = s.ansiWidth ?? 0, l = s.controlWidth ?? 0, n = s.tabWidth ?? 8, c = s.ambiguousWidth ?? 1, g = s.emojiWidth ?? 2, F = s.fullWidthWidth ?? 2, p = s.regularWidth ?? 1, E = s.wideWidth ?? 2;
8501
- let $ = 0, m = 0, h = t.length, y2 = 0, f = false, v = h, S2 = Math.max(0, i - o), I2 = 0, B2 = 0, A = 0, w = 0;
8775
+ let $2 = 0, m = 0, h = t.length, y2 = 0, f = false, v = h, S2 = Math.max(0, i - o), I2 = 0, B2 = 0, A = 0, w = 0;
8502
8776
  t:
8503
8777
  for (;; ) {
8504
- if (B2 > I2 || m >= h && m > $) {
8505
- const _2 = t.slice(I2, B2) || t.slice($, m);
8778
+ if (B2 > I2 || m >= h && m > $2) {
8779
+ const _2 = t.slice(I2, B2) || t.slice($2, m);
8506
8780
  y2 = 0;
8507
8781
  for (const D2 of _2.replaceAll(Fe, "")) {
8508
8782
  const T2 = D2.codePointAt(0) || 0;
8509
- if (ge(T2) ? w = F : fe(T2) ? w = E : c !== p && pe(T2) ? w = c : w = p, A + w > S2 && (v = Math.min(v, Math.max(I2, $) + y2)), A + w > i) {
8783
+ if (ge(T2) ? w = F : fe(T2) ? w = E : c !== p && pe(T2) ? w = c : w = p, A + w > S2 && (v = Math.min(v, Math.max(I2, $2) + y2)), A + w > i) {
8510
8784
  f = true;
8511
8785
  break t;
8512
8786
  }
@@ -8521,7 +8795,7 @@ var jt = (t, r = {}, s = {}) => {
8521
8795
  f = true;
8522
8796
  break;
8523
8797
  }
8524
- A += w, I2 = $, B2 = m, m = $ = at2.lastIndex;
8798
+ A += w, I2 = $2, B2 = m, m = $2 = at2.lastIndex;
8525
8799
  continue;
8526
8800
  }
8527
8801
  if (At2.lastIndex = m, At2.test(t)) {
@@ -8529,7 +8803,7 @@ var jt = (t, r = {}, s = {}) => {
8529
8803
  f = true;
8530
8804
  break;
8531
8805
  }
8532
- A += u, I2 = $, B2 = m, m = $ = At2.lastIndex;
8806
+ A += u, I2 = $2, B2 = m, m = $2 = At2.lastIndex;
8533
8807
  continue;
8534
8808
  }
8535
8809
  if (it2.lastIndex = m, it2.test(t)) {
@@ -8537,7 +8811,7 @@ var jt = (t, r = {}, s = {}) => {
8537
8811
  f = true;
8538
8812
  break;
8539
8813
  }
8540
- A += w, I2 = $, B2 = m, m = $ = it2.lastIndex;
8814
+ A += w, I2 = $2, B2 = m, m = $2 = it2.lastIndex;
8541
8815
  continue;
8542
8816
  }
8543
8817
  if (nt2.lastIndex = m, nt2.test(t)) {
@@ -8545,7 +8819,7 @@ var jt = (t, r = {}, s = {}) => {
8545
8819
  f = true;
8546
8820
  break;
8547
8821
  }
8548
- A += w, I2 = $, B2 = m, m = $ = nt2.lastIndex;
8822
+ A += w, I2 = $2, B2 = m, m = $2 = nt2.lastIndex;
8549
8823
  continue;
8550
8824
  }
8551
8825
  if (wt2.lastIndex = m, wt2.test(t)) {
@@ -8553,7 +8827,7 @@ var jt = (t, r = {}, s = {}) => {
8553
8827
  f = true;
8554
8828
  break;
8555
8829
  }
8556
- A += g, I2 = $, B2 = m, m = $ = wt2.lastIndex;
8830
+ A += g, I2 = $2, B2 = m, m = $2 = wt2.lastIndex;
8557
8831
  continue;
8558
8832
  }
8559
8833
  m += 1;
@@ -8616,34 +8890,34 @@ var Ie = (t, r, s = {}) => {
8616
8890
  let i = "", a, o;
8617
8891
  const u = t.split(" "), l = Ce(u);
8618
8892
  let n = [""];
8619
- for (const [$, m] of u.entries()) {
8893
+ for (const [$2, m] of u.entries()) {
8620
8894
  s.trim !== false && (n[n.length - 1] = (n.at(-1) ?? "").trimStart());
8621
8895
  let h = M2(n.at(-1) ?? "");
8622
- if ($ !== 0 && (h >= r && (s.wordWrap === false || s.trim === false) && (n.push(""), h = 0), (h > 0 || s.trim === false) && (n[n.length - 1] += " ", h++)), s.hard && l[$] > r) {
8623
- const y2 = r - h, f = 1 + Math.floor((l[$] - y2 - 1) / r);
8624
- Math.floor((l[$] - 1) / r) < f && n.push(""), It2(n, m, r);
8896
+ if ($2 !== 0 && (h >= r && (s.wordWrap === false || s.trim === false) && (n.push(""), h = 0), (h > 0 || s.trim === false) && (n[n.length - 1] += " ", h++)), s.hard && l[$2] > r) {
8897
+ const y2 = r - h, f = 1 + Math.floor((l[$2] - y2 - 1) / r);
8898
+ Math.floor((l[$2] - 1) / r) < f && n.push(""), It2(n, m, r);
8625
8899
  continue;
8626
8900
  }
8627
- if (h + l[$] > r && h > 0 && l[$] > 0) {
8901
+ if (h + l[$2] > r && h > 0 && l[$2] > 0) {
8628
8902
  if (s.wordWrap === false && h < r) {
8629
8903
  It2(n, m, r);
8630
8904
  continue;
8631
8905
  }
8632
8906
  n.push("");
8633
8907
  }
8634
- if (h + l[$] > r && s.wordWrap === false) {
8908
+ if (h + l[$2] > r && s.wordWrap === false) {
8635
8909
  It2(n, m, r);
8636
8910
  continue;
8637
8911
  }
8638
8912
  n[n.length - 1] += m;
8639
8913
  }
8640
- s.trim !== false && (n = n.map(($) => Se($)));
8914
+ s.trim !== false && (n = n.map(($2) => Se($2)));
8641
8915
  const c = n.join(`
8642
8916
  `), g = c[Symbol.iterator]();
8643
8917
  let F = g.next(), p = g.next(), E = 0;
8644
8918
  for (;!F.done; ) {
8645
- const $ = F.value, m = p.value;
8646
- if (i += $, $ === ot2 || $ === Gt) {
8919
+ const $2 = F.value, m = p.value;
8920
+ if (i += $2, $2 === ot2 || $2 === Gt) {
8647
8921
  Ht.lastIndex = E + 1;
8648
8922
  const f = Ht.exec(c)?.groups;
8649
8923
  if (f?.code !== undefined) {
@@ -8654,8 +8928,8 @@ var Ie = (t, r, s = {}) => {
8654
8928
  }
8655
8929
  const h = a ? we(a) : undefined;
8656
8930
  m === `
8657
- ` ? (o && (i += Kt("")), a && h && (i += Ut(h))) : $ === `
8658
- ` && (a && h && (i += Ut(a)), o && (i += Kt(o))), E += $.length, F = p, p = g.next();
8931
+ ` ? (o && (i += Kt("")), a && h && (i += Ut(h))) : $2 === `
8932
+ ` && (a && h && (i += Ut(a)), o && (i += Kt(o))), E += $2.length, F = p, p = g.next();
8659
8933
  }
8660
8934
  return i;
8661
8935
  };
@@ -8677,13 +8951,13 @@ var be = (t, r, s, i, a) => {
8677
8951
  };
8678
8952
  var X2 = (t) => {
8679
8953
  const { cursor: r, options: s, style: i } = t, a = t.output ?? process.stdout, o = rt(a), u = t.columnPadding ?? 0, l = t.rowPadding ?? 4, n = o - u, c = nt(a), g = import_picocolors2.default.dim("..."), F = t.maxItems ?? Number.POSITIVE_INFINITY, p = Math.max(c - l, 0), E = Math.max(Math.min(F, p), 5);
8680
- let $ = 0;
8681
- r >= E - 3 && ($ = Math.max(Math.min(r - E + 3, s.length - E), 0));
8682
- let m = E < s.length && $ > 0, h = E < s.length && $ + E < s.length;
8683
- const y2 = Math.min($ + E, s.length), f = [];
8954
+ let $2 = 0;
8955
+ r >= E - 3 && ($2 = Math.max(Math.min(r - E + 3, s.length - E), 0));
8956
+ let m = E < s.length && $2 > 0, h = E < s.length && $2 + E < s.length;
8957
+ const y2 = Math.min($2 + E, s.length), f = [];
8684
8958
  let v = 0;
8685
8959
  m && v++, h && v++;
8686
- const S2 = $ + (m ? 1 : 0), I2 = y2 - (h ? 1 : 0);
8960
+ const S2 = $2 + (m ? 1 : 0), I2 = y2 - (h ? 1 : 0);
8687
8961
  for (let A = S2;A < I2; A++) {
8688
8962
  const w = J2(i(s[A], A === r), n, { hard: true, trim: false }).split(`
8689
8963
  `);
@@ -8709,8 +8983,8 @@ function Yt(t, r, s, i) {
8709
8983
  }
8710
8984
  var Te = (t) => t;
8711
8985
  var Me = (t = "", r = "", s) => {
8712
- const i = s?.output ?? process.stdout, a = rt(i), o = 2, u = s?.titlePadding ?? 1, l = s?.contentPadding ?? 2, n = s?.width === undefined || s.width === "auto" ? 1 : Math.min(1, s.width), c = s?.withGuide ?? _.withGuide ? `${d} ` : "", g = s?.formatBorder ?? Te, F = ((s?.rounded) ? _e : De).map(g), p = g(rt2), E = g(d), $ = M2(c), m = M2(r), h = a - $;
8713
- let y2 = Math.floor(a * n) - $;
8986
+ const i = s?.output ?? process.stdout, a = rt(i), o = 2, u = s?.titlePadding ?? 1, l = s?.contentPadding ?? 2, n = s?.width === undefined || s.width === "auto" ? 1 : Math.min(1, s.width), c = s?.withGuide ?? _.withGuide ? `${d} ` : "", g = s?.formatBorder ?? Te, F = ((s?.rounded) ? _e : De).map(g), p = g(rt2), E = g(d), $2 = M2(c), m = M2(r), h = a - $2;
8987
+ let y2 = Math.floor(a * n) - $2;
8714
8988
  if (s?.width === "auto") {
8715
8989
  const _2 = t.split(`
8716
8990
  `);
@@ -8772,8 +9046,8 @@ var R2 = { message: (t = [], { symbol: r = import_picocolors2.default.gray(d), s
8772
9046
  if (F.length > 0) {
8773
9047
  const [p, ...E] = F;
8774
9048
  p.length > 0 ? u.push(`${c}${p}`) : u.push(l ? r : "");
8775
- for (const $ of E)
8776
- $.length > 0 ? u.push(`${g}${$}`) : u.push(l ? s : "");
9049
+ for (const $2 of E)
9050
+ $2.length > 0 ? u.push(`${g}${$2}`) : u.push(l ? s : "");
8777
9051
  }
8778
9052
  i.write(`${u.join(`
8779
9053
  `)}
@@ -8865,7 +9139,7 @@ ${import_picocolors2.default.cyan(x2)}
8865
9139
  var Ke = import_picocolors2.default.magenta;
8866
9140
  var bt2 = ({ indicator: t = "dots", onCancel: r, output: s = process.stdout, cancelMessage: i, errorMessage: a, frames: o = et2 ? ["\u25D2", "\u25D0", "\u25D3", "\u25D1"] : ["\u2022", "o", "O", "0"], delay: u = et2 ? 80 : 120, signal: l, ...n } = {}) => {
8867
9141
  const c = ct2();
8868
- let g, F, p = false, E = false, $ = "", m, h = performance.now();
9142
+ let g, F, p = false, E = false, $2 = "", m, h = performance.now();
8869
9143
  const y2 = rt(s), f = n?.styleFrame ?? Ke, v = (b) => {
8870
9144
  const O2 = b > 1 ? a ?? _.messages.error : i ?? _.messages.cancel;
8871
9145
  E = b === 1, p && (L2(O2, b), E && typeof r == "function" && r());
@@ -8885,22 +9159,22 @@ var bt2 = ({ indicator: t = "dots", onCancel: r, output: s = process.stdout, can
8885
9159
  const O2 = (performance.now() - b) / 1000, j2 = Math.floor(O2 / 60), G2 = Math.floor(O2 % 60);
8886
9160
  return j2 > 0 ? `[${j2}m ${G2}s]` : `[${G2}s]`;
8887
9161
  }, T2 = n.withGuide ?? _.withGuide, Y = (b = "") => {
8888
- p = true, g = Bt({ output: s }), $ = _2(b), h = performance.now(), T2 && s.write(`${import_picocolors2.default.gray(d)}
9162
+ p = true, g = Bt({ output: s }), $2 = _2(b), h = performance.now(), T2 && s.write(`${import_picocolors2.default.gray(d)}
8889
9163
  `);
8890
9164
  let O2 = 0, j2 = 0;
8891
9165
  B2(), F = setInterval(() => {
8892
- if (c && $ === m)
9166
+ if (c && $2 === m)
8893
9167
  return;
8894
- w(), m = $;
9168
+ w(), m = $2;
8895
9169
  const G2 = f(o[O2]);
8896
9170
  let tt2;
8897
9171
  if (c)
8898
- tt2 = `${G2} ${$}...`;
9172
+ tt2 = `${G2} ${$2}...`;
8899
9173
  else if (t === "timer")
8900
- tt2 = `${G2} ${$} ${D2(h)}`;
9174
+ tt2 = `${G2} ${$2} ${D2(h)}`;
8901
9175
  else {
8902
9176
  const te = ".".repeat(Math.floor(j2)).slice(0, 3);
8903
- tt2 = `${G2} ${$}${te}`;
9177
+ tt2 = `${G2} ${$2}${te}`;
8904
9178
  }
8905
9179
  const Zt = J2(tt2, y2, { hard: true, trim: false });
8906
9180
  s.write(Zt), O2 = O2 + 1 < o.length ? O2 + 1 : 0, j2 = j2 < 4 ? j2 + 0.125 : 0;
@@ -8910,12 +9184,12 @@ var bt2 = ({ indicator: t = "dots", onCancel: r, output: s = process.stdout, can
8910
9184
  return;
8911
9185
  p = false, clearInterval(F), w();
8912
9186
  const G2 = O2 === 0 ? import_picocolors2.default.green(V) : O2 === 1 ? import_picocolors2.default.red(dt2) : import_picocolors2.default.red($t2);
8913
- $ = b ?? $, j2 || (t === "timer" ? s.write(`${G2} ${$} ${D2(h)}
8914
- `) : s.write(`${G2} ${$}
9187
+ $2 = b ?? $2, j2 || (t === "timer" ? s.write(`${G2} ${$2} ${D2(h)}
9188
+ `) : s.write(`${G2} ${$2}
8915
9189
  `)), A(), g();
8916
9190
  };
8917
9191
  return { start: Y, stop: (b = "") => L2(b, 0), message: (b = "") => {
8918
- $ = _2(b ?? $);
9192
+ $2 = _2(b ?? $2);
8919
9193
  }, cancel: (b = "") => L2(b, 1), error: (b = "") => L2(b, 2), clear: () => L2("", 0, true), get isCancelled() {
8920
9194
  return E;
8921
9195
  } };
@@ -8937,13 +9211,13 @@ function qe({ style: t = "heavy", max: r = 100, size: s = 40, ...i } = {}) {
8937
9211
  default:
8938
9212
  return import_picocolors2.default.magenta;
8939
9213
  }
8940
- }, g = (E, $) => {
9214
+ }, g = (E, $2) => {
8941
9215
  const m = Math.floor(o / l * n);
8942
- return `${c(E)(zt[t].repeat(m))}${import_picocolors2.default.dim(zt[t].repeat(n - m))} ${$}`;
9216
+ return `${c(E)(zt[t].repeat(m))}${import_picocolors2.default.dim(zt[t].repeat(n - m))} ${$2}`;
8943
9217
  }, F = (E = "") => {
8944
9218
  u = E, a.start(g("initial", E));
8945
- }, p = (E = 1, $) => {
8946
- o = Math.min(l, E + o), a.message(g("active", $ ?? u)), u = $ ?? u;
9219
+ }, p = (E = 1, $2) => {
9220
+ o = Math.min(l, E + o), a.message(g("active", $2 ?? u)), u = $2 ?? u;
8947
9221
  };
8948
9222
  return { start: F, stop: a.stop, cancel: a.cancel, error: a.error, clear: a.clear, advance: p, isCancelled: a.isCancelled, message: (E) => p(0, E) };
8949
9223
  }
@@ -10502,13 +10776,13 @@ import {
10502
10776
  rmSync,
10503
10777
  writeFileSync as writeFileSync3
10504
10778
  } from "fs";
10505
- import { homedir as homedir5 } from "os";
10506
- import { join as join6 } from "path";
10779
+ import { homedir as homedir7 } from "os";
10780
+ import { join as join8 } from "path";
10507
10781
  function getConfigDir() {
10508
- return process.env.RUDEL_CONFIG_DIR ?? join6(homedir5(), ".rudel");
10782
+ return process.env.RUDEL_CONFIG_DIR ?? join8(homedir7(), ".rudel");
10509
10783
  }
10510
10784
  function getCredentialsPath() {
10511
- return join6(getConfigDir(), "credentials.json");
10785
+ return join8(getConfigDir(), "credentials.json");
10512
10786
  }
10513
10787
  function saveCredentials(token, apiBaseUrl) {
10514
10788
  const dir = getConfigDir();
@@ -10691,8 +10965,8 @@ async function pMap(iterable, mapper, {
10691
10965
  var pMapSkip = Symbol("skip");
10692
10966
 
10693
10967
  // src/lib/failed-uploads.ts
10694
- import { homedir as homedir6 } from "os";
10695
- import { join as join7 } from "path";
10968
+ import { homedir as homedir8 } from "os";
10969
+ import { join as join9 } from "path";
10696
10970
 
10697
10971
  // ../../node_modules/.bun/@orpc+contract@1.13.6/node_modules/@orpc/contract/dist/shared/contract.D_dZrO__.mjs
10698
10972
  function mergeErrorMap(errorMap1, errorMap2) {
@@ -10867,7 +11141,8 @@ var OverviewKPIsSchema = exports_external.object({
10867
11141
  distinct_projects: exports_external.number(),
10868
11142
  distinct_subagents: exports_external.number(),
10869
11143
  distinct_skills: exports_external.number(),
10870
- distinct_slash_commands: exports_external.number()
11144
+ distinct_slash_commands: exports_external.number(),
11145
+ total_sessions: exports_external.number()
10871
11146
  });
10872
11147
  var UsageTrendDataSchema = exports_external.object({
10873
11148
  date: exports_external.string(),
@@ -10946,6 +11221,8 @@ var DeveloperSessionSchema = exports_external.object({
10946
11221
  session_id: exports_external.string(),
10947
11222
  session_date: exports_external.string(),
10948
11223
  project_path: exports_external.string(),
11224
+ git_remote: exports_external.string().optional(),
11225
+ package_name: exports_external.string().optional(),
10949
11226
  duration_min: exports_external.number(),
10950
11227
  total_tokens: exports_external.number(),
10951
11228
  has_subagents: exports_external.boolean(),
@@ -10956,6 +11233,8 @@ var DeveloperSessionSchema = exports_external.object({
10956
11233
  });
10957
11234
  var DeveloperProjectSchema = exports_external.object({
10958
11235
  project_path: exports_external.string(),
11236
+ git_remote: exports_external.string().optional(),
11237
+ package_name: exports_external.string().optional(),
10959
11238
  sessions: exports_external.number(),
10960
11239
  total_duration_min: exports_external.number(),
10961
11240
  total_tokens: exports_external.number(),
@@ -11274,10 +11553,6 @@ var LearningsTrendDataPointSchema = exports_external.record(exports_external.str
11274
11553
  var LearningsTrendInputSchema = DaysInputSchema.extend({
11275
11554
  splitBy: exports_external.enum(["user_id", "repository"])
11276
11555
  });
11277
- var UserMappingSchema = exports_external.object({
11278
- user_id: exports_external.string(),
11279
- username: exports_external.string()
11280
- });
11281
11556
  // ../../packages/api-routes/src/index.ts
11282
11557
  var HealthSchema = exports_external.object({
11283
11558
  status: exports_external.literal("ok"),
@@ -11313,9 +11588,9 @@ var IngestSessionInputSchema = exports_external.object({
11313
11588
  source: SourceSchema.default("claude_code"),
11314
11589
  sessionId: exports_external.string(),
11315
11590
  projectPath: exports_external.string(),
11316
- repository: exports_external.string().optional(),
11317
11591
  gitRemote: exports_external.string().optional(),
11318
11592
  packageName: exports_external.string().optional(),
11593
+ packageType: exports_external.string().optional(),
11319
11594
  gitBranch: exports_external.string().optional(),
11320
11595
  gitSha: exports_external.string().optional(),
11321
11596
  tag: SessionTagSchema.optional(),
@@ -11333,18 +11608,15 @@ var contract = {
11333
11608
  listMyOrganizations: oc.output(exports_external.array(OrganizationSchema)),
11334
11609
  ingestSession: oc.input(IngestSessionInputSchema).output(IngestSessionOutputSchema),
11335
11610
  getOrganizationSessionCount: oc.input(exports_external.object({ organizationId: exports_external.string() })).output(exports_external.object({ count: exports_external.number() })),
11336
- deleteOrganization: oc.input(exports_external.object({
11337
- organizationId: exports_external.string(),
11338
- migrateSessionsTo: exports_external.string().optional()
11339
- })).output(exports_external.object({ success: exports_external.literal(true) })),
11611
+ deleteOrganization: oc.input(exports_external.object({ organizationId: exports_external.string() })).output(exports_external.object({ success: exports_external.literal(true) })),
11340
11612
  analytics: {
11341
11613
  overview: {
11342
- kpis: oc.input(DaysInputSchema).output(OverviewKPIsSchema),
11343
- usageTrend: oc.input(DaysInputSchema).output(exports_external.array(UsageTrendDataSchema)),
11344
- modelTokensTrend: oc.input(DaysInputSchema).output(exports_external.array(ModelTokensTrendDataSchema)),
11345
- insights: oc.input(DaysInputSchema).output(exports_external.array(InsightSchema)),
11346
- teamSummaryComparison: oc.input(DaysInputSchema).output(TeamSummaryComparisonSchema),
11347
- successRate: oc.input(DaysInputSchema).output(SuccessRateSchema)
11614
+ kpis: oc.input(DateRangeInputSchema).output(OverviewKPIsSchema),
11615
+ usageTrend: oc.input(DateRangeInputSchema).output(exports_external.array(UsageTrendDataSchema)),
11616
+ modelTokensTrend: oc.input(DateRangeInputSchema).output(exports_external.array(ModelTokensTrendDataSchema)),
11617
+ insights: oc.input(DateRangeInputSchema).output(exports_external.array(InsightSchema)),
11618
+ teamSummaryComparison: oc.input(DateRangeInputSchema).output(TeamSummaryComparisonSchema),
11619
+ successRate: oc.input(DateRangeInputSchema).output(SuccessRateSchema)
11348
11620
  },
11349
11621
  developers: {
11350
11622
  list: oc.input(DaysInputSchema).output(exports_external.array(DeveloperSummarySchema)),
@@ -11387,15 +11659,12 @@ var contract = {
11387
11659
  users: oc.output(exports_external.array(exports_external.string())),
11388
11660
  projects: oc.output(exports_external.array(exports_external.string())),
11389
11661
  trend: oc.input(LearningsTrendInputSchema).output(exports_external.array(LearningsTrendDataPointSchema))
11390
- },
11391
- users: {
11392
- mappings: oc.input(DaysInputSchema).output(exports_external.array(UserMappingSchema))
11393
11662
  }
11394
11663
  }
11395
11664
  };
11396
11665
 
11397
11666
  // src/lib/failed-uploads.ts
11398
- var FAILED_UPLOADS_PATH = join7(homedir6(), ".rudel", "failed-uploads.json");
11667
+ var FAILED_UPLOADS_PATH = join9(homedir8(), ".rudel", "failed-uploads.json");
11399
11668
  function normalizeSource(raw) {
11400
11669
  if (typeof raw !== "string")
11401
11670
  return;
@@ -11543,101 +11812,16 @@ function renderBatchSummary(summary, options) {
11543
11812
  }
11544
11813
  }
11545
11814
 
11546
- // src/lib/git-info.ts
11547
- import { join as join8 } from "path";
11548
- var {$ } = globalThis.Bun;
11549
- function normalizeRemoteUrl(url) {
11550
- return url.replace(/^(https?:\/\/|git@|ssh:\/\/)/, "").replace(/:/, "/").replace(/\.git$/, "");
11551
- }
11552
- async function getGitInfo(cwd) {
11553
- const [remoteUrl, branch, sha, packageName] = await Promise.all([
11554
- getGitRemoteUrl(cwd),
11555
- getGitBranch(cwd),
11556
- getGitSha(cwd),
11557
- getPackageName(cwd)
11558
- ]);
11559
- const gitRemote = remoteUrl ? normalizeRemoteUrl(remoteUrl) : undefined;
11560
- const repository = getRepositoryName(gitRemote, packageName, cwd);
11561
- return {
11562
- repository,
11563
- gitRemote,
11564
- packageName: packageName ?? undefined,
11565
- branch: branch ?? undefined,
11566
- sha: sha ?? undefined
11567
- };
11568
- }
11569
- function getRepositoryName(gitRemote, packageName, cwd) {
11570
- if (gitRemote) {
11571
- const parts = gitRemote.split("/");
11572
- if (parts.length >= 3) {
11573
- return parts.slice(1).join("/");
11574
- }
11575
- return gitRemote;
11576
- }
11577
- if (packageName)
11578
- return packageName;
11579
- const dirName = cwd.split("/").pop();
11580
- return dirName ?? undefined;
11581
- }
11582
- async function getPackageName(cwd) {
11583
- try {
11584
- const gitRootResult = await $`git -C ${cwd} rev-parse --show-toplevel`.quiet();
11585
- const gitRoot = gitRootResult.exitCode === 0 ? gitRootResult.text().trim() : cwd;
11586
- const packageJsonPath = join8(gitRoot, "package.json");
11587
- const packageFile = Bun.file(packageJsonPath);
11588
- if (await packageFile.exists()) {
11589
- try {
11590
- const packageJson = await packageFile.json();
11591
- if (packageJson.name)
11592
- return packageJson.name;
11593
- } catch {}
11594
- }
11595
- return null;
11596
- } catch {
11597
- return null;
11598
- }
11599
- }
11600
- async function getGitRemoteUrl(cwd) {
11601
- try {
11602
- const result = await $`git -C ${cwd} remote get-url origin`.quiet();
11603
- if (result.exitCode !== 0)
11604
- return null;
11605
- return result.text().trim() || null;
11606
- } catch {
11607
- return null;
11608
- }
11609
- }
11610
- async function getGitBranch(cwd) {
11611
- try {
11612
- const result = await $`git -C ${cwd} rev-parse --abbrev-ref HEAD`.quiet();
11613
- if (result.exitCode !== 0)
11614
- return null;
11615
- return result.text().trim();
11616
- } catch {
11617
- return null;
11618
- }
11619
- }
11620
- async function getGitSha(cwd) {
11621
- try {
11622
- const result = await $`git -C ${cwd} rev-parse HEAD`.quiet();
11623
- if (result.exitCode !== 0)
11624
- return null;
11625
- return result.text().trim();
11626
- } catch {
11627
- return null;
11628
- }
11629
- }
11630
-
11631
11815
  // src/lib/project-config.ts
11632
11816
  import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
11633
- import { homedir as homedir7 } from "os";
11634
- import { join as join9 } from "path";
11817
+ import { homedir as homedir9 } from "os";
11818
+ import { join as join10 } from "path";
11635
11819
  var {$: $2 } = globalThis.Bun;
11636
11820
  function getConfigDir2() {
11637
- return process.env.RUDEL_CONFIG_DIR ?? join9(homedir7(), ".rudel");
11821
+ return process.env.RUDEL_CONFIG_DIR ?? join10(homedir9(), ".rudel");
11638
11822
  }
11639
11823
  function getProjectsConfigPath() {
11640
- return join9(getConfigDir2(), "projects.json");
11824
+ return join10(getConfigDir2(), "projects.json");
11641
11825
  }
11642
11826
  function loadProjectsConfig() {
11643
11827
  const path = getProjectsConfigPath();
@@ -13481,8 +13665,8 @@ var ConfigError = class extends Error {
13481
13665
  };
13482
13666
 
13483
13667
  // src/logging.ts
13484
- import { homedir as homedir8 } from "os";
13485
- import { join as join11 } from "path";
13668
+ import { homedir as homedir10 } from "os";
13669
+ import { join as join12 } from "path";
13486
13670
 
13487
13671
  // ../../node_modules/.bun/@logtape+file@2.0.4+2d5de51540e256d2/node_modules/@logtape/file/dist/filesink.base.js
13488
13672
  var AdaptiveFlushStrategy = class {
@@ -13748,7 +13932,7 @@ function getBaseFileSink(path, options) {
13748
13932
 
13749
13933
  // ../../node_modules/.bun/@logtape+file@2.0.4+2d5de51540e256d2/node_modules/@logtape/file/dist/filesink.node.js
13750
13934
  import fs from "fs";
13751
- import { join as join10 } from "path";
13935
+ import { join as join11 } from "path";
13752
13936
  import { promisify } from "util";
13753
13937
  var nodeDriver = {
13754
13938
  openSync(path) {
@@ -13788,14 +13972,14 @@ var nodeTimeDriver = {
13788
13972
  readdirSync: fs.readdirSync,
13789
13973
  unlinkSync: fs.unlinkSync,
13790
13974
  mkdirSync: fs.mkdirSync,
13791
- joinPath: join10
13975
+ joinPath: join11
13792
13976
  };
13793
13977
  var nodeAsyncTimeDriver = {
13794
13978
  ...nodeAsyncDriver,
13795
13979
  readdirSync: fs.readdirSync,
13796
13980
  unlinkSync: fs.unlinkSync,
13797
13981
  mkdirSync: fs.mkdirSync,
13798
- joinPath: join10
13982
+ joinPath: join11
13799
13983
  };
13800
13984
  function getFileSink(path, options = {}) {
13801
13985
  if (options.nonBlocking)
@@ -13810,8 +13994,8 @@ function getFileSink(path, options = {}) {
13810
13994
  }
13811
13995
 
13812
13996
  // src/logging.ts
13813
- var LOG_DIR = join11(homedir8(), ".rudel", "logs");
13814
- var LOG_FILE = join11(LOG_DIR, "hook-upload.log");
13997
+ var LOG_DIR = join12(homedir10(), ".rudel", "logs");
13998
+ var LOG_FILE = join12(LOG_DIR, "hook-upload.log");
13815
13999
  async function setupHookLogging() {
13816
14000
  const { mkdir } = await import("fs/promises");
13817
14001
  await mkdir(LOG_DIR, { recursive: true }).catch(() => {});
@@ -14193,8 +14377,8 @@ var setOrgCommand = buildCommand({
14193
14377
 
14194
14378
  // src/lib/classifier.ts
14195
14379
  import { mkdir, unlink } from "fs/promises";
14196
- import { homedir as homedir9 } from "os";
14197
- import { join as join12 } from "path";
14380
+ import { homedir as homedir11 } from "os";
14381
+ import { join as join13 } from "path";
14198
14382
 
14199
14383
  // src/lib/types.ts
14200
14384
  var SESSION_TAGS = [
@@ -14221,8 +14405,8 @@ var SYSTEM_PROMPT = `You are a session classifier. Analyze the Claude Code sessi
14221
14405
  CRITICAL: Respond with ONLY the tag name. Nothing else. No explanation, no punctuation, no formatting. Just ONE of: research, new_feature, bug_fix, refactoring, documentation, tests`;
14222
14406
  async function classifySession(content) {
14223
14407
  const truncatedContent = content.slice(0, 50000);
14224
- const tempDir = join12(homedir9(), ".claude", "temp");
14225
- const tempFile = join12(tempDir, `classify-${Date.now()}.txt`);
14408
+ const tempDir = join13(homedir11(), ".claude", "temp");
14409
+ const tempFile = join13(tempDir, `classify-${Date.now()}.txt`);
14226
14410
  try {
14227
14411
  await mkdir(tempDir, { recursive: true });
14228
14412
  await Bun.write(tempFile, `Classify this session transcript:
@@ -14262,9 +14446,9 @@ ${truncatedContent}`);
14262
14446
 
14263
14447
  // src/lib/session-resolver.ts
14264
14448
  import { access, readdir as readdir3 } from "fs/promises";
14265
- import { homedir as homedir10 } from "os";
14266
- import { basename, dirname as dirname3, join as join13 } from "path";
14267
- var SESSIONS_BASE_DIR3 = join13(homedir10(), ".claude", "projects");
14449
+ import { homedir as homedir12 } from "os";
14450
+ import { basename, dirname as dirname3, join as join14 } from "path";
14451
+ var SESSIONS_BASE_DIR3 = join14(homedir12(), ".claude", "projects");
14268
14452
  async function resolveSession(input) {
14269
14453
  const isPath = input.includes("/") || input.endsWith(".jsonl");
14270
14454
  if (isPath) {
@@ -14296,11 +14480,11 @@ async function resolveFromId(sessionId) {
14296
14480
  throw new Error(`Session not found: ${sessionId}`);
14297
14481
  }
14298
14482
  for (const projectDir of projectDirs) {
14299
- const sessionDir = join13(SESSIONS_BASE_DIR3, projectDir);
14483
+ const sessionDir = join14(SESSIONS_BASE_DIR3, projectDir);
14300
14484
  try {
14301
14485
  const files = await readdir3(sessionDir);
14302
14486
  if (files.includes(sessionFileName)) {
14303
- const transcriptPath = join13(sessionDir, sessionFileName);
14487
+ const transcriptPath = join14(sessionDir, sessionFileName);
14304
14488
  const projectPath = await decodeProjectPath(projectDir);
14305
14489
  return {
14306
14490
  transcriptPath,
@@ -14330,45 +14514,26 @@ async function runInteractiveUpload(flags) {
14330
14514
  We("rudel upload");
14331
14515
  const spin = bt2();
14332
14516
  spin.start("Scanning projects...");
14333
- const adapters2 = getAvailableAdapters();
14334
- const allProjects = [];
14335
- for (const adapter of adapters2) {
14336
- const projects = await adapter.scanAllSessions();
14337
- allProjects.push(...projects);
14338
- }
14517
+ const { projects: allProjects, groups } = await scanAndGroupProjects();
14339
14518
  spin.stop(`Found ${allProjects.length} project(s)`);
14340
14519
  if (allProjects.length === 0) {
14341
14520
  R2.warn("No projects with sessions found.");
14342
14521
  Le("Nothing to upload.");
14343
14522
  return;
14344
14523
  }
14345
- const cwd = process.cwd();
14346
- const grouped = groupProjectsForCwd(allProjects, cwd);
14347
14524
  const options = [];
14348
14525
  const preSelected = [];
14349
- for (const proj of grouped.current) {
14350
- options.push({
14351
- value: proj,
14352
- label: `[${getAdapterName(proj.source)}] ${proj.displayPath}`,
14353
- hint: sessionCountHint(proj.sessionCount)
14354
- });
14355
- preSelected.push(proj);
14356
- }
14357
- for (const sub of grouped.subfolders) {
14358
- const relative = sub.projectPath.slice(cwd.length + 1);
14359
- options.push({
14360
- value: sub,
14361
- label: ` [${getAdapterName(sub.source)}] ${relative}`,
14362
- hint: sessionCountHint(sub.sessionCount)
14363
- });
14364
- preSelected.push(sub);
14365
- }
14366
- for (const other of grouped.others) {
14367
- options.push({
14368
- value: other,
14369
- label: `[${getAdapterName(other.source)}] ${other.displayPath}`,
14370
- hint: sessionCountHint(other.sessionCount)
14371
- });
14526
+ for (const group2 of groups) {
14527
+ for (const proj of group2.projects) {
14528
+ options.push({
14529
+ value: proj,
14530
+ label: `[${getAdapterName(proj.source)}] ${proj.displayPath}`,
14531
+ hint: sessionCountHint(proj.sessionCount)
14532
+ });
14533
+ if (group2.containsCwd) {
14534
+ preSelected.push(proj);
14535
+ }
14536
+ }
14372
14537
  }
14373
14538
  const selected = await je({
14374
14539
  message: "Select projects to upload",
@@ -14470,8 +14635,9 @@ async function runSingleUpload(flags, session) {
14470
14635
  }
14471
14636
  write(`Found session at: ${sessionInfo.transcriptPath}`);
14472
14637
  const gitInfo = await getGitInfo(sessionInfo.projectPath);
14473
- if (gitInfo.repository)
14474
- write(`Repository: ${gitInfo.repository}`);
14638
+ const displayName = gitInfo.gitRemote || gitInfo.packageName || sessionInfo.projectPath.split("/").pop();
14639
+ if (displayName)
14640
+ write(`Repository: ${displayName}`);
14475
14641
  if (gitInfo.branch)
14476
14642
  write(`Branch: ${gitInfo.branch}`);
14477
14643
  const organizationId = flags.org ?? await getProjectOrgId(sessionInfo.projectPath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rudel",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "type": "module",
5
5
  "description": "CLI for the Coding Agent Analytics Platform rudel.ai",
6
6
  "license": "MIT",