@olorehq/olore 0.1.2 → 0.1.3
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 +723 -371
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3,107 +3,25 @@
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { createRequire } from "module";
|
|
5
5
|
import { Command } from "commander";
|
|
6
|
-
import
|
|
6
|
+
import pc9 from "picocolors";
|
|
7
7
|
|
|
8
|
-
// src/commands/
|
|
9
|
-
import fs from "fs";
|
|
10
|
-
import path from "path";
|
|
11
|
-
import readline from "readline";
|
|
8
|
+
// src/commands/doctor.ts
|
|
12
9
|
import pc from "picocolors";
|
|
13
|
-
async function prompt(question, defaultValue) {
|
|
14
|
-
const rl = readline.createInterface({
|
|
15
|
-
input: process.stdin,
|
|
16
|
-
output: process.stdout
|
|
17
|
-
});
|
|
18
|
-
return new Promise((resolve) => {
|
|
19
|
-
const q = defaultValue ? `${question} (${defaultValue}): ` : `${question}: `;
|
|
20
|
-
rl.question(q, (answer) => {
|
|
21
|
-
rl.close();
|
|
22
|
-
resolve(answer || defaultValue || "");
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
async function init(options) {
|
|
27
|
-
const cwd = process.cwd();
|
|
28
|
-
const folderName = path.basename(cwd);
|
|
29
|
-
if (fs.existsSync(path.join(cwd, "olore.config.json"))) {
|
|
30
|
-
throw new Error("olore.config.json already exists. This folder is already initialized.");
|
|
31
|
-
}
|
|
32
|
-
let name;
|
|
33
|
-
let version2;
|
|
34
|
-
let description;
|
|
35
|
-
if (options.yes) {
|
|
36
|
-
name = options.name || folderName;
|
|
37
|
-
version2 = options.version || "1.0.0";
|
|
38
|
-
description = `Documentation for ${name}`;
|
|
39
|
-
} else {
|
|
40
|
-
console.log(pc.bold("\nInitialize olore documentation package\n"));
|
|
41
|
-
name = await prompt("Package name", options.name || folderName);
|
|
42
|
-
version2 = await prompt("Version", options.version || "1.0.0");
|
|
43
|
-
description = await prompt("Description", `Documentation for ${name}`);
|
|
44
|
-
}
|
|
45
|
-
const fullName = `olore-${name}-${version2}`;
|
|
46
|
-
const configContent = {
|
|
47
|
-
name,
|
|
48
|
-
version: version2,
|
|
49
|
-
description,
|
|
50
|
-
contentPath: "./docs",
|
|
51
|
-
outputPath: "./olore-package",
|
|
52
|
-
extensions: [".md", ".mdx"],
|
|
53
|
-
exclude: []
|
|
54
|
-
};
|
|
55
|
-
fs.writeFileSync(
|
|
56
|
-
path.join(cwd, "olore.config.json"),
|
|
57
|
-
JSON.stringify(configContent, null, 2) + "\n"
|
|
58
|
-
);
|
|
59
|
-
const docsDir = path.join(cwd, "docs");
|
|
60
|
-
if (!fs.existsSync(docsDir)) {
|
|
61
|
-
fs.mkdirSync(docsDir);
|
|
62
|
-
fs.writeFileSync(
|
|
63
|
-
path.join(docsDir, "getting-started.md"),
|
|
64
|
-
`# Getting Started with ${name}
|
|
65
|
-
|
|
66
|
-
Add your documentation content here.
|
|
67
|
-
|
|
68
|
-
## Overview
|
|
69
|
-
|
|
70
|
-
Describe what this documentation covers.
|
|
71
|
-
|
|
72
|
-
## Topics
|
|
73
|
-
|
|
74
|
-
- Topic 1
|
|
75
|
-
- Topic 2
|
|
76
|
-
`
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
console.log(pc.bold(`
|
|
80
|
-
Initialized olore package: `) + pc.cyan(fullName));
|
|
81
|
-
console.log("");
|
|
82
|
-
console.log(pc.gray("Created:"));
|
|
83
|
-
console.log(pc.green(" \u2713 olore.config.json"));
|
|
84
|
-
console.log(pc.green(" \u2713 docs/") + pc.gray(" (add your documentation here)"));
|
|
85
|
-
console.log("");
|
|
86
|
-
console.log(pc.gray("Next steps:"));
|
|
87
|
-
console.log(" 1. Add your .md files to the " + pc.cyan("docs/") + " folder");
|
|
88
|
-
console.log(" 2. Run " + pc.cyan("/olore-docs-packager-1.0.0") + " in Claude Code to build");
|
|
89
|
-
console.log(" 3. Run " + pc.cyan("olore install ./olore-package") + " to install");
|
|
90
|
-
console.log("");
|
|
91
|
-
}
|
|
92
10
|
|
|
93
|
-
// src/
|
|
94
|
-
import
|
|
95
|
-
import
|
|
96
|
-
import
|
|
97
|
-
import
|
|
11
|
+
// src/core/doctor.ts
|
|
12
|
+
import fs4 from "fs";
|
|
13
|
+
import os4 from "os";
|
|
14
|
+
import path4 from "path";
|
|
15
|
+
import fsExtra from "fs-extra";
|
|
98
16
|
|
|
99
17
|
// src/core/download.ts
|
|
100
18
|
import { createHash } from "crypto";
|
|
101
19
|
import { createWriteStream } from "fs";
|
|
102
20
|
import os from "os";
|
|
103
|
-
import
|
|
21
|
+
import path from "path";
|
|
104
22
|
import { Readable } from "stream";
|
|
105
23
|
import { pipeline } from "stream/promises";
|
|
106
|
-
import
|
|
24
|
+
import fs from "fs-extra";
|
|
107
25
|
import * as tar from "tar";
|
|
108
26
|
|
|
109
27
|
// src/core/constants.ts
|
|
@@ -138,7 +56,7 @@ async function downloadFile(url, dest) {
|
|
|
138
56
|
if (!response.body) {
|
|
139
57
|
throw new DownloadError("No response body", "NETWORK_ERROR");
|
|
140
58
|
}
|
|
141
|
-
await
|
|
59
|
+
await fs.ensureDir(path.dirname(dest));
|
|
142
60
|
const nodeReadable = Readable.fromWeb(response.body);
|
|
143
61
|
const writeStream = createWriteStream(dest);
|
|
144
62
|
await pipeline(nodeReadable, writeStream);
|
|
@@ -159,7 +77,7 @@ async function downloadFile(url, dest) {
|
|
|
159
77
|
}
|
|
160
78
|
async function calculateChecksum(filePath) {
|
|
161
79
|
const hash = createHash("sha256");
|
|
162
|
-
const stream =
|
|
80
|
+
const stream = fs.createReadStream(filePath);
|
|
163
81
|
return new Promise((resolve, reject) => {
|
|
164
82
|
stream.on("data", (data) => hash.update(data));
|
|
165
83
|
stream.on("end", () => resolve(`sha256-${hash.digest("base64")}`));
|
|
@@ -171,7 +89,7 @@ async function verifyChecksum(filePath, expected) {
|
|
|
171
89
|
return actual === expected;
|
|
172
90
|
}
|
|
173
91
|
async function extractTarball(tarball, dest) {
|
|
174
|
-
await
|
|
92
|
+
await fs.ensureDir(dest);
|
|
175
93
|
try {
|
|
176
94
|
await tar.extract({
|
|
177
95
|
file: tarball,
|
|
@@ -187,8 +105,8 @@ async function extractTarball(tarball, dest) {
|
|
|
187
105
|
}
|
|
188
106
|
}
|
|
189
107
|
async function downloadAndInstall(url, dest, integrity) {
|
|
190
|
-
const tempDir = await
|
|
191
|
-
const tarballPath =
|
|
108
|
+
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "olore-"));
|
|
109
|
+
const tarballPath = path.join(tempDir, "package.tar.gz");
|
|
192
110
|
try {
|
|
193
111
|
await downloadFile(url, tarballPath);
|
|
194
112
|
const checksumValid = await verifyChecksum(tarballPath, integrity);
|
|
@@ -201,37 +119,12 @@ Actual: ${actual}`,
|
|
|
201
119
|
"CHECKSUM_MISMATCH"
|
|
202
120
|
);
|
|
203
121
|
}
|
|
204
|
-
await
|
|
122
|
+
await fs.remove(dest);
|
|
205
123
|
await extractTarball(tarballPath, dest);
|
|
206
124
|
return dest;
|
|
207
125
|
} finally {
|
|
208
|
-
await
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
async function downloadAndExtractToTemp(url, integrity) {
|
|
213
|
-
const tempDir = await fs2.mkdtemp(path2.join(os.tmpdir(), "olore-"));
|
|
214
|
-
const tarballPath = path2.join(tempDir, "package.tar.gz");
|
|
215
|
-
const extractDir = path2.join(tempDir, "package");
|
|
216
|
-
try {
|
|
217
|
-
await downloadFile(url, tarballPath);
|
|
218
|
-
const checksumValid = await verifyChecksum(tarballPath, integrity);
|
|
219
|
-
if (!checksumValid) {
|
|
220
|
-
const actual = await calculateChecksum(tarballPath);
|
|
221
|
-
throw new DownloadError(
|
|
222
|
-
`Checksum verification failed.
|
|
223
|
-
Expected: ${integrity}
|
|
224
|
-
Actual: ${actual}`,
|
|
225
|
-
"CHECKSUM_MISMATCH"
|
|
226
|
-
);
|
|
227
|
-
}
|
|
228
|
-
await extractTarball(tarballPath, extractDir);
|
|
229
|
-
await fs2.remove(tarballPath);
|
|
230
|
-
return extractDir;
|
|
231
|
-
} catch (error) {
|
|
232
|
-
await fs2.remove(tempDir).catch(() => {
|
|
126
|
+
await fs.remove(tempDir).catch(() => {
|
|
233
127
|
});
|
|
234
|
-
throw error;
|
|
235
128
|
}
|
|
236
129
|
}
|
|
237
130
|
function formatBytes(bytes) {
|
|
@@ -243,28 +136,32 @@ function formatBytes(bytes) {
|
|
|
243
136
|
}
|
|
244
137
|
|
|
245
138
|
// src/core/paths.ts
|
|
246
|
-
import
|
|
139
|
+
import fs3 from "fs";
|
|
247
140
|
import os3 from "os";
|
|
248
|
-
import
|
|
141
|
+
import path3 from "path";
|
|
249
142
|
import { fileURLToPath } from "url";
|
|
250
143
|
|
|
251
144
|
// src/core/platform.ts
|
|
252
145
|
import os2 from "os";
|
|
253
|
-
import
|
|
254
|
-
import
|
|
146
|
+
import path2 from "path";
|
|
147
|
+
import fs2 from "fs-extra";
|
|
255
148
|
var isWindows = process.platform === "win32";
|
|
256
149
|
async function linkOrCopy(source, target) {
|
|
257
150
|
if (isWindows) {
|
|
258
|
-
|
|
151
|
+
try {
|
|
152
|
+
await fs2.symlink(source, target, "junction");
|
|
153
|
+
} catch {
|
|
154
|
+
await fs2.copy(source, target);
|
|
155
|
+
}
|
|
259
156
|
} else {
|
|
260
|
-
await
|
|
157
|
+
await fs2.symlink(source, target, "dir");
|
|
261
158
|
}
|
|
262
159
|
}
|
|
263
160
|
function expandPath(p) {
|
|
264
161
|
if (p.startsWith("~")) {
|
|
265
|
-
return
|
|
162
|
+
return path2.join(os2.homedir(), p.slice(1));
|
|
266
163
|
}
|
|
267
|
-
return
|
|
164
|
+
return path2.resolve(p);
|
|
268
165
|
}
|
|
269
166
|
function isLocalPath(p) {
|
|
270
167
|
if (p.startsWith(".") || p.startsWith("/") || p.startsWith("~")) {
|
|
@@ -285,33 +182,33 @@ function pathStartsWith(child, parent) {
|
|
|
285
182
|
return child.startsWith(parent);
|
|
286
183
|
}
|
|
287
184
|
function getLinkActionText() {
|
|
288
|
-
return
|
|
185
|
+
return "Linking";
|
|
289
186
|
}
|
|
290
187
|
function getLinkTypeText() {
|
|
291
|
-
return
|
|
188
|
+
return "linked to";
|
|
292
189
|
}
|
|
293
190
|
|
|
294
191
|
// src/core/paths.ts
|
|
295
192
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
296
|
-
var __dirname2 =
|
|
193
|
+
var __dirname2 = path3.dirname(__filename2);
|
|
297
194
|
function getOloreHome() {
|
|
298
|
-
return
|
|
195
|
+
return path3.join(os3.homedir(), ".olore");
|
|
299
196
|
}
|
|
300
197
|
function getOlorePackagePath(name, version2) {
|
|
301
|
-
return
|
|
198
|
+
return path3.join(getOloreHome(), "packages", name, version2);
|
|
302
199
|
}
|
|
303
200
|
function isInstalledPackage(symlinkPath) {
|
|
304
201
|
try {
|
|
305
|
-
const target =
|
|
202
|
+
const target = fs3.readlinkSync(symlinkPath);
|
|
306
203
|
const oloreHome = getOloreHome();
|
|
307
204
|
return pathStartsWith(target, oloreHome);
|
|
308
205
|
} catch {
|
|
309
|
-
return
|
|
206
|
+
return fs3.existsSync(symlinkPath) && fs3.statSync(symlinkPath).isDirectory();
|
|
310
207
|
}
|
|
311
208
|
}
|
|
312
209
|
function getSymlinkTarget(symlinkPath) {
|
|
313
210
|
try {
|
|
314
|
-
return
|
|
211
|
+
return fs3.readlinkSync(symlinkPath);
|
|
315
212
|
} catch {
|
|
316
213
|
return null;
|
|
317
214
|
}
|
|
@@ -319,17 +216,17 @@ function getSymlinkTarget(symlinkPath) {
|
|
|
319
216
|
function getAgentPaths() {
|
|
320
217
|
const home = os3.homedir();
|
|
321
218
|
return {
|
|
322
|
-
claude:
|
|
323
|
-
codex:
|
|
324
|
-
opencode:
|
|
219
|
+
claude: path3.join(home, ".claude", "skills"),
|
|
220
|
+
codex: path3.join(home, ".codex", "skills"),
|
|
221
|
+
opencode: path3.join(home, ".config", "opencode", "skills")
|
|
325
222
|
};
|
|
326
223
|
}
|
|
327
224
|
function detectAgents() {
|
|
328
225
|
const agents = [];
|
|
329
226
|
const paths = getAgentPaths();
|
|
330
227
|
for (const [agent, agentPath] of Object.entries(paths)) {
|
|
331
|
-
const parentDir =
|
|
332
|
-
if (
|
|
228
|
+
const parentDir = path3.dirname(agentPath);
|
|
229
|
+
if (fs3.existsSync(parentDir)) {
|
|
333
230
|
agents.push(agent);
|
|
334
231
|
}
|
|
335
232
|
}
|
|
@@ -340,18 +237,18 @@ async function getInstalledPackages() {
|
|
|
340
237
|
const packages = [];
|
|
341
238
|
const seen = /* @__PURE__ */ new Set();
|
|
342
239
|
for (const [agent, skillsDir] of Object.entries(agentPaths)) {
|
|
343
|
-
if (!
|
|
240
|
+
if (!fs3.existsSync(skillsDir)) {
|
|
344
241
|
continue;
|
|
345
242
|
}
|
|
346
|
-
const entries =
|
|
243
|
+
const entries = fs3.readdirSync(skillsDir, { withFileTypes: true });
|
|
347
244
|
for (const entry of entries) {
|
|
348
245
|
const isValidEntry = entry.isDirectory() || entry.isSymbolicLink();
|
|
349
246
|
if (!isValidEntry || !entry.name.startsWith("olore-")) {
|
|
350
247
|
continue;
|
|
351
248
|
}
|
|
352
|
-
const pkgPath =
|
|
353
|
-
const lockPath =
|
|
354
|
-
if (!
|
|
249
|
+
const pkgPath = path3.join(skillsDir, entry.name);
|
|
250
|
+
const lockPath = path3.join(pkgPath, "olore-lock.json");
|
|
251
|
+
if (!fs3.existsSync(lockPath)) {
|
|
355
252
|
continue;
|
|
356
253
|
}
|
|
357
254
|
const pkgName = entry.name;
|
|
@@ -360,7 +257,7 @@ async function getInstalledPackages() {
|
|
|
360
257
|
}
|
|
361
258
|
seen.add(pkgName);
|
|
362
259
|
try {
|
|
363
|
-
const lock = JSON.parse(
|
|
260
|
+
const lock = JSON.parse(fs3.readFileSync(lockPath, "utf-8"));
|
|
364
261
|
const stats = await getDirectoryStats(pkgPath);
|
|
365
262
|
const symlinkTarget = getSymlinkTarget(pkgPath);
|
|
366
263
|
let installType;
|
|
@@ -389,14 +286,14 @@ async function getDirectoryStats(dir) {
|
|
|
389
286
|
let files = 0;
|
|
390
287
|
let size = 0;
|
|
391
288
|
function walk(currentDir) {
|
|
392
|
-
const entries =
|
|
289
|
+
const entries = fs3.readdirSync(currentDir, { withFileTypes: true });
|
|
393
290
|
for (const entry of entries) {
|
|
394
|
-
const fullPath =
|
|
291
|
+
const fullPath = path3.join(currentDir, entry.name);
|
|
395
292
|
if (entry.isDirectory()) {
|
|
396
293
|
walk(fullPath);
|
|
397
294
|
} else if (entry.isFile()) {
|
|
398
295
|
files++;
|
|
399
|
-
size +=
|
|
296
|
+
size += fs3.statSync(fullPath).size;
|
|
400
297
|
}
|
|
401
298
|
}
|
|
402
299
|
}
|
|
@@ -404,6 +301,416 @@ async function getDirectoryStats(dir) {
|
|
|
404
301
|
return { files, size };
|
|
405
302
|
}
|
|
406
303
|
|
|
304
|
+
// src/core/doctor.ts
|
|
305
|
+
async function diagnose() {
|
|
306
|
+
const issues = [];
|
|
307
|
+
const [dangling, orphaned, partial] = await Promise.all([
|
|
308
|
+
scanAgentDirs(),
|
|
309
|
+
scanOrphanedPackages(),
|
|
310
|
+
scanPartialInstalls()
|
|
311
|
+
]);
|
|
312
|
+
issues.push(...dangling, ...orphaned, ...partial);
|
|
313
|
+
return {
|
|
314
|
+
ok: issues.length === 0,
|
|
315
|
+
issues
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
async function scanAgentDirs() {
|
|
319
|
+
const issues = [];
|
|
320
|
+
const agentPaths = getAgentPaths();
|
|
321
|
+
for (const [agent, skillsDir] of Object.entries(agentPaths)) {
|
|
322
|
+
if (!fs4.existsSync(skillsDir)) {
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
let entries;
|
|
326
|
+
try {
|
|
327
|
+
entries = fs4.readdirSync(skillsDir, { withFileTypes: true });
|
|
328
|
+
} catch {
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
for (const entry of entries) {
|
|
332
|
+
if (!entry.name.startsWith("olore-")) {
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
if (!entry.isSymbolicLink()) {
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
const fullPath = path4.join(skillsDir, entry.name);
|
|
339
|
+
const target = readSymlinkTarget(fullPath);
|
|
340
|
+
if (target === null) {
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
const resolvedTarget = path4.resolve(skillsDir, target);
|
|
344
|
+
if (!fs4.existsSync(resolvedTarget)) {
|
|
345
|
+
issues.push({
|
|
346
|
+
type: "dangling-symlink",
|
|
347
|
+
path: fullPath,
|
|
348
|
+
target: resolvedTarget,
|
|
349
|
+
agent
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return issues;
|
|
355
|
+
}
|
|
356
|
+
async function scanOrphanedPackages() {
|
|
357
|
+
const issues = [];
|
|
358
|
+
const packagesDir = path4.join(getOloreHome(), "packages");
|
|
359
|
+
if (!fs4.existsSync(packagesDir)) {
|
|
360
|
+
return issues;
|
|
361
|
+
}
|
|
362
|
+
const activeTargets = await collectActiveSymlinkTargets();
|
|
363
|
+
let nameEntries;
|
|
364
|
+
try {
|
|
365
|
+
nameEntries = fs4.readdirSync(packagesDir, { withFileTypes: true });
|
|
366
|
+
} catch {
|
|
367
|
+
return issues;
|
|
368
|
+
}
|
|
369
|
+
for (const nameEntry of nameEntries) {
|
|
370
|
+
if (!nameEntry.isDirectory()) {
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
const nameDir = path4.join(packagesDir, nameEntry.name);
|
|
374
|
+
let versionEntries;
|
|
375
|
+
try {
|
|
376
|
+
versionEntries = fs4.readdirSync(nameDir, { withFileTypes: true });
|
|
377
|
+
} catch {
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
for (const versionEntry of versionEntries) {
|
|
381
|
+
if (!versionEntry.isDirectory()) {
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
const versionDir = path4.join(nameDir, versionEntry.name);
|
|
385
|
+
const lockPath = path4.join(versionDir, "olore-lock.json");
|
|
386
|
+
if (!fs4.existsSync(lockPath)) {
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
const realPath = fs4.realpathSync(versionDir);
|
|
390
|
+
if (activeTargets.has(realPath)) {
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
const stats = getDirectoryStats2(versionDir);
|
|
394
|
+
issues.push({
|
|
395
|
+
type: "orphaned",
|
|
396
|
+
path: versionDir,
|
|
397
|
+
name: nameEntry.name,
|
|
398
|
+
version: versionEntry.name,
|
|
399
|
+
files: stats.files,
|
|
400
|
+
size: stats.size
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return issues;
|
|
405
|
+
}
|
|
406
|
+
async function scanPartialInstalls() {
|
|
407
|
+
const issues = [];
|
|
408
|
+
const packagesDir = path4.join(getOloreHome(), "packages");
|
|
409
|
+
if (!fs4.existsSync(packagesDir)) {
|
|
410
|
+
return issues;
|
|
411
|
+
}
|
|
412
|
+
let nameEntries;
|
|
413
|
+
try {
|
|
414
|
+
nameEntries = fs4.readdirSync(packagesDir, { withFileTypes: true });
|
|
415
|
+
} catch {
|
|
416
|
+
return issues;
|
|
417
|
+
}
|
|
418
|
+
for (const nameEntry of nameEntries) {
|
|
419
|
+
if (!nameEntry.isDirectory()) {
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
const nameDir = path4.join(packagesDir, nameEntry.name);
|
|
423
|
+
let versionEntries;
|
|
424
|
+
try {
|
|
425
|
+
versionEntries = fs4.readdirSync(nameDir, { withFileTypes: true });
|
|
426
|
+
} catch {
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
for (const versionEntry of versionEntries) {
|
|
430
|
+
if (!versionEntry.isDirectory()) {
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
const versionDir = path4.join(nameDir, versionEntry.name);
|
|
434
|
+
const lockPath = path4.join(versionDir, "olore-lock.json");
|
|
435
|
+
if (!fs4.existsSync(lockPath)) {
|
|
436
|
+
issues.push({
|
|
437
|
+
type: "partial-install",
|
|
438
|
+
path: versionDir,
|
|
439
|
+
name: nameEntry.name,
|
|
440
|
+
version: versionEntry.name
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return issues;
|
|
446
|
+
}
|
|
447
|
+
async function collectActiveSymlinkTargets() {
|
|
448
|
+
const targets = /* @__PURE__ */ new Set();
|
|
449
|
+
const agentPaths = getAgentPaths();
|
|
450
|
+
for (const skillsDir of Object.values(agentPaths)) {
|
|
451
|
+
if (!fs4.existsSync(skillsDir)) {
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
let entries;
|
|
455
|
+
try {
|
|
456
|
+
entries = fs4.readdirSync(skillsDir, { withFileTypes: true });
|
|
457
|
+
} catch {
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
for (const entry of entries) {
|
|
461
|
+
if (!entry.name.startsWith("olore-") || !entry.isSymbolicLink()) {
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
const fullPath = path4.join(skillsDir, entry.name);
|
|
465
|
+
try {
|
|
466
|
+
const realTarget = fs4.realpathSync(fullPath);
|
|
467
|
+
targets.add(realTarget);
|
|
468
|
+
} catch {
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return targets;
|
|
473
|
+
}
|
|
474
|
+
async function pruneIssues(issues) {
|
|
475
|
+
const removed = [];
|
|
476
|
+
const failed = [];
|
|
477
|
+
for (const issue of issues) {
|
|
478
|
+
if (!pathExists(issue.path)) {
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
try {
|
|
482
|
+
let freedBytes = 0;
|
|
483
|
+
if (issue.type === "dangling-symlink") {
|
|
484
|
+
await fsExtra.remove(issue.path);
|
|
485
|
+
} else if (issue.type === "orphaned") {
|
|
486
|
+
freedBytes = issue.size;
|
|
487
|
+
await fsExtra.remove(issue.path);
|
|
488
|
+
await cleanEmptyParentDir(issue.path);
|
|
489
|
+
} else if (issue.type === "partial-install") {
|
|
490
|
+
const stats = getDirectoryStats2(issue.path);
|
|
491
|
+
freedBytes = stats.size;
|
|
492
|
+
await fsExtra.remove(issue.path);
|
|
493
|
+
await cleanEmptyParentDir(issue.path);
|
|
494
|
+
}
|
|
495
|
+
removed.push({ issue, freedBytes });
|
|
496
|
+
} catch (error) {
|
|
497
|
+
failed.push({
|
|
498
|
+
issue,
|
|
499
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
return { removed, failed };
|
|
504
|
+
}
|
|
505
|
+
function pathExists(p) {
|
|
506
|
+
try {
|
|
507
|
+
fs4.lstatSync(p);
|
|
508
|
+
return true;
|
|
509
|
+
} catch {
|
|
510
|
+
return false;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
function readSymlinkTarget(symlinkPath) {
|
|
514
|
+
try {
|
|
515
|
+
return fs4.readlinkSync(symlinkPath);
|
|
516
|
+
} catch {
|
|
517
|
+
return null;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
async function cleanEmptyParentDir(removedPath) {
|
|
521
|
+
const parentDir = path4.dirname(removedPath);
|
|
522
|
+
try {
|
|
523
|
+
const remaining = fs4.readdirSync(parentDir);
|
|
524
|
+
if (remaining.length === 0) {
|
|
525
|
+
await fsExtra.remove(parentDir);
|
|
526
|
+
}
|
|
527
|
+
} catch {
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
function getDirectoryStats2(dir) {
|
|
531
|
+
let files = 0;
|
|
532
|
+
let size = 0;
|
|
533
|
+
function walk(currentDir) {
|
|
534
|
+
let entries;
|
|
535
|
+
try {
|
|
536
|
+
entries = fs4.readdirSync(currentDir, { withFileTypes: true });
|
|
537
|
+
} catch {
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
for (const entry of entries) {
|
|
541
|
+
const fullPath = path4.join(currentDir, entry.name);
|
|
542
|
+
if (entry.isDirectory()) {
|
|
543
|
+
walk(fullPath);
|
|
544
|
+
} else if (entry.isFile()) {
|
|
545
|
+
try {
|
|
546
|
+
files++;
|
|
547
|
+
size += fs4.statSync(fullPath).size;
|
|
548
|
+
} catch {
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
walk(dir);
|
|
554
|
+
return { files, size };
|
|
555
|
+
}
|
|
556
|
+
function displayPath(p) {
|
|
557
|
+
const home = os4.homedir();
|
|
558
|
+
if (p.startsWith(home)) {
|
|
559
|
+
return "~" + p.slice(home.length);
|
|
560
|
+
}
|
|
561
|
+
return p;
|
|
562
|
+
}
|
|
563
|
+
function formatIssue(issue) {
|
|
564
|
+
switch (issue.type) {
|
|
565
|
+
case "dangling-symlink":
|
|
566
|
+
return `${displayPath(issue.path)} -> ${displayPath(issue.target)} (missing)`;
|
|
567
|
+
case "orphaned":
|
|
568
|
+
return `${displayPath(issue.path)} (no agent links, ${issue.files} files, ${formatBytes(issue.size)})`;
|
|
569
|
+
case "partial-install":
|
|
570
|
+
return `${displayPath(issue.path)} (missing olore-lock.json)`;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// src/commands/doctor.ts
|
|
575
|
+
async function doctor(options) {
|
|
576
|
+
if (!options.json) {
|
|
577
|
+
console.log(pc.bold("\nChecking installed packages...\n"));
|
|
578
|
+
}
|
|
579
|
+
const result = await diagnose();
|
|
580
|
+
if (options.json) {
|
|
581
|
+
console.log(JSON.stringify(result, null, 2));
|
|
582
|
+
if (!result.ok) {
|
|
583
|
+
process.exit(1);
|
|
584
|
+
}
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
if (result.ok) {
|
|
588
|
+
console.log(pc.green("No issues found. Everything looks good."));
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
const dangling = result.issues.filter((i) => i.type === "dangling-symlink");
|
|
592
|
+
const orphaned = result.issues.filter((i) => i.type === "orphaned");
|
|
593
|
+
const partial = result.issues.filter((i) => i.type === "partial-install");
|
|
594
|
+
console.log(
|
|
595
|
+
`Found ${pc.yellow(String(result.issues.length))} issue${result.issues.length === 1 ? "" : "s"}:
|
|
596
|
+
`
|
|
597
|
+
);
|
|
598
|
+
if (dangling.length > 0) {
|
|
599
|
+
console.log(pc.bold(`Dangling symlinks (${dangling.length}):`));
|
|
600
|
+
for (const issue of dangling) {
|
|
601
|
+
console.log(` ${formatIssue(issue)}`);
|
|
602
|
+
}
|
|
603
|
+
console.log("");
|
|
604
|
+
}
|
|
605
|
+
if (orphaned.length > 0) {
|
|
606
|
+
console.log(pc.bold(`Orphaned packages (${orphaned.length}):`));
|
|
607
|
+
for (const issue of orphaned) {
|
|
608
|
+
console.log(` ${formatIssue(issue)}`);
|
|
609
|
+
}
|
|
610
|
+
console.log("");
|
|
611
|
+
}
|
|
612
|
+
if (partial.length > 0) {
|
|
613
|
+
console.log(pc.bold(`Partial installs (${partial.length}):`));
|
|
614
|
+
for (const issue of partial) {
|
|
615
|
+
console.log(` ${formatIssue(issue)}`);
|
|
616
|
+
}
|
|
617
|
+
console.log("");
|
|
618
|
+
}
|
|
619
|
+
console.log(`Run ${pc.cyan("olore prune")} to fix these issues.`);
|
|
620
|
+
process.exit(1);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// src/commands/init.ts
|
|
624
|
+
import fs5 from "fs";
|
|
625
|
+
import path5 from "path";
|
|
626
|
+
import readline from "readline";
|
|
627
|
+
import pc2 from "picocolors";
|
|
628
|
+
async function prompt(question, defaultValue) {
|
|
629
|
+
const rl = readline.createInterface({
|
|
630
|
+
input: process.stdin,
|
|
631
|
+
output: process.stdout
|
|
632
|
+
});
|
|
633
|
+
return new Promise((resolve) => {
|
|
634
|
+
const q = defaultValue ? `${question} (${defaultValue}): ` : `${question}: `;
|
|
635
|
+
rl.question(q, (answer) => {
|
|
636
|
+
rl.close();
|
|
637
|
+
resolve(answer || defaultValue || "");
|
|
638
|
+
});
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
async function init(options) {
|
|
642
|
+
const cwd = process.cwd();
|
|
643
|
+
const folderName = path5.basename(cwd);
|
|
644
|
+
if (fs5.existsSync(path5.join(cwd, "olore.config.json"))) {
|
|
645
|
+
throw new Error("olore.config.json already exists. This folder is already initialized.");
|
|
646
|
+
}
|
|
647
|
+
let name;
|
|
648
|
+
let version2;
|
|
649
|
+
let description;
|
|
650
|
+
if (options.yes) {
|
|
651
|
+
name = options.name || folderName;
|
|
652
|
+
version2 = options.version || "1.0.0";
|
|
653
|
+
description = `Documentation for ${name}`;
|
|
654
|
+
} else {
|
|
655
|
+
console.log(pc2.bold("\nInitialize olore documentation package\n"));
|
|
656
|
+
name = await prompt("Package name", options.name || folderName);
|
|
657
|
+
version2 = await prompt("Version", options.version || "1.0.0");
|
|
658
|
+
description = await prompt("Description", `Documentation for ${name}`);
|
|
659
|
+
}
|
|
660
|
+
const fullName = `olore-${name}-${version2}`;
|
|
661
|
+
const configContent = {
|
|
662
|
+
name,
|
|
663
|
+
version: version2,
|
|
664
|
+
description,
|
|
665
|
+
contentPath: "./docs",
|
|
666
|
+
outputPath: "./olore-package",
|
|
667
|
+
extensions: [".md", ".mdx"],
|
|
668
|
+
exclude: []
|
|
669
|
+
};
|
|
670
|
+
fs5.writeFileSync(
|
|
671
|
+
path5.join(cwd, "olore.config.json"),
|
|
672
|
+
JSON.stringify(configContent, null, 2) + "\n"
|
|
673
|
+
);
|
|
674
|
+
const docsDir = path5.join(cwd, "docs");
|
|
675
|
+
if (!fs5.existsSync(docsDir)) {
|
|
676
|
+
fs5.mkdirSync(docsDir);
|
|
677
|
+
fs5.writeFileSync(
|
|
678
|
+
path5.join(docsDir, "getting-started.md"),
|
|
679
|
+
`# Getting Started with ${name}
|
|
680
|
+
|
|
681
|
+
Add your documentation content here.
|
|
682
|
+
|
|
683
|
+
## Overview
|
|
684
|
+
|
|
685
|
+
Describe what this documentation covers.
|
|
686
|
+
|
|
687
|
+
## Topics
|
|
688
|
+
|
|
689
|
+
- Topic 1
|
|
690
|
+
- Topic 2
|
|
691
|
+
`
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
console.log(pc2.bold(`
|
|
695
|
+
Initialized olore package: `) + pc2.cyan(fullName));
|
|
696
|
+
console.log("");
|
|
697
|
+
console.log(pc2.gray("Created:"));
|
|
698
|
+
console.log(pc2.green(" \u2713 olore.config.json"));
|
|
699
|
+
console.log(pc2.green(" \u2713 docs/") + pc2.gray(" (add your documentation here)"));
|
|
700
|
+
console.log("");
|
|
701
|
+
console.log(pc2.gray("Next steps:"));
|
|
702
|
+
console.log(" 1. Add your .md files to the " + pc2.cyan("docs/") + " folder");
|
|
703
|
+
console.log(" 2. Run " + pc2.cyan("/olore-docs-packager-1.0.0") + " in Claude Code to build");
|
|
704
|
+
console.log(" 3. Run " + pc2.cyan("olore install ./olore-package") + " to install");
|
|
705
|
+
console.log("");
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// src/commands/install.ts
|
|
709
|
+
import path6 from "path";
|
|
710
|
+
import fs6 from "fs-extra";
|
|
711
|
+
import ora from "ora";
|
|
712
|
+
import pc3 from "picocolors";
|
|
713
|
+
|
|
407
714
|
// src/core/registry.ts
|
|
408
715
|
var RegistryError = class extends Error {
|
|
409
716
|
constructor(message, code) {
|
|
@@ -468,22 +775,22 @@ async function resolveVersion(name, version2) {
|
|
|
468
775
|
// src/commands/install.ts
|
|
469
776
|
async function installFromLocal(localPath) {
|
|
470
777
|
const fullPath = expandPath(localPath);
|
|
471
|
-
if (!await
|
|
778
|
+
if (!await fs6.pathExists(fullPath)) {
|
|
472
779
|
throw new Error(`Path not found: ${fullPath}`);
|
|
473
780
|
}
|
|
474
|
-
const stat = await
|
|
781
|
+
const stat = await fs6.stat(fullPath);
|
|
475
782
|
if (!stat.isDirectory()) {
|
|
476
783
|
throw new Error(`Not a directory: ${fullPath}`);
|
|
477
784
|
}
|
|
478
|
-
const lockPath =
|
|
479
|
-
if (!await
|
|
785
|
+
const lockPath = path6.join(fullPath, "olore-lock.json");
|
|
786
|
+
if (!await fs6.pathExists(lockPath)) {
|
|
480
787
|
throw new Error(`Missing olore-lock.json in ${fullPath}`);
|
|
481
788
|
}
|
|
482
|
-
const skillPath =
|
|
483
|
-
if (!await
|
|
789
|
+
const skillPath = path6.join(fullPath, "SKILL.md");
|
|
790
|
+
if (!await fs6.pathExists(skillPath)) {
|
|
484
791
|
throw new Error(`Missing SKILL.md in ${fullPath}. Run /generate-agent-skills first.`);
|
|
485
792
|
}
|
|
486
|
-
const lock = await
|
|
793
|
+
const lock = await fs6.readJson(lockPath);
|
|
487
794
|
const packageName = lock.name;
|
|
488
795
|
const packageVersion = lock.version;
|
|
489
796
|
if (!packageName) {
|
|
@@ -493,72 +800,57 @@ async function installFromLocal(localPath) {
|
|
|
493
800
|
throw new Error(`Invalid olore-lock.json: missing "version" field`);
|
|
494
801
|
}
|
|
495
802
|
const skillName = `olore-${packageName}-${packageVersion}`;
|
|
496
|
-
const skillContent = await
|
|
803
|
+
const skillContent = await fs6.readFile(skillPath, "utf-8");
|
|
497
804
|
const nameMatch = skillContent.match(/^name:\s*(.+)$/m);
|
|
498
805
|
const skillMdName = nameMatch ? nameMatch[1].trim() : null;
|
|
499
806
|
if (skillMdName !== skillName) {
|
|
500
|
-
console.log(
|
|
807
|
+
console.log(pc3.yellow(`
|
|
501
808
|
Warning: SKILL.md name mismatch`));
|
|
502
|
-
console.log(
|
|
503
|
-
console.log(
|
|
504
|
-
console.log(
|
|
809
|
+
console.log(pc3.gray(` Expected: ${skillName}`));
|
|
810
|
+
console.log(pc3.gray(` Found: ${skillMdName || "(none)"}`));
|
|
811
|
+
console.log(pc3.yellow(` Updating SKILL.md to fix...`));
|
|
505
812
|
const updatedContent = skillMdName ? skillContent.replace(/^name:\s*.+$/m, `name: ${skillName}`) : skillContent.replace(/^---\n/, `---
|
|
506
813
|
name: ${skillName}
|
|
507
814
|
`);
|
|
508
|
-
await
|
|
815
|
+
await fs6.writeFile(skillPath, updatedContent);
|
|
509
816
|
}
|
|
510
|
-
console.log(
|
|
817
|
+
console.log(pc3.bold(`
|
|
511
818
|
Installing ${packageName}@${packageVersion} from local path...
|
|
512
819
|
`));
|
|
513
820
|
const agents = detectAgents();
|
|
514
821
|
if (agents.length === 0) {
|
|
515
|
-
console.log(
|
|
822
|
+
console.log(pc3.yellow("No agents detected. Creating directories anyway."));
|
|
516
823
|
}
|
|
517
824
|
const agentPaths = getAgentPaths();
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
const olorePath = getOlorePackagePath(packageName, packageVersion);
|
|
533
|
-
const spinner = ora("Copying to ~/.olore...").start();
|
|
534
|
-
await fs5.ensureDir(path5.dirname(olorePath));
|
|
535
|
-
await fs5.remove(olorePath);
|
|
536
|
-
await fs5.copy(fullPath, olorePath);
|
|
537
|
-
spinner.succeed(`Copied to ${pc2.gray(olorePath)}`);
|
|
538
|
-
const linkSpinner = ora("Creating symlinks...").start();
|
|
539
|
-
const linked = [];
|
|
540
|
-
for (const [agent, skillsDir] of Object.entries(agentPaths)) {
|
|
541
|
-
const targetDir = path5.join(skillsDir, skillName);
|
|
542
|
-
await fs5.ensureDir(skillsDir);
|
|
543
|
-
await fs5.remove(targetDir);
|
|
544
|
-
await linkOrCopy(olorePath, targetDir);
|
|
545
|
-
linked.push(`${pc2.green("\u2713")} ${agent} ${pc2.gray("\u2192")} ${pc2.gray(targetDir)}`);
|
|
546
|
-
}
|
|
547
|
-
linkSpinner.stop();
|
|
548
|
-
linked.forEach((line) => console.log(` ${line}`));
|
|
549
|
-
console.log(pc2.gray(` \u2514\u2500 all symlinked to ${olorePath}`));
|
|
825
|
+
const olorePath = getOlorePackagePath(packageName, packageVersion);
|
|
826
|
+
const spinner = ora("Copying to ~/.olore...").start();
|
|
827
|
+
await fs6.ensureDir(path6.dirname(olorePath));
|
|
828
|
+
await fs6.remove(olorePath);
|
|
829
|
+
await fs6.copy(fullPath, olorePath);
|
|
830
|
+
spinner.succeed(`Copied to ${pc3.gray(olorePath)}`);
|
|
831
|
+
const linkSpinner = ora("Linking to agent directories...").start();
|
|
832
|
+
const linked = [];
|
|
833
|
+
for (const [agent, skillsDir] of Object.entries(agentPaths)) {
|
|
834
|
+
const targetDir = path6.join(skillsDir, skillName);
|
|
835
|
+
await fs6.ensureDir(skillsDir);
|
|
836
|
+
await fs6.remove(targetDir);
|
|
837
|
+
await linkOrCopy(olorePath, targetDir);
|
|
838
|
+
linked.push(`${pc3.green("\u2713")} ${agent} ${pc3.gray("\u2192")} ${pc3.gray(targetDir)}`);
|
|
550
839
|
}
|
|
840
|
+
linkSpinner.stop();
|
|
841
|
+
linked.forEach((line) => console.log(` ${line}`));
|
|
842
|
+
console.log(pc3.gray(` \u2514\u2500 all linked to ${olorePath}`));
|
|
551
843
|
console.log("");
|
|
552
|
-
console.log(
|
|
844
|
+
console.log(pc3.green("Installation complete!"));
|
|
553
845
|
console.log("");
|
|
554
|
-
console.log(
|
|
555
|
-
console.log(
|
|
556
|
-
console.log(
|
|
557
|
-
console.log(
|
|
846
|
+
console.log(pc3.gray("Skill is now available as:"));
|
|
847
|
+
console.log(pc3.cyan(` /${skillName}`) + pc3.gray(" (Claude Code)"));
|
|
848
|
+
console.log(pc3.cyan(` $${skillName}`) + pc3.gray(" (Codex)"));
|
|
849
|
+
console.log(pc3.cyan(` ${skillName}`) + pc3.gray(" (OpenCode)"));
|
|
558
850
|
}
|
|
559
851
|
async function install(pkg, options) {
|
|
560
852
|
if (options.force) {
|
|
561
|
-
console.log(
|
|
853
|
+
console.log(pc3.cyan("\n\u2728 May the Skill be with you.\n"));
|
|
562
854
|
}
|
|
563
855
|
if (isLocalPath(pkg)) {
|
|
564
856
|
await installFromLocal(pkg);
|
|
@@ -579,7 +871,7 @@ function parsePackageSpec(spec) {
|
|
|
579
871
|
async function installFromRemote(pkg, optionsVersion) {
|
|
580
872
|
const { name, version: specVersion } = parsePackageSpec(pkg);
|
|
581
873
|
const requestedVersion = optionsVersion || specVersion || "latest";
|
|
582
|
-
console.log(
|
|
874
|
+
console.log(pc3.bold(`
|
|
583
875
|
Installing ${name}@${requestedVersion} from registry...
|
|
584
876
|
`));
|
|
585
877
|
const spinner = ora("Fetching package info...").start();
|
|
@@ -591,18 +883,18 @@ Installing ${name}@${requestedVersion} from registry...
|
|
|
591
883
|
spinner.fail("Failed to resolve package");
|
|
592
884
|
if (error instanceof RegistryError) {
|
|
593
885
|
if (error.code === "NOT_FOUND") {
|
|
594
|
-
console.log(
|
|
886
|
+
console.log(pc3.yellow(`
|
|
595
887
|
Package "${name}" not found in registry.`));
|
|
596
|
-
console.log(
|
|
597
|
-
console.log(
|
|
888
|
+
console.log(pc3.gray("\nFor local packages, use a path:"));
|
|
889
|
+
console.log(pc3.cyan(` olore install ./vault/packages/${name}/<version>`));
|
|
598
890
|
console.log("");
|
|
599
891
|
} else if (error.code === "NETWORK_ERROR" || error.code === "TIMEOUT") {
|
|
600
|
-
console.log(
|
|
892
|
+
console.log(pc3.red(`
|
|
601
893
|
Network error: ${error.message}`));
|
|
602
|
-
console.log(
|
|
894
|
+
console.log(pc3.gray("Please check your internet connection and try again."));
|
|
603
895
|
}
|
|
604
896
|
} else {
|
|
605
|
-
console.log(
|
|
897
|
+
console.log(pc3.red(`
|
|
606
898
|
Error: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
607
899
|
}
|
|
608
900
|
process.exit(1);
|
|
@@ -610,114 +902,74 @@ Error: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
|
610
902
|
const skillName = `olore-${name}-${versionInfo.version}`;
|
|
611
903
|
const agents = detectAgents();
|
|
612
904
|
if (agents.length === 0) {
|
|
613
|
-
console.log(
|
|
905
|
+
console.log(pc3.yellow("No agents detected. Creating directories anyway."));
|
|
614
906
|
}
|
|
615
907
|
const agentPaths = getAgentPaths();
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
if (error
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
console.log(pc2.gray("The downloaded package may be corrupted or tampered with."));
|
|
628
|
-
} else {
|
|
629
|
-
console.log(pc2.red(`
|
|
630
|
-
Download error: ${error.message}`));
|
|
631
|
-
}
|
|
908
|
+
const olorePath = getOlorePackagePath(name, versionInfo.version);
|
|
909
|
+
const downloadSpinner = ora("Downloading package...").start();
|
|
910
|
+
try {
|
|
911
|
+
await downloadAndInstall(versionInfo.downloadUrl, olorePath, versionInfo.integrity);
|
|
912
|
+
downloadSpinner.succeed(`Downloaded to ${pc3.gray(olorePath)}`);
|
|
913
|
+
} catch (error) {
|
|
914
|
+
downloadSpinner.fail("Download failed");
|
|
915
|
+
if (error instanceof DownloadError) {
|
|
916
|
+
if (error.code === "CHECKSUM_MISMATCH") {
|
|
917
|
+
console.log(pc3.red("\nChecksum verification failed!"));
|
|
918
|
+
console.log(pc3.gray("The downloaded package may be corrupted or tampered with."));
|
|
632
919
|
} else {
|
|
633
|
-
console.log(
|
|
634
|
-
Error: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
635
|
-
}
|
|
636
|
-
process.exit(1);
|
|
637
|
-
}
|
|
638
|
-
try {
|
|
639
|
-
const copySpinner = ora("Copying to agent directories...").start();
|
|
640
|
-
const copied = [];
|
|
641
|
-
for (const [agent, skillsDir] of Object.entries(agentPaths)) {
|
|
642
|
-
const targetDir = path5.join(skillsDir, skillName);
|
|
643
|
-
await fs5.ensureDir(skillsDir);
|
|
644
|
-
await fs5.remove(targetDir);
|
|
645
|
-
await fs5.copy(tempDir, targetDir);
|
|
646
|
-
copied.push(`${pc2.green("\u2713")} ${agent} ${pc2.gray("\u2192")} ${pc2.gray(targetDir)}`);
|
|
647
|
-
}
|
|
648
|
-
copySpinner.stop();
|
|
649
|
-
copied.forEach((line) => console.log(` ${line}`));
|
|
650
|
-
} finally {
|
|
651
|
-
await fs5.remove(path5.dirname(tempDir)).catch(() => {
|
|
652
|
-
});
|
|
653
|
-
}
|
|
654
|
-
} else {
|
|
655
|
-
const olorePath = getOlorePackagePath(name, versionInfo.version);
|
|
656
|
-
const downloadSpinner = ora("Downloading package...").start();
|
|
657
|
-
try {
|
|
658
|
-
await downloadAndInstall(versionInfo.downloadUrl, olorePath, versionInfo.integrity);
|
|
659
|
-
downloadSpinner.succeed(`Downloaded to ${pc2.gray(olorePath)}`);
|
|
660
|
-
} catch (error) {
|
|
661
|
-
downloadSpinner.fail("Download failed");
|
|
662
|
-
if (error instanceof DownloadError) {
|
|
663
|
-
if (error.code === "CHECKSUM_MISMATCH") {
|
|
664
|
-
console.log(pc2.red("\nChecksum verification failed!"));
|
|
665
|
-
console.log(pc2.gray("The downloaded package may be corrupted or tampered with."));
|
|
666
|
-
} else {
|
|
667
|
-
console.log(pc2.red(`
|
|
920
|
+
console.log(pc3.red(`
|
|
668
921
|
Download error: ${error.message}`));
|
|
669
|
-
}
|
|
670
|
-
} else {
|
|
671
|
-
console.log(pc2.red(`
|
|
672
|
-
Error: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
673
922
|
}
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
const linked = [];
|
|
678
|
-
for (const [agent, skillsDir] of Object.entries(agentPaths)) {
|
|
679
|
-
const targetDir = path5.join(skillsDir, skillName);
|
|
680
|
-
await fs5.ensureDir(skillsDir);
|
|
681
|
-
await fs5.remove(targetDir);
|
|
682
|
-
await linkOrCopy(olorePath, targetDir);
|
|
683
|
-
linked.push(`${pc2.green("\u2713")} ${agent} ${pc2.gray("\u2192")} ${pc2.gray(targetDir)}`);
|
|
923
|
+
} else {
|
|
924
|
+
console.log(pc3.red(`
|
|
925
|
+
Error: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
684
926
|
}
|
|
685
|
-
|
|
686
|
-
linked.forEach((line) => console.log(` ${line}`));
|
|
687
|
-
console.log(pc2.gray(` \u2514\u2500 all symlinked to ${olorePath}`));
|
|
927
|
+
process.exit(1);
|
|
688
928
|
}
|
|
929
|
+
const linkSpinner = ora("Linking to agent directories...").start();
|
|
930
|
+
const linked = [];
|
|
931
|
+
for (const [agent, skillsDir] of Object.entries(agentPaths)) {
|
|
932
|
+
const targetDir = path6.join(skillsDir, skillName);
|
|
933
|
+
await fs6.ensureDir(skillsDir);
|
|
934
|
+
await fs6.remove(targetDir);
|
|
935
|
+
await linkOrCopy(olorePath, targetDir);
|
|
936
|
+
linked.push(`${pc3.green("\u2713")} ${agent} ${pc3.gray("\u2192")} ${pc3.gray(targetDir)}`);
|
|
937
|
+
}
|
|
938
|
+
linkSpinner.stop();
|
|
939
|
+
linked.forEach((line) => console.log(` ${line}`));
|
|
940
|
+
console.log(pc3.gray(` \u2514\u2500 all linked to ${olorePath}`));
|
|
689
941
|
console.log("");
|
|
690
|
-
console.log(
|
|
942
|
+
console.log(pc3.green("Installation complete!"));
|
|
691
943
|
console.log("");
|
|
692
|
-
console.log(
|
|
693
|
-
console.log(
|
|
694
|
-
console.log(
|
|
695
|
-
console.log(
|
|
944
|
+
console.log(pc3.gray("Skill is now available as:"));
|
|
945
|
+
console.log(pc3.cyan(` /${skillName}`) + pc3.gray(" (Claude Code)"));
|
|
946
|
+
console.log(pc3.cyan(` $${skillName}`) + pc3.gray(" (Codex)"));
|
|
947
|
+
console.log(pc3.cyan(` ${skillName}`) + pc3.gray(" (OpenCode)"));
|
|
696
948
|
}
|
|
697
949
|
|
|
698
950
|
// src/commands/link.ts
|
|
699
|
-
import
|
|
700
|
-
import
|
|
951
|
+
import path7 from "path";
|
|
952
|
+
import fs7 from "fs-extra";
|
|
701
953
|
import ora2 from "ora";
|
|
702
|
-
import
|
|
954
|
+
import pc4 from "picocolors";
|
|
703
955
|
async function link(localPath) {
|
|
704
956
|
const fullPath = expandPath(localPath);
|
|
705
|
-
if (!await
|
|
957
|
+
if (!await fs7.pathExists(fullPath)) {
|
|
706
958
|
throw new Error(`Path not found: ${fullPath}`);
|
|
707
959
|
}
|
|
708
|
-
const stat = await
|
|
960
|
+
const stat = await fs7.stat(fullPath);
|
|
709
961
|
if (!stat.isDirectory()) {
|
|
710
962
|
throw new Error(`Not a directory: ${fullPath}`);
|
|
711
963
|
}
|
|
712
|
-
const lockPath =
|
|
713
|
-
if (!await
|
|
964
|
+
const lockPath = path7.join(fullPath, "olore-lock.json");
|
|
965
|
+
if (!await fs7.pathExists(lockPath)) {
|
|
714
966
|
throw new Error(`Missing olore-lock.json in ${fullPath}`);
|
|
715
967
|
}
|
|
716
|
-
const skillPath =
|
|
717
|
-
if (!await
|
|
968
|
+
const skillPath = path7.join(fullPath, "SKILL.md");
|
|
969
|
+
if (!await fs7.pathExists(skillPath)) {
|
|
718
970
|
throw new Error(`Missing SKILL.md in ${fullPath}. Run /build-docs first.`);
|
|
719
971
|
}
|
|
720
|
-
const lock = await
|
|
972
|
+
const lock = await fs7.readJson(lockPath);
|
|
721
973
|
const packageName = lock.name;
|
|
722
974
|
const packageVersion = lock.version;
|
|
723
975
|
if (!packageName) {
|
|
@@ -727,87 +979,96 @@ async function link(localPath) {
|
|
|
727
979
|
throw new Error(`Invalid olore-lock.json: missing "version" field`);
|
|
728
980
|
}
|
|
729
981
|
const skillName = `olore-${packageName}-${packageVersion}`;
|
|
730
|
-
const skillContent = await
|
|
982
|
+
const skillContent = await fs7.readFile(skillPath, "utf-8");
|
|
731
983
|
const nameMatch = skillContent.match(/^name:\s*(.+)$/m);
|
|
732
984
|
const skillMdName = nameMatch ? nameMatch[1].trim() : null;
|
|
733
985
|
if (skillMdName !== skillName) {
|
|
734
|
-
console.log(
|
|
986
|
+
console.log(pc4.yellow(`
|
|
735
987
|
Warning: SKILL.md name mismatch`));
|
|
736
|
-
console.log(
|
|
737
|
-
console.log(
|
|
738
|
-
console.log(
|
|
988
|
+
console.log(pc4.gray(` Expected: ${skillName}`));
|
|
989
|
+
console.log(pc4.gray(` Found: ${skillMdName || "(none)"}`));
|
|
990
|
+
console.log(pc4.yellow(` Updating SKILL.md to fix...`));
|
|
739
991
|
const updatedContent = skillMdName ? skillContent.replace(/^name:\s*.+$/m, `name: ${skillName}`) : skillContent.replace(/^---\n/, `---
|
|
740
992
|
name: ${skillName}
|
|
741
993
|
`);
|
|
742
|
-
await
|
|
994
|
+
await fs7.writeFile(skillPath, updatedContent);
|
|
743
995
|
}
|
|
744
|
-
console.log(
|
|
996
|
+
console.log(pc4.bold(`
|
|
745
997
|
Linking ${packageName}@${packageVersion}...
|
|
746
998
|
`));
|
|
747
999
|
const agents = detectAgents();
|
|
748
1000
|
if (agents.length === 0) {
|
|
749
|
-
console.log(
|
|
1001
|
+
console.log(pc4.yellow("No agents detected. Creating directories anyway."));
|
|
750
1002
|
}
|
|
751
1003
|
const agentPaths = getAgentPaths();
|
|
752
1004
|
const spinner = ora2(`${getLinkActionText()}...`).start();
|
|
753
1005
|
const linked = [];
|
|
754
1006
|
for (const [agent, skillsDir] of Object.entries(agentPaths)) {
|
|
755
|
-
const targetDir =
|
|
756
|
-
await
|
|
757
|
-
await
|
|
1007
|
+
const targetDir = path7.join(skillsDir, skillName);
|
|
1008
|
+
await fs7.ensureDir(skillsDir);
|
|
1009
|
+
await fs7.remove(targetDir);
|
|
758
1010
|
await linkOrCopy(fullPath, targetDir);
|
|
759
|
-
linked.push(`${
|
|
1011
|
+
linked.push(`${pc4.blue("\u26D3")} ${agent} ${pc4.gray("\u2192")} ${pc4.gray(targetDir)}`);
|
|
760
1012
|
}
|
|
761
1013
|
spinner.stop();
|
|
762
1014
|
linked.forEach((line) => console.log(` ${line}`));
|
|
763
|
-
console.log(
|
|
1015
|
+
console.log(pc4.gray(` \u2514\u2500 ${getLinkTypeText()} ${fullPath}`));
|
|
764
1016
|
console.log("");
|
|
765
|
-
console.log(
|
|
1017
|
+
console.log(pc4.blue("Link complete!"));
|
|
766
1018
|
console.log("");
|
|
767
|
-
console.log(
|
|
768
|
-
console.log(
|
|
769
|
-
console.log(
|
|
770
|
-
console.log(
|
|
1019
|
+
console.log(pc4.gray("Skill is now available as:"));
|
|
1020
|
+
console.log(pc4.cyan(` /${skillName}`) + pc4.gray(" (Claude Code)"));
|
|
1021
|
+
console.log(pc4.cyan(` $${skillName}`) + pc4.gray(" (Codex)"));
|
|
1022
|
+
console.log(pc4.cyan(` ${skillName}`) + pc4.gray(" (OpenCode)"));
|
|
771
1023
|
console.log("");
|
|
772
|
-
console.log(
|
|
773
|
-
console.log(
|
|
774
|
-
console.log(pc3.gray("On Windows, re-run link after changes."));
|
|
1024
|
+
console.log(pc4.gray(`Development mode: ${getLinkTypeText()} source (bypasses ~/.olore).`));
|
|
1025
|
+
console.log(pc4.gray("Changes to source are immediately visible."));
|
|
775
1026
|
console.log(
|
|
776
|
-
|
|
1027
|
+
pc4.gray("Use ") + pc4.cyan("olore install") + pc4.gray(" for a stable copy in ~/.olore.")
|
|
777
1028
|
);
|
|
778
1029
|
}
|
|
779
1030
|
|
|
780
1031
|
// src/commands/list.ts
|
|
781
|
-
import
|
|
1032
|
+
import pc5 from "picocolors";
|
|
782
1033
|
async function list(options) {
|
|
783
1034
|
const packages = await getInstalledPackages();
|
|
784
1035
|
if (packages.length === 0) {
|
|
785
|
-
console.log(
|
|
786
|
-
console.log(
|
|
787
|
-
console.log(`Run ${
|
|
1036
|
+
console.log(pc5.yellow("\nI find your lack of skills disturbing.\n"));
|
|
1037
|
+
console.log(pc5.gray("No packages installed."));
|
|
1038
|
+
console.log(`Run ${pc5.cyan("olore install <package>")} to install documentation.`);
|
|
788
1039
|
return;
|
|
789
1040
|
}
|
|
790
1041
|
if (options.json) {
|
|
791
|
-
|
|
1042
|
+
const { issues: issues2 } = await diagnose();
|
|
1043
|
+
console.log(JSON.stringify({ packages, issueCount: issues2.length }, null, 2));
|
|
792
1044
|
return;
|
|
793
1045
|
}
|
|
794
|
-
console.log(
|
|
1046
|
+
console.log(pc5.bold("\nInstalled packages:\n"));
|
|
795
1047
|
console.log(
|
|
796
|
-
|
|
1048
|
+
pc5.gray(
|
|
797
1049
|
"PACKAGE".padEnd(25) + "VERSION".padEnd(12) + "TYPE".padEnd(10) + "FILES".padStart(8) + "SIZE".padStart(12)
|
|
798
1050
|
)
|
|
799
1051
|
);
|
|
800
|
-
console.log(
|
|
1052
|
+
console.log(pc5.gray("-".repeat(67)));
|
|
801
1053
|
for (const pkg of packages) {
|
|
802
|
-
const typeLabel = pkg.installType === "linked" ?
|
|
1054
|
+
const typeLabel = pkg.installType === "linked" ? pc5.blue("linked") : pkg.installType;
|
|
803
1055
|
console.log(
|
|
804
1056
|
pkg.name.padEnd(25) + pkg.version.padEnd(12) + typeLabel.padEnd(10) + String(pkg.files).padStart(8) + formatSize(pkg.size).padStart(12)
|
|
805
1057
|
);
|
|
806
1058
|
}
|
|
807
|
-
console.log(
|
|
808
|
-
console.log(
|
|
1059
|
+
console.log(pc5.gray("-".repeat(67)));
|
|
1060
|
+
console.log(pc5.gray(`Total: ${packages.length} packages`));
|
|
809
1061
|
console.log("");
|
|
810
|
-
console.log(
|
|
1062
|
+
console.log(pc5.gray("Types: installed = in ~/.olore, linked = dev symlink, copied = legacy"));
|
|
1063
|
+
const { issues } = await diagnose();
|
|
1064
|
+
if (issues.length > 0) {
|
|
1065
|
+
console.log("");
|
|
1066
|
+
console.log(
|
|
1067
|
+
pc5.yellow(
|
|
1068
|
+
`\u26A0 ${issues.length} issue${issues.length === 1 ? "" : "s"} detected. Run ${pc5.cyan("olore doctor")} for details.`
|
|
1069
|
+
)
|
|
1070
|
+
);
|
|
1071
|
+
}
|
|
811
1072
|
}
|
|
812
1073
|
function formatSize(bytes) {
|
|
813
1074
|
if (bytes < 1024) return `${bytes} B`;
|
|
@@ -816,54 +1077,129 @@ function formatSize(bytes) {
|
|
|
816
1077
|
}
|
|
817
1078
|
|
|
818
1079
|
// src/commands/order66.ts
|
|
819
|
-
import
|
|
820
|
-
import
|
|
1080
|
+
import path8 from "path";
|
|
1081
|
+
import fs8 from "fs-extra";
|
|
821
1082
|
import ora3 from "ora";
|
|
822
|
-
import
|
|
1083
|
+
import pc6 from "picocolors";
|
|
823
1084
|
async function order66() {
|
|
824
|
-
console.log(
|
|
825
|
-
console.log(
|
|
1085
|
+
console.log(pc6.red("\n\u26A0\uFE0F Execute Order 66?\n"));
|
|
1086
|
+
console.log(pc6.yellow("This will remove ALL installed documentation packages."));
|
|
826
1087
|
const packages = await getInstalledPackages();
|
|
827
1088
|
if (packages.length === 0) {
|
|
828
|
-
console.log(
|
|
1089
|
+
console.log(pc6.gray("\nNo packages to remove. The Jedi are already gone."));
|
|
829
1090
|
return;
|
|
830
1091
|
}
|
|
831
|
-
console.log(
|
|
1092
|
+
console.log(pc6.gray(`
|
|
832
1093
|
Packages to be removed: ${packages.length}`));
|
|
833
1094
|
for (const pkg of packages) {
|
|
834
|
-
console.log(
|
|
1095
|
+
console.log(pc6.gray(` - ${pkg.name}@${pkg.version}`));
|
|
835
1096
|
}
|
|
836
|
-
console.log(
|
|
1097
|
+
console.log(pc6.red('\n"It will be done, my lord."\n'));
|
|
837
1098
|
const spinner = ora3("Executing Order 66...").start();
|
|
838
1099
|
let removedCount = 0;
|
|
839
1100
|
const agentPaths = getAgentPaths();
|
|
840
1101
|
for (const pkg of packages) {
|
|
841
1102
|
const skillName = `olore-${pkg.name}-${pkg.version}`;
|
|
842
1103
|
for (const [, basePath] of Object.entries(agentPaths)) {
|
|
843
|
-
const skillPath =
|
|
844
|
-
if (await
|
|
845
|
-
await
|
|
1104
|
+
const skillPath = path8.join(basePath, skillName);
|
|
1105
|
+
if (await fs8.pathExists(skillPath)) {
|
|
1106
|
+
await fs8.remove(skillPath);
|
|
846
1107
|
}
|
|
847
1108
|
}
|
|
848
1109
|
if (pkg.installType === "installed") {
|
|
849
1110
|
const olorePath = getOlorePackagePath(pkg.name, pkg.version);
|
|
850
|
-
if (await
|
|
851
|
-
await
|
|
1111
|
+
if (await fs8.pathExists(olorePath)) {
|
|
1112
|
+
await fs8.remove(olorePath);
|
|
852
1113
|
}
|
|
853
1114
|
}
|
|
854
1115
|
removedCount++;
|
|
855
1116
|
}
|
|
856
1117
|
spinner.succeed(`Removed ${removedCount} packages`);
|
|
857
|
-
console.log(
|
|
1118
|
+
console.log(pc6.gray("\nThe Jedi have been eliminated."));
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
// src/commands/prune.ts
|
|
1122
|
+
import pc7 from "picocolors";
|
|
1123
|
+
async function prune(options) {
|
|
1124
|
+
if (!options.json) {
|
|
1125
|
+
console.log(pc7.bold("\nScanning for issues...\n"));
|
|
1126
|
+
}
|
|
1127
|
+
const { issues } = await diagnose();
|
|
1128
|
+
if (issues.length === 0) {
|
|
1129
|
+
if (options.json) {
|
|
1130
|
+
console.log(JSON.stringify({ removed: [], failed: [] }, null, 2));
|
|
1131
|
+
} else {
|
|
1132
|
+
console.log(pc7.green("Nothing to prune. Everything is clean."));
|
|
1133
|
+
}
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
if (options.dryRun) {
|
|
1137
|
+
if (options.json) {
|
|
1138
|
+
console.log(JSON.stringify({ dryRun: true, wouldRemove: issues }, null, 2));
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
console.log(
|
|
1142
|
+
pc7.yellow(`Would remove ${issues.length} item${issues.length === 1 ? "" : "s"}:
|
|
1143
|
+
`)
|
|
1144
|
+
);
|
|
1145
|
+
for (const issue of issues) {
|
|
1146
|
+
console.log(` ${issueLabel(issue.type)} ${displayPath(issue.path)}`);
|
|
1147
|
+
}
|
|
1148
|
+
console.log(`
|
|
1149
|
+
Run ${pc7.cyan("olore prune")} (without --dry-run) to remove them.`);
|
|
1150
|
+
return;
|
|
1151
|
+
}
|
|
1152
|
+
const result = await pruneIssues(issues);
|
|
1153
|
+
if (options.json) {
|
|
1154
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1155
|
+
return;
|
|
1156
|
+
}
|
|
1157
|
+
if (result.removed.length > 0) {
|
|
1158
|
+
let totalFreed = 0;
|
|
1159
|
+
console.log(`Removed ${result.removed.length} item${result.removed.length === 1 ? "" : "s"}:`);
|
|
1160
|
+
for (const entry of result.removed) {
|
|
1161
|
+
totalFreed += entry.freedBytes;
|
|
1162
|
+
console.log(
|
|
1163
|
+
` ${pc7.green("\u2713")} ${displayPath(entry.issue.path)} (${issueLabel(entry.issue.type)})`
|
|
1164
|
+
);
|
|
1165
|
+
}
|
|
1166
|
+
if (totalFreed > 0) {
|
|
1167
|
+
console.log(`
|
|
1168
|
+
${formatBytes(totalFreed)} freed.`);
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
if (result.failed.length > 0) {
|
|
1172
|
+
console.log("");
|
|
1173
|
+
console.log(
|
|
1174
|
+
pc7.red(
|
|
1175
|
+
`Failed to remove ${result.failed.length} item${result.failed.length === 1 ? "" : "s"}:`
|
|
1176
|
+
)
|
|
1177
|
+
);
|
|
1178
|
+
for (const entry of result.failed) {
|
|
1179
|
+
console.log(` ${pc7.red("\u2717")} ${displayPath(entry.issue.path)}: ${entry.error}`);
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
function issueLabel(type) {
|
|
1184
|
+
switch (type) {
|
|
1185
|
+
case "dangling-symlink":
|
|
1186
|
+
return "dangling symlink";
|
|
1187
|
+
case "orphaned":
|
|
1188
|
+
return "orphaned package";
|
|
1189
|
+
case "partial-install":
|
|
1190
|
+
return "partial install";
|
|
1191
|
+
default:
|
|
1192
|
+
return type;
|
|
1193
|
+
}
|
|
858
1194
|
}
|
|
859
1195
|
|
|
860
1196
|
// src/commands/remove.ts
|
|
861
|
-
import
|
|
862
|
-
import
|
|
1197
|
+
import path9 from "path";
|
|
1198
|
+
import fs9 from "fs-extra";
|
|
863
1199
|
import ora4 from "ora";
|
|
864
|
-
import
|
|
1200
|
+
import pc8 from "picocolors";
|
|
865
1201
|
async function remove(pkg) {
|
|
866
|
-
console.log(
|
|
1202
|
+
console.log(pc8.bold(`
|
|
867
1203
|
Removing ${pkg}...
|
|
868
1204
|
`));
|
|
869
1205
|
const packages = await getInstalledPackages();
|
|
@@ -879,20 +1215,20 @@ Removing ${pkg}...
|
|
|
879
1215
|
return p.name === name;
|
|
880
1216
|
});
|
|
881
1217
|
if (matches.length === 0) {
|
|
882
|
-
console.error(
|
|
1218
|
+
console.error(pc8.red(`Package not found: ${pkg}`));
|
|
883
1219
|
console.log("\nInstalled packages:");
|
|
884
1220
|
for (const p of packages) {
|
|
885
|
-
console.log(` ${
|
|
1221
|
+
console.log(` ${pc8.cyan(p.name)}@${p.version}`);
|
|
886
1222
|
}
|
|
887
1223
|
process.exit(1);
|
|
888
1224
|
}
|
|
889
1225
|
if (matches.length > 1 && !version2) {
|
|
890
|
-
console.error(
|
|
1226
|
+
console.error(pc8.red(`Multiple versions found for ${name}:`));
|
|
891
1227
|
for (const p of matches) {
|
|
892
|
-
console.log(` ${
|
|
1228
|
+
console.log(` ${pc8.cyan(p.name)}@${p.version} (${p.installType})`);
|
|
893
1229
|
}
|
|
894
1230
|
console.log(`
|
|
895
|
-
Specify version: ${
|
|
1231
|
+
Specify version: ${pc8.cyan(`olore remove ${name}@<version>`)}`);
|
|
896
1232
|
process.exit(1);
|
|
897
1233
|
}
|
|
898
1234
|
const found = matches[0];
|
|
@@ -901,26 +1237,26 @@ Specify version: ${pc6.cyan(`olore remove ${name}@<version>`)}`);
|
|
|
901
1237
|
const removed = [];
|
|
902
1238
|
const agentPaths = getAgentPaths();
|
|
903
1239
|
for (const [agent, basePath] of Object.entries(agentPaths)) {
|
|
904
|
-
const skillPath =
|
|
905
|
-
if (await
|
|
906
|
-
await
|
|
907
|
-
removed.push(`${
|
|
1240
|
+
const skillPath = path9.join(basePath, skillName);
|
|
1241
|
+
if (await fs9.pathExists(skillPath)) {
|
|
1242
|
+
await fs9.remove(skillPath);
|
|
1243
|
+
removed.push(`${pc8.green("\u2713")} ${agent}`);
|
|
908
1244
|
}
|
|
909
1245
|
}
|
|
910
1246
|
if (found.installType === "installed") {
|
|
911
1247
|
const olorePath = getOlorePackagePath(found.name, found.version);
|
|
912
|
-
if (await
|
|
913
|
-
await
|
|
914
|
-
removed.push(`${
|
|
1248
|
+
if (await fs9.pathExists(olorePath)) {
|
|
1249
|
+
await fs9.remove(olorePath);
|
|
1250
|
+
removed.push(`${pc8.green("\u2713")} ~/.olore`);
|
|
915
1251
|
}
|
|
916
1252
|
}
|
|
917
1253
|
spinner.stop();
|
|
918
1254
|
if (removed.length === 0) {
|
|
919
|
-
console.log(
|
|
1255
|
+
console.log(pc8.yellow("No files found to remove."));
|
|
920
1256
|
} else {
|
|
921
1257
|
removed.forEach((line) => console.log(` ${line}`));
|
|
922
1258
|
console.log("");
|
|
923
|
-
console.log(
|
|
1259
|
+
console.log(pc8.green(`Removed ${found.name}@${found.version}`));
|
|
924
1260
|
}
|
|
925
1261
|
}
|
|
926
1262
|
|
|
@@ -929,12 +1265,12 @@ var require2 = createRequire(import.meta.url);
|
|
|
929
1265
|
var { version } = require2("../package.json");
|
|
930
1266
|
var program = new Command();
|
|
931
1267
|
program.name("olore").description("Universal documentation for any AI coding agent").version(version).addHelpText("after", `
|
|
932
|
-
${
|
|
1268
|
+
${pc9.gray("May the Skill be with you.")}`);
|
|
933
1269
|
program.command("init").description("Initialize a documentation package in the current directory").option("-n, --name <name>", "Package name (default: folder name)").option("-v, --version <version>", "Package version (default: latest)").option("-y, --yes", "Skip prompts, use defaults").action(async (options) => {
|
|
934
1270
|
try {
|
|
935
1271
|
await init(options);
|
|
936
1272
|
} catch (error) {
|
|
937
|
-
console.error(
|
|
1273
|
+
console.error(pc9.red(`Error: ${error.message}`));
|
|
938
1274
|
process.exit(1);
|
|
939
1275
|
}
|
|
940
1276
|
});
|
|
@@ -942,7 +1278,7 @@ program.command("install <package>").alias("i").description("Install a documenta
|
|
|
942
1278
|
try {
|
|
943
1279
|
await install(pkg, options);
|
|
944
1280
|
} catch (error) {
|
|
945
|
-
console.error(
|
|
1281
|
+
console.error(pc9.red(`Error: ${error.message}`));
|
|
946
1282
|
process.exit(1);
|
|
947
1283
|
}
|
|
948
1284
|
});
|
|
@@ -950,7 +1286,7 @@ program.command("link <path>").description("Link a local package for development
|
|
|
950
1286
|
try {
|
|
951
1287
|
await link(localPath);
|
|
952
1288
|
} catch (error) {
|
|
953
|
-
console.error(
|
|
1289
|
+
console.error(pc9.red(`Error: ${error.message}`));
|
|
954
1290
|
process.exit(1);
|
|
955
1291
|
}
|
|
956
1292
|
});
|
|
@@ -958,7 +1294,7 @@ program.command("list").alias("ls").description("List installed documentation pa
|
|
|
958
1294
|
try {
|
|
959
1295
|
await list(options);
|
|
960
1296
|
} catch (error) {
|
|
961
|
-
console.error(
|
|
1297
|
+
console.error(pc9.red(`Error: ${error.message}`));
|
|
962
1298
|
process.exit(1);
|
|
963
1299
|
}
|
|
964
1300
|
});
|
|
@@ -966,7 +1302,23 @@ program.command("remove <package>").alias("rm").description("Remove an installed
|
|
|
966
1302
|
try {
|
|
967
1303
|
await remove(pkg);
|
|
968
1304
|
} catch (error) {
|
|
969
|
-
console.error(
|
|
1305
|
+
console.error(pc9.red(`Error: ${error.message}`));
|
|
1306
|
+
process.exit(1);
|
|
1307
|
+
}
|
|
1308
|
+
});
|
|
1309
|
+
program.command("doctor").description("Check for issues with installed packages").option("--json", "Output as JSON").action(async (options) => {
|
|
1310
|
+
try {
|
|
1311
|
+
await doctor(options);
|
|
1312
|
+
} catch (error) {
|
|
1313
|
+
console.error(pc9.red(`Error: ${error.message}`));
|
|
1314
|
+
process.exit(1);
|
|
1315
|
+
}
|
|
1316
|
+
});
|
|
1317
|
+
program.command("prune").description("Remove dangling symlinks, orphaned packages, and partial installs").option("--dry-run", "Show what would be removed without acting").option("--json", "Output as JSON").action(async (options) => {
|
|
1318
|
+
try {
|
|
1319
|
+
await prune(options);
|
|
1320
|
+
} catch (error) {
|
|
1321
|
+
console.error(pc9.red(`Error: ${error.message}`));
|
|
970
1322
|
process.exit(1);
|
|
971
1323
|
}
|
|
972
1324
|
});
|
|
@@ -974,7 +1326,7 @@ program.command("order66").description(false).action(async () => {
|
|
|
974
1326
|
try {
|
|
975
1327
|
await order66();
|
|
976
1328
|
} catch (error) {
|
|
977
|
-
console.error(
|
|
1329
|
+
console.error(pc9.red(`Error: ${error.message}`));
|
|
978
1330
|
process.exit(1);
|
|
979
1331
|
}
|
|
980
1332
|
});
|