devchain-cli 0.8.4 → 0.9.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.
Files changed (100) hide show
  1. package/dist/drizzle/0038_soft_texas_twister.sql +51 -0
  2. package/dist/drizzle/0039_early_the_spike.sql +1 -0
  3. package/dist/drizzle/0040_typical_mole_man.sql +3 -0
  4. package/dist/drizzle/meta/0037_snapshot.json +3670 -0
  5. package/dist/drizzle/meta/0038_snapshot.json +4019 -0
  6. package/dist/drizzle/meta/0039_snapshot.json +4026 -0
  7. package/dist/drizzle/meta/0040_snapshot.json +4028 -0
  8. package/dist/drizzle/meta/_journal.json +22 -1
  9. package/dist/server/app.module.js +2 -0
  10. package/dist/server/app.module.js.map +1 -1
  11. package/dist/server/modules/epics/controllers/epics.controller.js +4 -0
  12. package/dist/server/modules/epics/controllers/epics.controller.js.map +1 -1
  13. package/dist/server/modules/epics/services/epics.service.js +6 -6
  14. package/dist/server/modules/epics/services/epics.service.js.map +1 -1
  15. package/dist/server/modules/mcp/dtos/mcp.dto.d.ts +74 -0
  16. package/dist/server/modules/mcp/dtos/mcp.dto.js +18 -1
  17. package/dist/server/modules/mcp/dtos/mcp.dto.js.map +1 -1
  18. package/dist/server/modules/mcp/dtos/schema-registry.js +2 -0
  19. package/dist/server/modules/mcp/dtos/schema-registry.js.map +1 -1
  20. package/dist/server/modules/mcp/mcp.module.js +2 -0
  21. package/dist/server/modules/mcp/mcp.module.js.map +1 -1
  22. package/dist/server/modules/mcp/services/mcp.service.d.ts +7 -1
  23. package/dist/server/modules/mcp/services/mcp.service.js +126 -2
  24. package/dist/server/modules/mcp/services/mcp.service.js.map +1 -1
  25. package/dist/server/modules/mcp/tool-definitions.d.ts +177 -0
  26. package/dist/server/modules/mcp/tool-definitions.js +48 -0
  27. package/dist/server/modules/mcp/tool-definitions.js.map +1 -1
  28. package/dist/server/modules/projects/dtos/export.dto.d.ts +15 -15
  29. package/dist/server/modules/seeders/seeders/0002_seed_replace_permission_mode_plan.d.ts +3 -0
  30. package/dist/server/modules/seeders/seeders/0002_seed_replace_permission_mode_plan.js +45 -0
  31. package/dist/server/modules/seeders/seeders/0002_seed_replace_permission_mode_plan.js.map +1 -0
  32. package/dist/server/modules/seeders/services/data-seeder.service.js +5 -1
  33. package/dist/server/modules/seeders/services/data-seeder.service.js.map +1 -1
  34. package/dist/server/modules/settings/dtos/settings.dto.d.ts +18 -0
  35. package/dist/server/modules/settings/dtos/settings.dto.js +5 -0
  36. package/dist/server/modules/settings/dtos/settings.dto.js.map +1 -1
  37. package/dist/server/modules/settings/services/settings.service.d.ts +5 -0
  38. package/dist/server/modules/settings/services/settings.service.js +80 -1
  39. package/dist/server/modules/settings/services/settings.service.js.map +1 -1
  40. package/dist/server/modules/skills/adapters/anthropic-skill-source.adapter.d.ts +9 -0
  41. package/dist/server/modules/skills/adapters/anthropic-skill-source.adapter.js +169 -0
  42. package/dist/server/modules/skills/adapters/anthropic-skill-source.adapter.js.map +1 -0
  43. package/dist/server/modules/skills/adapters/github-skill-source.base.d.ts +48 -0
  44. package/dist/server/modules/skills/adapters/github-skill-source.base.js +382 -0
  45. package/dist/server/modules/skills/adapters/github-skill-source.base.js.map +1 -0
  46. package/dist/server/modules/skills/adapters/microsoft-skill-source.adapter.d.ts +11 -0
  47. package/dist/server/modules/skills/adapters/microsoft-skill-source.adapter.js +227 -0
  48. package/dist/server/modules/skills/adapters/microsoft-skill-source.adapter.js.map +1 -0
  49. package/dist/server/modules/skills/adapters/openai-skill-source.adapter.d.ts +13 -0
  50. package/dist/server/modules/skills/adapters/openai-skill-source.adapter.js +260 -0
  51. package/dist/server/modules/skills/adapters/openai-skill-source.adapter.js.map +1 -0
  52. package/dist/server/modules/skills/adapters/skill-source.adapter.d.ts +26 -0
  53. package/dist/server/modules/skills/adapters/skill-source.adapter.js +5 -0
  54. package/dist/server/modules/skills/adapters/skill-source.adapter.js.map +1 -0
  55. package/dist/server/modules/skills/adapters/trailofbits-skill-source.adapter.d.ts +14 -0
  56. package/dist/server/modules/skills/adapters/trailofbits-skill-source.adapter.js +253 -0
  57. package/dist/server/modules/skills/adapters/trailofbits-skill-source.adapter.js.map +1 -0
  58. package/dist/server/modules/skills/adapters/vercel-skill-source.adapter.d.ts +15 -0
  59. package/dist/server/modules/skills/adapters/vercel-skill-source.adapter.js +292 -0
  60. package/dist/server/modules/skills/adapters/vercel-skill-source.adapter.js.map +1 -0
  61. package/dist/server/modules/skills/controllers/skills.controller.d.ts +42 -0
  62. package/dist/server/modules/skills/controllers/skills.controller.js +257 -0
  63. package/dist/server/modules/skills/controllers/skills.controller.js.map +1 -0
  64. package/dist/server/modules/skills/dtos/skill.dto.d.ts +169 -0
  65. package/dist/server/modules/skills/dtos/skill.dto.js +116 -0
  66. package/dist/server/modules/skills/dtos/skill.dto.js.map +1 -0
  67. package/dist/server/modules/skills/services/skill-category.service.d.ts +5 -0
  68. package/dist/server/modules/skills/services/skill-category.service.js +155 -0
  69. package/dist/server/modules/skills/services/skill-category.service.js.map +1 -0
  70. package/dist/server/modules/skills/services/skill-sync.service.d.ts +38 -0
  71. package/dist/server/modules/skills/services/skill-sync.service.js +283 -0
  72. package/dist/server/modules/skills/services/skill-sync.service.js.map +1 -0
  73. package/dist/server/modules/skills/services/skills.service.d.ts +119 -0
  74. package/dist/server/modules/skills/services/skills.service.js +731 -0
  75. package/dist/server/modules/skills/services/skills.service.js.map +1 -0
  76. package/dist/server/modules/skills/skills.module.d.ts +2 -0
  77. package/dist/server/modules/skills/skills.module.js +70 -0
  78. package/dist/server/modules/skills/skills.module.js.map +1 -0
  79. package/dist/server/modules/storage/db/schema.d.ts +629 -0
  80. package/dist/server/modules/storage/db/schema.js +56 -1
  81. package/dist/server/modules/storage/db/schema.js.map +1 -1
  82. package/dist/server/modules/storage/interfaces/storage.interface.d.ts +1 -0
  83. package/dist/server/modules/storage/interfaces/storage.interface.js.map +1 -1
  84. package/dist/server/modules/storage/local/local-storage.service.d.ts +2 -0
  85. package/dist/server/modules/storage/local/local-storage.service.js +50 -7
  86. package/dist/server/modules/storage/local/local-storage.service.js.map +1 -1
  87. package/dist/server/modules/storage/models/domain.models.d.ts +47 -1
  88. package/dist/server/templates/dev-loop.json +86 -79
  89. package/dist/server/tsconfig.tsbuildinfo +1 -1
  90. package/dist/server/ui/assets/{ReviewDetailPage-C4NizGFT.js → ReviewDetailPage-D_-bS1MK.js} +1 -1
  91. package/dist/server/ui/assets/{ReviewsPage-DLD6BGoH.js → ReviewsPage-BE1gxYlC.js} +1 -1
  92. package/dist/server/ui/assets/index-C094CE5I.js +945 -0
  93. package/dist/server/ui/assets/index-CbtpBUHu.css +32 -0
  94. package/dist/server/ui/assets/useReviewSubscription-Bs-17h-m.js +77 -0
  95. package/dist/server/ui/index.html +2 -2
  96. package/dist/templates/dev-loop.json +86 -79
  97. package/package.json +17 -1
  98. package/dist/server/ui/assets/index-BkiWahA0.css +0 -32
  99. package/dist/server/ui/assets/index-CHBUsbMb.js +0 -914
  100. package/dist/server/ui/assets/useReviewSubscription-CptabtYL.js +0 -83
