@looplia/looplia-cli 0.7.5 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,241 @@
1
+ #!/usr/bin/env bun
2
+ import {
3
+ getLoopliaPluginPath
4
+ } from "./chunk-JJCGDGRS.js";
5
+ import {
6
+ isValidPathSegment,
7
+ pathExists
8
+ } from "./chunk-326UJHZM.js";
9
+ import {
10
+ init_esm_shims
11
+ } from "./chunk-Y55L47HC.js";
12
+
13
+ // ../../packages/provider/dist/discovery/index.js
14
+ init_esm_shims();
15
+ import { mkdir, readdir, writeFile } from "fs/promises";
16
+ import { join } from "path";
17
+ import { execFile } from "child_process";
18
+ import { promisify } from "util";
19
+ var AUTO_DISCOVERY_PLUGIN_NAME = "auto-discovery-plugin";
20
+ function getAutoDiscoveryPluginPath() {
21
+ return join(getLoopliaPluginPath(), "plugins", AUTO_DISCOVERY_PLUGIN_NAME);
22
+ }
23
+ async function ensureAutoDiscoveryPlugin() {
24
+ const pluginPath = getAutoDiscoveryPluginPath();
25
+ const pluginJsonPath = join(pluginPath, ".claude-plugin", "plugin.json");
26
+ const skillsDir = join(pluginPath, "skills");
27
+ if (await pathExists(join(pluginPath, ".claude-plugin"))) {
28
+ return;
29
+ }
30
+ await mkdir(join(pluginPath, ".claude-plugin"), { recursive: true });
31
+ await mkdir(skillsDir, { recursive: true });
32
+ const pluginJson = {
33
+ name: "auto-discovery-plugin",
34
+ description: "Auto-discovered skills from skills.sh registry",
35
+ version: "1.0.0",
36
+ author: { name: "Looplia" },
37
+ keywords: ["skills", "auto-discovery", "registry", "skills.sh"],
38
+ homepage: "https://github.com/memorysaver/looplia-core"
39
+ };
40
+ await writeFile(pluginJsonPath, JSON.stringify(pluginJson, null, 2), "utf-8");
41
+ }
42
+ async function installSkillToAutoDiscovery(skillName, skillContent, sourceUrl) {
43
+ if (!isValidPathSegment(skillName)) {
44
+ throw new Error(`Invalid skill name: ${skillName}`);
45
+ }
46
+ try {
47
+ await ensureAutoDiscoveryPlugin();
48
+ const skillDir = join(getAutoDiscoveryPluginPath(), "skills", skillName);
49
+ await mkdir(skillDir, { recursive: true });
50
+ const skillPath = join(skillDir, "SKILL.md");
51
+ await writeFile(skillPath, skillContent, "utf-8");
52
+ if (sourceUrl) {
53
+ const sourcePath = join(skillDir, "source.json");
54
+ await writeFile(
55
+ sourcePath,
56
+ JSON.stringify({ gitUrl: sourceUrl }, null, 2),
57
+ "utf-8"
58
+ );
59
+ }
60
+ return {
61
+ skill: skillName,
62
+ status: "installed",
63
+ path: skillDir
64
+ };
65
+ } catch (error) {
66
+ const message = error instanceof Error ? error.message : String(error);
67
+ return {
68
+ skill: skillName,
69
+ status: "failed",
70
+ error: `Failed to install skill: ${message}`
71
+ };
72
+ }
73
+ }
74
+ async function listAutoDiscoveredSkills() {
75
+ const skillsDir = join(getAutoDiscoveryPluginPath(), "skills");
76
+ try {
77
+ const entries = await readdir(skillsDir, { withFileTypes: true });
78
+ return entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
79
+ } catch {
80
+ return [];
81
+ }
82
+ }
83
+ async function isSkillAutoDiscovered(skillName) {
84
+ if (!isValidPathSegment(skillName)) {
85
+ return false;
86
+ }
87
+ const skillDir = join(getAutoDiscoveryPluginPath(), "skills", skillName);
88
+ return await pathExists(skillDir);
89
+ }
90
+ var execFileAsync = promisify(execFile);
91
+ var FORMAT1_PATTERN = /^(\S+)\s+-\s+(.+)\s+\(([^)]+)\)$/;
92
+ var FORMAT2_PATTERN = /^([^/]+)\/([^\s]+)\s+-\s+(.+)$/;
93
+ var FORMAT3_PATTERN = /^([^/\s]+)\/([^\s]+)$/;
94
+ var FORMAT4_PATTERN = /^([^/\s]+)\/([^@\s]+)@([^\s]+)$/;
95
+ var SKILL_SUFFIX_PATTERN = /-skill$/;
96
+ var SKILL_PREFIX_PATTERN = /^skill-/;
97
+ var ANSI_PATTERN = /\x1b\[[0-9;]*m/g;
98
+ var GITHUB_SEGMENT_PATTERN = /^[a-zA-Z0-9._-]+$/;
99
+ async function searchSkills(query) {
100
+ try {
101
+ const { stdout } = await execFileAsync("npx", ["skills", "find", query], {
102
+ timeout: 3e4
103
+ });
104
+ return parseSkillsOutput(stdout);
105
+ } catch (error) {
106
+ const message = error instanceof Error ? error.message : String(error);
107
+ console.warn(`Skills search failed: ${message}`);
108
+ console.warn("Continuing with local skills only");
109
+ return [];
110
+ }
111
+ }
112
+ function deriveSkillName(repo) {
113
+ return repo.replace(SKILL_SUFFIX_PATTERN, "").replace(SKILL_PREFIX_PATTERN, "");
114
+ }
115
+ function tryParseFormat1(line) {
116
+ const match = line.match(FORMAT1_PATTERN);
117
+ if (!match) {
118
+ return null;
119
+ }
120
+ const [, name, description, fullRepo] = match;
121
+ if (!(name && description && fullRepo)) {
122
+ return null;
123
+ }
124
+ const parts = fullRepo.split("/");
125
+ const owner = parts[0];
126
+ const repo = parts[1];
127
+ if (!(owner && repo)) {
128
+ return null;
129
+ }
130
+ return {
131
+ name,
132
+ description,
133
+ owner,
134
+ repo,
135
+ installCommand: `npx skills add ${fullRepo}`
136
+ };
137
+ }
138
+ function tryParseFormat2(line) {
139
+ const match = line.match(FORMAT2_PATTERN);
140
+ if (!match) {
141
+ return null;
142
+ }
143
+ const [, owner, repo, description] = match;
144
+ if (!(owner && repo && description)) {
145
+ return null;
146
+ }
147
+ return {
148
+ name: deriveSkillName(repo),
149
+ description,
150
+ owner,
151
+ repo,
152
+ installCommand: `npx skills add ${owner}/${repo}`
153
+ };
154
+ }
155
+ function tryParseFormat3(line) {
156
+ const match = line.match(FORMAT3_PATTERN);
157
+ if (!match) {
158
+ return null;
159
+ }
160
+ const [, owner, repo] = match;
161
+ if (!(owner && repo)) {
162
+ return null;
163
+ }
164
+ return {
165
+ name: deriveSkillName(repo),
166
+ description: "",
167
+ owner,
168
+ repo,
169
+ installCommand: `npx skills add ${owner}/${repo}`
170
+ };
171
+ }
172
+ function tryParseFormat4(line) {
173
+ const match = line.match(FORMAT4_PATTERN);
174
+ if (!match) {
175
+ return null;
176
+ }
177
+ const [, owner, repo, skillName] = match;
178
+ if (!(owner && repo && skillName)) {
179
+ return null;
180
+ }
181
+ return {
182
+ name: skillName,
183
+ description: "",
184
+ owner,
185
+ repo,
186
+ installCommand: `npx skills add ${owner}/${repo}@${skillName}`
187
+ };
188
+ }
189
+ function isValidGitHubSegment(s) {
190
+ return s.length > 0 && GITHUB_SEGMENT_PATTERN.test(s);
191
+ }
192
+ function stripAnsi(str) {
193
+ return str.replace(ANSI_PATTERN, "");
194
+ }
195
+ function parseSkillsOutput(output) {
196
+ const results = [];
197
+ const lines = output.split("\n").filter((line) => line.trim());
198
+ for (const rawLine of lines) {
199
+ const line = stripAnsi(rawLine);
200
+ const result = tryParseFormat4(line) ?? tryParseFormat1(line) ?? tryParseFormat2(line) ?? tryParseFormat3(line);
201
+ if (result) {
202
+ results.push(result);
203
+ }
204
+ }
205
+ return results;
206
+ }
207
+ async function fetchSkillContent(owner, repo, skillName) {
208
+ for (const [label, value] of [
209
+ ["owner", owner],
210
+ ["repo", repo],
211
+ ["skillName", skillName]
212
+ ]) {
213
+ if (!isValidGitHubSegment(value)) {
214
+ throw new Error(`Invalid ${label}: ${value}`);
215
+ }
216
+ }
217
+ const paths = [
218
+ `skills/${skillName}/SKILL.md`,
219
+ `${skillName}/SKILL.md`,
220
+ "SKILL.md"
221
+ ];
222
+ for (const path of paths) {
223
+ const url = `https://raw.githubusercontent.com/${owner}/${repo}/main/${path}`;
224
+ const response = await fetch(url);
225
+ if (response.ok) {
226
+ return response.text();
227
+ }
228
+ }
229
+ throw new Error(
230
+ `Failed to fetch skill content for ${owner}/${repo}/${skillName}`
231
+ );
232
+ }
233
+ export {
234
+ ensureAutoDiscoveryPlugin,
235
+ fetchSkillContent,
236
+ getAutoDiscoveryPluginPath,
237
+ installSkillToAutoDiscovery,
238
+ isSkillAutoDiscovered,
239
+ listAutoDiscoveredSkills,
240
+ searchSkills
241
+ };
@@ -11,11 +11,11 @@ import {
11
11
  loadCompiledRegistry,
12
12
  removeSkill,
13
13
  updateSkill
14
- } from "./chunk-4TKNQ5RW.js";
14
+ } from "./chunk-QKGHHAFR.js";
15
15
  import {
16
16
  syncRegistrySources,
17
17
  syncSource
18
- } from "./chunk-QQGRKUSM.js";
18
+ } from "./chunk-3WLHRD63.js";
19
19
  import {
20
20
  addSource,
21
21
  compileRegistry,
@@ -27,13 +27,15 @@ import {
27
27
  loadSources,
28
28
  removeSource,
29
29
  saveSources
30
- } from "./chunk-XTUQVJYH.js";
30
+ } from "./chunk-MTYPUSCH.js";
31
+ import {
32
+ ensureWorkspace
33
+ } from "./chunk-RAFHASLI.js";
31
34
  import {
32
35
  CORE_SKILLS,
33
- ensureWorkspace,
34
36
  isCoreSkill
35
- } from "./chunk-GIZRTNY3.js";
36
- import "./chunk-VRBGWKZ6.js";
37
+ } from "./chunk-JJCGDGRS.js";
38
+ import "./chunk-326UJHZM.js";
37
39
  import "./chunk-Y55L47HC.js";
38
40
  export {
39
41
  CORE_SKILLS,
@@ -2,14 +2,14 @@
2
2
  import {
3
3
  syncRegistrySources,
4
4
  syncSource
5
- } from "./chunk-QQGRKUSM.js";
6
- import "./chunk-XTUQVJYH.js";
7
- import "./chunk-VRBGWKZ6.js";
5
+ } from "./chunk-3WLHRD63.js";
6
+ import "./chunk-MTYPUSCH.js";
7
+ import "./chunk-326UJHZM.js";
8
8
  import {
9
9
  init_esm_shims
10
10
  } from "./chunk-Y55L47HC.js";
11
11
 
12
- // ../../packages/provider/dist/sync-E5PGFGNI.js
12
+ // ../../packages/provider/dist/sync-XZGFZXZF.js
13
13
  init_esm_shims();
14
14
  export {
15
15
  syncRegistrySources,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@looplia/looplia-cli",
3
- "version": "0.7.5",
3
+ "version": "0.8.0",
4
4
  "description": "Looplia CLI - AI-powered workflow automation tool",
5
5
  "type": "module",
6
6
  "license": "Elastic-2.0",