rudel 0.1.8 → 0.1.10
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/cli.js +523 -325
- package/package.json +3 -2
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
2
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
3
|
var __create = Object.create;
|
|
4
4
|
var __getProtoOf = Object.getPrototypeOf;
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
@@ -26,7 +26,7 @@ var __export = (target, all) => {
|
|
|
26
26
|
set: (newValue) => all[name] = () => newValue
|
|
27
27
|
});
|
|
28
28
|
};
|
|
29
|
-
var __require = import.meta.
|
|
29
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
30
30
|
|
|
31
31
|
// ../../node_modules/.bun/picocolors@1.1.1/node_modules/picocolors/picocolors.js
|
|
32
32
|
var require_picocolors = __commonJS((exports, module) => {
|
|
@@ -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.
|
|
1962
|
+
version: "0.1.10",
|
|
1963
1963
|
type: "module",
|
|
1964
1964
|
description: "CLI for the Coding Agent Analytics Platform rudel.ai",
|
|
1965
1965
|
license: "MIT",
|
|
@@ -1991,7 +1991,7 @@ var package_default = {
|
|
|
1991
1991
|
scripts: {
|
|
1992
1992
|
dev: "bun run src/bin/cli.ts",
|
|
1993
1993
|
"check-types": "tsc --noEmit",
|
|
1994
|
-
build: "bun build src/bin/cli.ts --outdir dist --target
|
|
1994
|
+
build: "bun build src/bin/cli.ts --outdir dist --target node",
|
|
1995
1995
|
test: "bun test"
|
|
1996
1996
|
},
|
|
1997
1997
|
dependencies: {
|
|
@@ -2007,15 +2007,16 @@ var package_default = {
|
|
|
2007
2007
|
"@rudel/agent-adapters": "workspace:*",
|
|
2008
2008
|
"@rudel/api-routes": "workspace:*",
|
|
2009
2009
|
"@rudel/typescript-config": "workspace:*",
|
|
2010
|
+
"@types/node": "^22",
|
|
2010
2011
|
"bun-types": "latest",
|
|
2011
2012
|
typescript: "5.9.2"
|
|
2012
2013
|
}
|
|
2013
2014
|
};
|
|
2014
2015
|
|
|
2015
2016
|
// ../../packages/agent-adapters/src/adapters/claude-code/index.ts
|
|
2016
|
-
import { readdir as readdir2, readFile as readFile2, stat } from "fs/promises";
|
|
2017
|
-
import { homedir as homedir2 } from "os";
|
|
2018
|
-
import { dirname as dirname2, join as join3 } from "path";
|
|
2017
|
+
import { readdir as readdir2, readFile as readFile2, stat } from "node:fs/promises";
|
|
2018
|
+
import { homedir as homedir2 } from "node:os";
|
|
2019
|
+
import { dirname as dirname2, join as join3 } from "node:path";
|
|
2019
2020
|
|
|
2020
2021
|
// ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/external.js
|
|
2021
2022
|
var exports_external = {};
|
|
@@ -6076,9 +6077,9 @@ async function ingestRudelCodexSessions(ingestor, rows, options) {
|
|
|
6076
6077
|
await ingestor.insert({ table: "rudel.codex_sessions", values: data });
|
|
6077
6078
|
}
|
|
6078
6079
|
// ../../packages/agent-adapters/src/utils.ts
|
|
6079
|
-
import { readdir, readFile } from "fs/promises";
|
|
6080
|
-
import { homedir } from "os";
|
|
6081
|
-
import { join } from "path";
|
|
6080
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
6081
|
+
import { homedir } from "node:os";
|
|
6082
|
+
import { join } from "node:path";
|
|
6082
6083
|
function toClickHouseDateTime(isoString) {
|
|
6083
6084
|
return isoString.replace("T", " ").replace("Z", "").replace(/\+.*$/, "");
|
|
6084
6085
|
}
|
|
@@ -6132,21 +6133,11 @@ function toDisplayPath(absolutePath) {
|
|
|
6132
6133
|
const home = homedir();
|
|
6133
6134
|
return absolutePath.startsWith(home) ? `~${absolutePath.slice(home.length)}` : absolutePath;
|
|
6134
6135
|
}
|
|
6135
|
-
function groupProjectsForCwd(projects, cwd) {
|
|
6136
|
-
const current = projects.filter((p) => p.projectPath === cwd);
|
|
6137
|
-
const currentSet = new Set(current);
|
|
6138
|
-
const subfolders = projects.filter((p) => !currentSet.has(p) && p.projectPath.startsWith(`${cwd}/`));
|
|
6139
|
-
const subfoldersSet = new Set(subfolders);
|
|
6140
|
-
const others = projects.filter((p) => !currentSet.has(p) && !subfoldersSet.has(p));
|
|
6141
|
-
subfolders.sort((a, b) => a.projectPath.localeCompare(b.projectPath));
|
|
6142
|
-
others.sort((a, b) => a.displayPath.localeCompare(b.displayPath));
|
|
6143
|
-
return { current, subfolders, others };
|
|
6144
|
-
}
|
|
6145
6136
|
|
|
6146
6137
|
// ../../packages/agent-adapters/src/adapters/claude-code/settings.ts
|
|
6147
|
-
import { execSync } from "child_process";
|
|
6148
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
6149
|
-
import { dirname, join as join2, resolve } from "path";
|
|
6138
|
+
import { execSync } from "node:child_process";
|
|
6139
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
6140
|
+
import { dirname, join as join2, resolve } from "node:path";
|
|
6150
6141
|
var HOOK_COMMAND = "rudel hooks claude session-end";
|
|
6151
6142
|
function findClaudeDir() {
|
|
6152
6143
|
let dir = resolve(process.cwd());
|
|
@@ -6465,14 +6456,14 @@ class ClaudeCodeAdapter {
|
|
|
6465
6456
|
}
|
|
6466
6457
|
var claudeCodeAdapter = new ClaudeCodeAdapter;
|
|
6467
6458
|
// ../../packages/agent-adapters/src/adapters/codex/index.ts
|
|
6468
|
-
import { readFile as readFile3 } from "fs/promises";
|
|
6469
|
-
import { homedir as homedir4 } from "os";
|
|
6470
|
-
import { join as join5 } from "path";
|
|
6459
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
6460
|
+
import { homedir as homedir4 } from "node:os";
|
|
6461
|
+
import { join as join5 } from "node:path";
|
|
6471
6462
|
|
|
6472
6463
|
// ../../packages/agent-adapters/src/adapters/codex/config.ts
|
|
6473
|
-
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
6474
|
-
import { homedir as homedir3 } from "os";
|
|
6475
|
-
import { join as join4 } from "path";
|
|
6464
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
6465
|
+
import { homedir as homedir3 } from "node:os";
|
|
6466
|
+
import { join as join4 } from "node:path";
|
|
6476
6467
|
|
|
6477
6468
|
// ../../node_modules/.bun/smol-toml@1.6.0/node_modules/smol-toml/dist/error.js
|
|
6478
6469
|
/*!
|
|
@@ -6594,7 +6585,7 @@ function skipComment(str, ptr) {
|
|
|
6594
6585
|
if (c === "\r" && str[i + 1] === `
|
|
6595
6586
|
`)
|
|
6596
6587
|
return i + 1;
|
|
6597
|
-
if (c < " " && c !== "\t" || c === "
|
|
6588
|
+
if (c < " " && c !== "\t" || c === "") {
|
|
6598
6589
|
throw new TomlError("control characters are not allowed in comments", {
|
|
6599
6590
|
toml: str,
|
|
6600
6591
|
ptr
|
|
@@ -6841,7 +6832,7 @@ function parseString(str, ptr = 0, endPtr = str.length) {
|
|
|
6841
6832
|
ptr: ptr - 1
|
|
6842
6833
|
});
|
|
6843
6834
|
}
|
|
6844
|
-
} else if (c < " " && c !== "\t" || c === "
|
|
6835
|
+
} else if (c < " " && c !== "\t" || c === "") {
|
|
6845
6836
|
throw new TomlError("control characters are not allowed in strings", {
|
|
6846
6837
|
toml: str,
|
|
6847
6838
|
ptr: ptr - 1
|
|
@@ -7756,7 +7747,7 @@ class CodexAdapter {
|
|
|
7756
7747
|
}
|
|
7757
7748
|
var codexAdapter = new CodexAdapter;
|
|
7758
7749
|
// ../../packages/agent-adapters/src/registry.ts
|
|
7759
|
-
import { existsSync as existsSync3 } from "fs";
|
|
7750
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
7760
7751
|
var adapters = new Map;
|
|
7761
7752
|
function registerAdapter(adapter) {
|
|
7762
7753
|
adapters.set(adapter.source, adapter);
|
|
@@ -7778,33 +7769,346 @@ function getAvailableAdapters() {
|
|
|
7778
7769
|
registerAdapter(claudeCodeAdapter);
|
|
7779
7770
|
registerAdapter(codexAdapter);
|
|
7780
7771
|
|
|
7781
|
-
// src/
|
|
7782
|
-
|
|
7772
|
+
// src/lib/project-grouping.ts
|
|
7773
|
+
import { homedir as homedir6 } from "node:os";
|
|
7774
|
+
|
|
7775
|
+
// src/lib/git-info.ts
|
|
7776
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "node:fs";
|
|
7777
|
+
import { join as join6 } from "node:path";
|
|
7778
|
+
|
|
7779
|
+
// src/lib/exec.ts
|
|
7780
|
+
import { execFile } from "node:child_process";
|
|
7781
|
+
function exec(cmd, args) {
|
|
7782
|
+
return new Promise((resolve2) => {
|
|
7783
|
+
execFile(cmd, args, { encoding: "utf8" }, (error, stdout, stderr) => {
|
|
7784
|
+
resolve2({
|
|
7785
|
+
exitCode: error ? error.code ?? 1 : 0,
|
|
7786
|
+
stdout,
|
|
7787
|
+
stderr
|
|
7788
|
+
});
|
|
7789
|
+
});
|
|
7790
|
+
});
|
|
7791
|
+
}
|
|
7792
|
+
|
|
7793
|
+
// src/lib/git-info.ts
|
|
7794
|
+
function normalizeRemoteUrl(url) {
|
|
7795
|
+
return url.replace(/^(https?:\/\/|git@|ssh:\/\/)/, "").replace(/:/, "/").replace(/\.git$/, "");
|
|
7796
|
+
}
|
|
7797
|
+
async function getGitInfo(cwd) {
|
|
7798
|
+
const [remoteUrl, branch, sha, packageInfo] = await Promise.all([
|
|
7799
|
+
getGitRemoteUrl(cwd),
|
|
7800
|
+
getGitBranch(cwd),
|
|
7801
|
+
getGitSha(cwd),
|
|
7802
|
+
getPackageInfo(cwd)
|
|
7803
|
+
]);
|
|
7804
|
+
const gitRemote = remoteUrl ? normalizeRemoteUrl(remoteUrl) : undefined;
|
|
7805
|
+
return {
|
|
7806
|
+
gitRemote,
|
|
7807
|
+
packageName: packageInfo?.name,
|
|
7808
|
+
packageType: packageInfo?.type,
|
|
7809
|
+
branch: branch ?? undefined,
|
|
7810
|
+
sha: sha ?? undefined
|
|
7811
|
+
};
|
|
7812
|
+
}
|
|
7813
|
+
async function getPackageInfo(cwd) {
|
|
7814
|
+
try {
|
|
7815
|
+
const result = await exec("git", [
|
|
7816
|
+
"-C",
|
|
7817
|
+
cwd,
|
|
7818
|
+
"rev-parse",
|
|
7819
|
+
"--show-toplevel"
|
|
7820
|
+
]);
|
|
7821
|
+
const root = result.exitCode === 0 ? result.stdout.trim() : cwd;
|
|
7822
|
+
return getNodePackage(root) ?? getPythonPackage(root) ?? getRustPackage(root) ?? getGoModule(root);
|
|
7823
|
+
} catch {
|
|
7824
|
+
return null;
|
|
7825
|
+
}
|
|
7826
|
+
}
|
|
7827
|
+
function getNodePackage(root) {
|
|
7828
|
+
try {
|
|
7829
|
+
const filePath = join6(root, "package.json");
|
|
7830
|
+
if (!existsSync4(filePath))
|
|
7831
|
+
return null;
|
|
7832
|
+
const pkg = JSON.parse(readFileSync3(filePath, "utf-8"));
|
|
7833
|
+
return pkg.name ? { name: pkg.name, type: "package.json" } : null;
|
|
7834
|
+
} catch {
|
|
7835
|
+
return null;
|
|
7836
|
+
}
|
|
7837
|
+
}
|
|
7838
|
+
function getPythonPackage(root) {
|
|
7839
|
+
try {
|
|
7840
|
+
const filePath = join6(root, "pyproject.toml");
|
|
7841
|
+
if (!existsSync4(filePath))
|
|
7842
|
+
return null;
|
|
7843
|
+
const content = readFileSync3(filePath, "utf-8");
|
|
7844
|
+
const name = content.match(/^\s*name\s*=\s*"([^"]+)"/m)?.[1];
|
|
7845
|
+
return name ? { name, type: "pyproject.toml" } : null;
|
|
7846
|
+
} catch {
|
|
7847
|
+
return null;
|
|
7848
|
+
}
|
|
7849
|
+
}
|
|
7850
|
+
function getRustPackage(root) {
|
|
7851
|
+
try {
|
|
7852
|
+
const filePath = join6(root, "Cargo.toml");
|
|
7853
|
+
if (!existsSync4(filePath))
|
|
7854
|
+
return null;
|
|
7855
|
+
const content = readFileSync3(filePath, "utf-8");
|
|
7856
|
+
const name = content.match(/^\s*name\s*=\s*"([^"]+)"/m)?.[1];
|
|
7857
|
+
return name ? { name, type: "Cargo.toml" } : null;
|
|
7858
|
+
} catch {
|
|
7859
|
+
return null;
|
|
7860
|
+
}
|
|
7861
|
+
}
|
|
7862
|
+
function getGoModule(root) {
|
|
7863
|
+
try {
|
|
7864
|
+
const filePath = join6(root, "go.mod");
|
|
7865
|
+
if (!existsSync4(filePath))
|
|
7866
|
+
return null;
|
|
7867
|
+
const content = readFileSync3(filePath, "utf-8");
|
|
7868
|
+
const name = content.match(/^module\s+(\S+)/m)?.[1];
|
|
7869
|
+
return name ? { name, type: "go.mod" } : null;
|
|
7870
|
+
} catch {
|
|
7871
|
+
return null;
|
|
7872
|
+
}
|
|
7873
|
+
}
|
|
7874
|
+
async function getGitRemoteUrl(cwd) {
|
|
7875
|
+
try {
|
|
7876
|
+
const result = await exec("git", [
|
|
7877
|
+
"-C",
|
|
7878
|
+
cwd,
|
|
7879
|
+
"remote",
|
|
7880
|
+
"get-url",
|
|
7881
|
+
"origin"
|
|
7882
|
+
]);
|
|
7883
|
+
if (result.exitCode !== 0)
|
|
7884
|
+
return null;
|
|
7885
|
+
return result.stdout.trim() || null;
|
|
7886
|
+
} catch {
|
|
7887
|
+
return null;
|
|
7888
|
+
}
|
|
7889
|
+
}
|
|
7890
|
+
async function getGitBranch(cwd) {
|
|
7891
|
+
try {
|
|
7892
|
+
const result = await exec("git", [
|
|
7893
|
+
"-C",
|
|
7894
|
+
cwd,
|
|
7895
|
+
"rev-parse",
|
|
7896
|
+
"--abbrev-ref",
|
|
7897
|
+
"HEAD"
|
|
7898
|
+
]);
|
|
7899
|
+
if (result.exitCode !== 0)
|
|
7900
|
+
return null;
|
|
7901
|
+
return result.stdout.trim();
|
|
7902
|
+
} catch {
|
|
7903
|
+
return null;
|
|
7904
|
+
}
|
|
7905
|
+
}
|
|
7906
|
+
async function getGitSha(cwd) {
|
|
7907
|
+
try {
|
|
7908
|
+
const result = await exec("git", ["-C", cwd, "rev-parse", "HEAD"]);
|
|
7909
|
+
if (result.exitCode !== 0)
|
|
7910
|
+
return null;
|
|
7911
|
+
return result.stdout.trim();
|
|
7912
|
+
} catch {
|
|
7913
|
+
return null;
|
|
7914
|
+
}
|
|
7915
|
+
}
|
|
7916
|
+
|
|
7917
|
+
// src/lib/remote-cache.ts
|
|
7918
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "node:fs";
|
|
7919
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
7920
|
+
import { homedir as homedir5 } from "node:os";
|
|
7921
|
+
import { dirname as dirname3, join as join7 } from "node:path";
|
|
7922
|
+
var CACHE_PATH = join7(homedir5(), ".rudel", "remote-cache.json");
|
|
7923
|
+
async function getRemoteCache() {
|
|
7924
|
+
try {
|
|
7925
|
+
if (!existsSync5(CACHE_PATH))
|
|
7926
|
+
return {};
|
|
7927
|
+
return JSON.parse(readFileSync4(CACHE_PATH, "utf-8"));
|
|
7928
|
+
} catch {
|
|
7929
|
+
return {};
|
|
7930
|
+
}
|
|
7931
|
+
}
|
|
7932
|
+
function getCachedRemote(cache, encodedDir) {
|
|
7933
|
+
return cache[encodedDir] ?? null;
|
|
7934
|
+
}
|
|
7935
|
+
function cacheRemote(cache, encodedDir, normalizedRemote) {
|
|
7936
|
+
cache[encodedDir] = normalizedRemote;
|
|
7937
|
+
}
|
|
7938
|
+
async function cacheRemotes(cache) {
|
|
7939
|
+
try {
|
|
7940
|
+
await mkdir(dirname3(CACHE_PATH), { recursive: true });
|
|
7941
|
+
await writeFile(CACHE_PATH, JSON.stringify(cache));
|
|
7942
|
+
} catch {}
|
|
7943
|
+
}
|
|
7944
|
+
|
|
7945
|
+
// src/lib/project-grouping.ts
|
|
7946
|
+
async function scanAndGroupProjects(cwd = process.cwd()) {
|
|
7783
7947
|
const adapters2 = getAvailableAdapters();
|
|
7784
|
-
const
|
|
7948
|
+
const projects = [];
|
|
7785
7949
|
for (const adapter of adapters2) {
|
|
7786
|
-
const
|
|
7787
|
-
|
|
7950
|
+
const scanned = await adapter.scanAllSessions();
|
|
7951
|
+
projects.push(...scanned);
|
|
7952
|
+
}
|
|
7953
|
+
const groups = await groupProjectsByRemote(projects, cwd);
|
|
7954
|
+
return { projects, groups };
|
|
7955
|
+
}
|
|
7956
|
+
function extractDisplayName(normalized) {
|
|
7957
|
+
const parts = normalized.split("/");
|
|
7958
|
+
if (parts.length >= 3) {
|
|
7959
|
+
return parts.slice(1).join("/");
|
|
7960
|
+
}
|
|
7961
|
+
return normalized;
|
|
7962
|
+
}
|
|
7963
|
+
function encodeProjectPath2(projectPath) {
|
|
7964
|
+
return projectPath.replace(/\//g, "-");
|
|
7965
|
+
}
|
|
7966
|
+
async function groupProjectsByRemote(projects, cwd) {
|
|
7967
|
+
const cache = await getRemoteCache();
|
|
7968
|
+
let cacheUpdated = false;
|
|
7969
|
+
const remotes = await Promise.all(projects.map((p) => getGitRemoteUrl(p.projectPath)));
|
|
7970
|
+
const grouped = new Map;
|
|
7971
|
+
const ungrouped = [];
|
|
7972
|
+
for (let i = 0;i < projects.length; i++) {
|
|
7973
|
+
const project = projects[i];
|
|
7974
|
+
const remote = remotes[i];
|
|
7975
|
+
if (remote) {
|
|
7976
|
+
const normalized = normalizeRemoteUrl(remote);
|
|
7977
|
+
const encodedDir = encodeProjectPath2(project.projectPath);
|
|
7978
|
+
if (getCachedRemote(cache, encodedDir) !== normalized) {
|
|
7979
|
+
cacheRemote(cache, encodedDir, normalized);
|
|
7980
|
+
cacheUpdated = true;
|
|
7981
|
+
}
|
|
7982
|
+
const existing = grouped.get(normalized);
|
|
7983
|
+
if (existing) {
|
|
7984
|
+
existing.projects.push(project);
|
|
7985
|
+
} else {
|
|
7986
|
+
grouped.set(normalized, { remote: normalized, projects: [project] });
|
|
7987
|
+
}
|
|
7988
|
+
} else {
|
|
7989
|
+
const encodedDir = encodeProjectPath2(project.projectPath);
|
|
7990
|
+
const cached = getCachedRemote(cache, encodedDir);
|
|
7991
|
+
if (cached) {
|
|
7992
|
+
const existing = grouped.get(cached);
|
|
7993
|
+
if (existing) {
|
|
7994
|
+
existing.projects.push(project);
|
|
7995
|
+
} else {
|
|
7996
|
+
grouped.set(cached, { remote: cached, projects: [project] });
|
|
7997
|
+
}
|
|
7998
|
+
} else {
|
|
7999
|
+
ungrouped.push(project);
|
|
8000
|
+
}
|
|
8001
|
+
}
|
|
8002
|
+
}
|
|
8003
|
+
const groups = [];
|
|
8004
|
+
for (const [, entry] of grouped) {
|
|
8005
|
+
const containsCwd = entry.projects.some((p) => cwd === p.projectPath || cwd.startsWith(`${p.projectPath}/`));
|
|
8006
|
+
groups.push({
|
|
8007
|
+
displayName: extractDisplayName(entry.remote),
|
|
8008
|
+
gitRemote: entry.remote,
|
|
8009
|
+
projects: entry.projects,
|
|
8010
|
+
totalSessions: entry.projects.reduce((s, p) => s + p.sessionCount, 0),
|
|
8011
|
+
containsCwd
|
|
8012
|
+
});
|
|
8013
|
+
}
|
|
8014
|
+
const homeSegments = homedir6().split("/").length;
|
|
8015
|
+
const remainingUngrouped = [];
|
|
8016
|
+
for (const project of ungrouped) {
|
|
8017
|
+
const match = findBestGroupByPath(project, groups, homeSegments);
|
|
8018
|
+
if (match) {
|
|
8019
|
+
match.projects.push(project);
|
|
8020
|
+
match.totalSessions += project.sessionCount;
|
|
8021
|
+
if (cwd === project.projectPath || cwd.startsWith(`${project.projectPath}/`)) {
|
|
8022
|
+
match.containsCwd = true;
|
|
8023
|
+
}
|
|
8024
|
+
} else {
|
|
8025
|
+
remainingUngrouped.push(project);
|
|
8026
|
+
}
|
|
8027
|
+
}
|
|
8028
|
+
for (const project of remainingUngrouped) {
|
|
8029
|
+
const containsCwd = cwd === project.projectPath || cwd.startsWith(`${project.projectPath}/`);
|
|
8030
|
+
groups.push({
|
|
8031
|
+
displayName: project.displayPath,
|
|
8032
|
+
gitRemote: null,
|
|
8033
|
+
projects: [project],
|
|
8034
|
+
totalSessions: project.sessionCount,
|
|
8035
|
+
containsCwd
|
|
8036
|
+
});
|
|
8037
|
+
}
|
|
8038
|
+
groups.sort((a, b) => {
|
|
8039
|
+
if (a.containsCwd !== b.containsCwd)
|
|
8040
|
+
return a.containsCwd ? -1 : 1;
|
|
8041
|
+
const aHasRemote = a.gitRemote !== null;
|
|
8042
|
+
const bHasRemote = b.gitRemote !== null;
|
|
8043
|
+
if (aHasRemote !== bHasRemote)
|
|
8044
|
+
return aHasRemote ? -1 : 1;
|
|
8045
|
+
return a.displayName.localeCompare(b.displayName);
|
|
8046
|
+
});
|
|
8047
|
+
if (cacheUpdated) {
|
|
8048
|
+
cacheRemotes(cache);
|
|
8049
|
+
}
|
|
8050
|
+
return groups;
|
|
8051
|
+
}
|
|
8052
|
+
function commonPrefixLength(a, b) {
|
|
8053
|
+
const partsA = a.split("/");
|
|
8054
|
+
const partsB = b.split("/");
|
|
8055
|
+
let count = 0;
|
|
8056
|
+
for (let i = 0;i < Math.min(partsA.length, partsB.length); i++) {
|
|
8057
|
+
if (partsA[i] === partsB[i])
|
|
8058
|
+
count++;
|
|
8059
|
+
else
|
|
8060
|
+
break;
|
|
8061
|
+
}
|
|
8062
|
+
return count;
|
|
8063
|
+
}
|
|
8064
|
+
function findBestGroupByPath(project, groups, homeSegments) {
|
|
8065
|
+
let bestGroup = null;
|
|
8066
|
+
let bestLen = 0;
|
|
8067
|
+
let secondBestLen = 0;
|
|
8068
|
+
for (const group2 of groups) {
|
|
8069
|
+
if (!group2.gitRemote)
|
|
8070
|
+
continue;
|
|
8071
|
+
let groupBest = 0;
|
|
8072
|
+
for (const p of group2.projects) {
|
|
8073
|
+
groupBest = Math.max(groupBest, commonPrefixLength(project.projectPath, p.projectPath));
|
|
8074
|
+
}
|
|
8075
|
+
if (groupBest > bestLen) {
|
|
8076
|
+
secondBestLen = bestLen;
|
|
8077
|
+
bestLen = groupBest;
|
|
8078
|
+
bestGroup = group2;
|
|
8079
|
+
} else if (groupBest > secondBestLen) {
|
|
8080
|
+
secondBestLen = groupBest;
|
|
8081
|
+
}
|
|
7788
8082
|
}
|
|
8083
|
+
if (bestGroup && bestLen > homeSegments && bestLen > secondBestLen) {
|
|
8084
|
+
return bestGroup;
|
|
8085
|
+
}
|
|
8086
|
+
return null;
|
|
8087
|
+
}
|
|
8088
|
+
|
|
8089
|
+
// src/commands/dev/list-sessions.ts
|
|
8090
|
+
async function runListSessions() {
|
|
8091
|
+
const { projects: allProjects, groups } = await scanAndGroupProjects();
|
|
7789
8092
|
if (allProjects.length === 0) {
|
|
7790
8093
|
console.log("No projects with sessions found.");
|
|
7791
8094
|
return;
|
|
7792
8095
|
}
|
|
7793
|
-
const cwd = process.cwd();
|
|
7794
|
-
const grouped = groupProjectsForCwd(allProjects, cwd);
|
|
7795
8096
|
const lines = [];
|
|
7796
|
-
for (const
|
|
7797
|
-
const
|
|
7798
|
-
|
|
7799
|
-
|
|
7800
|
-
|
|
7801
|
-
|
|
7802
|
-
|
|
7803
|
-
|
|
7804
|
-
|
|
7805
|
-
|
|
7806
|
-
const
|
|
7807
|
-
|
|
8097
|
+
for (const group2 of groups) {
|
|
8098
|
+
const isCurrent = group2.containsCwd ? " [current]" : "";
|
|
8099
|
+
if (group2.projects.length === 1) {
|
|
8100
|
+
const proj = group2.projects[0];
|
|
8101
|
+
const name = getAdapter(proj.source).name;
|
|
8102
|
+
lines.push(`[${name}] ${proj.displayPath} (${proj.sessionCount} sessions)${isCurrent}`);
|
|
8103
|
+
continue;
|
|
8104
|
+
}
|
|
8105
|
+
const totalSessions2 = group2.projects.reduce((s, p) => s + p.sessionCount, 0);
|
|
8106
|
+
lines.push(`${group2.displayName} (${group2.projects.length} projects, ${totalSessions2} sessions)${isCurrent}`);
|
|
8107
|
+
for (const proj of group2.projects) {
|
|
8108
|
+
const name = getAdapter(proj.source).name;
|
|
8109
|
+
const cwdMarker = proj.projectPath === process.cwd() ? " [cwd]" : "";
|
|
8110
|
+
lines.push(` [${name}] ${proj.displayPath} (${proj.sessionCount} sessions)${cwdMarker}`);
|
|
8111
|
+
}
|
|
7808
8112
|
}
|
|
7809
8113
|
const totalSessions = allProjects.reduce((s, p) => s + p.sessionCount, 0);
|
|
7810
8114
|
console.log(`${allProjects.length} projects, ${totalSessions} sessions
|
|
@@ -7834,10 +8138,10 @@ var devRouteMap = buildRouteMap({
|
|
|
7834
8138
|
// ../../node_modules/.bun/@clack+core@1.0.1/node_modules/@clack/core/dist/index.mjs
|
|
7835
8139
|
var import_picocolors = __toESM(require_picocolors(), 1);
|
|
7836
8140
|
var import_sisteransi = __toESM(require_src(), 1);
|
|
7837
|
-
import { stdout as R, stdin as q } from "process";
|
|
7838
|
-
import * as k from "readline";
|
|
7839
|
-
import ot from "readline";
|
|
7840
|
-
import { ReadStream as J } from "tty";
|
|
8141
|
+
import { stdout as R, stdin as q } from "node:process";
|
|
8142
|
+
import * as k from "node:readline";
|
|
8143
|
+
import ot from "node:readline";
|
|
8144
|
+
import { ReadStream as J } from "node:tty";
|
|
7841
8145
|
function B(t, e, s) {
|
|
7842
8146
|
if (!s.some((u) => !u.disabled))
|
|
7843
8147
|
return t;
|
|
@@ -7921,7 +8225,7 @@ var X = (t, e = {}, s = {}) => {
|
|
|
7921
8225
|
var pt = { limit: 1 / 0, ellipsis: "", ellipsisWidth: 0 };
|
|
7922
8226
|
var S = (t, e = {}) => X(t, pt, e).width;
|
|
7923
8227
|
var W = "\x1B";
|
|
7924
|
-
var Z = "
|
|
8228
|
+
var Z = "";
|
|
7925
8229
|
var Ft = 39;
|
|
7926
8230
|
var j = "\x07";
|
|
7927
8231
|
var Q = "[";
|
|
@@ -8242,7 +8546,7 @@ class Vt extends x {
|
|
|
8242
8546
|
if (!this.userInput)
|
|
8243
8547
|
return import_picocolors.default.inverse(import_picocolors.default.hidden("_"));
|
|
8244
8548
|
if (this._cursor >= this.userInput.length)
|
|
8245
|
-
return `${this.userInput}
|
|
8549
|
+
return `${this.userInput}█`;
|
|
8246
8550
|
const e = this.userInput.slice(0, this._cursor), [s, ...i] = this.userInput.slice(this._cursor);
|
|
8247
8551
|
return `${e}${import_picocolors.default.inverse(s)}${i.join("")}`;
|
|
8248
8552
|
}
|
|
@@ -8430,7 +8734,7 @@ class Wt extends x {
|
|
|
8430
8734
|
|
|
8431
8735
|
// ../../node_modules/.bun/@clack+prompts@1.0.1/node_modules/@clack/prompts/dist/index.mjs
|
|
8432
8736
|
var import_picocolors2 = __toESM(require_picocolors(), 1);
|
|
8433
|
-
import N2 from "process";
|
|
8737
|
+
import N2 from "node:process";
|
|
8434
8738
|
var import_sisteransi2 = __toESM(require_src(), 1);
|
|
8435
8739
|
function me() {
|
|
8436
8740
|
return N2.platform !== "win32" ? N2.env.TERM !== "linux" : !!N2.env.CI || !!N2.env.WT_SESSION || !!N2.env.TERMINUS_SUBLIME || N2.env.ConEmuTask === "{cmd::Cmder}" || N2.env.TERM_PROGRAM === "Terminus-Sublime" || N2.env.TERM_PROGRAM === "vscode" || N2.env.TERM === "xterm-256color" || N2.env.TERM === "alacritty" || N2.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
|
|
@@ -8438,31 +8742,31 @@ function me() {
|
|
|
8438
8742
|
var et2 = me();
|
|
8439
8743
|
var ct2 = () => process.env.CI === "true";
|
|
8440
8744
|
var C = (t, r) => et2 ? t : r;
|
|
8441
|
-
var Rt = C("
|
|
8442
|
-
var dt2 = C("
|
|
8443
|
-
var $t2 = C("
|
|
8444
|
-
var V = C("
|
|
8445
|
-
var ht2 = C("
|
|
8446
|
-
var d = C("
|
|
8447
|
-
var x2 = C("
|
|
8448
|
-
var Ot = C("
|
|
8449
|
-
var Pt = C("
|
|
8450
|
-
var Q2 = C("
|
|
8451
|
-
var H2 = C("
|
|
8452
|
-
var st2 = C("
|
|
8453
|
-
var U2 = C("
|
|
8454
|
-
var q2 = C("
|
|
8455
|
-
var Nt = C("
|
|
8456
|
-
var rt2 = C("
|
|
8457
|
-
var mt2 = C("
|
|
8458
|
-
var Wt2 = C("
|
|
8459
|
-
var pt2 = C("
|
|
8460
|
-
var gt2 = C("
|
|
8461
|
-
var Lt2 = C("
|
|
8462
|
-
var ft2 = C("
|
|
8463
|
-
var Ft2 = C("
|
|
8464
|
-
var yt2 = C("
|
|
8465
|
-
var Et2 = C("
|
|
8745
|
+
var Rt = C("◆", "*");
|
|
8746
|
+
var dt2 = C("■", "x");
|
|
8747
|
+
var $t2 = C("▲", "x");
|
|
8748
|
+
var V = C("◇", "o");
|
|
8749
|
+
var ht2 = C("┌", "T");
|
|
8750
|
+
var d = C("│", "|");
|
|
8751
|
+
var x2 = C("└", "—");
|
|
8752
|
+
var Ot = C("┐", "T");
|
|
8753
|
+
var Pt = C("┘", "—");
|
|
8754
|
+
var Q2 = C("●", ">");
|
|
8755
|
+
var H2 = C("○", " ");
|
|
8756
|
+
var st2 = C("◻", "[•]");
|
|
8757
|
+
var U2 = C("◼", "[+]");
|
|
8758
|
+
var q2 = C("◻", "[ ]");
|
|
8759
|
+
var Nt = C("▪", "•");
|
|
8760
|
+
var rt2 = C("─", "-");
|
|
8761
|
+
var mt2 = C("╮", "+");
|
|
8762
|
+
var Wt2 = C("├", "+");
|
|
8763
|
+
var pt2 = C("╯", "+");
|
|
8764
|
+
var gt2 = C("╰", "+");
|
|
8765
|
+
var Lt2 = C("╭", "+");
|
|
8766
|
+
var ft2 = C("●", "•");
|
|
8767
|
+
var Ft2 = C("◆", "*");
|
|
8768
|
+
var yt2 = C("▲", "!");
|
|
8769
|
+
var Et2 = C("■", "x");
|
|
8466
8770
|
var W2 = (t) => {
|
|
8467
8771
|
switch (t) {
|
|
8468
8772
|
case "initial":
|
|
@@ -8566,7 +8870,7 @@ var jt = (t, r = {}, s = {}) => {
|
|
|
8566
8870
|
var Ee = { limit: 1 / 0, ellipsis: "", ellipsisWidth: 0 };
|
|
8567
8871
|
var M2 = (t, r = {}) => jt(t, Ee, r).width;
|
|
8568
8872
|
var ot2 = "\x1B";
|
|
8569
|
-
var Gt = "
|
|
8873
|
+
var Gt = "";
|
|
8570
8874
|
var ve = 39;
|
|
8571
8875
|
var Ct2 = "\x07";
|
|
8572
8876
|
var kt2 = "[";
|
|
@@ -8866,7 +9170,7 @@ ${import_picocolors2.default.cyan(x2)}
|
|
|
8866
9170
|
} }).prompt();
|
|
8867
9171
|
};
|
|
8868
9172
|
var Ke = import_picocolors2.default.magenta;
|
|
8869
|
-
var bt2 = ({ indicator: t = "dots", onCancel: r, output: s = process.stdout, cancelMessage: i, errorMessage: a, frames: o = et2 ? ["
|
|
9173
|
+
var bt2 = ({ indicator: t = "dots", onCancel: r, output: s = process.stdout, cancelMessage: i, errorMessage: a, frames: o = et2 ? ["◒", "◐", "◓", "◑"] : ["•", "o", "O", "0"], delay: u = et2 ? 80 : 120, signal: l, ...n } = {}) => {
|
|
8870
9174
|
const c = ct2();
|
|
8871
9175
|
let g, F, p = false, E = false, $ = "", m, h = performance.now();
|
|
8872
9176
|
const y2 = rt(s), f = n?.styleFrame ?? Ke, v = (b) => {
|
|
@@ -8923,7 +9227,7 @@ var bt2 = ({ indicator: t = "dots", onCancel: r, output: s = process.stdout, can
|
|
|
8923
9227
|
return E;
|
|
8924
9228
|
} };
|
|
8925
9229
|
};
|
|
8926
|
-
var zt = { light: C("
|
|
9230
|
+
var zt = { light: C("─", "-"), heavy: C("━", "="), block: C("█", "#") };
|
|
8927
9231
|
function qe({ style: t = "heavy", max: r = 100, size: s = 40, ...i } = {}) {
|
|
8928
9232
|
const a = bt2(i);
|
|
8929
9233
|
let o = 0, u = "";
|
|
@@ -10499,23 +10803,23 @@ function createApiClient(config) {
|
|
|
10499
10803
|
|
|
10500
10804
|
// src/lib/credentials.ts
|
|
10501
10805
|
import {
|
|
10502
|
-
existsSync as
|
|
10806
|
+
existsSync as existsSync6,
|
|
10503
10807
|
mkdirSync as mkdirSync2,
|
|
10504
|
-
readFileSync as
|
|
10808
|
+
readFileSync as readFileSync5,
|
|
10505
10809
|
rmSync,
|
|
10506
10810
|
writeFileSync as writeFileSync3
|
|
10507
|
-
} from "fs";
|
|
10508
|
-
import { homedir as
|
|
10509
|
-
import { join as
|
|
10811
|
+
} from "node:fs";
|
|
10812
|
+
import { homedir as homedir7 } from "node:os";
|
|
10813
|
+
import { join as join8 } from "node:path";
|
|
10510
10814
|
function getConfigDir() {
|
|
10511
|
-
return process.env.RUDEL_CONFIG_DIR ??
|
|
10815
|
+
return process.env.RUDEL_CONFIG_DIR ?? join8(homedir7(), ".rudel");
|
|
10512
10816
|
}
|
|
10513
10817
|
function getCredentialsPath() {
|
|
10514
|
-
return
|
|
10818
|
+
return join8(getConfigDir(), "credentials.json");
|
|
10515
10819
|
}
|
|
10516
10820
|
function saveCredentials(token, apiBaseUrl) {
|
|
10517
10821
|
const dir = getConfigDir();
|
|
10518
|
-
if (!
|
|
10822
|
+
if (!existsSync6(dir)) {
|
|
10519
10823
|
mkdirSync2(dir, { recursive: true, mode: 448 });
|
|
10520
10824
|
}
|
|
10521
10825
|
const data = { token, apiBaseUrl };
|
|
@@ -10525,14 +10829,14 @@ function saveCredentials(token, apiBaseUrl) {
|
|
|
10525
10829
|
}
|
|
10526
10830
|
function loadCredentials() {
|
|
10527
10831
|
const path = getCredentialsPath();
|
|
10528
|
-
if (!
|
|
10832
|
+
if (!existsSync6(path))
|
|
10529
10833
|
return null;
|
|
10530
|
-
const content =
|
|
10834
|
+
const content = readFileSync5(path, "utf-8");
|
|
10531
10835
|
return JSON.parse(content);
|
|
10532
10836
|
}
|
|
10533
10837
|
function clearCredentials() {
|
|
10534
10838
|
const path = getCredentialsPath();
|
|
10535
|
-
if (
|
|
10839
|
+
if (existsSync6(path)) {
|
|
10536
10840
|
rmSync(path);
|
|
10537
10841
|
}
|
|
10538
10842
|
}
|
|
@@ -10694,8 +10998,10 @@ async function pMap(iterable, mapper, {
|
|
|
10694
10998
|
var pMapSkip = Symbol("skip");
|
|
10695
10999
|
|
|
10696
11000
|
// src/lib/failed-uploads.ts
|
|
10697
|
-
import {
|
|
10698
|
-
import {
|
|
11001
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6 } from "node:fs";
|
|
11002
|
+
import { mkdir as mkdir2, writeFile as writeFile2 } from "node:fs/promises";
|
|
11003
|
+
import { homedir as homedir8 } from "node:os";
|
|
11004
|
+
import { dirname as dirname4, join as join9 } from "node:path";
|
|
10699
11005
|
|
|
10700
11006
|
// ../../node_modules/.bun/@orpc+contract@1.13.6/node_modules/@orpc/contract/dist/shared/contract.D_dZrO__.mjs
|
|
10701
11007
|
function mergeErrorMap(errorMap1, errorMap2) {
|
|
@@ -11393,7 +11699,7 @@ var contract = {
|
|
|
11393
11699
|
};
|
|
11394
11700
|
|
|
11395
11701
|
// src/lib/failed-uploads.ts
|
|
11396
|
-
var FAILED_UPLOADS_PATH =
|
|
11702
|
+
var FAILED_UPLOADS_PATH = join9(homedir8(), ".rudel", "failed-uploads.json");
|
|
11397
11703
|
function normalizeSource(raw) {
|
|
11398
11704
|
if (typeof raw !== "string")
|
|
11399
11705
|
return;
|
|
@@ -11403,10 +11709,9 @@ function normalizeSource(raw) {
|
|
|
11403
11709
|
}
|
|
11404
11710
|
async function loadFailedUploads() {
|
|
11405
11711
|
try {
|
|
11406
|
-
|
|
11407
|
-
if (!await file.exists())
|
|
11712
|
+
if (!existsSync7(FAILED_UPLOADS_PATH))
|
|
11408
11713
|
return [];
|
|
11409
|
-
const data =
|
|
11714
|
+
const data = JSON.parse(readFileSync6(FAILED_UPLOADS_PATH, "utf-8"));
|
|
11410
11715
|
return data.failures.map((f) => ({
|
|
11411
11716
|
...f,
|
|
11412
11717
|
source: normalizeSource(f.source)
|
|
@@ -11417,11 +11722,9 @@ async function loadFailedUploads() {
|
|
|
11417
11722
|
}
|
|
11418
11723
|
async function saveFailedUploads(failures) {
|
|
11419
11724
|
try {
|
|
11420
|
-
|
|
11421
|
-
const { dirname: dirname3 } = await import("path");
|
|
11422
|
-
await mkdir(dirname3(FAILED_UPLOADS_PATH), { recursive: true });
|
|
11725
|
+
await mkdir2(dirname4(FAILED_UPLOADS_PATH), { recursive: true });
|
|
11423
11726
|
const data = { failures };
|
|
11424
|
-
await
|
|
11727
|
+
await writeFile2(FAILED_UPLOADS_PATH, JSON.stringify(data, null, 2));
|
|
11425
11728
|
} catch {}
|
|
11426
11729
|
}
|
|
11427
11730
|
async function recordFailedUpload(failure) {
|
|
@@ -11541,136 +11844,26 @@ function renderBatchSummary(summary, options) {
|
|
|
11541
11844
|
}
|
|
11542
11845
|
}
|
|
11543
11846
|
|
|
11544
|
-
// src/lib/git-info.ts
|
|
11545
|
-
import { join as join8 } from "path";
|
|
11546
|
-
var {$ } = globalThis.Bun;
|
|
11547
|
-
function normalizeRemoteUrl(url) {
|
|
11548
|
-
return url.replace(/^(https?:\/\/|git@|ssh:\/\/)/, "").replace(/:/, "/").replace(/\.git$/, "");
|
|
11549
|
-
}
|
|
11550
|
-
async function getGitInfo(cwd) {
|
|
11551
|
-
const [remoteUrl, branch, sha, packageInfo] = await Promise.all([
|
|
11552
|
-
getGitRemoteUrl(cwd),
|
|
11553
|
-
getGitBranch(cwd),
|
|
11554
|
-
getGitSha(cwd),
|
|
11555
|
-
getPackageInfo(cwd)
|
|
11556
|
-
]);
|
|
11557
|
-
const gitRemote = remoteUrl ? normalizeRemoteUrl(remoteUrl) : undefined;
|
|
11558
|
-
return {
|
|
11559
|
-
gitRemote,
|
|
11560
|
-
packageName: packageInfo?.name,
|
|
11561
|
-
packageType: packageInfo?.type,
|
|
11562
|
-
branch: branch ?? undefined,
|
|
11563
|
-
sha: sha ?? undefined
|
|
11564
|
-
};
|
|
11565
|
-
}
|
|
11566
|
-
async function getPackageInfo(cwd) {
|
|
11567
|
-
try {
|
|
11568
|
-
const gitRootResult = await $`git -C ${cwd} rev-parse --show-toplevel`.quiet();
|
|
11569
|
-
const root = gitRootResult.exitCode === 0 ? gitRootResult.text().trim() : cwd;
|
|
11570
|
-
return await getNodePackage(root) ?? await getPythonPackage(root) ?? await getRustPackage(root) ?? await getGoModule(root);
|
|
11571
|
-
} catch {
|
|
11572
|
-
return null;
|
|
11573
|
-
}
|
|
11574
|
-
}
|
|
11575
|
-
async function getNodePackage(root) {
|
|
11576
|
-
try {
|
|
11577
|
-
const file = Bun.file(join8(root, "package.json"));
|
|
11578
|
-
if (!await file.exists())
|
|
11579
|
-
return null;
|
|
11580
|
-
const pkg = await file.json();
|
|
11581
|
-
return pkg.name ? { name: pkg.name, type: "package.json" } : null;
|
|
11582
|
-
} catch {
|
|
11583
|
-
return null;
|
|
11584
|
-
}
|
|
11585
|
-
}
|
|
11586
|
-
async function getPythonPackage(root) {
|
|
11587
|
-
try {
|
|
11588
|
-
const file = Bun.file(join8(root, "pyproject.toml"));
|
|
11589
|
-
if (!await file.exists())
|
|
11590
|
-
return null;
|
|
11591
|
-
const content = await file.text();
|
|
11592
|
-
const name = content.match(/^\s*name\s*=\s*"([^"]+)"/m)?.[1];
|
|
11593
|
-
return name ? { name, type: "pyproject.toml" } : null;
|
|
11594
|
-
} catch {
|
|
11595
|
-
return null;
|
|
11596
|
-
}
|
|
11597
|
-
}
|
|
11598
|
-
async function getRustPackage(root) {
|
|
11599
|
-
try {
|
|
11600
|
-
const file = Bun.file(join8(root, "Cargo.toml"));
|
|
11601
|
-
if (!await file.exists())
|
|
11602
|
-
return null;
|
|
11603
|
-
const content = await file.text();
|
|
11604
|
-
const name = content.match(/^\s*name\s*=\s*"([^"]+)"/m)?.[1];
|
|
11605
|
-
return name ? { name, type: "Cargo.toml" } : null;
|
|
11606
|
-
} catch {
|
|
11607
|
-
return null;
|
|
11608
|
-
}
|
|
11609
|
-
}
|
|
11610
|
-
async function getGoModule(root) {
|
|
11611
|
-
try {
|
|
11612
|
-
const file = Bun.file(join8(root, "go.mod"));
|
|
11613
|
-
if (!await file.exists())
|
|
11614
|
-
return null;
|
|
11615
|
-
const content = await file.text();
|
|
11616
|
-
const name = content.match(/^module\s+(\S+)/m)?.[1];
|
|
11617
|
-
return name ? { name, type: "go.mod" } : null;
|
|
11618
|
-
} catch {
|
|
11619
|
-
return null;
|
|
11620
|
-
}
|
|
11621
|
-
}
|
|
11622
|
-
async function getGitRemoteUrl(cwd) {
|
|
11623
|
-
try {
|
|
11624
|
-
const result = await $`git -C ${cwd} remote get-url origin`.quiet();
|
|
11625
|
-
if (result.exitCode !== 0)
|
|
11626
|
-
return null;
|
|
11627
|
-
return result.text().trim() || null;
|
|
11628
|
-
} catch {
|
|
11629
|
-
return null;
|
|
11630
|
-
}
|
|
11631
|
-
}
|
|
11632
|
-
async function getGitBranch(cwd) {
|
|
11633
|
-
try {
|
|
11634
|
-
const result = await $`git -C ${cwd} rev-parse --abbrev-ref HEAD`.quiet();
|
|
11635
|
-
if (result.exitCode !== 0)
|
|
11636
|
-
return null;
|
|
11637
|
-
return result.text().trim();
|
|
11638
|
-
} catch {
|
|
11639
|
-
return null;
|
|
11640
|
-
}
|
|
11641
|
-
}
|
|
11642
|
-
async function getGitSha(cwd) {
|
|
11643
|
-
try {
|
|
11644
|
-
const result = await $`git -C ${cwd} rev-parse HEAD`.quiet();
|
|
11645
|
-
if (result.exitCode !== 0)
|
|
11646
|
-
return null;
|
|
11647
|
-
return result.text().trim();
|
|
11648
|
-
} catch {
|
|
11649
|
-
return null;
|
|
11650
|
-
}
|
|
11651
|
-
}
|
|
11652
|
-
|
|
11653
11847
|
// src/lib/project-config.ts
|
|
11654
|
-
import { existsSync as
|
|
11655
|
-
import { homedir as
|
|
11656
|
-
import { join as
|
|
11657
|
-
var {$: $2 } = globalThis.Bun;
|
|
11848
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
|
|
11849
|
+
import { homedir as homedir9 } from "node:os";
|
|
11850
|
+
import { join as join10 } from "node:path";
|
|
11658
11851
|
function getConfigDir2() {
|
|
11659
|
-
return process.env.RUDEL_CONFIG_DIR ??
|
|
11852
|
+
return process.env.RUDEL_CONFIG_DIR ?? join10(homedir9(), ".rudel");
|
|
11660
11853
|
}
|
|
11661
11854
|
function getProjectsConfigPath() {
|
|
11662
|
-
return
|
|
11855
|
+
return join10(getConfigDir2(), "projects.json");
|
|
11663
11856
|
}
|
|
11664
11857
|
function loadProjectsConfig() {
|
|
11665
11858
|
const path = getProjectsConfigPath();
|
|
11666
|
-
if (!
|
|
11859
|
+
if (!existsSync8(path))
|
|
11667
11860
|
return { projects: {} };
|
|
11668
|
-
const content =
|
|
11861
|
+
const content = readFileSync7(path, "utf-8");
|
|
11669
11862
|
return JSON.parse(content);
|
|
11670
11863
|
}
|
|
11671
11864
|
function saveProjectsConfig(config) {
|
|
11672
11865
|
const dir = getConfigDir2();
|
|
11673
|
-
if (!
|
|
11866
|
+
if (!existsSync8(dir)) {
|
|
11674
11867
|
mkdirSync3(dir, { recursive: true, mode: 448 });
|
|
11675
11868
|
}
|
|
11676
11869
|
writeFileSync4(getProjectsConfigPath(), JSON.stringify(config, null, 2), {
|
|
@@ -11679,16 +11872,27 @@ function saveProjectsConfig(config) {
|
|
|
11679
11872
|
}
|
|
11680
11873
|
async function getProjectKey(cwd) {
|
|
11681
11874
|
try {
|
|
11682
|
-
const result = await
|
|
11875
|
+
const result = await exec("git", [
|
|
11876
|
+
"-C",
|
|
11877
|
+
cwd,
|
|
11878
|
+
"remote",
|
|
11879
|
+
"get-url",
|
|
11880
|
+
"origin"
|
|
11881
|
+
]);
|
|
11683
11882
|
if (result.exitCode === 0) {
|
|
11684
|
-
const url = result.
|
|
11883
|
+
const url = result.stdout.trim();
|
|
11685
11884
|
return url.replace(/^(https?:\/\/|git@|ssh:\/\/)/, "").replace(/:/, "/").replace(/\.git$/, "");
|
|
11686
11885
|
}
|
|
11687
11886
|
} catch {}
|
|
11688
11887
|
try {
|
|
11689
|
-
const result = await
|
|
11888
|
+
const result = await exec("git", [
|
|
11889
|
+
"-C",
|
|
11890
|
+
cwd,
|
|
11891
|
+
"rev-parse",
|
|
11892
|
+
"--show-toplevel"
|
|
11893
|
+
]);
|
|
11690
11894
|
if (result.exitCode === 0) {
|
|
11691
|
-
return result.
|
|
11895
|
+
return result.stdout.trim();
|
|
11692
11896
|
}
|
|
11693
11897
|
} catch {}
|
|
11694
11898
|
return cwd;
|
|
@@ -12896,7 +13100,7 @@ var exports_util_node = {};
|
|
|
12896
13100
|
__export(exports_util_node, {
|
|
12897
13101
|
inspect: () => inspect
|
|
12898
13102
|
});
|
|
12899
|
-
import util3 from "util";
|
|
13103
|
+
import util3 from "node:util";
|
|
12900
13104
|
function inspect(obj, options) {
|
|
12901
13105
|
return util3.inspect(obj, options);
|
|
12902
13106
|
}
|
|
@@ -13072,7 +13276,7 @@ function getTextFormatter(options = {}) {
|
|
|
13072
13276
|
else
|
|
13073
13277
|
return tsOption;
|
|
13074
13278
|
})();
|
|
13075
|
-
const categorySeparator = options.category ?? "
|
|
13279
|
+
const categorySeparator = options.category ?? "·";
|
|
13076
13280
|
const valueRenderer = options.value ? (v) => options.value(v, inspect2) : inspect2;
|
|
13077
13281
|
const levelRenderer = (() => {
|
|
13078
13282
|
const levelOption = options.level;
|
|
@@ -13292,7 +13496,7 @@ function defaultConsoleFormatter(record) {
|
|
|
13292
13496
|
const date = new Date(record.timestamp);
|
|
13293
13497
|
const time = `${date.getUTCHours().toString().padStart(2, "0")}:${date.getUTCMinutes().toString().padStart(2, "0")}:${date.getUTCSeconds().toString().padStart(2, "0")}.${date.getUTCMilliseconds().toString().padStart(3, "0")}`;
|
|
13294
13498
|
return [
|
|
13295
|
-
`%c${time} %c${levelAbbreviations[record.level]}%c %c${record.category.join("
|
|
13499
|
+
`%c${time} %c${levelAbbreviations[record.level]}%c %c${record.category.join("·")} %c${msg}`,
|
|
13296
13500
|
"color: gray;",
|
|
13297
13501
|
logLevelStyles[record.level],
|
|
13298
13502
|
"background-color: default;",
|
|
@@ -13503,8 +13707,8 @@ var ConfigError = class extends Error {
|
|
|
13503
13707
|
};
|
|
13504
13708
|
|
|
13505
13709
|
// src/logging.ts
|
|
13506
|
-
import { homedir as
|
|
13507
|
-
import { join as
|
|
13710
|
+
import { homedir as homedir10 } from "node:os";
|
|
13711
|
+
import { join as join12 } from "node:path";
|
|
13508
13712
|
|
|
13509
13713
|
// ../../node_modules/.bun/@logtape+file@2.0.4+2d5de51540e256d2/node_modules/@logtape/file/dist/filesink.base.js
|
|
13510
13714
|
var AdaptiveFlushStrategy = class {
|
|
@@ -13769,9 +13973,9 @@ function getBaseFileSink(path, options) {
|
|
|
13769
13973
|
}
|
|
13770
13974
|
|
|
13771
13975
|
// ../../node_modules/.bun/@logtape+file@2.0.4+2d5de51540e256d2/node_modules/@logtape/file/dist/filesink.node.js
|
|
13772
|
-
import fs from "fs";
|
|
13773
|
-
import { join as
|
|
13774
|
-
import { promisify } from "util";
|
|
13976
|
+
import fs from "node:fs";
|
|
13977
|
+
import { join as join11 } from "node:path";
|
|
13978
|
+
import { promisify } from "node:util";
|
|
13775
13979
|
var nodeDriver = {
|
|
13776
13980
|
openSync(path) {
|
|
13777
13981
|
return fs.openSync(path, "a");
|
|
@@ -13810,14 +14014,14 @@ var nodeTimeDriver = {
|
|
|
13810
14014
|
readdirSync: fs.readdirSync,
|
|
13811
14015
|
unlinkSync: fs.unlinkSync,
|
|
13812
14016
|
mkdirSync: fs.mkdirSync,
|
|
13813
|
-
joinPath:
|
|
14017
|
+
joinPath: join11
|
|
13814
14018
|
};
|
|
13815
14019
|
var nodeAsyncTimeDriver = {
|
|
13816
14020
|
...nodeAsyncDriver,
|
|
13817
14021
|
readdirSync: fs.readdirSync,
|
|
13818
14022
|
unlinkSync: fs.unlinkSync,
|
|
13819
14023
|
mkdirSync: fs.mkdirSync,
|
|
13820
|
-
joinPath:
|
|
14024
|
+
joinPath: join11
|
|
13821
14025
|
};
|
|
13822
14026
|
function getFileSink(path, options = {}) {
|
|
13823
14027
|
if (options.nonBlocking)
|
|
@@ -13832,11 +14036,11 @@ function getFileSink(path, options = {}) {
|
|
|
13832
14036
|
}
|
|
13833
14037
|
|
|
13834
14038
|
// src/logging.ts
|
|
13835
|
-
var LOG_DIR =
|
|
13836
|
-
var LOG_FILE =
|
|
14039
|
+
var LOG_DIR = join12(homedir10(), ".rudel", "logs");
|
|
14040
|
+
var LOG_FILE = join12(LOG_DIR, "hook-upload.log");
|
|
13837
14041
|
async function setupHookLogging() {
|
|
13838
|
-
const { mkdir } = await import("fs/promises");
|
|
13839
|
-
await
|
|
14042
|
+
const { mkdir: mkdir3 } = await import("node:fs/promises");
|
|
14043
|
+
await mkdir3(LOG_DIR, { recursive: true }).catch(() => {});
|
|
13840
14044
|
await configure({
|
|
13841
14045
|
sinks: {
|
|
13842
14046
|
file: getFileSink(LOG_FILE)
|
|
@@ -14019,7 +14223,11 @@ var hooksRouteMap = buildRouteMap({
|
|
|
14019
14223
|
});
|
|
14020
14224
|
|
|
14021
14225
|
// src/commands/login.ts
|
|
14022
|
-
import {
|
|
14226
|
+
import { spawn } from "node:child_process";
|
|
14227
|
+
import { randomBytes } from "node:crypto";
|
|
14228
|
+
import {
|
|
14229
|
+
createServer
|
|
14230
|
+
} from "node:http";
|
|
14023
14231
|
var DEFAULT_API_BASE = "https://app.rudel.ai";
|
|
14024
14232
|
var DEFAULT_WEB_URL = "https://app.rudel.ai";
|
|
14025
14233
|
var CALLBACK_TIMEOUT_MS = 120000;
|
|
@@ -14038,41 +14246,53 @@ async function runLogin(flags) {
|
|
|
14038
14246
|
resolveCallback = resolve2;
|
|
14039
14247
|
rejectCallback = reject;
|
|
14040
14248
|
});
|
|
14041
|
-
const server =
|
|
14042
|
-
|
|
14043
|
-
|
|
14044
|
-
|
|
14045
|
-
|
|
14046
|
-
|
|
14047
|
-
|
|
14048
|
-
|
|
14049
|
-
|
|
14050
|
-
|
|
14051
|
-
|
|
14052
|
-
|
|
14053
|
-
|
|
14054
|
-
|
|
14055
|
-
|
|
14056
|
-
|
|
14057
|
-
|
|
14058
|
-
}
|
|
14059
|
-
|
|
14060
|
-
return
|
|
14249
|
+
const server = createServer((req, res) => {
|
|
14250
|
+
const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
|
|
14251
|
+
if (url.pathname !== "/callback") {
|
|
14252
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
14253
|
+
res.end("Not found");
|
|
14254
|
+
return;
|
|
14255
|
+
}
|
|
14256
|
+
const receivedToken = url.searchParams.get("token");
|
|
14257
|
+
const receivedState = url.searchParams.get("state");
|
|
14258
|
+
if (receivedState !== state) {
|
|
14259
|
+
rejectCallback(new Error("State mismatch — possible CSRF attack"));
|
|
14260
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
14261
|
+
res.end("<html><body><h1>Login failed</h1><p>State mismatch. Please try again.</p></body></html>");
|
|
14262
|
+
return;
|
|
14263
|
+
}
|
|
14264
|
+
if (!receivedToken) {
|
|
14265
|
+
rejectCallback(new Error("No token received"));
|
|
14266
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
14267
|
+
res.end("<html><body><h1>Login failed</h1><p>No token received.</p></body></html>");
|
|
14268
|
+
return;
|
|
14061
14269
|
}
|
|
14270
|
+
resolveCallback(receivedToken);
|
|
14271
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
14272
|
+
res.end("<html><body><h1>Login successful!</h1><p>You can close this tab and return to the terminal.</p></body></html>");
|
|
14273
|
+
});
|
|
14274
|
+
await new Promise((resolve2) => {
|
|
14275
|
+
server.listen(0, "127.0.0.1", resolve2);
|
|
14062
14276
|
});
|
|
14063
|
-
const
|
|
14277
|
+
const port = server.address().port;
|
|
14278
|
+
const callbackUrl = `http://127.0.0.1:${port}/callback`;
|
|
14064
14279
|
const loginUrl = `${flags.webUrl}?cli_callback=${encodeURIComponent(callbackUrl)}&state=${state}`;
|
|
14065
14280
|
R2.info(`If the browser doesn't open, visit:
|
|
14066
14281
|
${loginUrl}`);
|
|
14067
14282
|
if (!flags.noBrowser) {
|
|
14068
14283
|
if (process.platform === "win32") {
|
|
14069
|
-
|
|
14070
|
-
|
|
14071
|
-
|
|
14284
|
+
const child = spawn("cmd", ["/c", "start", "", loginUrl], {
|
|
14285
|
+
detached: true,
|
|
14286
|
+
stdio: "ignore"
|
|
14072
14287
|
});
|
|
14288
|
+
child.unref();
|
|
14073
14289
|
} else {
|
|
14074
14290
|
const opener = process.platform === "darwin" ? "open" : "xdg-open";
|
|
14075
|
-
|
|
14291
|
+
const child = spawn(opener, [loginUrl], {
|
|
14292
|
+
detached: true,
|
|
14293
|
+
stdio: "ignore"
|
|
14294
|
+
});
|
|
14295
|
+
child.unref();
|
|
14076
14296
|
}
|
|
14077
14297
|
}
|
|
14078
14298
|
const timeout = setTimeout(() => {
|
|
@@ -14085,14 +14305,14 @@ ${loginUrl}`);
|
|
|
14085
14305
|
token = await tokenPromise;
|
|
14086
14306
|
} catch (error) {
|
|
14087
14307
|
clearTimeout(timeout);
|
|
14088
|
-
server.
|
|
14308
|
+
server.close();
|
|
14089
14309
|
spin.stop("Authentication failed");
|
|
14090
14310
|
R2.error(error instanceof Error ? error.message : String(error));
|
|
14091
14311
|
process.exitCode = 1;
|
|
14092
14312
|
return;
|
|
14093
14313
|
}
|
|
14094
14314
|
clearTimeout(timeout);
|
|
14095
|
-
server.
|
|
14315
|
+
server.close();
|
|
14096
14316
|
spin.message("Validating token...");
|
|
14097
14317
|
const meResponse = await fetch(`${flags.apiBase}/rpc/me`, {
|
|
14098
14318
|
method: "POST",
|
|
@@ -14214,9 +14434,9 @@ var setOrgCommand = buildCommand({
|
|
|
14214
14434
|
});
|
|
14215
14435
|
|
|
14216
14436
|
// src/lib/classifier.ts
|
|
14217
|
-
import { mkdir, unlink } from "fs/promises";
|
|
14218
|
-
import { homedir as
|
|
14219
|
-
import { join as
|
|
14437
|
+
import { mkdir as mkdir3, unlink, writeFile as writeFile3 } from "node:fs/promises";
|
|
14438
|
+
import { homedir as homedir11 } from "node:os";
|
|
14439
|
+
import { join as join13 } from "node:path";
|
|
14220
14440
|
|
|
14221
14441
|
// src/lib/types.ts
|
|
14222
14442
|
var SESSION_TAGS = [
|
|
@@ -14243,27 +14463,24 @@ var SYSTEM_PROMPT = `You are a session classifier. Analyze the Claude Code sessi
|
|
|
14243
14463
|
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`;
|
|
14244
14464
|
async function classifySession(content) {
|
|
14245
14465
|
const truncatedContent = content.slice(0, 50000);
|
|
14246
|
-
const tempDir =
|
|
14247
|
-
const tempFile =
|
|
14466
|
+
const tempDir = join13(homedir11(), ".claude", "temp");
|
|
14467
|
+
const tempFile = join13(tempDir, `classify-${Date.now()}.txt`);
|
|
14248
14468
|
try {
|
|
14249
|
-
await
|
|
14250
|
-
await
|
|
14469
|
+
await mkdir3(tempDir, { recursive: true });
|
|
14470
|
+
await writeFile3(tempFile, `Classify this session transcript:
|
|
14251
14471
|
|
|
14252
14472
|
${truncatedContent}`);
|
|
14253
14473
|
const prompt = `Read and classify the session transcript in this file: ${tempFile}`;
|
|
14254
14474
|
const escapedPrompt = prompt.replace(/'/g, "'\\''");
|
|
14255
14475
|
const escapedSystemPrompt = SYSTEM_PROMPT.replace(/'/g, "'\\''");
|
|
14256
|
-
const
|
|
14257
|
-
"sh",
|
|
14476
|
+
const result = await exec("sh", [
|
|
14258
14477
|
"-c",
|
|
14259
14478
|
`echo '${escapedPrompt}' | claude --output-format text --print --model haiku --no-session-persistence --dangerously-skip-permissions --system-prompt '${escapedSystemPrompt}'`
|
|
14260
|
-
]
|
|
14261
|
-
|
|
14262
|
-
const stdout = await new Response(proc.stdout).text();
|
|
14263
|
-
if (exitCode !== 0) {
|
|
14479
|
+
]);
|
|
14480
|
+
if (result.exitCode !== 0) {
|
|
14264
14481
|
return "other";
|
|
14265
14482
|
}
|
|
14266
|
-
const output = stdout.trim().toLowerCase();
|
|
14483
|
+
const output = result.stdout.trim().toLowerCase();
|
|
14267
14484
|
if (SESSION_TAGS.includes(output)) {
|
|
14268
14485
|
return output;
|
|
14269
14486
|
}
|
|
@@ -14283,10 +14500,10 @@ ${truncatedContent}`);
|
|
|
14283
14500
|
}
|
|
14284
14501
|
|
|
14285
14502
|
// src/lib/session-resolver.ts
|
|
14286
|
-
import { access, readdir as readdir3 } from "fs/promises";
|
|
14287
|
-
import { homedir as
|
|
14288
|
-
import { basename, dirname as
|
|
14289
|
-
var SESSIONS_BASE_DIR3 =
|
|
14503
|
+
import { access, readdir as readdir3 } from "node:fs/promises";
|
|
14504
|
+
import { homedir as homedir12 } from "node:os";
|
|
14505
|
+
import { basename, dirname as dirname5, join as join14 } from "node:path";
|
|
14506
|
+
var SESSIONS_BASE_DIR3 = join14(homedir12(), ".claude", "projects");
|
|
14290
14507
|
async function resolveSession(input) {
|
|
14291
14508
|
const isPath = input.includes("/") || input.endsWith(".jsonl");
|
|
14292
14509
|
if (isPath) {
|
|
@@ -14303,7 +14520,7 @@ async function resolveFromPath(filePath) {
|
|
|
14303
14520
|
throw new Error(`Session file not found: ${filePath}`);
|
|
14304
14521
|
}
|
|
14305
14522
|
const sessionId = filename.replace(/\.jsonl$/, "");
|
|
14306
|
-
const sessionDir =
|
|
14523
|
+
const sessionDir = dirname5(filePath);
|
|
14307
14524
|
const parentDir = basename(sessionDir);
|
|
14308
14525
|
const projectPath = await decodeProjectPath(parentDir);
|
|
14309
14526
|
return { transcriptPath: filePath, projectPath, sessionDir, sessionId };
|
|
@@ -14318,11 +14535,11 @@ async function resolveFromId(sessionId) {
|
|
|
14318
14535
|
throw new Error(`Session not found: ${sessionId}`);
|
|
14319
14536
|
}
|
|
14320
14537
|
for (const projectDir of projectDirs) {
|
|
14321
|
-
const sessionDir =
|
|
14538
|
+
const sessionDir = join14(SESSIONS_BASE_DIR3, projectDir);
|
|
14322
14539
|
try {
|
|
14323
14540
|
const files = await readdir3(sessionDir);
|
|
14324
14541
|
if (files.includes(sessionFileName)) {
|
|
14325
|
-
const transcriptPath =
|
|
14542
|
+
const transcriptPath = join14(sessionDir, sessionFileName);
|
|
14326
14543
|
const projectPath = await decodeProjectPath(projectDir);
|
|
14327
14544
|
return {
|
|
14328
14545
|
transcriptPath,
|
|
@@ -14352,45 +14569,26 @@ async function runInteractiveUpload(flags) {
|
|
|
14352
14569
|
We("rudel upload");
|
|
14353
14570
|
const spin = bt2();
|
|
14354
14571
|
spin.start("Scanning projects...");
|
|
14355
|
-
const
|
|
14356
|
-
const allProjects = [];
|
|
14357
|
-
for (const adapter of adapters2) {
|
|
14358
|
-
const projects = await adapter.scanAllSessions();
|
|
14359
|
-
allProjects.push(...projects);
|
|
14360
|
-
}
|
|
14572
|
+
const { projects: allProjects, groups } = await scanAndGroupProjects();
|
|
14361
14573
|
spin.stop(`Found ${allProjects.length} project(s)`);
|
|
14362
14574
|
if (allProjects.length === 0) {
|
|
14363
14575
|
R2.warn("No projects with sessions found.");
|
|
14364
14576
|
Le("Nothing to upload.");
|
|
14365
14577
|
return;
|
|
14366
14578
|
}
|
|
14367
|
-
const cwd = process.cwd();
|
|
14368
|
-
const grouped = groupProjectsForCwd(allProjects, cwd);
|
|
14369
14579
|
const options = [];
|
|
14370
14580
|
const preSelected = [];
|
|
14371
|
-
for (const
|
|
14372
|
-
|
|
14373
|
-
|
|
14374
|
-
|
|
14375
|
-
|
|
14376
|
-
|
|
14377
|
-
|
|
14378
|
-
|
|
14379
|
-
|
|
14380
|
-
|
|
14381
|
-
|
|
14382
|
-
value: sub,
|
|
14383
|
-
label: ` [${getAdapterName(sub.source)}] ${relative}`,
|
|
14384
|
-
hint: sessionCountHint(sub.sessionCount)
|
|
14385
|
-
});
|
|
14386
|
-
preSelected.push(sub);
|
|
14387
|
-
}
|
|
14388
|
-
for (const other of grouped.others) {
|
|
14389
|
-
options.push({
|
|
14390
|
-
value: other,
|
|
14391
|
-
label: `[${getAdapterName(other.source)}] ${other.displayPath}`,
|
|
14392
|
-
hint: sessionCountHint(other.sessionCount)
|
|
14393
|
-
});
|
|
14581
|
+
for (const group2 of groups) {
|
|
14582
|
+
for (const proj of group2.projects) {
|
|
14583
|
+
options.push({
|
|
14584
|
+
value: proj,
|
|
14585
|
+
label: `[${getAdapterName(proj.source)}] ${proj.displayPath}`,
|
|
14586
|
+
hint: sessionCountHint(proj.sessionCount)
|
|
14587
|
+
});
|
|
14588
|
+
if (group2.containsCwd) {
|
|
14589
|
+
preSelected.push(proj);
|
|
14590
|
+
}
|
|
14591
|
+
}
|
|
14394
14592
|
}
|
|
14395
14593
|
const selected = await je({
|
|
14396
14594
|
message: "Select projects to upload",
|
|
@@ -14452,7 +14650,7 @@ async function runInteractiveUpload(flags) {
|
|
|
14452
14650
|
});
|
|
14453
14651
|
renderBatchSummary(summary, { showRetryHint: summary.failed > 0 });
|
|
14454
14652
|
if (flags.dryRun) {
|
|
14455
|
-
Le("Dry run complete
|
|
14653
|
+
Le("Dry run complete — no sessions were uploaded.");
|
|
14456
14654
|
} else {
|
|
14457
14655
|
Le("Done!");
|
|
14458
14656
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rudel",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "CLI for the Coding Agent Analytics Platform rudel.ai",
|
|
6
6
|
"license": "MIT",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"scripts": {
|
|
33
33
|
"dev": "bun run src/bin/cli.ts",
|
|
34
34
|
"check-types": "tsc --noEmit",
|
|
35
|
-
"build": "bun build src/bin/cli.ts --outdir dist --target
|
|
35
|
+
"build": "bun build src/bin/cli.ts --outdir dist --target node",
|
|
36
36
|
"test": "bun test"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"@rudel/agent-adapters": "0.0.0",
|
|
49
49
|
"@rudel/api-routes": "0.0.0",
|
|
50
50
|
"@rudel/typescript-config": "0.0.0",
|
|
51
|
+
"@types/node": "^22",
|
|
51
52
|
"bun-types": "latest",
|
|
52
53
|
"typescript": "5.9.2"
|
|
53
54
|
}
|