@sporesec/arcana 3.0.2 → 4.0.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/cli.js +25 -298
- package/dist/command-defs.d.ts +28 -0
- package/dist/command-defs.js +414 -0
- package/dist/commands/audit.js +18 -4
- package/dist/commands/clean.d.ts +1 -0
- package/dist/commands/clean.js +80 -0
- package/dist/commands/compress.d.ts +5 -0
- package/dist/commands/compress.js +38 -0
- package/dist/commands/config.js +40 -26
- package/dist/commands/create.js +2 -0
- package/dist/commands/curate.d.ts +39 -0
- package/dist/commands/curate.js +222 -0
- package/dist/commands/diff.js +2 -0
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/doctor.js +61 -2
- package/dist/commands/import-cmd.js +5 -0
- package/dist/commands/index.d.ts +5 -0
- package/dist/commands/index.js +107 -0
- package/dist/commands/info.js +19 -8
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.js +71 -0
- package/dist/commands/install.js +2 -0
- package/dist/commands/list.js +8 -0
- package/dist/commands/load.d.ts +10 -0
- package/dist/commands/load.js +130 -0
- package/dist/commands/lock.js +35 -24
- package/dist/commands/mcp.d.ts +4 -0
- package/dist/commands/mcp.js +87 -0
- package/dist/commands/outdated.js +8 -6
- package/dist/commands/providers.js +29 -21
- package/dist/commands/recommend.js +11 -3
- package/dist/commands/remember.d.ts +12 -0
- package/dist/commands/remember.js +111 -0
- package/dist/commands/scan.d.ts +2 -0
- package/dist/commands/scan.js +46 -8
- package/dist/commands/search.js +6 -0
- package/dist/commands/uninstall.js +36 -0
- package/dist/commands/update.js +27 -0
- package/dist/commands/validate.js +8 -0
- package/dist/commands/verify.js +2 -0
- package/dist/compress/engine.d.ts +21 -0
- package/dist/compress/engine.js +106 -0
- package/dist/compress/index.d.ts +7 -0
- package/dist/compress/index.js +10 -0
- package/dist/compress/rules/generic.d.ts +1 -0
- package/dist/compress/rules/generic.js +9 -0
- package/dist/compress/rules/git.d.ts +1 -0
- package/dist/compress/rules/git.js +113 -0
- package/dist/compress/rules/npm.d.ts +1 -0
- package/dist/compress/rules/npm.js +99 -0
- package/dist/compress/rules/test-runner.d.ts +1 -0
- package/dist/compress/rules/test-runner.js +103 -0
- package/dist/compress/rules/tsc.d.ts +1 -0
- package/dist/compress/rules/tsc.js +39 -0
- package/dist/compress/tracker.d.ts +16 -0
- package/dist/compress/tracker.js +45 -0
- package/dist/constants.d.ts +12 -0
- package/dist/constants.js +29 -0
- package/dist/interactive/helpers.js +1 -0
- package/dist/interactive/menu.js +6 -1
- package/dist/interactive/optimize-flow.js +4 -4
- package/dist/mcp/install.d.ts +10 -0
- package/dist/mcp/install.js +109 -0
- package/dist/mcp/registry.d.ts +11 -0
- package/dist/mcp/registry.js +27 -0
- package/dist/providers/anthropics.d.ts +4 -0
- package/dist/providers/anthropics.js +10 -0
- package/dist/registry.js +4 -0
- package/dist/session/trim.d.ts +23 -0
- package/dist/session/trim.js +132 -0
- package/dist/utils/cache.js +2 -2
- package/dist/utils/config.d.ts +2 -0
- package/dist/utils/config.js +33 -14
- package/dist/utils/help.js +16 -8
- package/dist/utils/install-core.js +23 -1
- package/dist/utils/memory.d.ts +25 -0
- package/dist/utils/memory.js +103 -0
- package/dist/utils/project-context.js +4 -0
- package/dist/utils/scanner.d.ts +22 -1
- package/dist/utils/scanner.js +81 -9
- package/dist/utils/sessions.d.ts +2 -0
- package/dist/utils/sessions.js +36 -0
- package/dist/utils/ui.js +5 -0
- package/dist/utils/usage.d.ts +17 -0
- package/dist/utils/usage.js +83 -0
- package/package.json +42 -7
- package/dist/command-registry.d.ts +0 -10
- package/dist/command-registry.js +0 -65
- package/dist/commands/benchmark.d.ts +0 -4
- package/dist/commands/benchmark.js +0 -178
- package/dist/commands/compact.d.ts +0 -6
- package/dist/commands/compact.js +0 -239
- package/dist/commands/optimize.d.ts +0 -3
- package/dist/commands/optimize.js +0 -356
- package/dist/commands/profile.d.ts +0 -3
- package/dist/commands/profile.js +0 -274
- package/dist/commands/stats.d.ts +0 -3
- package/dist/commands/stats.js +0 -210
- package/dist/commands/team.d.ts +0 -3
- package/dist/commands/team.js +0 -291
- package/dist/interactive.d.ts +0 -1
- package/dist/interactive.js +0 -841
package/dist/commands/profile.js
DELETED
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync, mkdirSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { homedir } from "node:os";
|
|
4
|
-
import { atomicWriteSync } from "../utils/atomic.js";
|
|
5
|
-
import { validateSlug } from "../utils/validate.js";
|
|
6
|
-
function getProfilesPath() {
|
|
7
|
-
return join(homedir(), ".arcana", "profiles.json");
|
|
8
|
-
}
|
|
9
|
-
function readProfiles() {
|
|
10
|
-
const path = getProfilesPath();
|
|
11
|
-
if (!existsSync(path))
|
|
12
|
-
return {};
|
|
13
|
-
try {
|
|
14
|
-
return JSON.parse(readFileSync(path, "utf-8"));
|
|
15
|
-
}
|
|
16
|
-
catch {
|
|
17
|
-
return {};
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
function writeProfiles(profiles) {
|
|
21
|
-
const dir = join(homedir(), ".arcana");
|
|
22
|
-
if (!existsSync(dir)) {
|
|
23
|
-
mkdirSync(dir, { recursive: true });
|
|
24
|
-
}
|
|
25
|
-
atomicWriteSync(getProfilesPath(), JSON.stringify(profiles, null, 2) + "\n");
|
|
26
|
-
}
|
|
27
|
-
export async function profileCommand(action, name, skills, opts) {
|
|
28
|
-
const resolved = action ?? "list";
|
|
29
|
-
switch (resolved) {
|
|
30
|
-
case "list":
|
|
31
|
-
return listProfiles(opts.json);
|
|
32
|
-
case "create":
|
|
33
|
-
return createProfile(name, skills, opts.json);
|
|
34
|
-
case "delete":
|
|
35
|
-
return deleteProfile(name, opts.json);
|
|
36
|
-
case "show":
|
|
37
|
-
return showProfile(name, opts.json);
|
|
38
|
-
case "apply":
|
|
39
|
-
return applyProfile(name, opts.json);
|
|
40
|
-
default:
|
|
41
|
-
console.error(`Unknown action: ${resolved}`);
|
|
42
|
-
console.error("Valid actions: list, create, delete, show, apply");
|
|
43
|
-
process.exit(1);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
function listProfiles(json) {
|
|
47
|
-
const profiles = readProfiles();
|
|
48
|
-
const names = Object.keys(profiles);
|
|
49
|
-
if (json) {
|
|
50
|
-
console.log(JSON.stringify({ profiles }));
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
if (names.length === 0) {
|
|
54
|
-
console.log("No profiles defined.");
|
|
55
|
-
console.log("Create one: arcana profile create <name> <skill1> <skill2> ...");
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
console.log(`${names.length} profile(s):\n`);
|
|
59
|
-
for (const profileName of names) {
|
|
60
|
-
const skillList = profiles[profileName];
|
|
61
|
-
console.log(` ${profileName.padEnd(20)} ${skillList.length} skill(s): ${skillList.join(", ")}`);
|
|
62
|
-
}
|
|
63
|
-
console.log();
|
|
64
|
-
}
|
|
65
|
-
function createProfile(name, skills, json) {
|
|
66
|
-
if (!name) {
|
|
67
|
-
if (json) {
|
|
68
|
-
console.log(JSON.stringify({ error: "Profile name is required" }));
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
console.error("Profile name is required.");
|
|
72
|
-
console.error("Usage: arcana profile create <name> <skill1> <skill2> ...");
|
|
73
|
-
}
|
|
74
|
-
process.exit(1);
|
|
75
|
-
}
|
|
76
|
-
try {
|
|
77
|
-
validateSlug(name, "profile name");
|
|
78
|
-
}
|
|
79
|
-
catch (err) {
|
|
80
|
-
if (json) {
|
|
81
|
-
console.log(JSON.stringify({ error: err instanceof Error ? err.message : "Invalid profile name" }));
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
console.error(err instanceof Error ? err.message : "Invalid profile name");
|
|
85
|
-
}
|
|
86
|
-
process.exit(1);
|
|
87
|
-
}
|
|
88
|
-
if (skills.length === 0) {
|
|
89
|
-
if (json) {
|
|
90
|
-
console.log(JSON.stringify({ error: "At least one skill is required" }));
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
console.error("At least one skill is required.");
|
|
94
|
-
console.error("Usage: arcana profile create <name> <skill1> <skill2> ...");
|
|
95
|
-
}
|
|
96
|
-
process.exit(1);
|
|
97
|
-
}
|
|
98
|
-
for (const skill of skills) {
|
|
99
|
-
try {
|
|
100
|
-
validateSlug(skill, "skill name");
|
|
101
|
-
}
|
|
102
|
-
catch (err) {
|
|
103
|
-
if (json) {
|
|
104
|
-
console.log(JSON.stringify({ error: err instanceof Error ? err.message : `Invalid skill name: ${skill}` }));
|
|
105
|
-
}
|
|
106
|
-
else {
|
|
107
|
-
console.error(err instanceof Error ? err.message : `Invalid skill name: ${skill}`);
|
|
108
|
-
}
|
|
109
|
-
process.exit(1);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
const profiles = readProfiles();
|
|
113
|
-
if (profiles[name]) {
|
|
114
|
-
if (json) {
|
|
115
|
-
console.log(JSON.stringify({ error: `Profile "${name}" already exists` }));
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
console.error(`Profile "${name}" already exists. Delete it first or choose a different name.`);
|
|
119
|
-
}
|
|
120
|
-
process.exit(1);
|
|
121
|
-
}
|
|
122
|
-
profiles[name] = skills;
|
|
123
|
-
writeProfiles(profiles);
|
|
124
|
-
if (json) {
|
|
125
|
-
console.log(JSON.stringify({ created: name, skills }));
|
|
126
|
-
}
|
|
127
|
-
else {
|
|
128
|
-
console.log(`Created profile "${name}" with ${skills.length} skill(s): ${skills.join(", ")}`);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
function deleteProfile(name, json) {
|
|
132
|
-
if (!name) {
|
|
133
|
-
if (json) {
|
|
134
|
-
console.log(JSON.stringify({ error: "Profile name is required" }));
|
|
135
|
-
}
|
|
136
|
-
else {
|
|
137
|
-
console.error("Profile name is required.");
|
|
138
|
-
console.error("Usage: arcana profile delete <name>");
|
|
139
|
-
}
|
|
140
|
-
process.exit(1);
|
|
141
|
-
}
|
|
142
|
-
const profiles = readProfiles();
|
|
143
|
-
if (!profiles[name]) {
|
|
144
|
-
if (json) {
|
|
145
|
-
console.log(JSON.stringify({ error: `Profile "${name}" not found` }));
|
|
146
|
-
}
|
|
147
|
-
else {
|
|
148
|
-
console.error(`Profile "${name}" not found.`);
|
|
149
|
-
}
|
|
150
|
-
process.exit(1);
|
|
151
|
-
}
|
|
152
|
-
delete profiles[name];
|
|
153
|
-
writeProfiles(profiles);
|
|
154
|
-
if (json) {
|
|
155
|
-
console.log(JSON.stringify({ deleted: name }));
|
|
156
|
-
}
|
|
157
|
-
else {
|
|
158
|
-
console.log(`Deleted profile "${name}".`);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
function showProfile(name, json) {
|
|
162
|
-
if (!name) {
|
|
163
|
-
if (json) {
|
|
164
|
-
console.log(JSON.stringify({ error: "Profile name is required" }));
|
|
165
|
-
}
|
|
166
|
-
else {
|
|
167
|
-
console.error("Profile name is required.");
|
|
168
|
-
console.error("Usage: arcana profile show <name>");
|
|
169
|
-
}
|
|
170
|
-
process.exit(1);
|
|
171
|
-
}
|
|
172
|
-
const profiles = readProfiles();
|
|
173
|
-
const skills = profiles[name];
|
|
174
|
-
if (!skills) {
|
|
175
|
-
if (json) {
|
|
176
|
-
console.log(JSON.stringify({ error: `Profile "${name}" not found` }));
|
|
177
|
-
}
|
|
178
|
-
else {
|
|
179
|
-
console.error(`Profile "${name}" not found.`);
|
|
180
|
-
}
|
|
181
|
-
process.exit(1);
|
|
182
|
-
}
|
|
183
|
-
if (json) {
|
|
184
|
-
console.log(JSON.stringify({ name, skills }));
|
|
185
|
-
}
|
|
186
|
-
else {
|
|
187
|
-
console.log(`Profile "${name}" (${skills.length} skill(s)):\n`);
|
|
188
|
-
for (const skill of skills) {
|
|
189
|
-
console.log(` - ${skill}`);
|
|
190
|
-
}
|
|
191
|
-
console.log();
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
async function applyProfile(name, json) {
|
|
195
|
-
if (!name) {
|
|
196
|
-
if (json) {
|
|
197
|
-
console.log(JSON.stringify({ error: "Profile name is required" }));
|
|
198
|
-
}
|
|
199
|
-
else {
|
|
200
|
-
console.error("Profile name is required.");
|
|
201
|
-
console.error("Usage: arcana profile apply <name>");
|
|
202
|
-
}
|
|
203
|
-
process.exit(1);
|
|
204
|
-
}
|
|
205
|
-
const profiles = readProfiles();
|
|
206
|
-
const skills = profiles[name];
|
|
207
|
-
if (!skills) {
|
|
208
|
-
if (json) {
|
|
209
|
-
console.log(JSON.stringify({ error: `Profile "${name}" not found` }));
|
|
210
|
-
}
|
|
211
|
-
else {
|
|
212
|
-
console.error(`Profile "${name}" not found.`);
|
|
213
|
-
}
|
|
214
|
-
process.exit(1);
|
|
215
|
-
}
|
|
216
|
-
if (skills.length === 0) {
|
|
217
|
-
if (json) {
|
|
218
|
-
console.log(JSON.stringify({ applied: name, installed: [], skipped: [], failed: [] }));
|
|
219
|
-
}
|
|
220
|
-
else {
|
|
221
|
-
console.log(`Profile "${name}" has no skills.`);
|
|
222
|
-
}
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
const { getProvider } = await import("../registry.js");
|
|
226
|
-
const { installSkill, writeSkillMeta, isSkillInstalled } = await import("../utils/fs.js");
|
|
227
|
-
const { loadConfig } = await import("../utils/config.js");
|
|
228
|
-
const config = loadConfig();
|
|
229
|
-
const provider = getProvider(config.defaultProvider);
|
|
230
|
-
const installed = [];
|
|
231
|
-
const skipped = [];
|
|
232
|
-
const failed = [];
|
|
233
|
-
for (const skillName of skills) {
|
|
234
|
-
if (isSkillInstalled(skillName)) {
|
|
235
|
-
skipped.push(skillName);
|
|
236
|
-
continue;
|
|
237
|
-
}
|
|
238
|
-
try {
|
|
239
|
-
const files = await provider.fetch(skillName);
|
|
240
|
-
installSkill(skillName, files);
|
|
241
|
-
const remote = await provider.info(skillName);
|
|
242
|
-
writeSkillMeta(skillName, {
|
|
243
|
-
version: remote?.version ?? "0.0.0",
|
|
244
|
-
installedAt: new Date().toISOString(),
|
|
245
|
-
source: provider.name,
|
|
246
|
-
description: remote?.description,
|
|
247
|
-
fileCount: files.length,
|
|
248
|
-
sizeBytes: files.reduce((s, f) => s + f.content.length, 0),
|
|
249
|
-
});
|
|
250
|
-
installed.push(skillName);
|
|
251
|
-
}
|
|
252
|
-
catch (err) {
|
|
253
|
-
failed.push(skillName);
|
|
254
|
-
if (!json && err instanceof Error) {
|
|
255
|
-
console.error(` Failed to install ${skillName}: ${err.message}`);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
if (json) {
|
|
260
|
-
console.log(JSON.stringify({ applied: name, installed, skipped, failed }));
|
|
261
|
-
}
|
|
262
|
-
else {
|
|
263
|
-
console.log(`Applied profile "${name}":`);
|
|
264
|
-
if (installed.length > 0)
|
|
265
|
-
console.log(` Installed: ${installed.join(", ")}`);
|
|
266
|
-
if (skipped.length > 0)
|
|
267
|
-
console.log(` Skipped (already installed): ${skipped.join(", ")}`);
|
|
268
|
-
if (failed.length > 0)
|
|
269
|
-
console.log(` Failed: ${failed.join(", ")}`);
|
|
270
|
-
console.log();
|
|
271
|
-
}
|
|
272
|
-
if (failed.length > 0)
|
|
273
|
-
process.exit(1);
|
|
274
|
-
}
|
package/dist/commands/stats.d.ts
DELETED
package/dist/commands/stats.js
DELETED
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
import { existsSync, openSync, readSync, closeSync, readdirSync, statSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { homedir } from "node:os";
|
|
4
|
-
import { ui, banner, table } from "../utils/ui.js";
|
|
5
|
-
import { readHistory } from "../utils/history.js";
|
|
6
|
-
import { getDirSize } from "../utils/fs.js";
|
|
7
|
-
function discoverSessions() {
|
|
8
|
-
const projectsDir = join(homedir(), ".claude", "projects");
|
|
9
|
-
if (!existsSync(projectsDir))
|
|
10
|
-
return [];
|
|
11
|
-
const sessions = [];
|
|
12
|
-
for (const project of readdirSync(projectsDir)) {
|
|
13
|
-
const projDir = join(projectsDir, project);
|
|
14
|
-
if (!statSync(projDir).isDirectory())
|
|
15
|
-
continue;
|
|
16
|
-
for (const file of readdirSync(projDir)) {
|
|
17
|
-
if (!file.endsWith(".jsonl"))
|
|
18
|
-
continue;
|
|
19
|
-
const fullPath = join(projDir, file);
|
|
20
|
-
const stat = statSync(fullPath);
|
|
21
|
-
// Count newlines by streaming in 64KB chunks (avoids loading entire file)
|
|
22
|
-
let lines = 0;
|
|
23
|
-
try {
|
|
24
|
-
const fd = openSync(fullPath, "r");
|
|
25
|
-
try {
|
|
26
|
-
const chunk = Buffer.alloc(65536);
|
|
27
|
-
let bytesRead;
|
|
28
|
-
while ((bytesRead = readSync(fd, chunk, 0, chunk.length, null)) > 0) {
|
|
29
|
-
for (let i = 0; i < bytesRead; i++) {
|
|
30
|
-
if (chunk[i] === 10)
|
|
31
|
-
lines++;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
finally {
|
|
36
|
-
closeSync(fd);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
catch {
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
sessions.push({
|
|
43
|
-
project,
|
|
44
|
-
file,
|
|
45
|
-
sizeBytes: stat.size,
|
|
46
|
-
lines,
|
|
47
|
-
modified: stat.mtime,
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return sessions;
|
|
52
|
-
}
|
|
53
|
-
function formatBytes(bytes) {
|
|
54
|
-
if (bytes < 1024)
|
|
55
|
-
return `${bytes} B`;
|
|
56
|
-
if (bytes < 1024 * 1024)
|
|
57
|
-
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
58
|
-
if (bytes < 1024 * 1024 * 1024)
|
|
59
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
60
|
-
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
|
61
|
-
}
|
|
62
|
-
function getDiskBreakdown() {
|
|
63
|
-
const claudeDir = join(homedir(), ".claude");
|
|
64
|
-
const dirs = [
|
|
65
|
-
"projects",
|
|
66
|
-
"file-history",
|
|
67
|
-
"skills",
|
|
68
|
-
"debug",
|
|
69
|
-
"todos",
|
|
70
|
-
"shell-snapshots",
|
|
71
|
-
"plans",
|
|
72
|
-
"cache",
|
|
73
|
-
"usage-data",
|
|
74
|
-
];
|
|
75
|
-
const breakdown = [];
|
|
76
|
-
for (const dirName of dirs) {
|
|
77
|
-
const dir = join(claudeDir, dirName);
|
|
78
|
-
if (!existsSync(dir))
|
|
79
|
-
continue;
|
|
80
|
-
breakdown.push({ name: dirName, sizeBytes: getDirSize(dir) });
|
|
81
|
-
}
|
|
82
|
-
breakdown.sort((a, b) => b.sizeBytes - a.sizeBytes);
|
|
83
|
-
return breakdown;
|
|
84
|
-
}
|
|
85
|
-
function getProjectBreakdown() {
|
|
86
|
-
const projectsDir = join(homedir(), ".claude", "projects");
|
|
87
|
-
if (!existsSync(projectsDir))
|
|
88
|
-
return [];
|
|
89
|
-
const projects = [];
|
|
90
|
-
for (const entry of readdirSync(projectsDir)) {
|
|
91
|
-
const full = join(projectsDir, entry);
|
|
92
|
-
if (!statSync(full).isDirectory())
|
|
93
|
-
continue;
|
|
94
|
-
const jsonlCount = readdirSync(full).filter((f) => f.endsWith(".jsonl")).length;
|
|
95
|
-
projects.push({ name: entry, sizeBytes: getDirSize(full), sessionCount: jsonlCount });
|
|
96
|
-
}
|
|
97
|
-
projects.sort((a, b) => b.sizeBytes - a.sizeBytes);
|
|
98
|
-
return projects;
|
|
99
|
-
}
|
|
100
|
-
export async function statsCommand(opts) {
|
|
101
|
-
if (!opts.json)
|
|
102
|
-
banner();
|
|
103
|
-
const sessions = discoverSessions();
|
|
104
|
-
if (sessions.length === 0) {
|
|
105
|
-
if (opts.json) {
|
|
106
|
-
console.log(JSON.stringify({ totalSessions: 0, totalProjects: 0 }));
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
console.log(ui.dim(" No session data found in ~/.claude/projects/"));
|
|
110
|
-
console.log();
|
|
111
|
-
}
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
const totalSize = sessions.reduce((sum, s) => sum + s.sizeBytes, 0);
|
|
115
|
-
const totalLines = sessions.reduce((sum, s) => sum + s.lines, 0);
|
|
116
|
-
const avgLines = Math.round(totalLines / sessions.length);
|
|
117
|
-
// Rough token estimate: ~4 chars per token for LLM tokenizers
|
|
118
|
-
const estimatedTokens = Math.round(totalSize / 4);
|
|
119
|
-
const projects = new Set(sessions.map((s) => s.project));
|
|
120
|
-
const sorted = [...sessions].sort((a, b) => b.modified.getTime() - a.modified.getTime());
|
|
121
|
-
const newest = sorted[0];
|
|
122
|
-
const oldest = sorted[sorted.length - 1];
|
|
123
|
-
// Sessions per project (top 5)
|
|
124
|
-
const projectCounts = new Map();
|
|
125
|
-
for (const s of sessions) {
|
|
126
|
-
projectCounts.set(s.project, (projectCounts.get(s.project) ?? 0) + 1);
|
|
127
|
-
}
|
|
128
|
-
const topProjects = [...projectCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5);
|
|
129
|
-
// Disk breakdown
|
|
130
|
-
const diskBreakdown = getDiskBreakdown();
|
|
131
|
-
const projectBreakdown = getProjectBreakdown();
|
|
132
|
-
const totalDisk = diskBreakdown.reduce((sum, d) => sum + d.sizeBytes, 0);
|
|
133
|
-
// Calculate reclaimable space (auxiliary dirs)
|
|
134
|
-
const reclaimableDirs = ["file-history", "debug", "shell-snapshots", "todos", "plans"];
|
|
135
|
-
const reclaimable = diskBreakdown
|
|
136
|
-
.filter((d) => reclaimableDirs.includes(d.name))
|
|
137
|
-
.reduce((sum, d) => sum + d.sizeBytes, 0);
|
|
138
|
-
if (opts.json) {
|
|
139
|
-
const data = {
|
|
140
|
-
totalSessions: sessions.length,
|
|
141
|
-
totalProjects: projects.size,
|
|
142
|
-
totalSizeBytes: totalSize,
|
|
143
|
-
estimatedTokens,
|
|
144
|
-
avgLinesPerSession: avgLines,
|
|
145
|
-
topProjects: topProjects.map(([name, count]) => ({ name, sessions: count })),
|
|
146
|
-
diskBreakdown: diskBreakdown.map((d) => ({ name: d.name, sizeBytes: d.sizeBytes })),
|
|
147
|
-
projectBreakdown: projectBreakdown
|
|
148
|
-
.slice(0, 10)
|
|
149
|
-
.map((p) => ({ name: p.name, sizeBytes: p.sizeBytes, sessions: p.sessionCount })),
|
|
150
|
-
reclaimableBytes: reclaimable,
|
|
151
|
-
};
|
|
152
|
-
console.log(JSON.stringify(data, null, 2));
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
console.log(ui.bold(" Session Analytics\n"));
|
|
156
|
-
const rows = [
|
|
157
|
-
[ui.dim("Sessions"), String(sessions.length)],
|
|
158
|
-
[ui.dim("Projects"), String(projects.size)],
|
|
159
|
-
[ui.dim("Total session data"), formatBytes(totalSize)],
|
|
160
|
-
[ui.dim("Est. tokens"), `~${(estimatedTokens / 1_000_000).toFixed(1)}M (rough)`],
|
|
161
|
-
[ui.dim("Avg lines/session"), String(avgLines)],
|
|
162
|
-
[ui.dim("Newest session"), newest.modified.toLocaleDateString()],
|
|
163
|
-
[ui.dim("Oldest session"), oldest.modified.toLocaleDateString()],
|
|
164
|
-
];
|
|
165
|
-
table(rows);
|
|
166
|
-
// Disk breakdown
|
|
167
|
-
console.log();
|
|
168
|
-
console.log(ui.bold(" Disk Breakdown\n"));
|
|
169
|
-
const diskRows = diskBreakdown
|
|
170
|
-
.filter((d) => d.sizeBytes > 0)
|
|
171
|
-
.map((d) => [ui.dim(`~/.claude/${d.name}/`), formatBytes(d.sizeBytes)]);
|
|
172
|
-
diskRows.push([ui.bold("Total"), ui.bold(formatBytes(totalDisk))]);
|
|
173
|
-
table(diskRows);
|
|
174
|
-
// Per-project breakdown (top 5)
|
|
175
|
-
if (projectBreakdown.length > 0) {
|
|
176
|
-
console.log();
|
|
177
|
-
console.log(ui.bold(" Projects by Size\n"));
|
|
178
|
-
const projRows = projectBreakdown
|
|
179
|
-
.slice(0, 5)
|
|
180
|
-
.map((p) => [
|
|
181
|
-
ui.dim(p.name.length > 45 ? p.name.slice(0, 42) + "..." : p.name),
|
|
182
|
-
formatBytes(p.sizeBytes),
|
|
183
|
-
`${p.sessionCount} sessions`,
|
|
184
|
-
]);
|
|
185
|
-
table(projRows);
|
|
186
|
-
}
|
|
187
|
-
if (topProjects.length > 0) {
|
|
188
|
-
console.log();
|
|
189
|
-
console.log(ui.bold(" Most Active Projects\n"));
|
|
190
|
-
const projRows = topProjects.map(([name, count]) => [
|
|
191
|
-
ui.dim(name.length > 40 ? name.slice(0, 37) + "..." : name),
|
|
192
|
-
`${count} sessions`,
|
|
193
|
-
]);
|
|
194
|
-
table(projRows);
|
|
195
|
-
}
|
|
196
|
-
// Cleanup suggestion
|
|
197
|
-
if (reclaimable > 1024 * 1024) {
|
|
198
|
-
console.log();
|
|
199
|
-
console.log(ui.warn(` ${formatBytes(reclaimable)} reclaimable in temp directories. Run: arcana clean`));
|
|
200
|
-
}
|
|
201
|
-
const history = readHistory();
|
|
202
|
-
if (history.length > 0) {
|
|
203
|
-
console.log();
|
|
204
|
-
console.log(ui.bold(" Recent Arcana Activity\n"));
|
|
205
|
-
const recent = history.slice(-5).reverse();
|
|
206
|
-
const histRows = recent.map((e) => [ui.dim(new Date(e.timestamp).toLocaleDateString()), e.action, e.target ?? ""]);
|
|
207
|
-
table(histRows);
|
|
208
|
-
}
|
|
209
|
-
console.log();
|
|
210
|
-
}
|
package/dist/commands/team.d.ts
DELETED