@looplia/looplia-cli 0.7.3 → 0.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-4TKNQ5RW.js +305 -0
- package/dist/{chunk-MHR5TPHE.js → chunk-GIZRTNY3.js} +1991 -417
- package/dist/{claude-agent-sdk-SQ6YU4VE.js → claude-agent-sdk-W5MXMV4Q.js} +5 -1
- package/dist/cli.js +157 -35
- package/dist/{dist-PMEIK6PJ.js → dist-3XSIQAV3.js} +2 -2
- package/package.json +1 -1
- package/plugins/looplia-core/skills/workflow-executor/SKILL.md +73 -18
- package/plugins/looplia-writer/workflows/writing-kit.md +1 -3
- package/dist/chunk-VYGRYFSY.js +0 -1148
- package/plugins/looplia-core/hooks/hooks.json +0 -22
- package/plugins/looplia-core/scripts/hooks/compact-inject-state.sh +0 -36
- package/plugins/looplia-core/scripts/hooks/post-write-validate.sh +0 -81
- package/plugins/looplia-core/scripts/hooks/stop-guard.sh +0 -56
- package/plugins/looplia-core/skills/workflow-executor-inline/SKILL.md +0 -217
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import {
|
|
3
|
+
compileRegistry,
|
|
4
|
+
getCompiledRegistryPath
|
|
5
|
+
} from "./chunk-XTUQVJYH.js";
|
|
6
|
+
import {
|
|
7
|
+
extractWorkflowSkills
|
|
8
|
+
} from "./chunk-GIZRTNY3.js";
|
|
9
|
+
import {
|
|
10
|
+
pathExists
|
|
11
|
+
} from "./chunk-VRBGWKZ6.js";
|
|
12
|
+
import {
|
|
13
|
+
init_esm_shims
|
|
14
|
+
} from "./chunk-Y55L47HC.js";
|
|
15
|
+
|
|
16
|
+
// ../../packages/provider/dist/index.js
|
|
17
|
+
init_esm_shims();
|
|
18
|
+
import { exec } from "child_process";
|
|
19
|
+
import { readFile, rm } from "fs/promises";
|
|
20
|
+
import { homedir } from "os";
|
|
21
|
+
import { join } from "path";
|
|
22
|
+
import { promisify } from "util";
|
|
23
|
+
var execAsync = promisify(exec);
|
|
24
|
+
var GITHUB_FULL_PATTERN = /^https?:\/\/github\.com\/([^/]+\/[^/]+)(?:\/tree\/[^/]+\/(.+))?$/;
|
|
25
|
+
var GITHUB_SIMPLE_PATTERN = /^(?:https?:\/\/)?github\.com\/([^/]+\/[^/]+)\/?$/;
|
|
26
|
+
async function loadCompiledRegistry(autoSync = false) {
|
|
27
|
+
const compiledPath = getCompiledRegistryPath();
|
|
28
|
+
if (!await pathExists(compiledPath)) {
|
|
29
|
+
return await compileRegistry();
|
|
30
|
+
}
|
|
31
|
+
if (autoSync) {
|
|
32
|
+
return await compileRegistry();
|
|
33
|
+
}
|
|
34
|
+
const content = await readFile(compiledPath, "utf-8");
|
|
35
|
+
return JSON.parse(content);
|
|
36
|
+
}
|
|
37
|
+
function findSkill(registry, skillName) {
|
|
38
|
+
return registry.skills.find((s) => s.name === skillName);
|
|
39
|
+
}
|
|
40
|
+
function getInstalledSkills(registry) {
|
|
41
|
+
return registry.skills.filter((s) => s.installed);
|
|
42
|
+
}
|
|
43
|
+
function getAvailableSkills(registry) {
|
|
44
|
+
return registry.skills.filter((s) => !s.installed);
|
|
45
|
+
}
|
|
46
|
+
function getSkillsByCategory(registry, category) {
|
|
47
|
+
return registry.skills.filter((s) => s.category === category);
|
|
48
|
+
}
|
|
49
|
+
function getSkillsBySource(registry, source) {
|
|
50
|
+
return registry.skills.filter((s) => s.source === source);
|
|
51
|
+
}
|
|
52
|
+
async function installThirdPartySkill(skill, showProgress = false) {
|
|
53
|
+
if (!skill.gitUrl) {
|
|
54
|
+
return {
|
|
55
|
+
skill: skill.name,
|
|
56
|
+
status: "failed",
|
|
57
|
+
error: "No git URL available for skill"
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const source = {
|
|
61
|
+
id: `temp:${skill.name}`,
|
|
62
|
+
type: "github",
|
|
63
|
+
url: skill.gitUrl,
|
|
64
|
+
enabled: true,
|
|
65
|
+
priority: 0,
|
|
66
|
+
addedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
67
|
+
};
|
|
68
|
+
const { syncSource: sync } = await import("./sync-E5PGFGNI-IGGJR7IL.js");
|
|
69
|
+
const result = await sync(source, {
|
|
70
|
+
showProgress,
|
|
71
|
+
skillPath: skill.skillPath
|
|
72
|
+
// For marketplace skills
|
|
73
|
+
});
|
|
74
|
+
const firstPlugin = result.plugins[0];
|
|
75
|
+
if (firstPlugin) {
|
|
76
|
+
return firstPlugin;
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
skill: skill.name,
|
|
80
|
+
status: "failed",
|
|
81
|
+
error: result.error ?? "Unknown error during installation"
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
async function installSkill(skillName, registry) {
|
|
85
|
+
const reg = registry ?? await loadCompiledRegistry();
|
|
86
|
+
const skill = findSkill(reg, skillName);
|
|
87
|
+
if (!skill) {
|
|
88
|
+
return {
|
|
89
|
+
skill: skillName,
|
|
90
|
+
status: "failed",
|
|
91
|
+
error: `Skill not found in registry: ${skillName}`
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
if (skill.installed) {
|
|
95
|
+
return {
|
|
96
|
+
skill: skillName,
|
|
97
|
+
status: "already_installed",
|
|
98
|
+
path: skill.installedPath
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
if (skill.sourceType === "builtin") {
|
|
102
|
+
return {
|
|
103
|
+
skill: skillName,
|
|
104
|
+
status: "failed",
|
|
105
|
+
error: "Built-in skill not found in local installation. Run 'looplia init' to reinstall."
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return await installThirdPartySkill(skill);
|
|
109
|
+
}
|
|
110
|
+
async function updateSkill(skillName, registry) {
|
|
111
|
+
const reg = registry ?? await loadCompiledRegistry();
|
|
112
|
+
const skill = findSkill(reg, skillName);
|
|
113
|
+
if (!skill) {
|
|
114
|
+
return {
|
|
115
|
+
skill: skillName,
|
|
116
|
+
status: "failed",
|
|
117
|
+
error: `Skill not found in registry: ${skillName}`
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
if (!(skill.installed && skill.installedPath)) {
|
|
121
|
+
return {
|
|
122
|
+
skill: skillName,
|
|
123
|
+
status: "failed",
|
|
124
|
+
error: "Skill is not installed"
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
if (skill.sourceType === "builtin") {
|
|
128
|
+
return {
|
|
129
|
+
skill: skillName,
|
|
130
|
+
status: "failed",
|
|
131
|
+
error: "Cannot update built-in skills. Run 'looplia init' to refresh."
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
const skillsDir = join(skill.installedPath, "..", "..");
|
|
136
|
+
await execAsync("git pull", { cwd: skillsDir });
|
|
137
|
+
return {
|
|
138
|
+
skill: skillName,
|
|
139
|
+
status: "updated",
|
|
140
|
+
path: skill.installedPath
|
|
141
|
+
};
|
|
142
|
+
} catch (error) {
|
|
143
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
144
|
+
return {
|
|
145
|
+
skill: skillName,
|
|
146
|
+
status: "failed",
|
|
147
|
+
error: message
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async function ensureWorkflowSkills(workflow, registry) {
|
|
152
|
+
const reg = registry ?? await loadCompiledRegistry();
|
|
153
|
+
const requiredSkills = extractWorkflowSkills(workflow);
|
|
154
|
+
const installed = [];
|
|
155
|
+
const failed = [];
|
|
156
|
+
for (const skillName of requiredSkills) {
|
|
157
|
+
const skill = findSkill(reg, skillName);
|
|
158
|
+
if (!skill) {
|
|
159
|
+
failed.push(skillName);
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
if (skill.installed) {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (skill.sourceType === "builtin") {
|
|
166
|
+
failed.push(skillName);
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const result = await installThirdPartySkill(skill);
|
|
170
|
+
if (result.status === "failed") {
|
|
171
|
+
failed.push(skillName);
|
|
172
|
+
} else {
|
|
173
|
+
installed.push(result);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
ready: failed.length === 0,
|
|
178
|
+
installed,
|
|
179
|
+
failed
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
async function installSkillFromUrl(url, showProgress = false) {
|
|
183
|
+
const urlParts = parseGitHubUrl(url);
|
|
184
|
+
if (!urlParts) {
|
|
185
|
+
return {
|
|
186
|
+
skill: "unknown",
|
|
187
|
+
status: "failed",
|
|
188
|
+
error: `Invalid GitHub URL: ${url}`
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
const { repoUrl, skillPath } = urlParts;
|
|
192
|
+
const source = {
|
|
193
|
+
id: `url:${url.replace(/[^a-zA-Z0-9]/g, "-")}`,
|
|
194
|
+
type: "github",
|
|
195
|
+
url: repoUrl,
|
|
196
|
+
enabled: true,
|
|
197
|
+
priority: 0,
|
|
198
|
+
addedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
199
|
+
};
|
|
200
|
+
const { syncSource: sync } = await import("./sync-E5PGFGNI-IGGJR7IL.js");
|
|
201
|
+
const result = await sync(source, {
|
|
202
|
+
showProgress,
|
|
203
|
+
skillPath
|
|
204
|
+
});
|
|
205
|
+
if (result.status === "synced") {
|
|
206
|
+
await compileRegistry();
|
|
207
|
+
}
|
|
208
|
+
const firstPlugin = result.plugins[0];
|
|
209
|
+
if (firstPlugin) {
|
|
210
|
+
return firstPlugin;
|
|
211
|
+
}
|
|
212
|
+
return {
|
|
213
|
+
skill: "unknown",
|
|
214
|
+
status: "failed",
|
|
215
|
+
error: result.error ?? "Unknown error during installation"
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function parseGitHubUrl(url) {
|
|
219
|
+
const match = url.match(GITHUB_FULL_PATTERN);
|
|
220
|
+
if (!match) {
|
|
221
|
+
const simpleMatch = url.match(GITHUB_SIMPLE_PATTERN);
|
|
222
|
+
if (simpleMatch?.[1]) {
|
|
223
|
+
return { repoUrl: `https://github.com/${simpleMatch[1]}.git` };
|
|
224
|
+
}
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
const repo = match[1];
|
|
228
|
+
const path = match[2];
|
|
229
|
+
return {
|
|
230
|
+
repoUrl: `https://github.com/${repo}.git`,
|
|
231
|
+
skillPath: path
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
async function removeSkill(skillName, registry) {
|
|
235
|
+
const reg = registry ?? await loadCompiledRegistry();
|
|
236
|
+
const skill = findSkill(reg, skillName);
|
|
237
|
+
if (!skill) {
|
|
238
|
+
return {
|
|
239
|
+
skill: skillName,
|
|
240
|
+
status: "failed",
|
|
241
|
+
error: `Skill not found in registry: ${skillName}`
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
if (!skill.installed) {
|
|
245
|
+
return {
|
|
246
|
+
skill: skillName,
|
|
247
|
+
status: "failed",
|
|
248
|
+
error: "Skill is not installed"
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
if (skill.sourceType === "builtin") {
|
|
252
|
+
return {
|
|
253
|
+
skill: skillName,
|
|
254
|
+
status: "failed",
|
|
255
|
+
error: "Cannot remove built-in skills"
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
if (!skill.installedPath) {
|
|
259
|
+
return {
|
|
260
|
+
skill: skillName,
|
|
261
|
+
status: "failed",
|
|
262
|
+
error: "Skill installation path not found"
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
try {
|
|
266
|
+
const pluginDir = join(skill.installedPath, "..", "..");
|
|
267
|
+
const loopliaPath = join(homedir(), ".looplia");
|
|
268
|
+
const pluginsDir = join(loopliaPath, "plugins");
|
|
269
|
+
if (!pluginDir.startsWith(pluginsDir)) {
|
|
270
|
+
return {
|
|
271
|
+
skill: skillName,
|
|
272
|
+
status: "failed",
|
|
273
|
+
error: "Cannot remove: path is outside plugins directory"
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
await rm(pluginDir, { recursive: true, force: true });
|
|
277
|
+
await compileRegistry();
|
|
278
|
+
return {
|
|
279
|
+
skill: skillName,
|
|
280
|
+
status: "removed",
|
|
281
|
+
path: pluginDir
|
|
282
|
+
};
|
|
283
|
+
} catch (error) {
|
|
284
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
285
|
+
return {
|
|
286
|
+
skill: skillName,
|
|
287
|
+
status: "failed",
|
|
288
|
+
error: message
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export {
|
|
294
|
+
loadCompiledRegistry,
|
|
295
|
+
findSkill,
|
|
296
|
+
getInstalledSkills,
|
|
297
|
+
getAvailableSkills,
|
|
298
|
+
getSkillsByCategory,
|
|
299
|
+
getSkillsBySource,
|
|
300
|
+
installSkill,
|
|
301
|
+
updateSkill,
|
|
302
|
+
ensureWorkflowSkills,
|
|
303
|
+
installSkillFromUrl,
|
|
304
|
+
removeSkill
|
|
305
|
+
};
|