@loom-framework/core 0.1.0-alpha.141 → 0.1.0-alpha.143

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.
Files changed (157) hide show
  1. package/dist/adapters/base.d.ts +31 -0
  2. package/dist/adapters/base.d.ts.map +1 -0
  3. package/dist/adapters/base.js +69 -0
  4. package/dist/adapters/base.js.map +1 -0
  5. package/dist/adapters/factory.d.ts +8 -0
  6. package/dist/adapters/factory.d.ts.map +1 -0
  7. package/dist/adapters/factory.js +25 -0
  8. package/dist/adapters/factory.js.map +1 -0
  9. package/dist/adapters/filesystem.d.ts +46 -0
  10. package/dist/adapters/filesystem.d.ts.map +1 -0
  11. package/dist/adapters/filesystem.js +321 -0
  12. package/dist/adapters/filesystem.js.map +1 -0
  13. package/dist/adapters/index.d.ts +10 -0
  14. package/dist/adapters/index.d.ts.map +1 -0
  15. package/dist/adapters/index.js +8 -0
  16. package/dist/adapters/index.js.map +1 -0
  17. package/dist/adapters/sqlite.d.ts +37 -0
  18. package/dist/adapters/sqlite.d.ts.map +1 -0
  19. package/dist/adapters/sqlite.js +264 -0
  20. package/dist/adapters/sqlite.js.map +1 -0
  21. package/dist/backend/ai/button-resolver.d.ts +1 -1
  22. package/dist/backend/ai/button-resolver.d.ts.map +1 -1
  23. package/dist/backend/ai/engine.d.ts +1 -1
  24. package/dist/backend/ai/engine.d.ts.map +1 -1
  25. package/dist/backend/ai/output-parser.d.ts +1 -1
  26. package/dist/backend/ai/output-parser.d.ts.map +1 -1
  27. package/dist/backend/ai/session-manager.d.ts +1 -1
  28. package/dist/backend/ai/session-manager.d.ts.map +1 -1
  29. package/dist/backend/index.d.ts +3 -5
  30. package/dist/backend/index.d.ts.map +1 -1
  31. package/dist/backend/index.js +3 -15
  32. package/dist/backend/index.js.map +1 -1
  33. package/dist/backend/observe/logger.d.ts +1 -4
  34. package/dist/backend/observe/logger.d.ts.map +1 -1
  35. package/dist/backend/observe/logger.js +3 -18
  36. package/dist/backend/observe/logger.js.map +1 -1
  37. package/dist/backend/routes/chat-sse.d.ts +17 -0
  38. package/dist/backend/routes/chat-sse.d.ts.map +1 -0
  39. package/dist/backend/routes/chat-sse.js +60 -0
  40. package/dist/backend/routes/chat-sse.js.map +1 -0
  41. package/dist/backend/routes/chat.d.ts +2 -6
  42. package/dist/backend/routes/chat.d.ts.map +1 -1
  43. package/dist/backend/routes/chat.js +4 -137
  44. package/dist/backend/routes/chat.js.map +1 -1
  45. package/dist/backend/routes/data.d.ts +2 -1
  46. package/dist/backend/routes/data.d.ts.map +1 -1
  47. package/dist/backend/routes/data.js +1 -1
  48. package/dist/backend/routes/data.js.map +1 -1
  49. package/dist/backend/routes/health.d.ts +1 -1
  50. package/dist/backend/routes/health.d.ts.map +1 -1
  51. package/dist/backend/routes/index.d.ts +2 -0
  52. package/dist/backend/routes/index.d.ts.map +1 -1
  53. package/dist/backend/routes/index.js +1 -0
  54. package/dist/backend/routes/index.js.map +1 -1
  55. package/dist/backend/routes/session-routes.d.ts +20 -0
  56. package/dist/backend/routes/session-routes.d.ts.map +1 -0
  57. package/dist/backend/routes/session-routes.js +94 -0
  58. package/dist/backend/routes/session-routes.js.map +1 -0
  59. package/dist/backend/routes/skill-archive.d.ts +13 -0
  60. package/dist/backend/routes/skill-archive.d.ts.map +1 -0
  61. package/dist/backend/routes/skill-archive.js +84 -0
  62. package/dist/backend/routes/skill-archive.js.map +1 -0
  63. package/dist/backend/routes/skill-parser.d.ts +41 -0
  64. package/dist/backend/routes/skill-parser.d.ts.map +1 -0
  65. package/dist/backend/routes/skill-parser.js +184 -0
  66. package/dist/backend/routes/skill-parser.js.map +1 -0
  67. package/dist/backend/routes/skill-service.d.ts +113 -0
  68. package/dist/backend/routes/skill-service.d.ts.map +1 -0
  69. package/dist/backend/routes/skill-service.js +265 -0
  70. package/dist/backend/routes/skill-service.js.map +1 -0
  71. package/dist/backend/routes/skills.d.ts +3 -0
  72. package/dist/backend/routes/skills.d.ts.map +1 -1
  73. package/dist/backend/routes/skills.js +33 -456
  74. package/dist/backend/routes/skills.js.map +1 -1
  75. package/dist/backend/services/skill-archive.d.ts +13 -0
  76. package/dist/backend/services/skill-archive.d.ts.map +1 -0
  77. package/dist/backend/services/skill-archive.js +84 -0
  78. package/dist/backend/services/skill-archive.js.map +1 -0
  79. package/dist/backend/services/skill-parser.d.ts +41 -0
  80. package/dist/backend/services/skill-parser.d.ts.map +1 -0
  81. package/dist/backend/services/skill-parser.js +184 -0
  82. package/dist/backend/services/skill-parser.js.map +1 -0
  83. package/dist/backend/services/skill-service.d.ts +113 -0
  84. package/dist/backend/services/skill-service.d.ts.map +1 -0
  85. package/dist/backend/services/skill-service.js +265 -0
  86. package/dist/backend/services/skill-service.js.map +1 -0
  87. package/dist/cli/commands/data.d.ts.map +1 -1
  88. package/dist/cli/commands/data.js +3 -1
  89. package/dist/cli/commands/data.js.map +1 -1
  90. package/dist/cli/commands/dev.d.ts.map +1 -1
  91. package/dist/cli/commands/dev.js +2 -1
  92. package/dist/cli/commands/dev.js.map +1 -1
  93. package/dist/cli/commands/generate-capabilities.d.ts.map +1 -1
  94. package/dist/cli/commands/generate-capabilities.js +2 -1
  95. package/dist/cli/commands/generate-capabilities.js.map +1 -1
  96. package/dist/cli/commands/generate-dashboard.d.ts.map +1 -1
  97. package/dist/cli/commands/generate-dashboard.js +3 -452
  98. package/dist/cli/commands/generate-dashboard.js.map +1 -1
  99. package/dist/cli/commands/generate-page.d.ts.map +1 -1
  100. package/dist/cli/commands/generate-page.js +6 -436
  101. package/dist/cli/commands/generate-page.js.map +1 -1
  102. package/dist/cli/commands/serve.d.ts +12 -0
  103. package/dist/cli/commands/serve.d.ts.map +1 -0
  104. package/dist/cli/commands/serve.js +43 -0
  105. package/dist/cli/commands/serve.js.map +1 -0
  106. package/dist/cli/framework.d.ts +28 -0
  107. package/dist/cli/framework.d.ts.map +1 -0
  108. package/dist/cli/framework.js +29 -0
  109. package/dist/cli/framework.js.map +1 -0
  110. package/dist/cli/helpers/app-tsx-wiring.d.ts.map +1 -1
  111. package/dist/cli/helpers/app-tsx-wiring.js +39 -24
  112. package/dist/cli/helpers/app-tsx-wiring.js.map +1 -1
  113. package/dist/cli/helpers/column-template.d.ts +3 -1
  114. package/dist/cli/helpers/column-template.d.ts.map +1 -1
  115. package/dist/cli/helpers/column-template.js +1 -1
  116. package/dist/cli/helpers/column-template.js.map +1 -1
  117. package/dist/cli/helpers/duration.d.ts +2 -2
  118. package/dist/cli/helpers/duration.d.ts.map +1 -1
  119. package/dist/cli/helpers/duration.js +2 -16
  120. package/dist/cli/helpers/duration.js.map +1 -1
  121. package/dist/cli/helpers/field-template.d.ts +1 -1
  122. package/dist/cli/helpers/field-template.d.ts.map +1 -1
  123. package/dist/cli/helpers/field-template.js +1 -1
  124. package/dist/cli/helpers/field-template.js.map +1 -1
  125. package/dist/cli/helpers/i18n-template.d.ts +13 -0
  126. package/dist/cli/helpers/i18n-template.d.ts.map +1 -0
  127. package/dist/cli/helpers/i18n-template.js +48 -0
  128. package/dist/cli/helpers/i18n-template.js.map +1 -0
  129. package/dist/cli/templates/crud-page.d.ts +14 -0
  130. package/dist/cli/templates/crud-page.d.ts.map +1 -0
  131. package/dist/cli/templates/crud-page.js +405 -0
  132. package/dist/cli/templates/crud-page.js.map +1 -0
  133. package/dist/cli/templates/dashboard-page.d.ts +11 -0
  134. package/dist/cli/templates/dashboard-page.d.ts.map +1 -0
  135. package/dist/cli/templates/dashboard-page.js +456 -0
  136. package/dist/cli/templates/dashboard-page.js.map +1 -0
  137. package/dist/cli/templates/index.d.ts +2 -0
  138. package/dist/cli/templates/index.d.ts.map +1 -1
  139. package/dist/cli/templates/index.js +2 -0
  140. package/dist/cli/templates/index.js.map +1 -1
  141. package/dist/config.d.ts +22 -22
  142. package/dist/dashboard-config.d.ts +10 -10
  143. package/dist/index.d.ts +8 -8
  144. package/dist/index.d.ts.map +1 -1
  145. package/dist/index.js +5 -5
  146. package/dist/index.js.map +1 -1
  147. package/dist/types/chat.d.ts +33 -32
  148. package/dist/types/chat.d.ts.map +1 -1
  149. package/dist/types/chat.js +4 -1
  150. package/dist/types/chat.js.map +1 -1
  151. package/dist/types/index.d.ts +1 -1
  152. package/dist/types/index.d.ts.map +1 -1
  153. package/dist/utils/duration.d.ts +8 -0
  154. package/dist/utils/duration.d.ts.map +1 -0
  155. package/dist/utils/duration.js +22 -0
  156. package/dist/utils/duration.js.map +1 -0
  157. package/package.json +2 -2
