@chakresh/kresh 0.1.1 → 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/package.json CHANGED
@@ -1,16 +1,17 @@
1
1
  {
2
2
  "name": "@chakresh/kresh",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "bin": {
5
5
  "kresh": "./src/index.js"
6
6
  },
7
7
  "type": "module",
8
8
  "dependencies": {
9
- "commander": "^11.1.0",
9
+ "@chakresh/kresh": "^0.1.2",
10
10
  "axios": "^1.6.8",
11
11
  "chalk": "^5.3.0",
12
- "ora": "^8.0.1",
13
- "inquirer": "^9.2.16"
12
+ "commander": "^11.1.0",
13
+ "inquirer": "^9.2.16",
14
+ "ora": "^8.0.1"
14
15
  },
15
16
  "publishConfig": {
16
17
  "access": "public"
@@ -24,6 +24,7 @@ export async function searchSkills(query) {
24
24
  if (skill.description) {
25
25
  console.log(` ${skill.description}`);
26
26
  }
27
+ console.log(` ${logger.dim('Install:')} kresh install ${skill.slug}`);
27
28
  console.log();
28
29
  });
29
30
  } catch (error) {
@@ -2,13 +2,59 @@ import fs from 'fs/promises';
2
2
  import path from 'path';
3
3
 
4
4
  /**
5
- * Writes the SKILL.md and metadata.json files directly in a folder named after the skill's slug inside the skills folder.
5
+ * Helper to find the workspace or project root directory by scanning up.
6
+ */
7
+ async function getWorkspaceRoot(startDir = process.cwd()) {
8
+ let currentDir = startDir;
9
+ while (true) {
10
+ try {
11
+ const hasPackageJson = await fs.access(path.join(currentDir, 'package.json')).then(() => true).catch(() => false);
12
+ const isKreshRoot = await fs.access(path.join(currentDir, 'next.config.mjs')).then(() => true).catch(() => false);
13
+
14
+ if (hasPackageJson && isKreshRoot) {
15
+ return currentDir;
16
+ }
17
+ } catch (e) {
18
+ // Ignore
19
+ }
20
+
21
+ const parentDir = path.dirname(currentDir);
22
+ if (parentDir === currentDir) {
23
+ break;
24
+ }
25
+ currentDir = parentDir;
26
+ }
27
+
28
+ // Secondary check: look for any package.json up the tree
29
+ currentDir = startDir;
30
+ while (true) {
31
+ try {
32
+ const hasPackageJson = await fs.access(path.join(currentDir, 'package.json')).then(() => true).catch(() => false);
33
+ if (hasPackageJson) {
34
+ return currentDir;
35
+ }
36
+ } catch (e) {
37
+ // Ignore
38
+ }
39
+ const parentDir = path.dirname(currentDir);
40
+ if (parentDir === currentDir) {
41
+ break;
42
+ }
43
+ currentDir = parentDir;
44
+ }
45
+
46
+ return startDir;
47
+ }
48
+
49
+ /**
50
+ * Writes the SKILL.md and metadata.json files directly in a folder named after the skill's slug (excluding @owner scope folder) inside the skills folder.
6
51
  */
7
52
  export async function writeLocalSkill(slug, skillContent, metadata) {
8
53
  try {
9
- // Sanitize folder name, use slug
10
- const folderName = slug || metadata.slug;
11
- const targetDir = path.join(process.cwd(), 'skills', folderName);
54
+ const rootDir = await getWorkspaceRoot();
55
+ const folderName = (slug || metadata.slug).split('/').pop();
56
+ const targetDir = path.join(rootDir, 'skills', folderName);
57
+
12
58
  await fs.mkdir(targetDir, { recursive: true });
13
59
  await fs.writeFile(path.join(targetDir, 'SKILL.md'), skillContent, 'utf8');
14
60
  await fs.writeFile(path.join(targetDir, 'metadata.json'), JSON.stringify(metadata, null, 2), 'utf8');
@@ -24,11 +70,21 @@ export async function writeLocalSkill(slug, skillContent, metadata) {
24
70
  */
25
71
  export async function readLocalSkillMetadata(slug) {
26
72
  try {
27
- const metadataPath = path.join(process.cwd(), 'skills', slug, 'metadata.json');
73
+ const rootDir = await getWorkspaceRoot();
74
+ const folderName = slug.split('/').pop();
75
+ const metadataPath = path.join(rootDir, 'skills', folderName, 'metadata.json');
28
76
  const data = await fs.readFile(metadataPath, 'utf8');
29
77
  return JSON.parse(data);
30
78
  } catch (error) {
31
- return null;
79
+ // Also try legacy path for backwards compatibility
80
+ try {
81
+ const rootDir = await getWorkspaceRoot();
82
+ const legacyPath = path.join(rootDir, 'skills', slug, 'metadata.json');
83
+ const data = await fs.readFile(legacyPath, 'utf8');
84
+ return JSON.parse(data);
85
+ } catch (e) {
86
+ return null;
87
+ }
32
88
  }
33
89
  }
34
90
 
@@ -37,8 +93,28 @@ export async function readLocalSkillMetadata(slug) {
37
93
  */
38
94
  export async function removeLocalSkill(slug) {
39
95
  try {
40
- const targetDir = path.join(process.cwd(), 'skills', slug);
96
+ const rootDir = await getWorkspaceRoot();
97
+ const folderName = slug.split('/').pop();
98
+ const targetDir = path.join(rootDir, 'skills', folderName);
41
99
  await fs.rm(targetDir, { recursive: true, force: true });
100
+
101
+ // Also clean up legacy directory if it exists
102
+ const legacyDir = path.join(rootDir, 'skills', slug);
103
+ await fs.rm(legacyDir, { recursive: true, force: true });
104
+
105
+ // Clean up empty parent directory (e.g. @username) if it becomes empty
106
+ if (slug.includes('/')) {
107
+ const parts = slug.split('/');
108
+ const parentDir = path.join(rootDir, 'skills', parts[0]);
109
+ try {
110
+ const files = await fs.readdir(parentDir);
111
+ if (files.length === 0) {
112
+ await fs.rmdir(parentDir);
113
+ }
114
+ } catch (e) {
115
+ // Ignore
116
+ }
117
+ }
42
118
  return true;
43
119
  } catch (error) {
44
120
  console.error('Failed to remove skill files from the skills directory:', error);
@@ -51,7 +127,8 @@ export async function removeLocalSkill(slug) {
51
127
  */
52
128
  export async function listLocalSkills() {
53
129
  try {
54
- const skillsDir = path.join(process.cwd(), 'skills');
130
+ const rootDir = await getWorkspaceRoot();
131
+ const skillsDir = path.join(rootDir, 'skills');
55
132
  let entries = [];
56
133
  try {
57
134
  entries = await fs.readdir(skillsDir, { withFileTypes: true });
@@ -63,15 +140,49 @@ export async function listLocalSkills() {
63
140
  const skills = [];
64
141
  for (const entry of entries) {
65
142
  if (entry.isDirectory()) {
66
- const metadata = await readLocalSkillMetadata(entry.name);
67
- if (metadata) {
68
- skills.push({
69
- slug: metadata.slug || entry.name,
70
- installed: true,
71
- version: metadata.version || metadata.currentVersion || 'unknown',
72
- name: metadata.name || 'unknown',
73
- description: metadata.description || ''
74
- });
143
+ if (entry.name.startsWith('@')) {
144
+ // Legacy support: It's a user scope directory, scan subdirectories inside it
145
+ const scopeDir = path.join(skillsDir, entry.name);
146
+ let subEntries = [];
147
+ try {
148
+ subEntries = await fs.readdir(scopeDir, { withFileTypes: true });
149
+ } catch (e) {
150
+ continue;
151
+ }
152
+
153
+ for (const subEntry of subEntries) {
154
+ if (subEntry.isDirectory()) {
155
+ const scopedSlug = `${entry.name}/${subEntry.name}`;
156
+ const metadata = await readLocalSkillMetadata(scopedSlug);
157
+ if (metadata) {
158
+ skills.push({
159
+ slug: metadata.slug || scopedSlug,
160
+ installed: true,
161
+ version: metadata.version || metadata.currentVersion || 'unknown',
162
+ name: metadata.name || 'unknown',
163
+ description: metadata.description || ''
164
+ });
165
+ }
166
+ }
167
+ }
168
+ } else {
169
+ // New format: Directly under skills/
170
+ try {
171
+ const metadataPath = path.join(skillsDir, entry.name, 'metadata.json');
172
+ const data = await fs.readFile(metadataPath, 'utf8');
173
+ const metadata = JSON.parse(data);
174
+ if (metadata) {
175
+ skills.push({
176
+ slug: metadata.slug || entry.name,
177
+ installed: true,
178
+ version: metadata.version || metadata.currentVersion || 'unknown',
179
+ name: metadata.name || 'unknown',
180
+ description: metadata.description || ''
181
+ });
182
+ }
183
+ } catch (e) {
184
+ // Ignore if metadata is invalid/missing
185
+ }
75
186
  }
76
187
  }
77
188
  }