@@ -0,0 +1,731 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.SkillsService = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ const node_crypto_1 = require("node:crypto");
18
+ const drizzle_orm_1 = require("drizzle-orm");
19
+ const error_types_1 = require("../../../common/errors/error-types");
20
+ const logger_1 = require("../../../common/logging/logger");
21
+ const settings_service_1 = require("../../settings/services/settings.service");
22
+ const db_provider_1 = require("../../storage/db/db.provider");
23
+ const schema_1 = require("../../storage/db/schema");
24
+ const skill_source_adapter_1 = require("../adapters/skill-source.adapter");
25
+ const logger = (0, logger_1.createLogger)('SkillsService');
26
+ const VALID_SKILL_STATUSES = ['available', 'outdated', 'sync_error'];
27
+ let SkillsService = class SkillsService {
28
+ constructor(db, settingsService, adapters) {
29
+ this.db = db;
30
+ this.settingsService = settingsService;
31
+ this.adapters = adapters;
32
+ }
33
+ async listSkills(options = {}) {
34
+ const enabledSources = this.getEnabledSources();
35
+ if (enabledSources.length === 0) {
36
+ return [];
37
+ }
38
+ const conditions = [];
39
+ conditions.push((0, drizzle_orm_1.inArray)(schema_1.skills.source, enabledSources));
40
+ if (options.source) {
41
+ conditions.push((0, drizzle_orm_1.eq)(schema_1.skills.source, options.source.trim().toLowerCase()));
42
+ }
43
+ if (options.category) {
44
+ conditions.push((0, drizzle_orm_1.eq)(schema_1.skills.category, options.category));
45
+ }
46
+ if (options.status) {
47
+ conditions.push((0, drizzle_orm_1.eq)(schema_1.skills.status, options.status));
48
+ }
49
+ const searchTerm = options.q?.trim().toLowerCase();
50
+ if (searchTerm) {
51
+ const likePattern = `%${searchTerm}%`;
52
+ conditions.push((0, drizzle_orm_1.sql) `(
53
+ lower(${schema_1.skills.slug}) LIKE ${likePattern}
54
+ OR lower(${schema_1.skills.name}) LIKE ${likePattern}
55
+ OR lower(${schema_1.skills.displayName}) LIKE ${likePattern}
56
+ OR lower(coalesce(${schema_1.skills.description}, '')) LIKE ${likePattern}
57
+ OR lower(coalesce(${schema_1.skills.shortDescription}, '')) LIKE ${likePattern}
58
+ OR lower(coalesce(${schema_1.skills.compatibility}, '')) LIKE ${likePattern}
59
+ )`);
60
+ }
61
+ const whereClause = this.combineConditions(conditions);
62
+ const query = this.db.select().from(schema_1.skills);
63
+ if (whereClause) {
64
+ query.where(whereClause);
65
+ }
66
+ const rows = await query.orderBy((0, drizzle_orm_1.asc)(schema_1.skills.name), (0, drizzle_orm_1.asc)(schema_1.skills.slug));
67
+ return rows.map((row) => this.mapSkillRow(row));
68
+ }
69
+ async listAllForProject(projectId, options = {}) {
70
+ const enabledSources = this.getEnabledSources();
71
+ if (enabledSources.length === 0) {
72
+ return [];
73
+ }
74
+ const normalizedProjectId = this.requireNonEmpty(projectId, 'projectId');
75
+ const query = this.db
76
+ .select({
77
+ skill: schema_1.skills,
78
+ disabled: (0, drizzle_orm_1.sql) `case when ${schema_1.skillProjectDisabled.id} is null then 0 else 1 end`,
79
+ })
80
+ .from(schema_1.skills)
81
+ .leftJoin(schema_1.skillProjectDisabled, (0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.skillProjectDisabled.skillId, schema_1.skills.id), (0, drizzle_orm_1.eq)(schema_1.skillProjectDisabled.projectId, normalizedProjectId)));
82
+ const conditions = [(0, drizzle_orm_1.inArray)(schema_1.skills.source, enabledSources)];
83
+ this.appendProjectSkillFilterConditions(conditions, options);
84
+ const whereClause = this.combineConditions(conditions);
85
+ if (whereClause) {
86
+ query.where(whereClause);
87
+ }
88
+ const rows = await query.orderBy((0, drizzle_orm_1.asc)(schema_1.skills.name), (0, drizzle_orm_1.asc)(schema_1.skills.slug));
89
+ return rows.map((row) => ({
90
+ ...this.mapSkillRow(row.skill),
91
+ disabled: Number(row.disabled) === 1,
92
+ }));
93
+ }
94
+ async listDiscoverable(projectId, options = {}) {
95
+ const enabledSources = this.getEnabledSources();
96
+ if (enabledSources.length === 0) {
97
+ return [];
98
+ }
99
+ const normalizedProjectId = this.requireNonEmpty(projectId, 'projectId');
100
+ const query = this.db
101
+ .select({ skill: schema_1.skills })
102
+ .from(schema_1.skills)
103
+ .leftJoin(schema_1.skillProjectDisabled, (0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.skillProjectDisabled.skillId, schema_1.skills.id), (0, drizzle_orm_1.eq)(schema_1.skillProjectDisabled.projectId, normalizedProjectId)));
104
+ const conditions = [(0, drizzle_orm_1.isNull)(schema_1.skillProjectDisabled.id)];
105
+ conditions.push((0, drizzle_orm_1.inArray)(schema_1.skills.source, enabledSources));
106
+ this.appendProjectSkillFilterConditions(conditions, options);
107
+ const whereClause = this.combineConditions(conditions);
108
+ if (whereClause) {
109
+ query.where(whereClause);
110
+ }
111
+ const rows = await query.orderBy((0, drizzle_orm_1.asc)(schema_1.skills.name), (0, drizzle_orm_1.asc)(schema_1.skills.slug));
112
+ return rows.map((row) => this.mapSkillRow(row.skill));
113
+ }
114
+ async getSkill(id) {
115
+ const skillId = this.requireNonEmpty(id, 'id');
116
+ const row = await this.db.select().from(schema_1.skills).where((0, drizzle_orm_1.eq)(schema_1.skills.id, skillId)).limit(1);
117
+ if (!row[0]) {
118
+ throw new error_types_1.NotFoundError('Skill', skillId);
119
+ }
120
+ return this.mapSkillRow(row[0]);
121
+ }
122
+ async getSkillBySlug(slug) {
123
+ const normalizedSlug = this.requireNonEmpty(slug, 'slug');
124
+ const row = await this.db.select().from(schema_1.skills).where((0, drizzle_orm_1.eq)(schema_1.skills.slug, normalizedSlug)).limit(1);
125
+ if (!row[0]) {
126
+ throw new error_types_1.NotFoundError('Skill', normalizedSlug);
127
+ }
128
+ return this.mapSkillRow(row[0]);
129
+ }
130
+ async resolveSkillSummariesBySlugs(slugsToResolve) {
131
+ const uniqueSlugs = Array.from(new Set(slugsToResolve.map((slug) => this.requireNonEmpty(slug, 'slug').toLowerCase())));
132
+ if (uniqueSlugs.length === 0) {
133
+ return {};
134
+ }
135
+ const rows = await this.db
136
+ .select({
137
+ id: schema_1.skills.id,
138
+ slug: schema_1.skills.slug,
139
+ name: schema_1.skills.name,
140
+ displayName: schema_1.skills.displayName,
141
+ source: schema_1.skills.source,
142
+ category: schema_1.skills.category,
143
+ shortDescription: schema_1.skills.shortDescription,
144
+ description: schema_1.skills.description,
145
+ })
146
+ .from(schema_1.skills)
147
+ .where((0, drizzle_orm_1.inArray)(schema_1.skills.slug, uniqueSlugs));
148
+ const resolved = {};
149
+ for (const row of rows) {
150
+ resolved[row.slug] = {
151
+ id: row.id,
152
+ slug: row.slug,
153
+ name: row.name,
154
+ displayName: row.displayName,
155
+ source: row.source,
156
+ category: row.category,
157
+ shortDescription: row.shortDescription,
158
+ description: row.description,
159
+ };
160
+ }
161
+ return resolved;
162
+ }
163
+ async upsertSkill(slug, data) {
164
+ const normalizedSlug = this.requireNonEmpty(slug, 'slug');
165
+ const now = new Date().toISOString();
166
+ const existing = await this.db
167
+ .select()
168
+ .from(schema_1.skills)
169
+ .where((0, drizzle_orm_1.eq)(schema_1.skills.slug, normalizedSlug))
170
+ .limit(1);
171
+ if (existing[0]) {
172
+ const updatePayload = { updatedAt: now };
173
+ if (data.name !== undefined) {
174
+ updatePayload.name = this.requireNonEmpty(data.name, 'name');
175
+ }
176
+ if (data.displayName !== undefined) {
177
+ updatePayload.displayName = this.requireNonEmpty(data.displayName, 'displayName');
178
+ }
179
+ if (data.source !== undefined) {
180
+ updatePayload.source = this.requireNonEmpty(data.source, 'source');
181
+ }
182
+ if (data.description !== undefined) {
183
+ updatePayload.description = this.normalizeNullableString(data.description);
184
+ }
185
+ if (data.shortDescription !== undefined) {
186
+ updatePayload.shortDescription = this.normalizeNullableString(data.shortDescription);
187
+ }
188
+ if (data.sourceUrl !== undefined) {
189
+ updatePayload.sourceUrl = this.normalizeNullableString(data.sourceUrl);
190
+ }
191
+ if (data.sourceCommit !== undefined) {
192
+ updatePayload.sourceCommit = this.normalizeNullableString(data.sourceCommit);
193
+ }
194
+ if (data.category !== undefined) {
195
+ updatePayload.category = this.normalizeNullableString(data.category);
196
+ }
197
+ if (data.license !== undefined) {
198
+ updatePayload.license = this.normalizeNullableString(data.license);
199
+ }
200
+ if (data.compatibility !== undefined) {
201
+ updatePayload.compatibility = this.normalizeNullableString(data.compatibility);
202
+ }
203
+ if (data.instructionContent !== undefined) {
204
+ updatePayload.instructionContent = this.normalizeNullableString(data.instructionContent);
205
+ }
206
+ if (data.contentPath !== undefined) {
207
+ updatePayload.contentPath = this.normalizeNullableString(data.contentPath);
208
+ }
209
+ if (data.lastSyncedAt !== undefined) {
210
+ updatePayload.lastSyncedAt = this.normalizeNullableString(data.lastSyncedAt);
211
+ }
212
+ if (data.frontmatter !== undefined) {
213
+ updatePayload.frontmatter = this.serializeJsonObject(data.frontmatter, 'frontmatter');
214
+ }
215
+ if (data.resources !== undefined) {
216
+ updatePayload.resources = this.serializeResources(data.resources);
217
+ }
218
+ if (data.status !== undefined) {
219
+ updatePayload.status = this.validateStatus(data.status);
220
+ }
221
+ await this.db.update(schema_1.skills).set(updatePayload).where((0, drizzle_orm_1.eq)(schema_1.skills.slug, normalizedSlug));
222
+ return this.getSkillBySlug(normalizedSlug);
223
+ }
224
+ const source = this.requireNonEmpty(data.source ?? '', 'source');
225
+ const name = this.requireNonEmpty(data.name ?? normalizedSlug, 'name');
226
+ const displayName = this.requireNonEmpty(data.displayName ?? data.name ?? normalizedSlug, 'displayName');
227
+ const insertPayload = {
228
+ id: (0, node_crypto_1.randomUUID)(),
229
+ slug: normalizedSlug,
230
+ name,
231
+ displayName,
232
+ description: this.normalizeNullableString(data.description ?? null),
233
+ shortDescription: this.normalizeNullableString(data.shortDescription ?? null),
234
+ source,
235
+ sourceUrl: this.normalizeNullableString(data.sourceUrl ?? null),
236
+ sourceCommit: this.normalizeNullableString(data.sourceCommit ?? null),
237
+ category: this.normalizeNullableString(data.category ?? null),
238
+ license: this.normalizeNullableString(data.license ?? null),
239
+ compatibility: this.normalizeNullableString(data.compatibility ?? null),
240
+ frontmatter: this.serializeJsonObject(data.frontmatter ?? null, 'frontmatter'),
241
+ instructionContent: this.normalizeNullableString(data.instructionContent ?? null),
242
+ contentPath: this.normalizeNullableString(data.contentPath ?? null),
243
+ resources: this.serializeResources(data.resources ?? []),
244
+ status: this.validateStatus(data.status ?? 'available'),
245
+ lastSyncedAt: this.normalizeNullableString(data.lastSyncedAt ?? null),
246
+ createdAt: now,
247
+ updatedAt: now,
248
+ };
249
+ await this.db.insert(schema_1.skills).values(insertPayload);
250
+ return this.getSkillBySlug(normalizedSlug);
251
+ }
252
+ async disableSkill(projectId, skillId) {
253
+ const normalizedProjectId = this.requireNonEmpty(projectId, 'projectId');
254
+ const normalizedSkillId = this.requireNonEmpty(skillId, 'skillId');
255
+ const now = new Date().toISOString();
256
+ try {
257
+ await this.db.insert(schema_1.skillProjectDisabled).values({
258
+ id: (0, node_crypto_1.randomUUID)(),
259
+ projectId: normalizedProjectId,
260
+ skillId: normalizedSkillId,
261
+ createdAt: now,
262
+ });
263
+ }
264
+ catch (error) {
265
+ if (this.isUniqueConstraintError(error)) {
266
+ return;
267
+ }
268
+ if (this.isForeignKeyConstraintError(error)) {
269
+ throw new error_types_1.ValidationError('Cannot disable skill for unknown project or skill.', {
270
+ projectId: normalizedProjectId,
271
+ skillId: normalizedSkillId,
272
+ });
273
+ }
274
+ throw new error_types_1.StorageError('Failed to disable skill for project.', {
275
+ projectId: normalizedProjectId,
276
+ skillId: normalizedSkillId,
277
+ cause: error instanceof Error ? error.message : String(error),
278
+ });
279
+ }
280
+ }
281
+ async enableSkill(projectId, skillId) {
282
+ const normalizedProjectId = this.requireNonEmpty(projectId, 'projectId');
283
+ const normalizedSkillId = this.requireNonEmpty(skillId, 'skillId');
284
+ await this.db
285
+ .delete(schema_1.skillProjectDisabled)
286
+ .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.skillProjectDisabled.projectId, normalizedProjectId), (0, drizzle_orm_1.eq)(schema_1.skillProjectDisabled.skillId, normalizedSkillId)));
287
+ }
288
+ async listDisabled(projectId) {
289
+ const normalizedProjectId = this.requireNonEmpty(projectId, 'projectId');
290
+ const rows = await this.db
291
+ .select({ skillId: schema_1.skillProjectDisabled.skillId })
292
+ .from(schema_1.skillProjectDisabled)
293
+ .where((0, drizzle_orm_1.eq)(schema_1.skillProjectDisabled.projectId, normalizedProjectId))
294
+ .orderBy((0, drizzle_orm_1.asc)(schema_1.skillProjectDisabled.createdAt));
295
+ return rows.map((row) => row.skillId);
296
+ }
297
+ async disableAll(projectId) {
298
+ const enabledSources = this.getEnabledSources();
299
+ if (enabledSources.length === 0) {
300
+ return 0;
301
+ }
302
+ const normalizedProjectId = this.requireNonEmpty(projectId, 'projectId');
303
+ const now = new Date().toISOString();
304
+ const [allSkills, disabledRows] = await Promise.all([
305
+ this.db
306
+ .select({ skillId: schema_1.skills.id })
307
+ .from(schema_1.skills)
308
+ .where((0, drizzle_orm_1.inArray)(schema_1.skills.source, enabledSources)),
309
+ this.db
310
+ .select({ skillId: schema_1.skillProjectDisabled.skillId })
311
+ .from(schema_1.skillProjectDisabled)
312
+ .innerJoin(schema_1.skills, (0, drizzle_orm_1.eq)(schema_1.skills.id, schema_1.skillProjectDisabled.skillId))
313
+ .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.skillProjectDisabled.projectId, normalizedProjectId), (0, drizzle_orm_1.inArray)(schema_1.skills.source, enabledSources))),
314
+ ]);
315
+ const disabledSet = new Set(disabledRows.map((row) => row.skillId));
316
+ const rowsToInsert = allSkills
317
+ .filter((row) => !disabledSet.has(row.skillId))
318
+ .map((row) => ({
319
+ id: (0, node_crypto_1.randomUUID)(),
320
+ projectId: normalizedProjectId,
321
+ skillId: row.skillId,
322
+ createdAt: now,
323
+ }));
324
+ if (rowsToInsert.length === 0) {
325
+ return 0;
326
+ }
327
+ await this.db.insert(schema_1.skillProjectDisabled).values(rowsToInsert);
328
+ return rowsToInsert.length;
329
+ }
330
+ async enableAll(projectId) {
331
+ const enabledSources = this.getEnabledSources();
332
+ if (enabledSources.length === 0) {
333
+ return 0;
334
+ }
335
+ const normalizedProjectId = this.requireNonEmpty(projectId, 'projectId');
336
+ const disabledRows = await this.db
337
+ .select({ skillId: schema_1.skillProjectDisabled.skillId })
338
+ .from(schema_1.skillProjectDisabled)
339
+ .innerJoin(schema_1.skills, (0, drizzle_orm_1.eq)(schema_1.skills.id, schema_1.skillProjectDisabled.skillId))
340
+ .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.skillProjectDisabled.projectId, normalizedProjectId), (0, drizzle_orm_1.inArray)(schema_1.skills.source, enabledSources)));
341
+ if (disabledRows.length === 0) {
342
+ return 0;
343
+ }
344
+ const disabledSkillIds = disabledRows.map((row) => row.skillId);
345
+ await this.db
346
+ .delete(schema_1.skillProjectDisabled)
347
+ .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.skillProjectDisabled.projectId, normalizedProjectId), (0, drizzle_orm_1.inArray)(schema_1.skillProjectDisabled.skillId, disabledSkillIds)));
348
+ return disabledRows.length;
349
+ }
350
+ async listSources() {
351
+ const sourceRows = await this.db
352
+ .select({ source: schema_1.skills.source, skillCount: (0, drizzle_orm_1.count)() })
353
+ .from(schema_1.skills)
354
+ .groupBy(schema_1.skills.source);
355
+ const sourceCountMap = new Map(sourceRows.map((row) => [row.source.trim().toLowerCase(), Number(row.skillCount)]));
356
+ const sourceSettings = this.settingsService.getSkillSourcesEnabled();
357
+ return this.getRegisteredSources().map((source) => ({
358
+ name: source.name,
359
+ enabled: this.isSourceEnabled(source.name, sourceSettings),
360
+ repoUrl: source.repoUrl,
361
+ skillCount: sourceCountMap.get(source.name) ?? 0,
362
+ }));
363
+ }
364
+ async setSourceEnabled(sourceName, enabled) {
365
+ const normalizedSourceName = this.requireKnownSourceName(sourceName);
366
+ await this.settingsService.setSkillSourceEnabled(normalizedSourceName, enabled);
367
+ return { name: normalizedSourceName, enabled };
368
+ }
369
+ async logUsage(skillId, skillSlug, projectId, agentId, agentNameSnapshot) {
370
+ const normalizedSkillId = this.requireNonEmpty(skillId, 'skillId');
371
+ const normalizedSkillSlug = this.requireNonEmpty(skillSlug, 'skillSlug');
372
+ const now = new Date().toISOString();
373
+ const usageRecord = {
374
+ id: (0, node_crypto_1.randomUUID)(),
375
+ skillId: normalizedSkillId,
376
+ skillSlug: normalizedSkillSlug,
377
+ projectId: this.normalizeNullableString(projectId ?? null) ?? null,
378
+ agentId: this.normalizeNullableString(agentId ?? null) ?? null,
379
+ agentNameSnapshot: this.normalizeNullableString(agentNameSnapshot ?? null) ?? null,
380
+ accessedAt: now,
381
+ };
382
+ await this.db.insert(schema_1.skillUsageLog).values({
383
+ id: usageRecord.id,
384
+ skillId: usageRecord.skillId,
385
+ skillSlug: usageRecord.skillSlug,
386
+ projectId: usageRecord.projectId,
387
+ agentId: usageRecord.agentId,
388
+ agentNameSnapshot: usageRecord.agentNameSnapshot,
389
+ accessedAt: usageRecord.accessedAt,
390
+ });
391
+ return usageRecord;
392
+ }
393
+ async getUsageStats(options = {}) {
394
+ const conditions = [];
395
+ if (options.projectId !== undefined) {
396
+ if (options.projectId === null) {
397
+ conditions.push((0, drizzle_orm_1.isNull)(schema_1.skillUsageLog.projectId));
398
+ }
399
+ else {
400
+ conditions.push((0, drizzle_orm_1.eq)(schema_1.skillUsageLog.projectId, options.projectId));
401
+ }
402
+ }
403
+ if (options.from) {
404
+ conditions.push((0, drizzle_orm_1.gte)(schema_1.skillUsageLog.accessedAt, options.from));
405
+ }
406
+ if (options.to) {
407
+ conditions.push((0, drizzle_orm_1.lte)(schema_1.skillUsageLog.accessedAt, options.to));
408
+ }
409
+ const usageCountExpr = (0, drizzle_orm_1.count)();
410
+ const firstAccessedExpr = (0, drizzle_orm_1.sql) `min(${schema_1.skillUsageLog.accessedAt})`;
411
+ const lastAccessedExpr = (0, drizzle_orm_1.sql) `max(${schema_1.skillUsageLog.accessedAt})`;
412
+ const whereClause = this.combineConditions(conditions);
413
+ const limit = options.limit ?? 100;
414
+ const offset = options.offset ?? 0;
415
+ const query = this.db
416
+ .select({
417
+ skillId: schema_1.skillUsageLog.skillId,
418
+ skillSlug: schema_1.skillUsageLog.skillSlug,
419
+ usageCount: usageCountExpr,
420
+ firstAccessedAt: firstAccessedExpr,
421
+ lastAccessedAt: lastAccessedExpr,
422
+ skillName: schema_1.skills.name,
423
+ skillDisplayName: schema_1.skills.displayName,
424
+ })
425
+ .from(schema_1.skillUsageLog)
426
+ .leftJoin(schema_1.skills, (0, drizzle_orm_1.eq)(schema_1.skills.id, schema_1.skillUsageLog.skillId));
427
+ if (whereClause) {
428
+ query.where(whereClause);
429
+ }
430
+ const rows = await query
431
+ .groupBy(schema_1.skillUsageLog.skillId, schema_1.skillUsageLog.skillSlug, schema_1.skills.name, schema_1.skills.displayName)
432
+ .orderBy((0, drizzle_orm_1.desc)(usageCountExpr), (0, drizzle_orm_1.desc)(lastAccessedExpr))
433
+ .limit(limit)
434
+ .offset(offset);
435
+ return rows.map((row) => ({
436
+ skillId: row.skillId,
437
+ skillSlug: row.skillSlug,
438
+ usageCount: Number(row.usageCount ?? 0),
439
+ firstAccessedAt: row.firstAccessedAt,
440
+ lastAccessedAt: row.lastAccessedAt,
441
+ skillName: row.skillName ?? null,
442
+ skillDisplayName: row.skillDisplayName ?? null,
443
+ }));
444
+ }
445
+ async listUsageLog(options = {}) {
446
+ const conditions = [];
447
+ if (options.projectId) {
448
+ conditions.push((0, drizzle_orm_1.eq)(schema_1.skillUsageLog.projectId, options.projectId));
449
+ }
450
+ if (options.skillId) {
451
+ conditions.push((0, drizzle_orm_1.eq)(schema_1.skillUsageLog.skillId, options.skillId));
452
+ }
453
+ if (options.agentId) {
454
+ conditions.push((0, drizzle_orm_1.eq)(schema_1.skillUsageLog.agentId, options.agentId));
455
+ }
456
+ if (options.from) {
457
+ conditions.push((0, drizzle_orm_1.gte)(schema_1.skillUsageLog.accessedAt, options.from));
458
+ }
459
+ if (options.to) {
460
+ conditions.push((0, drizzle_orm_1.lte)(schema_1.skillUsageLog.accessedAt, options.to));
461
+ }
462
+ const whereClause = this.combineConditions(conditions);
463
+ const limit = options.limit ?? 100;
464
+ const offset = options.offset ?? 0;
465
+ const itemsQuery = this.db.select().from(schema_1.skillUsageLog);
466
+ if (whereClause) {
467
+ itemsQuery.where(whereClause);
468
+ }
469
+ const rows = await itemsQuery
470
+ .orderBy((0, drizzle_orm_1.desc)(schema_1.skillUsageLog.accessedAt))
471
+ .limit(limit)
472
+ .offset(offset);
473
+ const totalQuery = this.db.select({ count: (0, drizzle_orm_1.sql) `count(*)` }).from(schema_1.skillUsageLog);
474
+ if (whereClause) {
475
+ totalQuery.where(whereClause);
476
+ }
477
+ const totalResult = await totalQuery;
478
+ const total = Number(totalResult[0]?.count ?? 0);
479
+ return {
480
+ items: rows.map((row) => ({
481
+ id: row.id,
482
+ skillId: row.skillId,
483
+ skillSlug: row.skillSlug,
484
+ projectId: row.projectId,
485
+ agentId: row.agentId,
486
+ agentNameSnapshot: row.agentNameSnapshot,
487
+ accessedAt: row.accessedAt,
488
+ })),
489
+ total,
490
+ limit,
491
+ offset,
492
+ };
493
+ }
494
+ appendProjectSkillFilterConditions(conditions, options) {
495
+ const searchTerm = options.q?.trim().toLowerCase();
496
+ if (searchTerm) {
497
+ const likePattern = `%${searchTerm}%`;
498
+ conditions.push((0, drizzle_orm_1.sql) `(
499
+ lower(${schema_1.skills.slug}) LIKE ${likePattern}
500
+ OR lower(${schema_1.skills.name}) LIKE ${likePattern}
501
+ OR lower(${schema_1.skills.displayName}) LIKE ${likePattern}
502
+ OR lower(coalesce(${schema_1.skills.description}, '')) LIKE ${likePattern}
503
+ OR lower(coalesce(${schema_1.skills.shortDescription}, '')) LIKE ${likePattern}
504
+ OR lower(coalesce(${schema_1.skills.compatibility}, '')) LIKE ${likePattern}
505
+ )`);
506
+ }
507
+ if (options.source) {
508
+ conditions.push((0, drizzle_orm_1.eq)(schema_1.skills.source, options.source.trim().toLowerCase()));
509
+ }
510
+ if (options.category) {
511
+ conditions.push((0, drizzle_orm_1.eq)(schema_1.skills.category, options.category));
512
+ }
513
+ }
514
+ mapSkillRow(row) {
515
+ return {
516
+ id: row.id,
517
+ slug: row.slug,
518
+ name: row.name,
519
+ displayName: row.displayName,
520
+ description: row.description,
521
+ shortDescription: row.shortDescription,
522
+ source: row.source,
523
+ sourceUrl: row.sourceUrl,
524
+ sourceCommit: row.sourceCommit,
525
+ category: row.category,
526
+ license: row.license,
527
+ compatibility: row.compatibility,
528
+ frontmatter: this.parseJsonObject(row.frontmatter, 'frontmatter'),
529
+ instructionContent: row.instructionContent,
530
+ contentPath: row.contentPath,
531
+ resources: this.parseResources(row.resources),
532
+ status: this.parseStatus(row.status),
533
+ lastSyncedAt: row.lastSyncedAt,
534
+ createdAt: row.createdAt,
535
+ updatedAt: row.updatedAt,
536
+ };
537
+ }
538
+ parseJsonObject(rawValue, fieldName) {
539
+ if (rawValue === null) {
540
+ return null;
541
+ }
542
+ try {
543
+ const parsed = JSON.parse(rawValue);
544
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
545
+ return parsed;
546
+ }
547
+ logger.warn({ fieldName }, 'Expected JSON object but found different type');
548
+ return null;
549
+ }
550
+ catch (error) {
551
+ logger.warn({
552
+ fieldName,
553
+ error: error instanceof Error ? error.message : String(error),
554
+ }, 'Failed to parse JSON object field');
555
+ return null;
556
+ }
557
+ }
558
+ parseResources(rawValue) {
559
+ if (rawValue === null) {
560
+ return [];
561
+ }
562
+ try {
563
+ const parsed = JSON.parse(rawValue);
564
+ if (!Array.isArray(parsed)) {
565
+ logger.warn('Expected resources JSON array but found different type');
566
+ return [];
567
+ }
568
+ return parsed
569
+ .filter((item) => typeof item === 'string')
570
+ .map((item) => item.trim())
571
+ .filter((item) => item.length > 0);
572
+ }
573
+ catch (error) {
574
+ logger.warn({ error: error instanceof Error ? error.message : String(error) }, 'Failed to parse resources JSON field');
575
+ return [];
576
+ }
577
+ }
578
+ serializeJsonObject(value, fieldName) {
579
+ if (value === undefined) {
580
+ return undefined;
581
+ }
582
+ if (value === null) {
583
+ return null;
584
+ }
585
+ try {
586
+ return JSON.stringify(value);
587
+ }
588
+ catch (error) {
589
+ throw new error_types_1.ValidationError(`Invalid ${fieldName}: value is not serializable JSON.`, {
590
+ fieldName,
591
+ cause: error instanceof Error ? error.message : String(error),
592
+ });
593
+ }
594
+ }
595
+ serializeResources(resources) {
596
+ if (resources === undefined) {
597
+ return undefined;
598
+ }
599
+ if (resources === null) {
600
+ return null;
601
+ }
602
+ if (!Array.isArray(resources)) {
603
+ throw new error_types_1.ValidationError('Invalid resources: expected an array of strings.');
604
+ }
605
+ const normalizedResources = resources
606
+ .filter((item) => typeof item === 'string')
607
+ .map((item) => item.trim())
608
+ .filter((item) => item.length > 0);
609
+ try {
610
+ return JSON.stringify(normalizedResources);
611
+ }
612
+ catch (error) {
613
+ throw new error_types_1.ValidationError('Invalid resources: value is not serializable JSON.', {
614
+ cause: error instanceof Error ? error.message : String(error),
615
+ });
616
+ }
617
+ }
618
+ validateStatus(status) {
619
+ if (VALID_SKILL_STATUSES.includes(status)) {
620
+ return status;
621
+ }
622
+ throw new error_types_1.ValidationError('Invalid skill status value.', {
623
+ status,
624
+ supportedStatuses: VALID_SKILL_STATUSES,
625
+ });
626
+ }
627
+ parseStatus(status) {
628
+ if (VALID_SKILL_STATUSES.includes(status)) {
629
+ return status;
630
+ }
631
+ logger.warn({ status }, 'Unknown skill status in database; defaulting to available');
632
+ return 'available';
633
+ }
634
+ normalizeNullableString(value) {
635
+ if (value === undefined) {
636
+ return undefined;
637
+ }
638
+ if (value === null) {
639
+ return null;
640
+ }
641
+ const trimmed = value.trim();
642
+ return trimmed.length > 0 ? trimmed : null;
643
+ }
644
+ requireNonEmpty(value, fieldName) {
645
+ const normalized = value.trim();
646
+ if (!normalized) {
647
+ throw new error_types_1.ValidationError(`${fieldName} is required.`, { fieldName });
648
+ }
649
+ return normalized;
650
+ }
651
+ combineConditions(conditions) {
652
+ if (conditions.length === 0) {
653
+ return undefined;
654
+ }
655
+ if (conditions.length === 1) {
656
+ return conditions[0];
657
+ }
658
+ return (0, drizzle_orm_1.and)(...conditions);
659
+ }
660
+ getRegisteredSources() {
661
+ const sourceMap = new Map();
662
+ for (const adapter of this.adapters) {
663
+ const normalizedName = adapter.sourceName.trim().toLowerCase();
664
+ if (!normalizedName || sourceMap.has(normalizedName)) {
665
+ continue;
666
+ }
667
+ sourceMap.set(normalizedName, adapter.repoUrl);
668
+ }
669
+ return Array.from(sourceMap.entries())
670
+ .map(([name, repoUrl]) => ({ name, repoUrl }))
671
+ .sort((left, right) => left.name.localeCompare(right.name));
672
+ }
673
+ getEnabledSources() {
674
+ const sourceSettings = this.settingsService.getSkillSourcesEnabled();
675
+ return this.getRegisteredSources()
676
+ .map((source) => source.name)
677
+ .filter((sourceName) => this.isSourceEnabled(sourceName, sourceSettings));
678
+ }
679
+ isSourceEnabled(sourceName, sourceSettings) {
680
+ return sourceSettings[sourceName] !== false;
681
+ }
682
+ requireKnownSourceName(sourceName) {
683
+ const normalized = this.requireNonEmpty(sourceName, 'sourceName').toLowerCase();
684
+ const knownSources = new Set(this.getRegisteredSources().map((source) => source.name));
685
+ if (!knownSources.has(normalized)) {
686
+ throw new error_types_1.ValidationError(`Unknown skill source: ${normalized}`, { sourceName: normalized });
687
+ }
688
+ return normalized;
689
+ }
690
+ isUniqueConstraintError(error) {
691
+ const code = this.readErrorCode(error);
692
+ const message = this.readErrorMessage(error);
693
+ return (code === 'SQLITE_CONSTRAINT' ||
694
+ code === 'SQLITE_CONSTRAINT_UNIQUE' ||
695
+ code === 19 ||
696
+ message.includes('UNIQUE constraint failed'));
697
+ }
698
+ isForeignKeyConstraintError(error) {
699
+ const code = this.readErrorCode(error);
700
+ const message = this.readErrorMessage(error);
701
+ return (code === 'SQLITE_CONSTRAINT_FOREIGNKEY' ||
702
+ code === 'SQLITE_CONSTRAINT' ||
703
+ code === 19 ||
704
+ message.includes('FOREIGN KEY constraint failed'));
705
+ }
706
+ readErrorCode(error) {
707
+ if (typeof error !== 'object' || error === null || !('code' in error)) {
708
+ return undefined;
709
+ }
710
+ const code = error.code;
711
+ if (typeof code === 'string' || typeof code === 'number') {
712
+ return code;
713
+ }
714
+ return undefined;
715
+ }
716
+ readErrorMessage(error) {
717
+ if (typeof error !== 'object' || error === null || !('message' in error)) {
718
+ return '';
719
+ }
720
+ const message = error.message;
721
+ return typeof message === 'string' ? message : '';
722
+ }
723
+ };
724
+ exports.SkillsService = SkillsService;
725
+ exports.SkillsService = SkillsService = __decorate([
726
+ (0, common_1.Injectable)(),
727
+ __param(0, (0, common_1.Inject)(db_provider_1.DB_CONNECTION)),
728
+ __param(2, (0, common_1.Inject)(skill_source_adapter_1.SKILL_SOURCE_ADAPTERS)),
729
+ __metadata("design:paramtypes", [Function, settings_service_1.SettingsService, Array])
730
+ ], SkillsService);
731
+ //# sourceMappingURL=skills.service.js.map