@@ -0,0 +1,41 @@
1
+ /** Skill file parsing utilities */
2
+ export interface SkillMetadata {
3
+ name?: string;
4
+ description?: string;
5
+ version?: string;
6
+ 'allowed-tools'?: string[];
7
+ [key: string]: unknown;
8
+ }
9
+ /**
10
+ * Parse YAML frontmatter from markdown content using simple regex.
11
+ */
12
+ export declare function parseFrontmatter(content: string): {
13
+ metadata: SkillMetadata;
14
+ body: string;
15
+ };
16
+ /**
17
+ * Recursively scan a directory for files, returning relative paths.
18
+ * Excludes SKILL.md (handled separately).
19
+ */
20
+ export declare function scanDirFiles(dir: string, base: string): Promise<string[]>;
21
+ /**
22
+ * Safely resolve a file path within a directory. Rejects '..' traversal.
23
+ */
24
+ export declare function safeResolve(baseDir: string, filePath: string): string | null;
25
+ /**
26
+ * Validate skill name.
27
+ */
28
+ export declare function isValidSkillName(name: string): boolean;
29
+ /**
30
+ * Text file extensions that should be kept when parsing zip archives.
31
+ */
32
+ export declare const TEXT_EXTENSIONS: Set<string>;
33
+ /**
34
+ * Check if a file path looks like a text file based on extension.
35
+ */
36
+ export declare function isTextFile(filePath: string): boolean;
37
+ /**
38
+ * Get MIME type for a file extension.
39
+ */
40
+ export declare function getContentType(filePath: string): string;
41
+ //# sourceMappingURL=skill-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-parser.d.ts","sourceRoot":"","sources":["../../../src/backend/routes/skill-parser.ts"],"names":[],"mappings":"AAAA,mCAAmC;AAQnC,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAID;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAqG3F;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAkB/E;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK5E;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED;;GAEG;AACH,eAAO,MAAM,eAAe,aAK1B,CAAC;AAEH;;GAEG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAQpD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAevD"}
@@ -0,0 +1,184 @@
1
+ /** Skill file parsing utilities */
2
+ import { promises as fs } from 'node:fs';
3
+ import path from 'node:path';
4
+ // ── Functions ──
5
+ /**
6
+ * Parse YAML frontmatter from markdown content using simple regex.
7
+ */
8
+ export function parseFrontmatter(content) {
9
+ // The \n before closing --- is optional to handle empty frontmatter (---\n---\nBody)
10
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n?---\r?\n?([\s\S]*)$/);
11
+ if (!match)
12
+ return { metadata: {}, body: content };
13
+ const metadataRaw = match[1];
14
+ const body = match[2];
15
+ try {
16
+ const metadata = {};
17
+ const lines = metadataRaw.split('\n');
18
+ let currentKey = '';
19
+ let currentValues = [];
20
+ let inArray = false;
21
+ let inBlock = false;
22
+ let blockLines = [];
23
+ const flushArray = () => {
24
+ if (inArray && currentKey) {
25
+ // If array items were collected, store as array; otherwise store empty string
26
+ metadata[currentKey] = currentValues.length > 0 ? currentValues : '';
27
+ inArray = false;
28
+ currentValues = [];
29
+ }
30
+ };
31
+ const flushBlock = () => {
32
+ if (inBlock && currentKey) {
33
+ metadata[currentKey] = blockLines.join('\n').trim();
34
+ }
35
+ inBlock = false;
36
+ blockLines = [];
37
+ };
38
+ for (const line of lines) {
39
+ if (inBlock) {
40
+ if (/^[ \t]+/.test(line) || (line === '' && blockLines.length > 0)) {
41
+ blockLines.push(line.replace(/^[ \t]+/, ''));
42
+ continue;
43
+ }
44
+ else {
45
+ flushBlock();
46
+ }
47
+ }
48
+ const trimmed = line.trim();
49
+ if (!trimmed)
50
+ continue;
51
+ const arrayMatch = trimmed.match(/^-\s+(.+)$/);
52
+ if (inArray && arrayMatch) {
53
+ currentValues.push(arrayMatch[1].replace(/^['"]|['"]$/g, '').trim());
54
+ continue;
55
+ }
56
+ if (inArray) {
57
+ flushArray();
58
+ }
59
+ const kvMatch = trimmed.match(/^([a-zA-Z_-][a-zA-Z0-9_-]*):\s*(.*)$/);
60
+ if (kvMatch) {
61
+ currentKey = kvMatch[1];
62
+ let val = kvMatch[2].trim();
63
+ if ((val.startsWith("'") && val.endsWith("'")) || (val.startsWith('"') && val.endsWith('"'))) {
64
+ val = val.slice(1, -1);
65
+ }
66
+ if (val === '|' || val === '>') {
67
+ inBlock = true;
68
+ blockLines = [];
69
+ continue;
70
+ }
71
+ if (val === '[') {
72
+ inArray = true;
73
+ currentValues = [];
74
+ continue;
75
+ }
76
+ if (val.startsWith('[') && val.endsWith(']')) {
77
+ metadata[currentKey] = val.slice(1, -1).split(',').map((s) => s.trim().replace(/^['"]|['"]$/g, ''));
78
+ currentKey = '';
79
+ continue;
80
+ }
81
+ // Empty value means multi-line array follows (YAML list syntax: "key:\n - item")
82
+ if (val === '') {
83
+ inArray = true;
84
+ currentValues = [];
85
+ continue;
86
+ }
87
+ metadata[currentKey] = val;
88
+ currentKey = '';
89
+ }
90
+ }
91
+ flushArray();
92
+ flushBlock();
93
+ return { metadata, body };
94
+ }
95
+ catch {
96
+ return { metadata: {}, body: content };
97
+ }
98
+ }
99
+ /**
100
+ * Recursively scan a directory for files, returning relative paths.
101
+ * Excludes SKILL.md (handled separately).
102
+ */
103
+ export async function scanDirFiles(dir, base) {
104
+ const results = [];
105
+ let entries;
106
+ try {
107
+ entries = await fs.readdir(dir, { withFileTypes: true });
108
+ }
109
+ catch {
110
+ return results;
111
+ }
112
+ for (const entry of entries) {
113
+ if (entry.isDirectory()) {
114
+ const subFiles = await scanDirFiles(path.join(dir, entry.name), base);
115
+ results.push(...subFiles);
116
+ }
117
+ else {
118
+ const relPath = path.relative(base, path.join(dir, entry.name));
119
+ results.push(relPath);
120
+ }
121
+ }
122
+ return results;
123
+ }
124
+ /**
125
+ * Safely resolve a file path within a directory. Rejects '..' traversal.
126
+ */
127
+ export function safeResolve(baseDir, filePath) {
128
+ if (filePath.includes('..'))
129
+ return null;
130
+ const resolved = path.resolve(baseDir, filePath);
131
+ if (!resolved.startsWith(baseDir))
132
+ return null;
133
+ return resolved;
134
+ }
135
+ /**
136
+ * Validate skill name.
137
+ */
138
+ export function isValidSkillName(name) {
139
+ return /^[a-zA-Z][a-zA-Z0-9_-]*$/.test(name);
140
+ }
141
+ /**
142
+ * Text file extensions that should be kept when parsing zip archives.
143
+ */
144
+ export const TEXT_EXTENSIONS = new Set([
145
+ '.md', '.json', '.yaml', '.yml', '.txt', '.ts', '.js', '.tsx', '.jsx',
146
+ '.xml', '.html', '.css', '.sh', '.py', '.rb', '.go', '.rs', '.java',
147
+ '.kt', '.swift', '.c', '.cpp', '.h', '.hpp', '.toml', '.ini', '.cfg',
148
+ '.env', '.gitignore', '.prettierrc', '.eslintrc', '.conf', '.properties',
149
+ ]);
150
+ /**
151
+ * Check if a file path looks like a text file based on extension.
152
+ */
153
+ export function isTextFile(filePath) {
154
+ const ext = path.extname(filePath).toLowerCase();
155
+ if (TEXT_EXTENSIONS.has(ext))
156
+ return true;
157
+ // Files with no extension but common names
158
+ const basename = path.basename(filePath).toLowerCase();
159
+ if (['readme', 'license', 'makefile', 'dockerfile', '.gitkeep', '.gitignore'].includes(basename))
160
+ return true;
161
+ if (basename.startsWith('.git'))
162
+ return true;
163
+ return false;
164
+ }
165
+ /**
166
+ * Get MIME type for a file extension.
167
+ */
168
+ export function getContentType(filePath) {
169
+ const ext = path.extname(filePath).toLowerCase();
170
+ const types = {
171
+ '.md': 'text/markdown',
172
+ '.txt': 'text/plain',
173
+ '.json': 'application/json',
174
+ '.xml': 'application/xml',
175
+ '.yaml': 'text/yaml',
176
+ '.yml': 'text/yaml',
177
+ '.ts': 'text/typescript',
178
+ '.js': 'text/javascript',
179
+ '.html': 'text/html',
180
+ '.css': 'text/css',
181
+ };
182
+ return types[ext] || 'text/plain';
183
+ }
184
+ //# sourceMappingURL=skill-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-parser.js","sourceRoot":"","sources":["../../../src/backend/routes/skill-parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,113 @@
1
+ /** Skill business logic — filesystem operations independent of Fastify */
2
+ import type { SkillMetadata } from './skill-parser.js';
3
+ import { parseZipArchive } from './skill-archive.js';
4
+ export interface SkillInfo {
5
+ name: string;
6
+ metadata: SkillMetadata;
7
+ subDirs: string[];
8
+ }
9
+ export interface SkillDetail {
10
+ name: string;
11
+ metadata: SkillMetadata;
12
+ skillMd: string;
13
+ files: {
14
+ name: string;
15
+ path: string;
16
+ }[];
17
+ }
18
+ export interface CreateSkillBody {
19
+ name: string;
20
+ skillMd?: string;
21
+ references?: Array<{
22
+ name: string;
23
+ content: string;
24
+ }>;
25
+ scripts?: Array<{
26
+ name: string;
27
+ content: string;
28
+ }>;
29
+ files?: Array<{
30
+ path: string;
31
+ content: string;
32
+ }>;
33
+ }
34
+ export interface UploadFileBody {
35
+ filename: string;
36
+ content: string;
37
+ }
38
+ /**
39
+ * List all skills with metadata.
40
+ */
41
+ export declare function listSkills(skillsDir: string): Promise<SkillInfo[]>;
42
+ /**
43
+ * Get detailed information about a single skill.
44
+ */
45
+ export declare function getSkillDetail(skillsDir: string, skillName: string): Promise<SkillDetail | null>;
46
+ /**
47
+ * Create a new skill directory with SKILL.md and optional files.
48
+ * Returns the created skill info, or throws on conflict / invalid name.
49
+ */
50
+ export declare function createSkill(skillsDir: string, body: CreateSkillBody): Promise<{
51
+ created: true;
52
+ info: SkillInfo;
53
+ } | {
54
+ created: false;
55
+ status: number;
56
+ error: string;
57
+ }>;
58
+ /**
59
+ * Delete a skill directory.
60
+ */
61
+ export declare function deleteSkill(skillsDir: string, skillName: string): Promise<{
62
+ ok: true;
63
+ } | {
64
+ ok: false;
65
+ status: number;
66
+ error: string;
67
+ }>;
68
+ /**
69
+ * Read a file within a skill directory.
70
+ */
71
+ export declare function getSkillFile(skillsDir: string, skillName: string, filePath: string): Promise<{
72
+ ok: true;
73
+ content: string;
74
+ path: string;
75
+ } | {
76
+ ok: false;
77
+ status: number;
78
+ error: string;
79
+ }>;
80
+ /**
81
+ * Upload/replace a file in a skill directory.
82
+ */
83
+ export declare function uploadSkillFile(skillsDir: string, skillName: string, filename: string, content: string): Promise<{
84
+ ok: true;
85
+ path: string;
86
+ } | {
87
+ ok: false;
88
+ status: number;
89
+ error: string;
90
+ }>;
91
+ /**
92
+ * Create a ZIP archive of a skill directory.
93
+ */
94
+ export declare function createSkillArchive(skillsDir: string, skillName: string): Promise<{
95
+ ok: true;
96
+ buffer: Buffer;
97
+ } | {
98
+ ok: false;
99
+ status: number;
100
+ error: string;
101
+ }>;
102
+ /**
103
+ * Parse a base64-encoded ZIP archive and extract skill structure.
104
+ */
105
+ export declare function parseSkillArchive(base64Data: string): {
106
+ ok: true;
107
+ data: ReturnType<typeof parseZipArchive>;
108
+ } | {
109
+ ok: false;
110
+ status: number;
111
+ error: string;
112
+ };
113
+ //# sourceMappingURL=skill-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-service.d.ts","sourceRoot":"","sources":["../../../src/backend/routes/skill-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,265 @@
1
+ /** Skill business logic — filesystem operations independent of Fastify */
2
+ import { promises as fs } from 'node:fs';
3
+ import path from 'node:path';
4
+ import AdmZip from 'adm-zip';
5
+ import { parseFrontmatter, scanDirFiles, safeResolve, isValidSkillName } from './skill-parser.js';
6
+ import { parseZipArchive } from './skill-archive.js';
7
+ // ── Service functions ──
8
+ /**
9
+ * Ensure the skills directory exists.
10
+ */
11
+ async function ensureSkillsDir(skillsDir) {
12
+ await fs.mkdir(skillsDir, { recursive: true });
13
+ }
14
+ /**
15
+ * List all skills with metadata.
16
+ */
17
+ export async function listSkills(skillsDir) {
18
+ await ensureSkillsDir(skillsDir);
19
+ let entries;
20
+ try {
21
+ entries = await fs.readdir(skillsDir, { withFileTypes: true });
22
+ }
23
+ catch {
24
+ return [];
25
+ }
26
+ const skills = [];
27
+ for (const entry of entries) {
28
+ if (!entry.isDirectory())
29
+ continue;
30
+ const skillDir = path.join(skillsDir, entry.name);
31
+ const skillMdPath = path.join(skillDir, 'SKILL.md');
32
+ let metadata = {};
33
+ try {
34
+ const content = await fs.readFile(skillMdPath, 'utf-8');
35
+ metadata = parseFrontmatter(content).metadata;
36
+ }
37
+ catch {
38
+ // SKILL.md doesn't exist or is unreadable
39
+ }
40
+ // Scan subdirectories dynamically (any folder name, not just 'references')
41
+ let subDirs = [];
42
+ try {
43
+ const dirEntries = await fs.readdir(skillDir, { withFileTypes: true });
44
+ subDirs = dirEntries.filter((e) => e.isDirectory()).map((e) => e.name);
45
+ }
46
+ catch {
47
+ // ignore
48
+ }
49
+ skills.push({ name: entry.name, metadata, subDirs });
50
+ }
51
+ return skills;
52
+ }
53
+ /**
54
+ * Get detailed information about a single skill.
55
+ */
56
+ export async function getSkillDetail(skillsDir, skillName) {
57
+ const skillDir = path.join(skillsDir, skillName);
58
+ const exists = await fs.access(skillDir).then(() => true, () => false);
59
+ if (!exists)
60
+ return null;
61
+ let skillMd = '';
62
+ let metadata = {};
63
+ try {
64
+ skillMd = await fs.readFile(path.join(skillDir, 'SKILL.md'), 'utf-8');
65
+ metadata = parseFrontmatter(skillMd).metadata;
66
+ }
67
+ catch {
68
+ // SKILL.md doesn't exist
69
+ }
70
+ // Dynamically scan all files in subdirectories
71
+ const allFiles = await scanDirFiles(skillDir, skillDir);
72
+ const files = allFiles.map((p) => ({
73
+ name: path.basename(p),
74
+ path: p,
75
+ }));
76
+ return { name: skillName, metadata, skillMd, files };
77
+ }
78
+ /**
79
+ * Create a new skill directory with SKILL.md and optional files.
80
+ * Returns the created skill info, or throws on conflict / invalid name.
81
+ */
82
+ export async function createSkill(skillsDir, body) {
83
+ const { name, skillMd, references, scripts: scriptFiles, files: archiveFiles } = body;
84
+ if (!isValidSkillName(name)) {
85
+ return { created: false, status: 400, error: 'Invalid skill name. Use alphanumeric, hyphens, and underscores only.' };
86
+ }
87
+ const skillDir = path.join(skillsDir, name);
88
+ const exists = await fs.access(skillDir).then(() => true, () => false);
89
+ if (exists) {
90
+ return { created: false, status: 409, error: `Skill "${name}" already exists` };
91
+ }
92
+ await fs.mkdir(skillDir, { recursive: true });
93
+ const contentToWrite = skillMd || `---
94
+ name: ${name}
95
+ description: ''
96
+ version: 1.0.0
97
+ ---
98
+
99
+ # ${name}
100
+
101
+ ## Overview
102
+
103
+ [Describe what this skill does]
104
+
105
+ ## Usage Scenarios
106
+
107
+ - "User prompt" → [what the skill does]
108
+ `;
109
+ await fs.writeFile(path.join(skillDir, 'SKILL.md'), contentToWrite, 'utf-8');
110
+ if (references && references.length > 0) {
111
+ const refDir = path.join(skillDir, 'references');
112
+ await fs.mkdir(refDir, { recursive: true });
113
+ for (const ref of references) {
114
+ const safePath = safeResolve(refDir, ref.name);
115
+ if (safePath) {
116
+ await fs.mkdir(path.dirname(safePath), { recursive: true });
117
+ await fs.writeFile(safePath, ref.content, 'utf-8');
118
+ }
119
+ }
120
+ }
121
+ if (scriptFiles && scriptFiles.length > 0) {
122
+ const scriptsDir = path.join(skillDir, 'scripts');
123
+ await fs.mkdir(scriptsDir, { recursive: true });
124
+ for (const script of scriptFiles) {
125
+ const safePath = safeResolve(scriptsDir, script.name);
126
+ if (safePath) {
127
+ await fs.mkdir(path.dirname(safePath), { recursive: true });
128
+ await fs.writeFile(safePath, script.content, 'utf-8');
129
+ }
130
+ }
131
+ }
132
+ if (archiveFiles && archiveFiles.length > 0) {
133
+ for (const file of archiveFiles) {
134
+ const safePath = safeResolve(skillDir, file.path);
135
+ if (safePath) {
136
+ await fs.mkdir(path.dirname(safePath), { recursive: true });
137
+ await fs.writeFile(safePath, file.content, 'utf-8');
138
+ }
139
+ }
140
+ }
141
+ const metadata = parseFrontmatter(contentToWrite).metadata;
142
+ return { created: true, info: { name, metadata, subDirs: [] } };
143
+ }
144
+ /**
145
+ * Delete a skill directory.
146
+ */
147
+ export async function deleteSkill(skillsDir, skillName) {
148
+ const skillDir = path.join(skillsDir, skillName);
149
+ const exists = await fs.access(skillDir).then(() => true, () => false);
150
+ if (!exists) {
151
+ return { ok: false, status: 404, error: `Skill "${skillName}" not found` };
152
+ }
153
+ await fs.rm(skillDir, { recursive: true, force: true });
154
+ return { ok: true };
155
+ }
156
+ /**
157
+ * Read a file within a skill directory.
158
+ */
159
+ export async function getSkillFile(skillsDir, skillName, filePath) {
160
+ const skillDir = path.join(skillsDir, skillName);
161
+ const exists = await fs.access(skillDir).then(() => true, () => false);
162
+ if (!exists) {
163
+ return { ok: false, status: 404, error: `Skill "${skillName}" not found` };
164
+ }
165
+ const resolvedPath = safeResolve(skillDir, filePath);
166
+ if (!resolvedPath) {
167
+ return { ok: false, status: 400, error: 'Invalid file path' };
168
+ }
169
+ const fileExists = await fs.access(resolvedPath).then(() => true, () => false);
170
+ if (!fileExists) {
171
+ return { ok: false, status: 404, error: `File "${filePath}" not found` };
172
+ }
173
+ try {
174
+ const content = await fs.readFile(resolvedPath, 'utf-8');
175
+ return { ok: true, content, path: filePath };
176
+ }
177
+ catch (err) {
178
+ return { ok: false, status: 500, error: `Failed to read file: ${err}` };
179
+ }
180
+ }
181
+ /**
182
+ * Upload/replace a file in a skill directory.
183
+ */
184
+ export async function uploadSkillFile(skillsDir, skillName, filename, content) {
185
+ const skillDir = path.join(skillsDir, skillName);
186
+ const exists = await fs.access(skillDir).then(() => true, () => false);
187
+ if (!exists) {
188
+ return { ok: false, status: 404, error: `Skill "${skillName}" not found` };
189
+ }
190
+ const resolvedPath = safeResolve(skillDir, filename);
191
+ if (!resolvedPath) {
192
+ return { ok: false, status: 400, error: 'Invalid file path' };
193
+ }
194
+ await fs.mkdir(path.dirname(resolvedPath), { recursive: true });
195
+ await fs.writeFile(resolvedPath, content, 'utf-8');
196
+ return { ok: true, path: filename };
197
+ }
198
+ /**
199
+ * Create a ZIP archive of a skill directory.
200
+ */
201
+ export async function createSkillArchive(skillsDir, skillName) {
202
+ const skillDir = path.join(skillsDir, skillName);
203
+ const exists = await fs.access(skillDir).then(() => true, () => false);
204
+ if (!exists) {
205
+ return { ok: false, status: 404, error: `Skill "${skillName}" not found` };
206
+ }
207
+ const zip = new AdmZip();
208
+ // Add SKILL.md
209
+ const skillMdPath = path.join(skillDir, 'SKILL.md');
210
+ try {
211
+ const skillMd = await fs.readFile(skillMdPath, 'utf-8');
212
+ zip.addFile('SKILL.md', Buffer.from(skillMd, 'utf-8'));
213
+ }
214
+ catch {
215
+ // SKILL.md doesn't exist, skip
216
+ }
217
+ // Add all other files, preserving directory structure
218
+ const allFiles = await scanDirFiles(skillDir, skillDir);
219
+ for (const filePath of allFiles) {
220
+ if (filePath === 'SKILL.md')
221
+ continue;
222
+ const resolvedPath = safeResolve(skillDir, filePath);
223
+ if (!resolvedPath)
224
+ continue;
225
+ try {
226
+ const content = await fs.readFile(resolvedPath);
227
+ // Store under skillName/ prefix for proper extraction
228
+ zip.addFile(`${skillName}/${filePath}`, content);
229
+ }
230
+ catch {
231
+ // skip unreadable files
232
+ }
233
+ }
234
+ // Also put SKILL.md under the prefix
235
+ zip.deleteFile('SKILL.md');
236
+ try {
237
+ const skillMd = await fs.readFile(skillMdPath, 'utf-8');
238
+ zip.addFile(`${skillName}/SKILL.md`, Buffer.from(skillMd, 'utf-8'));
239
+ }
240
+ catch {
241
+ // skip
242
+ }
243
+ return { ok: true, buffer: zip.toBuffer() };
244
+ }
245
+ /**
246
+ * Parse a base64-encoded ZIP archive and extract skill structure.
247
+ */
248
+ export function parseSkillArchive(base64Data) {
249
+ let buffer;
250
+ try {
251
+ buffer = Buffer.from(base64Data, 'base64');
252
+ }
253
+ catch {
254
+ return { ok: false, status: 400, error: '无效的 base64 编码' };
255
+ }
256
+ try {
257
+ const result = parseZipArchive(buffer);
258
+ return { ok: true, data: result };
259
+ }
260
+ catch (err) {
261
+ const msg = err instanceof Error ? err.message : '解析压缩包失败';
262
+ return { ok: false, status: 400, error: msg };
263
+ }
264
+ }
265
+ //# sourceMappingURL=skill-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-service.js","sourceRoot":"","sources":["../../../src/backend/routes/skill-service.ts"],"names":[],"mappings":"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"}
@@ -9,6 +9,9 @@
9
9
  * POST /api/v1/skills/:skillName/file — Upload/replace a file in a skill
10
10
  */
11
11
  import type { FastifyInstance } from 'fastify';
12
+ export { parseFrontmatter, scanDirFiles, safeResolve, isValidSkillName, isTextFile, getContentType, TEXT_EXTENSIONS } from '../services/skill-parser.js';
13
+ export type { SkillMetadata } from '../services/skill-parser.js';
14
+ export { parseZipArchive } from '../services/skill-archive.js';
12
15
  export interface SkillRouteOptions {
13
16
  projectRoot: string;
14
17
  }
@@ -1 +1 @@
1
- {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../../src/backend/routes/skills.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AA2X/C,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,iBAAiB,GACzB,IAAI,CAoSN"}
1
+ {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../../src/backend/routes/skills.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAI/C,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,UAAU,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AACzJ,YAAY,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AA4E/D,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,iBAAiB,GACzB,IAAI,CA8FN"}