shiva-code 0.2.2 → 0.3.0
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/chunk-4GL27U2Z.js +174 -0
- package/dist/chunk-66D4NGIK.js +560 -0
- package/dist/chunk-G2G6UUWM.js +169 -0
- package/dist/chunk-GHAT2D55.js +175 -0
- package/dist/chunk-KXYP4OCK.js +236 -0
- package/dist/chunk-MDMZWOX7.js +304 -0
- package/dist/github-R3I7U2DQ.js +49 -0
- package/dist/index.js +5249 -89
- package/dist/package-manager-OOJUNEKG.js +26 -0
- package/dist/project-config-GELL5QUH.js +40 -0
- package/dist/session-manager-THTEDIUG.js +45 -0
- package/dist/tags-P6L3BWO7.js +28 -0
- package/package.json +7 -2
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cache
|
|
3
|
+
} from "./chunk-G2G6UUWM.js";
|
|
4
|
+
|
|
5
|
+
// src/services/session-manager.ts
|
|
6
|
+
import * as fs from "fs";
|
|
7
|
+
import * as path from "path";
|
|
8
|
+
import * as os from "os";
|
|
9
|
+
import * as readline from "readline";
|
|
10
|
+
function getClaudeProjectsPath() {
|
|
11
|
+
return path.join(os.homedir(), ".claude", "projects");
|
|
12
|
+
}
|
|
13
|
+
function encodeProjectPath(projectPath) {
|
|
14
|
+
return projectPath.replace(/\//g, "-").replace(/^-/, "");
|
|
15
|
+
}
|
|
16
|
+
function decodeProjectPath(encoded) {
|
|
17
|
+
const parts = encoded.split("-").filter(Boolean);
|
|
18
|
+
return "/" + parts.join("/");
|
|
19
|
+
}
|
|
20
|
+
function getProjectName(projectPath) {
|
|
21
|
+
return path.basename(projectPath);
|
|
22
|
+
}
|
|
23
|
+
async function getAllClaudeProjects(skipCache = false) {
|
|
24
|
+
if (!skipCache) {
|
|
25
|
+
const cached = cache.getSessions();
|
|
26
|
+
if (cached) {
|
|
27
|
+
return cached;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const projectsPath = getClaudeProjectsPath();
|
|
31
|
+
if (!fs.existsSync(projectsPath)) {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
const projects = [];
|
|
35
|
+
const entries = fs.readdirSync(projectsPath, { withFileTypes: true });
|
|
36
|
+
for (const entry of entries) {
|
|
37
|
+
if (!entry.isDirectory()) continue;
|
|
38
|
+
const encodedPath = entry.name;
|
|
39
|
+
const absolutePath = decodeProjectPath(encodedPath);
|
|
40
|
+
const projectName = getProjectName(absolutePath);
|
|
41
|
+
const sessionIndex = getSessionIndex(encodedPath);
|
|
42
|
+
const sessions = sessionIndex?.entries || [];
|
|
43
|
+
sessions.sort(
|
|
44
|
+
(a, b) => new Date(b.modified).getTime() - new Date(a.modified).getTime()
|
|
45
|
+
);
|
|
46
|
+
projects.push({
|
|
47
|
+
encodedPath,
|
|
48
|
+
absolutePath,
|
|
49
|
+
projectName,
|
|
50
|
+
sessions,
|
|
51
|
+
latestSession: sessions[0]
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
projects.sort((a, b) => {
|
|
55
|
+
const aTime = a.latestSession ? new Date(a.latestSession.modified).getTime() : 0;
|
|
56
|
+
const bTime = b.latestSession ? new Date(b.latestSession.modified).getTime() : 0;
|
|
57
|
+
return bTime - aTime;
|
|
58
|
+
});
|
|
59
|
+
cache.setSessions(projects);
|
|
60
|
+
return projects;
|
|
61
|
+
}
|
|
62
|
+
function invalidateSessionsCache() {
|
|
63
|
+
cache.invalidateSessions();
|
|
64
|
+
}
|
|
65
|
+
function getSessionIndex(encodedPath) {
|
|
66
|
+
const indexPath = path.join(
|
|
67
|
+
getClaudeProjectsPath(),
|
|
68
|
+
encodedPath,
|
|
69
|
+
"sessions-index.json"
|
|
70
|
+
);
|
|
71
|
+
if (!fs.existsSync(indexPath)) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
const content = fs.readFileSync(indexPath, "utf-8");
|
|
76
|
+
return JSON.parse(content);
|
|
77
|
+
} catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async function findProject(nameOrPath) {
|
|
82
|
+
const projects = await getAllClaudeProjects();
|
|
83
|
+
const absolutePath = path.resolve(nameOrPath);
|
|
84
|
+
const byPath = projects.find((p) => p.absolutePath === absolutePath);
|
|
85
|
+
if (byPath) return byPath;
|
|
86
|
+
const lowerName = nameOrPath.toLowerCase();
|
|
87
|
+
const byName = projects.find(
|
|
88
|
+
(p) => p.projectName.toLowerCase() === lowerName || p.projectName.toLowerCase().includes(lowerName)
|
|
89
|
+
);
|
|
90
|
+
return byName || null;
|
|
91
|
+
}
|
|
92
|
+
function findSessionByBranch(project, branch) {
|
|
93
|
+
const branchSessions = project.sessions.filter((s) => s.gitBranch === branch);
|
|
94
|
+
if (branchSessions.length === 0) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
return branchSessions[0];
|
|
98
|
+
}
|
|
99
|
+
async function findProjectForCurrentDir() {
|
|
100
|
+
const cwd = process.cwd();
|
|
101
|
+
return findProject(cwd);
|
|
102
|
+
}
|
|
103
|
+
function getSessionFilePath(session) {
|
|
104
|
+
return session.fullPath;
|
|
105
|
+
}
|
|
106
|
+
function isSessionCorrupted(session) {
|
|
107
|
+
const filePath = getSessionFilePath(session);
|
|
108
|
+
if (!fs.existsSync(filePath)) {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
const stats = fs.statSync(filePath);
|
|
113
|
+
if (stats.size > 100 * 1024 * 1024) {
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
const fd = fs.openSync(filePath, "r");
|
|
117
|
+
const buffer = Buffer.alloc(4096);
|
|
118
|
+
const bytesRead = fs.readSync(fd, buffer, 0, 4096, 0);
|
|
119
|
+
fs.closeSync(fd);
|
|
120
|
+
if (bytesRead === 0) {
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
const firstLine = buffer.toString("utf-8").split("\n")[0];
|
|
124
|
+
JSON.parse(firstLine);
|
|
125
|
+
return false;
|
|
126
|
+
} catch {
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function isSessionActive(session) {
|
|
131
|
+
const filePath = getSessionFilePath(session);
|
|
132
|
+
if (!fs.existsSync(filePath)) {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
const stats = fs.statSync(filePath);
|
|
137
|
+
return Date.now() - stats.mtimeMs < 3e4;
|
|
138
|
+
} catch {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
async function parseSessionFile(jsonlPath, level = "standard") {
|
|
143
|
+
const summaries = [];
|
|
144
|
+
if (!fs.existsSync(jsonlPath)) {
|
|
145
|
+
return summaries;
|
|
146
|
+
}
|
|
147
|
+
return new Promise((resolve2) => {
|
|
148
|
+
const fileStream = fs.createReadStream(jsonlPath, { encoding: "utf-8" });
|
|
149
|
+
const rl = readline.createInterface({
|
|
150
|
+
input: fileStream,
|
|
151
|
+
crlfDelay: Infinity
|
|
152
|
+
});
|
|
153
|
+
let lineCount = 0;
|
|
154
|
+
const maxLines = level === "minimal" ? 1e3 : level === "standard" ? 1e4 : Infinity;
|
|
155
|
+
rl.on("line", (line) => {
|
|
156
|
+
lineCount++;
|
|
157
|
+
if (lineCount > maxLines) {
|
|
158
|
+
rl.close();
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
const entry = JSON.parse(line);
|
|
163
|
+
if (entry.type === "summary") {
|
|
164
|
+
summaries.push({
|
|
165
|
+
type: "summary",
|
|
166
|
+
content: entry.summary || entry.content || "",
|
|
167
|
+
timestamp: entry.timestamp
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
if (level === "full") {
|
|
171
|
+
if (entry.type === "user" && entry.message?.content) {
|
|
172
|
+
const content = typeof entry.message.content === "string" ? entry.message.content : JSON.stringify(entry.message.content);
|
|
173
|
+
summaries.push({
|
|
174
|
+
type: "user",
|
|
175
|
+
content: content.substring(0, 500),
|
|
176
|
+
timestamp: entry.timestamp
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
} catch {
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
rl.on("close", () => {
|
|
184
|
+
resolve2(summaries);
|
|
185
|
+
});
|
|
186
|
+
rl.on("error", () => {
|
|
187
|
+
resolve2(summaries);
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
async function getRecoveredContext(session, level = "standard") {
|
|
192
|
+
const summaries = await parseSessionFile(session.fullPath, level);
|
|
193
|
+
const summaryTexts = summaries.filter((s) => s.type === "summary").map((s) => s.content).filter(Boolean);
|
|
194
|
+
const filteredSummaries = level === "minimal" ? summaryTexts.slice(-5) : summaryTexts;
|
|
195
|
+
return {
|
|
196
|
+
projectPath: session.projectPath,
|
|
197
|
+
summaries: filteredSummaries,
|
|
198
|
+
firstPrompt: session.firstPrompt,
|
|
199
|
+
lastModified: session.modified,
|
|
200
|
+
messageCount: session.messageCount,
|
|
201
|
+
gitBranch: session.gitBranch
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function formatRecoveredContextAsMarkdown(context) {
|
|
205
|
+
const lines = [];
|
|
206
|
+
lines.push("# Session Recovery Context");
|
|
207
|
+
lines.push("");
|
|
208
|
+
lines.push(`**Project:** ${context.projectPath}`);
|
|
209
|
+
lines.push(`**Branch:** ${context.gitBranch}`);
|
|
210
|
+
lines.push(`**Last Modified:** ${context.lastModified}`);
|
|
211
|
+
lines.push(`**Messages:** ~${context.messageCount}`);
|
|
212
|
+
lines.push("");
|
|
213
|
+
if (context.firstPrompt) {
|
|
214
|
+
lines.push("## Original Task");
|
|
215
|
+
lines.push("");
|
|
216
|
+
lines.push(context.firstPrompt);
|
|
217
|
+
lines.push("");
|
|
218
|
+
}
|
|
219
|
+
if (context.summaries.length > 0) {
|
|
220
|
+
lines.push("## Session Summaries");
|
|
221
|
+
lines.push("");
|
|
222
|
+
for (const summary of context.summaries) {
|
|
223
|
+
lines.push("---");
|
|
224
|
+
lines.push("");
|
|
225
|
+
lines.push(summary);
|
|
226
|
+
lines.push("");
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return lines.join("\n");
|
|
230
|
+
}
|
|
231
|
+
function saveRecoveredContext(context, outputDir) {
|
|
232
|
+
const dir = outputDir || path.join(os.homedir(), ".shiva", "recovery");
|
|
233
|
+
if (!fs.existsSync(dir)) {
|
|
234
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
235
|
+
}
|
|
236
|
+
const projectSlug = getProjectName(context.projectPath).toLowerCase().replace(/[^a-z0-9]+/g, "-");
|
|
237
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
238
|
+
const filename = `${projectSlug}-${date}.md`;
|
|
239
|
+
const filepath = path.join(dir, filename);
|
|
240
|
+
const content = formatRecoveredContextAsMarkdown(context);
|
|
241
|
+
fs.writeFileSync(filepath, content, "utf-8");
|
|
242
|
+
return filepath;
|
|
243
|
+
}
|
|
244
|
+
async function getSessionStats() {
|
|
245
|
+
const projects = await getAllClaudeProjects();
|
|
246
|
+
let totalSessions = 0;
|
|
247
|
+
let activeProjects = 0;
|
|
248
|
+
for (const project of projects) {
|
|
249
|
+
totalSessions += project.sessions.length;
|
|
250
|
+
if (project.sessions.length > 0) {
|
|
251
|
+
activeProjects++;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return {
|
|
255
|
+
totalProjects: projects.length,
|
|
256
|
+
totalSessions,
|
|
257
|
+
activeProjects
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
function formatRelativeTime(date) {
|
|
261
|
+
const now = /* @__PURE__ */ new Date();
|
|
262
|
+
const then = new Date(date);
|
|
263
|
+
const diffMs = now.getTime() - then.getTime();
|
|
264
|
+
const minutes = Math.floor(diffMs / (1e3 * 60));
|
|
265
|
+
const hours = Math.floor(diffMs / (1e3 * 60 * 60));
|
|
266
|
+
const days = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
|
|
267
|
+
if (minutes < 1) return "gerade eben";
|
|
268
|
+
if (minutes < 60) return `${minutes}m`;
|
|
269
|
+
if (hours < 24) return `${hours}h`;
|
|
270
|
+
if (days < 7) return `${days}d`;
|
|
271
|
+
return then.toLocaleDateString("de-DE", { month: "short", day: "numeric" });
|
|
272
|
+
}
|
|
273
|
+
function formatDate(date) {
|
|
274
|
+
const d = new Date(date);
|
|
275
|
+
return d.toLocaleDateString("de-DE", {
|
|
276
|
+
month: "short",
|
|
277
|
+
day: "numeric",
|
|
278
|
+
hour: "2-digit",
|
|
279
|
+
minute: "2-digit"
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export {
|
|
284
|
+
getClaudeProjectsPath,
|
|
285
|
+
encodeProjectPath,
|
|
286
|
+
decodeProjectPath,
|
|
287
|
+
getProjectName,
|
|
288
|
+
getAllClaudeProjects,
|
|
289
|
+
invalidateSessionsCache,
|
|
290
|
+
getSessionIndex,
|
|
291
|
+
findProject,
|
|
292
|
+
findSessionByBranch,
|
|
293
|
+
findProjectForCurrentDir,
|
|
294
|
+
getSessionFilePath,
|
|
295
|
+
isSessionCorrupted,
|
|
296
|
+
isSessionActive,
|
|
297
|
+
parseSessionFile,
|
|
298
|
+
getRecoveredContext,
|
|
299
|
+
formatRecoveredContextAsMarkdown,
|
|
300
|
+
saveRecoveredContext,
|
|
301
|
+
getSessionStats,
|
|
302
|
+
formatRelativeTime,
|
|
303
|
+
formatDate
|
|
304
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {
|
|
2
|
+
findMentionedIssueNumbers,
|
|
3
|
+
formatContextAsMarkdown,
|
|
4
|
+
generateGitHubContext,
|
|
5
|
+
getCIStatus,
|
|
6
|
+
getCurrentBranch,
|
|
7
|
+
getGhUser,
|
|
8
|
+
getGitHubRemote,
|
|
9
|
+
getIssue,
|
|
10
|
+
getOpenIssues,
|
|
11
|
+
getOpenPRs,
|
|
12
|
+
getPR,
|
|
13
|
+
getPRForBranch,
|
|
14
|
+
getRecentCommits,
|
|
15
|
+
getRepoInfo,
|
|
16
|
+
invalidateGitHubCache,
|
|
17
|
+
isGhAuthenticated,
|
|
18
|
+
isGhInstalled,
|
|
19
|
+
isGitHubRepo,
|
|
20
|
+
isGitRepo,
|
|
21
|
+
parseGitHubUrl,
|
|
22
|
+
runGh,
|
|
23
|
+
runGhRaw
|
|
24
|
+
} from "./chunk-66D4NGIK.js";
|
|
25
|
+
import "./chunk-G2G6UUWM.js";
|
|
26
|
+
export {
|
|
27
|
+
findMentionedIssueNumbers,
|
|
28
|
+
formatContextAsMarkdown,
|
|
29
|
+
generateGitHubContext,
|
|
30
|
+
getCIStatus,
|
|
31
|
+
getCurrentBranch,
|
|
32
|
+
getGhUser,
|
|
33
|
+
getGitHubRemote,
|
|
34
|
+
getIssue,
|
|
35
|
+
getOpenIssues,
|
|
36
|
+
getOpenPRs,
|
|
37
|
+
getPR,
|
|
38
|
+
getPRForBranch,
|
|
39
|
+
getRecentCommits,
|
|
40
|
+
getRepoInfo,
|
|
41
|
+
invalidateGitHubCache,
|
|
42
|
+
isGhAuthenticated,
|
|
43
|
+
isGhInstalled,
|
|
44
|
+
isGitHubRepo,
|
|
45
|
+
isGitRepo,
|
|
46
|
+
parseGitHubUrl,
|
|
47
|
+
runGh,
|
|
48
|
+
runGhRaw
|
|
49
|
+
};
|