abelworkflow 1.1.3 → 1.2.1
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/LICENSE +21 -0
- package/README.md +30 -29
- package/extensions/gpt-responses-compat.ts +213 -0
- package/extensions/k2think-compat.ts +40 -0
- package/lib/cli/args.mjs +5 -19
- package/lib/cli/main.mjs +105 -72
- package/lib/cli/prompts.mjs +9 -2
- package/lib/config/dotenv.mjs +1 -1
- package/lib/config/jsonc.mjs +1 -1
- package/lib/config/store.mjs +197 -45
- package/lib/config/toml.mjs +147 -453
- package/lib/installer/assets.mjs +67 -45
- package/lib/installer/install.mjs +202 -45
- package/lib/installer/links.mjs +124 -101
- package/lib/installer/lock.mjs +82 -0
- package/lib/installer/state.mjs +14 -21
- package/lib/paths.mjs +9 -9
- package/lib/providers/claude.mjs +58 -114
- package/lib/providers/codex.mjs +101 -198
- package/lib/providers/pi.mjs +331 -496
- package/lib/providers/skills.mjs +88 -68
- package/lib/providers/url.mjs +35 -0
- package/lib/templates/workflow/commands/abel-design.md +23 -161
- package/lib/templates/workflow/commands/abel-diagnose.md +30 -56
- package/lib/templates/workflow/commands/abel-implement.md +30 -150
- package/lib/templates/workflow/commands/abel-init.md +1 -1
- package/lib/tools/cli-installer.mjs +29 -190
- package/package.json +7 -9
- package/skills/context7-auto-research/context7-api.cjs +75 -17
- package/skills/dev-browser/SKILL.md +15 -1
- package/skills/dev-browser/dist/scripts/start.d.ts +0 -1
- package/skills/dev-browser/dist/scripts/start.js +8 -36
- package/skills/dev-browser/dist/src/client.d.ts +3 -4
- package/skills/dev-browser/dist/src/client.js +117 -117
- package/skills/dev-browser/dist/src/entrypoint.d.ts +1 -1
- package/skills/dev-browser/dist/src/entrypoint.js +38 -31
- package/skills/dev-browser/dist/src/index.d.ts +0 -1
- package/skills/dev-browser/dist/src/index.js +0 -1
- package/skills/dev-browser/dist/src/page-api.d.ts +0 -1
- package/skills/dev-browser/dist/src/page-api.js +0 -1
- package/skills/dev-browser/dist/src/relay.d.ts +0 -1
- package/skills/dev-browser/dist/src/relay.js +53 -7
- package/skills/dev-browser/dist/src/runtime.d.ts +3 -14
- package/skills/dev-browser/dist/src/runtime.js +5 -15
- package/skills/dev-browser/dist/src/snapshot/browser-script.d.ts +0 -1
- package/skills/dev-browser/dist/src/snapshot/browser-script.js +0 -1
- package/skills/dev-browser/dist/src/snapshot/index.d.ts +0 -1
- package/skills/dev-browser/dist/src/snapshot/index.js +0 -1
- package/skills/dev-browser/dist/src/snapshot/inject.d.ts +0 -1
- package/skills/dev-browser/dist/src/snapshot/inject.js +0 -1
- package/skills/dev-browser/dist/src/standalone.d.ts +0 -1
- package/skills/dev-browser/dist/src/standalone.js +2 -3
- package/skills/dev-browser/dist/src/startup.d.ts +3 -12
- package/skills/dev-browser/dist/src/startup.js +6 -10
- package/skills/dev-browser/dist/src/target-registry.d.ts +0 -1
- package/skills/dev-browser/dist/src/target-registry.js +0 -1
- package/skills/dev-browser/dist/src/types.d.ts +0 -1
- package/skills/dev-browser/dist/src/types.js +0 -1
- package/skills/dev-browser/package-lock.json +1 -1
- package/skills/dev-browser/package.json +2 -2
- package/skills/grok-search/.env.example +1 -1
- package/skills/grok-search/SKILL.md +13 -16
- package/skills/grok-search/scripts/_dotenv.py +1 -1
- package/skills/grok-search/scripts/groksearch_cli.py +271 -195
- package/skills/time/SKILL.md +5 -2
- package/skills/time/scripts/time_cli.py +61 -22
- package/extensions/pi-gpt-responses-compat/index.ts +0 -25
- package/lib/templates/codex/config-base.toml +0 -103
- package/skills/dev-browser/dist/scripts/start.d.ts.map +0 -1
- package/skills/dev-browser/dist/scripts/start.js.map +0 -1
- package/skills/dev-browser/dist/src/client.d.ts.map +0 -1
- package/skills/dev-browser/dist/src/client.js.map +0 -1
- package/skills/dev-browser/dist/src/entrypoint.d.ts.map +0 -1
- package/skills/dev-browser/dist/src/entrypoint.js.map +0 -1
- package/skills/dev-browser/dist/src/index.d.ts.map +0 -1
- package/skills/dev-browser/dist/src/index.js.map +0 -1
- package/skills/dev-browser/dist/src/page-api.d.ts.map +0 -1
- package/skills/dev-browser/dist/src/page-api.js.map +0 -1
- package/skills/dev-browser/dist/src/relay.d.ts.map +0 -1
- package/skills/dev-browser/dist/src/relay.js.map +0 -1
- package/skills/dev-browser/dist/src/runtime.d.ts.map +0 -1
- package/skills/dev-browser/dist/src/runtime.js.map +0 -1
- package/skills/dev-browser/dist/src/snapshot/browser-script.d.ts.map +0 -1
- package/skills/dev-browser/dist/src/snapshot/browser-script.js.map +0 -1
- package/skills/dev-browser/dist/src/snapshot/index.d.ts.map +0 -1
- package/skills/dev-browser/dist/src/snapshot/index.js.map +0 -1
- package/skills/dev-browser/dist/src/snapshot/inject.d.ts.map +0 -1
- package/skills/dev-browser/dist/src/snapshot/inject.js.map +0 -1
- package/skills/dev-browser/dist/src/standalone.d.ts.map +0 -1
- package/skills/dev-browser/dist/src/standalone.js.map +0 -1
- package/skills/dev-browser/dist/src/startup.d.ts.map +0 -1
- package/skills/dev-browser/dist/src/startup.js.map +0 -1
- package/skills/dev-browser/dist/src/target-registry.d.ts.map +0 -1
- package/skills/dev-browser/dist/src/target-registry.js.map +0 -1
- package/skills/dev-browser/dist/src/types.d.ts.map +0 -1
- package/skills/dev-browser/dist/src/types.js.map +0 -1
- package/skills/grok-search/gitignore.template +0 -4
- package/skills/grok-search/scripts/groksearch_entry.py +0 -131
package/lib/installer/assets.mjs
CHANGED
|
@@ -2,7 +2,6 @@ import * as fs from "node:fs/promises";
|
|
|
2
2
|
import { join, relative, resolve } from "node:path";
|
|
3
3
|
import {
|
|
4
4
|
backupIfNeeded,
|
|
5
|
-
pathExists,
|
|
6
5
|
pathTargetExists,
|
|
7
6
|
readJsonFileSafe,
|
|
8
7
|
writeText
|
|
@@ -17,9 +16,13 @@ const ignoredSkillPathPatterns = [
|
|
|
17
16
|
/(^|\/)__pycache__(\/|$)/u,
|
|
18
17
|
/(^|\/)node_modules(\/|$)/u,
|
|
19
18
|
/(^|\/)tmp(\/|$)/u,
|
|
20
|
-
/(^|\/)build(\/|$)/u
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
/(^|\/)build(\/|$)/u
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
const devBrowserRuntimePathPatterns = [
|
|
23
|
+
/^dev-browser\/SKILL\.md$/u,
|
|
24
|
+
/^dev-browser\/package[^/]*\.json$/u,
|
|
25
|
+
/^dev-browser\/(?:dist|references)(?:\/|$)/u
|
|
23
26
|
];
|
|
24
27
|
|
|
25
28
|
function normalizeRelativePath(path) {
|
|
@@ -72,7 +75,11 @@ function mapGitignoreTemplate(relativePath) {
|
|
|
72
75
|
function shouldCopySkillPath(skillsRoot, sourcePath) {
|
|
73
76
|
const relativePath = normalizeRelativePath(relative(skillsRoot, sourcePath));
|
|
74
77
|
if (!relativePath) return true;
|
|
75
|
-
if (
|
|
78
|
+
if (relativePath === "dev-browser") return true;
|
|
79
|
+
if (relativePath.startsWith("dev-browser/")) {
|
|
80
|
+
return devBrowserRuntimePathPatterns.some((pattern) => pattern.test(relativePath));
|
|
81
|
+
}
|
|
82
|
+
if (/(^|\/)dist(\/|$)/u.test(relativePath)) return false;
|
|
76
83
|
return !ignoredSkillPathPatterns.some((pattern) => pattern.test(relativePath));
|
|
77
84
|
}
|
|
78
85
|
|
|
@@ -84,8 +91,7 @@ async function collectFiles(root, outputPrefix, { filter, mapTargetPath } = {})
|
|
|
84
91
|
const sourcePath = join(directory, entry.name);
|
|
85
92
|
if (filter && !filter(root, sourcePath)) continue;
|
|
86
93
|
if (entry.isDirectory()) {
|
|
87
|
-
|
|
88
|
-
for (const [key, value] of nested) assets.set(key, value);
|
|
94
|
+
await visit(sourcePath);
|
|
89
95
|
continue;
|
|
90
96
|
}
|
|
91
97
|
if (!entry.isFile() && !entry.isSymbolicLink()) continue;
|
|
@@ -100,14 +106,12 @@ async function collectFiles(root, outputPrefix, { filter, mapTargetPath } = {})
|
|
|
100
106
|
assets.set(targetRelativePath, {
|
|
101
107
|
content: sourceContent,
|
|
102
108
|
hash: hashBytes(sourceContent),
|
|
103
|
-
|
|
104
|
-
mode: sourceStat.mode & 0o777,
|
|
105
|
-
sourcePath
|
|
109
|
+
mode: sourceStat.mode & 0o777
|
|
106
110
|
});
|
|
107
111
|
}
|
|
108
|
-
return assets;
|
|
109
112
|
}
|
|
110
|
-
|
|
113
|
+
await visit(root);
|
|
114
|
+
return assets;
|
|
111
115
|
}
|
|
112
116
|
|
|
113
117
|
async function addAssetTree(manifest, root, outputPrefix, options = {}) {
|
|
@@ -131,9 +135,7 @@ async function collectManagedAssets(paths) {
|
|
|
131
135
|
manifest.set("README.md", {
|
|
132
136
|
content: readmeContent,
|
|
133
137
|
hash: hashBytes(readmeContent),
|
|
134
|
-
|
|
135
|
-
mode: (await fs.stat(readmePath)).mode & 0o777,
|
|
136
|
-
sourcePath: readmePath
|
|
138
|
+
mode: (await fs.stat(readmePath)).mode & 0o777
|
|
137
139
|
});
|
|
138
140
|
|
|
139
141
|
const skillsRoot = join(paths.packageRoot, "skills");
|
|
@@ -171,23 +173,30 @@ async function readRegularTarget(targetPath) {
|
|
|
171
173
|
}
|
|
172
174
|
if (!targetStat.isFile()) return { exists: true, regular: false };
|
|
173
175
|
const content = await fs.readFile(targetPath);
|
|
174
|
-
return {
|
|
176
|
+
return {
|
|
177
|
+
exists: true,
|
|
178
|
+
regular: true,
|
|
179
|
+
hash: hashBytes(content),
|
|
180
|
+
mode: targetStat.mode & 0o777
|
|
181
|
+
};
|
|
175
182
|
} catch (error) {
|
|
176
183
|
if (error?.code === "ENOENT") return { exists: false, regular: false };
|
|
177
184
|
throw error;
|
|
178
185
|
}
|
|
179
186
|
}
|
|
180
187
|
|
|
181
|
-
async function applyManifestEntry(paths, relativePath, asset, previousHash, force, report) {
|
|
188
|
+
async function applyManifestEntry(paths, relativePath, asset, previousHash, force, report, beforeMutation) {
|
|
182
189
|
const targetPath = await resolveManagedFilePath(paths, relativePath);
|
|
183
190
|
const current = await readRegularTarget(targetPath);
|
|
184
191
|
if (!current.exists) {
|
|
192
|
+
await beforeMutation();
|
|
185
193
|
await writeText(targetPath, asset.content, { backupLimit: 3, mode: asset.mode });
|
|
186
194
|
report.created.push(relativePath);
|
|
187
195
|
return asset.hash;
|
|
188
196
|
}
|
|
189
197
|
if (!current.regular) {
|
|
190
198
|
if (force && current.symbolicLink) {
|
|
199
|
+
await beforeMutation();
|
|
191
200
|
await backupIfNeeded(targetPath);
|
|
192
201
|
await writeText(targetPath, asset.content, { backupLimit: 3, mode: asset.mode });
|
|
193
202
|
report.updated.push(relativePath);
|
|
@@ -197,16 +206,13 @@ async function applyManifestEntry(paths, relativePath, asset, previousHash, forc
|
|
|
197
206
|
return previousHash;
|
|
198
207
|
}
|
|
199
208
|
if (current.hash === asset.hash) {
|
|
209
|
+
if (process.platform !== "win32" && current.mode !== asset.mode) await beforeMutation();
|
|
200
210
|
const result = await writeText(targetPath, asset.content, { backupLimit: 3, mode: asset.mode });
|
|
201
211
|
report[result.status === "permission-repaired" ? "updated" : "unchanged"].push(relativePath);
|
|
202
212
|
return asset.hash;
|
|
203
213
|
}
|
|
204
|
-
if (!previousHash && current.hash === asset.sourceHash) {
|
|
205
|
-
await writeText(targetPath, asset.content, { backupLimit: 3, mode: asset.mode });
|
|
206
|
-
report.updated.push(relativePath);
|
|
207
|
-
return asset.hash;
|
|
208
|
-
}
|
|
209
214
|
if (previousHash && current.hash === previousHash || force) {
|
|
215
|
+
await beforeMutation();
|
|
210
216
|
await writeText(targetPath, asset.content, { backupLimit: 3, mode: asset.mode });
|
|
211
217
|
report.updated.push(relativePath);
|
|
212
218
|
return asset.hash;
|
|
@@ -215,7 +221,7 @@ async function applyManifestEntry(paths, relativePath, asset, previousHash, forc
|
|
|
215
221
|
return previousHash;
|
|
216
222
|
}
|
|
217
223
|
|
|
218
|
-
async function removeStaleManagedFile(paths, relativePath, previousHash, force, report) {
|
|
224
|
+
async function removeStaleManagedFile(paths, relativePath, previousHash, force, report, beforeMutation) {
|
|
219
225
|
if (relativePath.endsWith(".skill-lock.json")) return;
|
|
220
226
|
const targetPath = await resolveManagedFilePath(paths, relativePath);
|
|
221
227
|
const current = await readRegularTarget(targetPath);
|
|
@@ -224,12 +230,14 @@ async function removeStaleManagedFile(paths, relativePath, previousHash, force,
|
|
|
224
230
|
recordConflict(report, relativePath);
|
|
225
231
|
return undefined;
|
|
226
232
|
}
|
|
227
|
-
if (current.
|
|
233
|
+
if (current.hash === previousHash) {
|
|
234
|
+
await beforeMutation();
|
|
228
235
|
await fs.rm(targetPath, { force: true });
|
|
229
236
|
report.removed.push(relativePath);
|
|
230
237
|
return undefined;
|
|
231
238
|
}
|
|
232
239
|
if (force) {
|
|
240
|
+
await beforeMutation();
|
|
233
241
|
await backupIfNeeded(targetPath);
|
|
234
242
|
report.removed.push(relativePath);
|
|
235
243
|
return undefined;
|
|
@@ -250,14 +258,21 @@ async function readPackageVersion(paths) {
|
|
|
250
258
|
async function syncManagedFiles({
|
|
251
259
|
paths,
|
|
252
260
|
force = false,
|
|
253
|
-
pathPrefix = null
|
|
261
|
+
pathPrefix = null,
|
|
262
|
+
beforeMutation = async () => {}
|
|
254
263
|
}) {
|
|
264
|
+
const packageVersion = await readPackageVersion(paths);
|
|
255
265
|
const previousMetadata = await readInstallMetadata(paths);
|
|
256
266
|
const previousManagedFiles = await readManagedFiles(paths, previousMetadata);
|
|
257
267
|
const manifest = await collectManagedAssets(paths);
|
|
258
268
|
const report = createInstallReport();
|
|
259
269
|
const managedFiles = pathPrefix ? { ...previousManagedFiles } : {};
|
|
260
270
|
|
|
271
|
+
for (const [relativePath] of manifest) {
|
|
272
|
+
if (!pathPrefix || relativePath.startsWith(pathPrefix)) {
|
|
273
|
+
await resolveManagedFilePath(paths, relativePath);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
261
276
|
for (const [relativePath, asset] of manifest) {
|
|
262
277
|
if (pathPrefix && !relativePath.startsWith(pathPrefix)) continue;
|
|
263
278
|
const managedHash = await applyManifestEntry(
|
|
@@ -266,7 +281,8 @@ async function syncManagedFiles({
|
|
|
266
281
|
asset,
|
|
267
282
|
previousManagedFiles[relativePath],
|
|
268
283
|
force,
|
|
269
|
-
report
|
|
284
|
+
report,
|
|
285
|
+
beforeMutation
|
|
270
286
|
);
|
|
271
287
|
if (managedHash) managedFiles[relativePath] = managedHash;
|
|
272
288
|
else delete managedFiles[relativePath];
|
|
@@ -274,7 +290,14 @@ async function syncManagedFiles({
|
|
|
274
290
|
for (const [relativePath, previousHash] of Object.entries(previousManagedFiles)) {
|
|
275
291
|
if (pathPrefix && !relativePath.startsWith(pathPrefix)) continue;
|
|
276
292
|
if (!manifest.has(relativePath)) {
|
|
277
|
-
const managedHash = await removeStaleManagedFile(
|
|
293
|
+
const managedHash = await removeStaleManagedFile(
|
|
294
|
+
paths,
|
|
295
|
+
relativePath,
|
|
296
|
+
previousHash,
|
|
297
|
+
force,
|
|
298
|
+
report,
|
|
299
|
+
beforeMutation
|
|
300
|
+
);
|
|
278
301
|
if (managedHash) managedFiles[relativePath] = managedHash;
|
|
279
302
|
else delete managedFiles[relativePath];
|
|
280
303
|
}
|
|
@@ -282,39 +305,38 @@ async function syncManagedFiles({
|
|
|
282
305
|
|
|
283
306
|
return {
|
|
284
307
|
previousMetadata,
|
|
285
|
-
packageVersion
|
|
308
|
+
packageVersion,
|
|
286
309
|
managedFiles,
|
|
287
310
|
report
|
|
288
311
|
};
|
|
289
312
|
}
|
|
290
313
|
|
|
291
|
-
async function
|
|
292
|
-
if (resolve(sourcePath) === resolve(targetPath)) return true;
|
|
314
|
+
async function assertManagedContainerDirectory(targetPath) {
|
|
293
315
|
try {
|
|
294
|
-
|
|
295
|
-
|
|
316
|
+
const targetStat = await fs.lstat(targetPath);
|
|
317
|
+
if (targetStat.isSymbolicLink() || !targetStat.isDirectory()) {
|
|
318
|
+
throw new Error(`Managed container conflict at ${targetPath}`);
|
|
319
|
+
}
|
|
320
|
+
return true;
|
|
321
|
+
} catch (error) {
|
|
322
|
+
if (error?.code !== "ENOENT") throw error;
|
|
296
323
|
return false;
|
|
297
324
|
}
|
|
298
325
|
}
|
|
299
326
|
|
|
300
|
-
async function
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
}
|
|
327
|
+
async function ensureManagedContainerDirectories(targetPaths, options = {}) {
|
|
328
|
+
const existing = await Promise.all(targetPaths.map(assertManagedContainerDirectory));
|
|
329
|
+
for (let index = 0; index < targetPaths.length; index += 1) {
|
|
330
|
+
const targetPath = targetPaths[index];
|
|
331
|
+
if (!existing[index]) await options.beforeMutation?.();
|
|
332
|
+
await fs.mkdir(targetPath, { recursive: true });
|
|
333
|
+
await assertManagedContainerDirectory(targetPath);
|
|
308
334
|
}
|
|
309
|
-
await fs.mkdir(targetPath, { recursive: true });
|
|
310
335
|
}
|
|
311
336
|
|
|
312
337
|
export {
|
|
313
|
-
|
|
314
|
-
ensureManagedContainerDirectory,
|
|
315
|
-
pathsReferToSameEntry,
|
|
338
|
+
ensureManagedContainerDirectories,
|
|
316
339
|
readManagedFiles,
|
|
317
340
|
readPackageVersion,
|
|
318
|
-
shouldCopySkillPath,
|
|
319
341
|
syncManagedFiles
|
|
320
342
|
};
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { stat } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
2
3
|
import { createPaths } from "../paths.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
4
|
+
import { readPackageVersion, syncManagedFiles } from "./assets.mjs";
|
|
5
|
+
import {
|
|
6
|
+
linkClaude,
|
|
7
|
+
linkCodex,
|
|
8
|
+
linkPi,
|
|
9
|
+
linkPiExtensions
|
|
10
|
+
} from "./links.mjs";
|
|
11
|
+
import { withDeploymentLock } from "./lock.mjs";
|
|
5
12
|
import {
|
|
6
13
|
buildInstallMetadata,
|
|
7
14
|
createInstallReport,
|
|
@@ -29,7 +36,7 @@ function validatedPaths(paths) {
|
|
|
29
36
|
function reportFromLinkResults(results) {
|
|
30
37
|
const report = createInstallReport();
|
|
31
38
|
for (const result of results) {
|
|
32
|
-
if (result.status === "linked"
|
|
39
|
+
if (result.status === "linked") report.linked.push(result.targetPath);
|
|
33
40
|
else if (result.status === "unchanged") report.unchanged.push(result.targetPath);
|
|
34
41
|
else if (result.status === "removed") report.removed.push(result.targetPath);
|
|
35
42
|
else if (result.status === "conflict") {
|
|
@@ -51,78 +58,228 @@ async function packageVersionFor(paths, previousMetadata) {
|
|
|
51
58
|
return readPackageVersion(paths);
|
|
52
59
|
}
|
|
53
60
|
|
|
54
|
-
async function writeInstallState(paths, assetResult, linkedTargets) {
|
|
61
|
+
async function writeInstallState(paths, assetResult, linkedTargets, workflowInstalled, guard) {
|
|
55
62
|
const metadata = buildInstallMetadata({
|
|
56
63
|
previousMetadata: assetResult.previousMetadata,
|
|
57
64
|
packageVersion: assetResult.packageVersion,
|
|
65
|
+
workflowInstalled,
|
|
58
66
|
managedFiles: assetResult.managedFiles,
|
|
59
67
|
linkedTargets
|
|
60
68
|
});
|
|
61
|
-
await writeInstallMetadata(paths, metadata);
|
|
69
|
+
await writeInstallMetadata(paths, metadata, { guard });
|
|
62
70
|
}
|
|
63
71
|
|
|
64
|
-
|
|
65
|
-
|
|
72
|
+
function onceAsync(operation) {
|
|
73
|
+
let result;
|
|
74
|
+
return () => {
|
|
75
|
+
result ??= operation();
|
|
76
|
+
return result;
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function writeIncompleteInstallState(paths, previousMetadata, guard) {
|
|
81
|
+
const packageVersion = await packageVersionFor(paths, previousMetadata);
|
|
82
|
+
await writeInstallMetadata(paths, buildInstallMetadata({
|
|
83
|
+
previousMetadata,
|
|
84
|
+
packageVersion,
|
|
85
|
+
workflowInstalled: false,
|
|
86
|
+
managedFiles: previousMetadata.managedFiles ?? {},
|
|
87
|
+
linkedTargets: previousMetadata.linkedTargets ?? {}
|
|
88
|
+
}), { guard });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function validatedSkillName(skillName) {
|
|
92
|
+
if (typeof skillName !== "string"
|
|
93
|
+
|| !skillName
|
|
94
|
+
|| skillName === "."
|
|
95
|
+
|| skillName === ".."
|
|
96
|
+
|| skillName.includes("/")
|
|
97
|
+
|| skillName.includes("\\")
|
|
98
|
+
|| skillName.includes("\0")) {
|
|
99
|
+
throw new TypeError(`Invalid skill name: ${String(skillName)}`);
|
|
100
|
+
}
|
|
101
|
+
return skillName;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function assertRequiredSkill(paths, skillName) {
|
|
105
|
+
const skillPath = join(paths.packageRoot, "skills", skillName, "SKILL.md");
|
|
106
|
+
try {
|
|
107
|
+
if ((await stat(skillPath)).isFile()) return;
|
|
108
|
+
} catch (error) {
|
|
109
|
+
if (error?.code !== "ENOENT") throw error;
|
|
110
|
+
}
|
|
111
|
+
throw new Error(`Missing required skill: ${skillPath}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async function installWorkflowLocked(paths, options, guard) {
|
|
115
|
+
await guard();
|
|
66
116
|
const previousMetadata = await readInstallMetadata(paths);
|
|
117
|
+
const previousLinkedTargets = previousMetadata.linkedTargets ?? {};
|
|
118
|
+
const markIncomplete = onceAsync(() => writeIncompleteInstallState(paths, previousMetadata, guard));
|
|
119
|
+
const beforeMutation = async () => {
|
|
120
|
+
await guard();
|
|
121
|
+
await markIncomplete();
|
|
122
|
+
};
|
|
67
123
|
let assetResult;
|
|
68
|
-
|
|
69
|
-
if (options.relinkOnly) {
|
|
70
|
-
if (!(await pathExists(paths.agentsDir))) {
|
|
71
|
-
throw new Error(`${paths.agentsDir} does not exist; remove --link-only or install first`);
|
|
72
|
-
}
|
|
73
|
-
assetResult = {
|
|
74
|
-
previousMetadata,
|
|
75
|
-
packageVersion: await packageVersionFor(paths, previousMetadata),
|
|
76
|
-
managedFiles: await readManagedFiles(paths, previousMetadata),
|
|
77
|
-
report: createInstallReport()
|
|
78
|
-
};
|
|
79
|
-
} else {
|
|
124
|
+
try {
|
|
80
125
|
assetResult = await syncManagedFiles({
|
|
81
126
|
paths,
|
|
82
|
-
force: options.force ?? false
|
|
127
|
+
force: options.force ?? false,
|
|
128
|
+
beforeMutation
|
|
83
129
|
});
|
|
130
|
+
} catch (error) {
|
|
131
|
+
await markIncomplete();
|
|
132
|
+
throw error;
|
|
84
133
|
}
|
|
85
134
|
|
|
86
|
-
const
|
|
87
|
-
const linkOptions = {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
135
|
+
const linkResults = [];
|
|
136
|
+
const linkOptions = {
|
|
137
|
+
force: options.force ?? false,
|
|
138
|
+
beforeMutation,
|
|
139
|
+
onResult: (result) => linkResults.push(result)
|
|
140
|
+
};
|
|
141
|
+
try {
|
|
142
|
+
await linkClaude(paths, previousLinkedTargets, linkOptions);
|
|
143
|
+
await linkCodex(paths, previousLinkedTargets, linkOptions);
|
|
144
|
+
await linkPi(paths, previousLinkedTargets, linkOptions);
|
|
145
|
+
} catch (error) {
|
|
146
|
+
await writeInstallState(
|
|
147
|
+
paths,
|
|
148
|
+
assetResult,
|
|
149
|
+
mergeLinkedTargets(previousLinkedTargets, linkResults),
|
|
150
|
+
false,
|
|
151
|
+
guard
|
|
152
|
+
);
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
const report = mergeInstallReports(assetResult.report, reportFromLinkResults(linkResults));
|
|
156
|
+
await writeInstallState(
|
|
157
|
+
paths,
|
|
158
|
+
assetResult,
|
|
159
|
+
mergeLinkedTargets(previousLinkedTargets, linkResults),
|
|
160
|
+
report.conflicts.length === 0,
|
|
161
|
+
guard
|
|
162
|
+
);
|
|
163
|
+
return report;
|
|
94
164
|
}
|
|
95
165
|
|
|
96
|
-
async function
|
|
97
|
-
|
|
166
|
+
async function installWorkflow(options) {
|
|
167
|
+
const paths = validatedPaths(options.paths);
|
|
168
|
+
return withDeploymentLock(paths, (guard) => installWorkflowLocked(paths, options, guard));
|
|
98
169
|
}
|
|
99
170
|
|
|
100
|
-
async function
|
|
101
|
-
|
|
171
|
+
async function ensureSkillPresentLocked(paths, validatedName, guard) {
|
|
172
|
+
await guard();
|
|
102
173
|
const previousMetadata = await readInstallMetadata(paths);
|
|
103
|
-
const
|
|
174
|
+
const markIncomplete = onceAsync(() => writeIncompleteInstallState(paths, previousMetadata, guard));
|
|
175
|
+
const beforeMutation = async () => {
|
|
176
|
+
await guard();
|
|
177
|
+
await markIncomplete();
|
|
178
|
+
};
|
|
179
|
+
let assetResult;
|
|
180
|
+
try {
|
|
181
|
+
assetResult = await syncManagedFiles({
|
|
182
|
+
paths,
|
|
183
|
+
force: false,
|
|
184
|
+
pathPrefix: `skills/${validatedName}/`,
|
|
185
|
+
beforeMutation
|
|
186
|
+
});
|
|
187
|
+
} catch (error) {
|
|
188
|
+
await markIncomplete();
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
await writeInstallState(
|
|
104
192
|
paths,
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
193
|
+
assetResult,
|
|
194
|
+
previousMetadata.linkedTargets ?? {},
|
|
195
|
+
previousMetadata.workflowInstalled === true
|
|
196
|
+
&& previousMetadata.packageVersion === assetResult.packageVersion
|
|
197
|
+
&& assetResult.report.conflicts.length === 0,
|
|
198
|
+
guard
|
|
199
|
+
);
|
|
109
200
|
return assetResult.report;
|
|
110
201
|
}
|
|
111
202
|
|
|
112
|
-
async function
|
|
203
|
+
async function ensureSkillPresent(inputPaths, skillName, continuation) {
|
|
204
|
+
if (continuation !== undefined && typeof continuation !== "function") {
|
|
205
|
+
throw new TypeError("Expected a skill continuation function");
|
|
206
|
+
}
|
|
113
207
|
const paths = validatedPaths(inputPaths);
|
|
208
|
+
const validatedName = validatedSkillName(skillName);
|
|
209
|
+
await assertRequiredSkill(paths, validatedName);
|
|
210
|
+
return withDeploymentLock(paths, async (guard) => {
|
|
211
|
+
const report = await ensureSkillPresentLocked(paths, validatedName, guard);
|
|
212
|
+
return continuation ? continuation(report, guard) : report;
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async function ensurePiExtensionsPresentLocked(paths, guard) {
|
|
217
|
+
await guard();
|
|
114
218
|
const previousMetadata = await readInstallMetadata(paths);
|
|
115
|
-
const assetResult = await syncManagedFiles({ paths, force: false });
|
|
116
219
|
const previousLinkedTargets = previousMetadata.linkedTargets ?? {};
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
220
|
+
const markIncomplete = onceAsync(() => writeIncompleteInstallState(paths, previousMetadata, guard));
|
|
221
|
+
const beforeMutation = async () => {
|
|
222
|
+
await guard();
|
|
223
|
+
await markIncomplete();
|
|
224
|
+
};
|
|
225
|
+
let assetResult;
|
|
226
|
+
try {
|
|
227
|
+
assetResult = await syncManagedFiles({
|
|
228
|
+
paths,
|
|
229
|
+
force: false,
|
|
230
|
+
pathPrefix: "extensions/",
|
|
231
|
+
beforeMutation
|
|
232
|
+
});
|
|
233
|
+
} catch (error) {
|
|
234
|
+
await markIncomplete();
|
|
235
|
+
throw error;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const linkResults = [];
|
|
239
|
+
try {
|
|
240
|
+
await linkPiExtensions(paths, previousLinkedTargets, {
|
|
241
|
+
force: false,
|
|
242
|
+
beforeMutation,
|
|
243
|
+
onResult: (result) => linkResults.push(result)
|
|
244
|
+
});
|
|
245
|
+
} catch (error) {
|
|
246
|
+
await writeInstallState(
|
|
247
|
+
paths,
|
|
248
|
+
assetResult,
|
|
249
|
+
mergeLinkedTargets(previousLinkedTargets, linkResults),
|
|
250
|
+
false,
|
|
251
|
+
guard
|
|
252
|
+
);
|
|
253
|
+
throw error;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const report = mergeInstallReports(assetResult.report, reportFromLinkResults(linkResults));
|
|
257
|
+
await writeInstallState(
|
|
258
|
+
paths,
|
|
259
|
+
assetResult,
|
|
260
|
+
mergeLinkedTargets(previousLinkedTargets, linkResults),
|
|
261
|
+
previousMetadata.workflowInstalled === true
|
|
262
|
+
&& previousMetadata.packageVersion === assetResult.packageVersion
|
|
263
|
+
&& report.conflicts.length === 0,
|
|
264
|
+
guard
|
|
265
|
+
);
|
|
266
|
+
return report;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async function ensurePiExtensionsPresent(inputPaths, continuation) {
|
|
270
|
+
if (continuation !== undefined && typeof continuation !== "function") {
|
|
271
|
+
throw new TypeError("Expected an Extensions continuation function");
|
|
272
|
+
}
|
|
273
|
+
const paths = validatedPaths(inputPaths);
|
|
274
|
+
return withDeploymentLock(paths, async (guard) => {
|
|
275
|
+
const report = await ensurePiExtensionsPresentLocked(paths, guard);
|
|
276
|
+
return continuation ? continuation(report, guard) : report;
|
|
277
|
+
});
|
|
120
278
|
}
|
|
121
279
|
|
|
122
280
|
export {
|
|
123
|
-
|
|
281
|
+
ensurePiExtensionsPresent,
|
|
124
282
|
ensureSkillPresent,
|
|
125
|
-
installManagedWorkflow,
|
|
126
283
|
installWorkflow,
|
|
127
284
|
packageVersionFor
|
|
128
285
|
};
|