@sporesec/arcana 2.3.1 → 3.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.d.ts +0 -1
- package/dist/cli.js +140 -10
- package/dist/command-registry.d.ts +10 -0
- package/dist/command-registry.js +65 -0
- package/dist/commands/audit.d.ts +0 -1
- package/dist/commands/audit.js +16 -6
- package/dist/commands/benchmark.d.ts +4 -0
- package/dist/commands/benchmark.js +178 -0
- package/dist/commands/clean.d.ts +2 -1
- package/dist/commands/clean.js +198 -47
- package/dist/commands/compact.d.ts +6 -0
- package/dist/commands/compact.js +239 -0
- package/dist/commands/completions.d.ts +3 -0
- package/dist/commands/completions.js +104 -0
- package/dist/commands/config.d.ts +0 -1
- package/dist/commands/config.js +15 -6
- package/dist/commands/create.d.ts +0 -1
- package/dist/commands/create.js +1 -1
- package/dist/commands/diff.d.ts +4 -0
- package/dist/commands/diff.js +166 -0
- package/dist/commands/doctor.d.ts +0 -1
- package/dist/commands/doctor.js +153 -24
- package/dist/commands/export-cmd.d.ts +4 -0
- package/dist/commands/export-cmd.js +66 -0
- package/dist/commands/import-cmd.d.ts +4 -0
- package/dist/commands/import-cmd.js +131 -0
- package/dist/commands/info.d.ts +0 -1
- package/dist/commands/info.js +29 -4
- package/dist/commands/init.d.ts +0 -1
- package/dist/commands/init.js +156 -117
- package/dist/commands/install.d.ts +1 -1
- package/dist/commands/install.js +118 -205
- package/dist/commands/list.d.ts +0 -1
- package/dist/commands/list.js +12 -4
- package/dist/commands/lock.d.ts +4 -0
- package/dist/commands/lock.js +171 -0
- package/dist/commands/optimize.d.ts +3 -0
- package/dist/commands/optimize.js +356 -0
- package/dist/commands/outdated.d.ts +4 -0
- package/dist/commands/outdated.js +159 -0
- package/dist/commands/profile.d.ts +3 -0
- package/dist/commands/profile.js +274 -0
- package/dist/commands/providers.d.ts +0 -1
- package/dist/commands/providers.js +1 -4
- package/dist/commands/recommend.d.ts +5 -0
- package/dist/commands/recommend.js +96 -0
- package/dist/commands/scan.d.ts +0 -1
- package/dist/commands/scan.js +13 -7
- package/dist/commands/search.d.ts +2 -1
- package/dist/commands/search.js +32 -9
- package/dist/commands/stats.d.ts +0 -1
- package/dist/commands/stats.js +83 -16
- package/dist/commands/team.d.ts +3 -0
- package/dist/commands/team.js +291 -0
- package/dist/commands/uninstall.d.ts +0 -1
- package/dist/commands/uninstall.js +18 -4
- package/dist/commands/update.d.ts +0 -1
- package/dist/commands/update.js +155 -155
- package/dist/commands/validate.d.ts +0 -1
- package/dist/commands/validate.js +14 -6
- package/dist/commands/verify.d.ts +4 -0
- package/dist/commands/verify.js +116 -0
- package/dist/constants.d.ts +10 -0
- package/dist/constants.js +13 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/interactive/browse.d.ts +4 -0
- package/dist/interactive/browse.js +103 -0
- package/dist/interactive/categories.d.ts +4 -0
- package/dist/interactive/categories.js +87 -0
- package/dist/interactive/health.d.ts +1 -0
- package/dist/interactive/health.js +57 -0
- package/dist/interactive/helpers.d.ts +11 -0
- package/dist/interactive/helpers.js +66 -0
- package/dist/interactive/index.d.ts +1 -0
- package/dist/interactive/index.js +1 -0
- package/dist/interactive/manage.d.ts +2 -0
- package/dist/interactive/manage.js +187 -0
- package/dist/interactive/menu.d.ts +1 -0
- package/dist/interactive/menu.js +107 -0
- package/dist/interactive/search.d.ts +2 -0
- package/dist/interactive/search.js +66 -0
- package/dist/interactive/setup.d.ts +2 -0
- package/dist/interactive/setup.js +48 -0
- package/dist/interactive/skill-detail.d.ts +5 -0
- package/dist/interactive/skill-detail.js +126 -0
- package/dist/interactive.d.ts +0 -1
- package/dist/interactive.js +89 -66
- package/dist/providers/arcana.d.ts +0 -1
- package/dist/providers/arcana.js +0 -1
- package/dist/providers/base.d.ts +0 -1
- package/dist/providers/base.js +0 -1
- package/dist/providers/github.d.ts +0 -1
- package/dist/providers/github.js +8 -3
- package/dist/registry.d.ts +0 -1
- package/dist/registry.js +1 -4
- package/dist/types.d.ts +10 -1
- package/dist/types.js +0 -1
- package/dist/utils/atomic.d.ts +0 -1
- package/dist/utils/atomic.js +3 -2
- package/dist/utils/cache.d.ts +0 -1
- package/dist/utils/cache.js +3 -2
- package/dist/utils/config.d.ts +2 -1
- package/dist/utils/config.js +30 -5
- package/dist/utils/conflict-check.d.ts +8 -0
- package/dist/utils/conflict-check.js +72 -0
- package/dist/utils/errors.d.ts +0 -1
- package/dist/utils/errors.js +0 -1
- package/dist/utils/frontmatter.d.ts +0 -1
- package/dist/utils/frontmatter.js +37 -10
- package/dist/utils/fs.d.ts +19 -1
- package/dist/utils/fs.js +105 -8
- package/dist/utils/help.d.ts +0 -1
- package/dist/utils/help.js +15 -28
- package/dist/utils/history.d.ts +0 -1
- package/dist/utils/history.js +0 -1
- package/dist/utils/http.d.ts +0 -1
- package/dist/utils/http.js +14 -5
- package/dist/utils/install-core.d.ts +48 -0
- package/dist/utils/install-core.js +108 -0
- package/dist/utils/integrity.d.ts +17 -0
- package/dist/utils/integrity.js +84 -0
- package/dist/utils/parallel.d.ts +0 -1
- package/dist/utils/parallel.js +0 -1
- package/dist/utils/project-context.d.ts +19 -0
- package/dist/utils/project-context.js +283 -0
- package/dist/utils/scanner.d.ts +0 -1
- package/dist/utils/scanner.js +138 -10
- package/dist/utils/scoring.d.ts +10 -0
- package/dist/utils/scoring.js +84 -0
- package/dist/utils/ui.d.ts +0 -1
- package/dist/utils/ui.js +11 -4
- package/dist/utils/validate.d.ts +0 -1
- package/dist/utils/validate.js +4 -1
- package/package.json +19 -7
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/commands/audit.d.ts.map +0 -1
- package/dist/commands/audit.js.map +0 -1
- package/dist/commands/audit.test.d.ts +0 -2
- package/dist/commands/audit.test.d.ts.map +0 -1
- package/dist/commands/audit.test.js +0 -217
- package/dist/commands/audit.test.js.map +0 -1
- package/dist/commands/clean.d.ts.map +0 -1
- package/dist/commands/clean.js.map +0 -1
- package/dist/commands/config.d.ts.map +0 -1
- package/dist/commands/config.js.map +0 -1
- package/dist/commands/create.d.ts.map +0 -1
- package/dist/commands/create.js.map +0 -1
- package/dist/commands/doctor.d.ts.map +0 -1
- package/dist/commands/doctor.js.map +0 -1
- package/dist/commands/info.d.ts.map +0 -1
- package/dist/commands/info.js.map +0 -1
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/install.d.ts.map +0 -1
- package/dist/commands/install.js.map +0 -1
- package/dist/commands/list.d.ts.map +0 -1
- package/dist/commands/list.js.map +0 -1
- package/dist/commands/providers.d.ts.map +0 -1
- package/dist/commands/providers.js.map +0 -1
- package/dist/commands/scan.d.ts.map +0 -1
- package/dist/commands/scan.js.map +0 -1
- package/dist/commands/search.d.ts.map +0 -1
- package/dist/commands/search.js.map +0 -1
- package/dist/commands/stats.d.ts.map +0 -1
- package/dist/commands/stats.js.map +0 -1
- package/dist/commands/uninstall.d.ts.map +0 -1
- package/dist/commands/uninstall.js.map +0 -1
- package/dist/commands/update.d.ts.map +0 -1
- package/dist/commands/update.js.map +0 -1
- package/dist/commands/validate.d.ts.map +0 -1
- package/dist/commands/validate.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/interactive.d.ts.map +0 -1
- package/dist/interactive.js.map +0 -1
- package/dist/providers/arcana.d.ts.map +0 -1
- package/dist/providers/arcana.js.map +0 -1
- package/dist/providers/base.d.ts.map +0 -1
- package/dist/providers/base.js.map +0 -1
- package/dist/providers/github.d.ts.map +0 -1
- package/dist/providers/github.js.map +0 -1
- package/dist/registry.d.ts.map +0 -1
- package/dist/registry.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/utils/atomic.d.ts.map +0 -1
- package/dist/utils/atomic.js.map +0 -1
- package/dist/utils/atomic.test.d.ts +0 -2
- package/dist/utils/atomic.test.d.ts.map +0 -1
- package/dist/utils/atomic.test.js +0 -31
- package/dist/utils/atomic.test.js.map +0 -1
- package/dist/utils/cache.d.ts.map +0 -1
- package/dist/utils/cache.js.map +0 -1
- package/dist/utils/config.d.ts.map +0 -1
- package/dist/utils/config.js.map +0 -1
- package/dist/utils/config.test.d.ts +0 -2
- package/dist/utils/config.test.d.ts.map +0 -1
- package/dist/utils/config.test.js +0 -38
- package/dist/utils/config.test.js.map +0 -1
- package/dist/utils/errors.d.ts.map +0 -1
- package/dist/utils/errors.js.map +0 -1
- package/dist/utils/frontmatter.d.ts.map +0 -1
- package/dist/utils/frontmatter.js.map +0 -1
- package/dist/utils/frontmatter.test.d.ts +0 -2
- package/dist/utils/frontmatter.test.d.ts.map +0 -1
- package/dist/utils/frontmatter.test.js +0 -152
- package/dist/utils/frontmatter.test.js.map +0 -1
- package/dist/utils/fs.d.ts.map +0 -1
- package/dist/utils/fs.js.map +0 -1
- package/dist/utils/fs.test.d.ts +0 -2
- package/dist/utils/fs.test.d.ts.map +0 -1
- package/dist/utils/fs.test.js +0 -145
- package/dist/utils/fs.test.js.map +0 -1
- package/dist/utils/help.d.ts.map +0 -1
- package/dist/utils/help.js.map +0 -1
- package/dist/utils/help.test.d.ts +0 -2
- package/dist/utils/help.test.d.ts.map +0 -1
- package/dist/utils/help.test.js +0 -66
- package/dist/utils/help.test.js.map +0 -1
- package/dist/utils/history.d.ts.map +0 -1
- package/dist/utils/history.js.map +0 -1
- package/dist/utils/http.d.ts.map +0 -1
- package/dist/utils/http.js.map +0 -1
- package/dist/utils/http.test.d.ts +0 -2
- package/dist/utils/http.test.d.ts.map +0 -1
- package/dist/utils/http.test.js +0 -55
- package/dist/utils/http.test.js.map +0 -1
- package/dist/utils/parallel.d.ts.map +0 -1
- package/dist/utils/parallel.js.map +0 -1
- package/dist/utils/scanner.d.ts.map +0 -1
- package/dist/utils/scanner.js.map +0 -1
- package/dist/utils/ui.d.ts.map +0 -1
- package/dist/utils/ui.js.map +0 -1
- package/dist/utils/ui.test.d.ts +0 -2
- package/dist/utils/ui.test.d.ts.map +0 -1
- package/dist/utils/ui.test.js +0 -31
- package/dist/utils/ui.test.js.map +0 -1
- package/dist/utils/validate.d.ts.map +0 -1
- package/dist/utils/validate.js.map +0 -1
package/dist/commands/stats.js
CHANGED
|
@@ -3,6 +3,7 @@ import { join } from "node:path";
|
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { ui, banner, table } from "../utils/ui.js";
|
|
5
5
|
import { readHistory } from "../utils/history.js";
|
|
6
|
+
import { getDirSize } from "../utils/fs.js";
|
|
6
7
|
function discoverSessions() {
|
|
7
8
|
const projectsDir = join(homedir(), ".claude", "projects");
|
|
8
9
|
if (!existsSync(projectsDir))
|
|
@@ -58,6 +59,44 @@ function formatBytes(bytes) {
|
|
|
58
59
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
59
60
|
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
|
60
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
|
+
}
|
|
61
100
|
export async function statsCommand(opts) {
|
|
62
101
|
if (!opts.json)
|
|
63
102
|
banner();
|
|
@@ -77,9 +116,7 @@ export async function statsCommand(opts) {
|
|
|
77
116
|
const avgLines = Math.round(totalLines / sessions.length);
|
|
78
117
|
// Rough token estimate: ~4 chars per token for LLM tokenizers
|
|
79
118
|
const estimatedTokens = Math.round(totalSize / 4);
|
|
80
|
-
// Find unique projects
|
|
81
119
|
const projects = new Set(sessions.map((s) => s.project));
|
|
82
|
-
// Find most recent and oldest sessions
|
|
83
120
|
const sorted = [...sessions].sort((a, b) => b.modified.getTime() - a.modified.getTime());
|
|
84
121
|
const newest = sorted[0];
|
|
85
122
|
const oldest = sorted[sorted.length - 1];
|
|
@@ -88,9 +125,16 @@ export async function statsCommand(opts) {
|
|
|
88
125
|
for (const s of sessions) {
|
|
89
126
|
projectCounts.set(s.project, (projectCounts.get(s.project) ?? 0) + 1);
|
|
90
127
|
}
|
|
91
|
-
const topProjects = [...projectCounts.entries()]
|
|
92
|
-
|
|
93
|
-
|
|
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);
|
|
94
138
|
if (opts.json) {
|
|
95
139
|
const data = {
|
|
96
140
|
totalSessions: sessions.length,
|
|
@@ -98,10 +142,12 @@ export async function statsCommand(opts) {
|
|
|
98
142
|
totalSizeBytes: totalSize,
|
|
99
143
|
estimatedTokens,
|
|
100
144
|
avgLinesPerSession: avgLines,
|
|
101
|
-
topProjects: topProjects.map(([name, count]) => ({
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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,
|
|
105
151
|
};
|
|
106
152
|
console.log(JSON.stringify(data, null, 2));
|
|
107
153
|
return;
|
|
@@ -110,13 +156,34 @@ export async function statsCommand(opts) {
|
|
|
110
156
|
const rows = [
|
|
111
157
|
[ui.dim("Sessions"), String(sessions.length)],
|
|
112
158
|
[ui.dim("Projects"), String(projects.size)],
|
|
113
|
-
[ui.dim("Total data"), formatBytes(totalSize)],
|
|
159
|
+
[ui.dim("Total session data"), formatBytes(totalSize)],
|
|
114
160
|
[ui.dim("Est. tokens"), `~${(estimatedTokens / 1_000_000).toFixed(1)}M (rough)`],
|
|
115
161
|
[ui.dim("Avg lines/session"), String(avgLines)],
|
|
116
162
|
[ui.dim("Newest session"), newest.modified.toLocaleDateString()],
|
|
117
163
|
[ui.dim("Oldest session"), oldest.modified.toLocaleDateString()],
|
|
118
164
|
];
|
|
119
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
|
+
}
|
|
120
187
|
if (topProjects.length > 0) {
|
|
121
188
|
console.log();
|
|
122
189
|
console.log(ui.bold(" Most Active Projects\n"));
|
|
@@ -126,18 +193,18 @@ export async function statsCommand(opts) {
|
|
|
126
193
|
]);
|
|
127
194
|
table(projRows);
|
|
128
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
|
+
}
|
|
129
201
|
const history = readHistory();
|
|
130
202
|
if (history.length > 0) {
|
|
131
203
|
console.log();
|
|
132
204
|
console.log(ui.bold(" Recent Arcana Activity\n"));
|
|
133
205
|
const recent = history.slice(-5).reverse();
|
|
134
|
-
const histRows = recent.map(e => [
|
|
135
|
-
ui.dim(new Date(e.timestamp).toLocaleDateString()),
|
|
136
|
-
e.action,
|
|
137
|
-
e.target ?? "",
|
|
138
|
-
]);
|
|
206
|
+
const histRows = recent.map((e) => [ui.dim(new Date(e.timestamp).toLocaleDateString()), e.action, e.target ?? ""]);
|
|
139
207
|
table(histRows);
|
|
140
208
|
}
|
|
141
209
|
console.log();
|
|
142
210
|
}
|
|
143
|
-
//# sourceMappingURL=stats.js.map
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { existsSync, readFileSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { atomicWriteSync } from "../utils/atomic.js";
|
|
4
|
+
import { isSkillInstalled, readSkillMeta } from "../utils/fs.js";
|
|
5
|
+
import { validateSlug } from "../utils/validate.js";
|
|
6
|
+
const TEAM_DIR = ".arcana";
|
|
7
|
+
const TEAM_FILE = "team.json";
|
|
8
|
+
function getTeamConfigPath() {
|
|
9
|
+
return join(process.cwd(), TEAM_DIR, TEAM_FILE);
|
|
10
|
+
}
|
|
11
|
+
function readTeamConfig() {
|
|
12
|
+
const configPath = getTeamConfigPath();
|
|
13
|
+
if (!existsSync(configPath))
|
|
14
|
+
return null;
|
|
15
|
+
try {
|
|
16
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
17
|
+
return JSON.parse(raw);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function writeTeamConfig(config) {
|
|
24
|
+
const dir = join(process.cwd(), TEAM_DIR);
|
|
25
|
+
if (!existsSync(dir)) {
|
|
26
|
+
mkdirSync(dir, { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
atomicWriteSync(getTeamConfigPath(), JSON.stringify(config, null, 2) + "\n", 0o644);
|
|
29
|
+
}
|
|
30
|
+
function output(json, message) {
|
|
31
|
+
if (!json) {
|
|
32
|
+
console.log(message);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export async function teamCommand(action, skill, opts) {
|
|
36
|
+
if (action === "init") {
|
|
37
|
+
return teamInit(opts);
|
|
38
|
+
}
|
|
39
|
+
if (action === "sync") {
|
|
40
|
+
return teamSync(opts);
|
|
41
|
+
}
|
|
42
|
+
if (action === "add") {
|
|
43
|
+
if (!skill) {
|
|
44
|
+
if (opts.json) {
|
|
45
|
+
console.log(JSON.stringify({ error: "Skill name required for add" }));
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
console.error("Error: Skill name required. Usage: arcana team add <skill>");
|
|
49
|
+
}
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
return teamAdd(skill, opts);
|
|
53
|
+
}
|
|
54
|
+
if (action === "remove") {
|
|
55
|
+
if (!skill) {
|
|
56
|
+
if (opts.json) {
|
|
57
|
+
console.log(JSON.stringify({ error: "Skill name required for remove" }));
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
console.error("Error: Skill name required. Usage: arcana team remove <skill>");
|
|
61
|
+
}
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
return teamRemove(skill, opts);
|
|
65
|
+
}
|
|
66
|
+
if (action === undefined) {
|
|
67
|
+
return teamList(opts);
|
|
68
|
+
}
|
|
69
|
+
if (opts.json) {
|
|
70
|
+
console.log(JSON.stringify({ error: `Unknown action: ${action}` }));
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
console.error(`Error: Unknown action "${action}". Valid actions: init, sync, add, remove`);
|
|
74
|
+
}
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
function teamInit(opts) {
|
|
78
|
+
const configPath = getTeamConfigPath();
|
|
79
|
+
if (existsSync(configPath)) {
|
|
80
|
+
if (opts.json) {
|
|
81
|
+
console.log(JSON.stringify({ error: "Team config already exists", path: configPath }));
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
console.error(`Error: ${configPath} already exists.`);
|
|
85
|
+
}
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
const config = {
|
|
89
|
+
skills: [],
|
|
90
|
+
updatedAt: new Date().toISOString(),
|
|
91
|
+
};
|
|
92
|
+
writeTeamConfig(config);
|
|
93
|
+
if (opts.json) {
|
|
94
|
+
console.log(JSON.stringify({ created: configPath }));
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
console.log(`Created ${configPath}`);
|
|
98
|
+
console.log("Add skills with: arcana team add <skill-name>");
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async function teamSync(opts) {
|
|
102
|
+
const config = readTeamConfig();
|
|
103
|
+
if (!config) {
|
|
104
|
+
if (opts.json) {
|
|
105
|
+
console.log(JSON.stringify({ error: "No team config found. Run: arcana team init" }));
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
console.error("Error: No team config found. Run: arcana team init");
|
|
109
|
+
}
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
if (config.skills.length === 0) {
|
|
113
|
+
if (opts.json) {
|
|
114
|
+
console.log(JSON.stringify({ installed: [], skipped: [], message: "No skills in team config" }));
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
console.log("No skills listed in team config.");
|
|
118
|
+
}
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const { installSkill } = await import("../utils/fs.js");
|
|
122
|
+
const installed = [];
|
|
123
|
+
const skipped = [];
|
|
124
|
+
const failed = [];
|
|
125
|
+
for (const entry of config.skills) {
|
|
126
|
+
if (isSkillInstalled(entry.name)) {
|
|
127
|
+
skipped.push(entry.name);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
validateSlug(entry.name, "skill name");
|
|
132
|
+
}
|
|
133
|
+
catch (_err) {
|
|
134
|
+
output(opts.json, `Invalid skill name: ${entry.name}`);
|
|
135
|
+
failed.push(entry.name);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
// installSkill requires files, but for sync we re-fetch from provider
|
|
139
|
+
// Lazy import the registry to avoid circular deps at module load
|
|
140
|
+
try {
|
|
141
|
+
const { getProvider } = await import("../registry.js");
|
|
142
|
+
const { loadConfig } = await import("../utils/config.js");
|
|
143
|
+
const { updateLockEntry } = await import("../utils/integrity.js");
|
|
144
|
+
const { writeSkillMeta } = await import("../utils/fs.js");
|
|
145
|
+
const providerName = entry.source ?? loadConfig().defaultProvider;
|
|
146
|
+
const provider = getProvider(providerName);
|
|
147
|
+
const files = await provider.fetch(entry.name);
|
|
148
|
+
installSkill(entry.name, files);
|
|
149
|
+
const remote = await provider.info(entry.name);
|
|
150
|
+
const version = remote?.version ?? entry.version ?? "0.0.0";
|
|
151
|
+
writeSkillMeta(entry.name, {
|
|
152
|
+
version,
|
|
153
|
+
installedAt: new Date().toISOString(),
|
|
154
|
+
source: provider.name,
|
|
155
|
+
description: remote?.description,
|
|
156
|
+
fileCount: files.length,
|
|
157
|
+
sizeBytes: files.reduce((s, f) => s + f.content.length, 0),
|
|
158
|
+
});
|
|
159
|
+
updateLockEntry(entry.name, version, provider.name, files);
|
|
160
|
+
installed.push(entry.name);
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
const msg = err instanceof Error ? err.message : "unknown error";
|
|
164
|
+
output(opts.json, `Failed to install ${entry.name}: ${msg}`);
|
|
165
|
+
failed.push(entry.name);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (opts.json) {
|
|
169
|
+
console.log(JSON.stringify({ installed, skipped, failed }));
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
console.log(`Sync complete: ${installed.length} installed, ${skipped.length} skipped, ${failed.length} failed`);
|
|
173
|
+
}
|
|
174
|
+
if (failed.length > 0)
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
function teamAdd(skill, opts) {
|
|
178
|
+
try {
|
|
179
|
+
validateSlug(skill, "skill name");
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
if (opts.json) {
|
|
183
|
+
console.log(JSON.stringify({ error: err instanceof Error ? err.message : "Invalid skill name" }));
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
console.error(err instanceof Error ? err.message : "Invalid skill name");
|
|
187
|
+
}
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
const config = readTeamConfig();
|
|
191
|
+
if (!config) {
|
|
192
|
+
if (opts.json) {
|
|
193
|
+
console.log(JSON.stringify({ error: "No team config found. Run: arcana team init" }));
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
console.error("Error: No team config found. Run: arcana team init");
|
|
197
|
+
}
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
const exists = config.skills.find((s) => s.name === skill);
|
|
201
|
+
if (exists) {
|
|
202
|
+
if (opts.json) {
|
|
203
|
+
console.log(JSON.stringify({ error: `${skill} is already in team config` }));
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
console.error(`Error: ${skill} is already in team config.`);
|
|
207
|
+
}
|
|
208
|
+
process.exit(1);
|
|
209
|
+
}
|
|
210
|
+
const entry = { name: skill };
|
|
211
|
+
// If skill is installed locally, read meta for version/source
|
|
212
|
+
const meta = readSkillMeta(skill);
|
|
213
|
+
if (meta) {
|
|
214
|
+
if (meta.version)
|
|
215
|
+
entry.version = meta.version;
|
|
216
|
+
if (meta.source)
|
|
217
|
+
entry.source = meta.source;
|
|
218
|
+
}
|
|
219
|
+
config.skills.push(entry);
|
|
220
|
+
config.updatedAt = new Date().toISOString();
|
|
221
|
+
writeTeamConfig(config);
|
|
222
|
+
if (opts.json) {
|
|
223
|
+
console.log(JSON.stringify({ added: entry }));
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
console.log(`Added ${skill} to team config.`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
function teamRemove(skill, opts) {
|
|
230
|
+
const config = readTeamConfig();
|
|
231
|
+
if (!config) {
|
|
232
|
+
if (opts.json) {
|
|
233
|
+
console.log(JSON.stringify({ error: "No team config found. Run: arcana team init" }));
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
console.error("Error: No team config found. Run: arcana team init");
|
|
237
|
+
}
|
|
238
|
+
process.exit(1);
|
|
239
|
+
}
|
|
240
|
+
const idx = config.skills.findIndex((s) => s.name === skill);
|
|
241
|
+
if (idx < 0) {
|
|
242
|
+
if (opts.json) {
|
|
243
|
+
console.log(JSON.stringify({ error: `${skill} is not in team config` }));
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
console.error(`Error: ${skill} is not in team config.`);
|
|
247
|
+
}
|
|
248
|
+
process.exit(1);
|
|
249
|
+
}
|
|
250
|
+
config.skills.splice(idx, 1);
|
|
251
|
+
config.updatedAt = new Date().toISOString();
|
|
252
|
+
writeTeamConfig(config);
|
|
253
|
+
if (opts.json) {
|
|
254
|
+
console.log(JSON.stringify({ removed: skill }));
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
console.log(`Removed ${skill} from team config.`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
function teamList(opts) {
|
|
261
|
+
const config = readTeamConfig();
|
|
262
|
+
if (!config) {
|
|
263
|
+
if (opts.json) {
|
|
264
|
+
console.log(JSON.stringify({ error: "No team config found. Run: arcana team init" }));
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
console.error("Error: No team config found. Run: arcana team init");
|
|
268
|
+
}
|
|
269
|
+
process.exit(1);
|
|
270
|
+
}
|
|
271
|
+
if (opts.json) {
|
|
272
|
+
console.log(JSON.stringify({ skills: config.skills, updatedAt: config.updatedAt }));
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
if (config.skills.length === 0) {
|
|
276
|
+
console.log("No skills in team config. Add one with: arcana team add <skill>");
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
console.log(`Team skills (${config.skills.length}):`);
|
|
280
|
+
for (const entry of config.skills) {
|
|
281
|
+
const parts = [entry.name];
|
|
282
|
+
if (entry.version)
|
|
283
|
+
parts.push(`v${entry.version}`);
|
|
284
|
+
if (entry.source)
|
|
285
|
+
parts.push(`(${entry.source})`);
|
|
286
|
+
const installed = isSkillInstalled(entry.name);
|
|
287
|
+
if (installed)
|
|
288
|
+
parts.push("[installed]");
|
|
289
|
+
console.log(` ${parts.join(" ")}`);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
@@ -125,7 +125,12 @@ function uninstallJson(skillNames) {
|
|
|
125
125
|
validateSlug(skillName, "skill name");
|
|
126
126
|
}
|
|
127
127
|
catch (err) {
|
|
128
|
-
results.push({
|
|
128
|
+
results.push({
|
|
129
|
+
name: skillName,
|
|
130
|
+
success: false,
|
|
131
|
+
error: err instanceof Error ? err.message : "Invalid name",
|
|
132
|
+
symlinksRemoved: 0,
|
|
133
|
+
});
|
|
129
134
|
continue;
|
|
130
135
|
}
|
|
131
136
|
const skillDir = getSkillDir(skillName);
|
|
@@ -139,7 +144,13 @@ function uninstallJson(skillNames) {
|
|
|
139
144
|
rmSync(skillDir, { recursive: true, force: true });
|
|
140
145
|
}
|
|
141
146
|
catch (err) {
|
|
142
|
-
results.push({
|
|
147
|
+
results.push({
|
|
148
|
+
name: skillName,
|
|
149
|
+
success: false,
|
|
150
|
+
version: meta?.version,
|
|
151
|
+
error: err instanceof Error ? err.message : "Failed to remove",
|
|
152
|
+
symlinksRemoved: 0,
|
|
153
|
+
});
|
|
143
154
|
continue;
|
|
144
155
|
}
|
|
145
156
|
const symlinksRemoved = removeSymlinksFor(skillName);
|
|
@@ -147,7 +158,11 @@ function uninstallJson(skillNames) {
|
|
|
147
158
|
}
|
|
148
159
|
if (results.length === 1) {
|
|
149
160
|
const r = results[0];
|
|
150
|
-
const output = {
|
|
161
|
+
const output = {
|
|
162
|
+
uninstalled: r.name,
|
|
163
|
+
success: r.success,
|
|
164
|
+
symlinksRemoved: r.symlinksRemoved,
|
|
165
|
+
};
|
|
151
166
|
if (r.version)
|
|
152
167
|
output.version = r.version;
|
|
153
168
|
if (r.error)
|
|
@@ -160,4 +175,3 @@ function uninstallJson(skillNames) {
|
|
|
160
175
|
if (results.some((r) => !r.success))
|
|
161
176
|
process.exit(1);
|
|
162
177
|
}
|
|
163
|
-
//# sourceMappingURL=uninstall.js.map
|