@skillshub-labs/cli 0.1.18 → 0.1.19
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/README.md +16 -2
- package/bin/skills-hub +193 -15
- package/data/official-presets/catalog.json +436 -0
- package/data/official-presets/policies/policy-azure-cloud.md +18 -0
- package/data/official-presets/policies/policy-cloudflare-edge.md +18 -0
- package/data/official-presets/policies/policy-fastapi-py.md +31 -0
- package/data/official-presets/policies/policy-fullstack-web.md +24 -0
- package/data/official-presets/policies/policy-go-service.md +31 -0
- package/data/official-presets/policies/policy-hf-ml.md +18 -0
- package/data/official-presets/policies/policy-langchain-apps.md +18 -0
- package/data/official-presets/policies/policy-literature-review.md +18 -0
- package/data/official-presets/policies/policy-monorepo-turbo.md +31 -0
- package/data/official-presets/policies/policy-nextjs-ts-strict.md +31 -0
- package/data/official-presets/policies/policy-node-api-ts.md +31 -0
- package/data/official-presets/policies/policy-python-api.md +18 -0
- package/data/official-presets/policies/policy-release-ci.md +18 -0
- package/data/official-presets/policies/policy-release-maintainer.md +31 -0
- package/data/official-presets/policies/policy-scientific-discovery.md +18 -0
- package/data/official-presets/policies/policy-scientific-python.md +31 -0
- package/data/official-presets/policies/policy-security-audit.md +18 -0
- package/data/official-presets/policies/policy-web-frontend.md +18 -0
- package/lib/core/kit-core.d.ts +14 -3
- package/lib/core/kit-core.mjs +327 -20
- package/lib/core/kit-types.ts +128 -3
- package/lib/services/kit-loadout-import.mjs +599 -0
- package/lib/services/kit-service.d.ts +90 -2
- package/lib/services/kit-service.mjs +665 -38
- package/package.json +9 -1
package/lib/core/kit-core.mjs
CHANGED
|
@@ -29,6 +29,7 @@ function ensureDb() {
|
|
|
29
29
|
id TEXT PRIMARY KEY,
|
|
30
30
|
name TEXT NOT NULL,
|
|
31
31
|
description TEXT,
|
|
32
|
+
import_source_json TEXT,
|
|
32
33
|
created_at INTEGER NOT NULL,
|
|
33
34
|
updated_at INTEGER NOT NULL
|
|
34
35
|
);
|
|
@@ -50,6 +51,7 @@ function ensureDb() {
|
|
|
50
51
|
description TEXT,
|
|
51
52
|
policy_id TEXT NOT NULL,
|
|
52
53
|
loadout_id TEXT NOT NULL,
|
|
54
|
+
managed_source_json TEXT,
|
|
53
55
|
last_applied_at INTEGER,
|
|
54
56
|
last_applied_target_json TEXT,
|
|
55
57
|
created_at INTEGER NOT NULL,
|
|
@@ -62,10 +64,21 @@ function ensureDb() {
|
|
|
62
64
|
CREATE INDEX IF NOT EXISTS idx_kit_presets_loadout_id
|
|
63
65
|
ON kit_presets(loadout_id);
|
|
64
66
|
`)
|
|
67
|
+
ensureColumn(dbInstance, 'kit_loadouts', 'import_source_json', 'TEXT')
|
|
68
|
+
ensureColumn(dbInstance, 'kit_presets', 'managed_source_json', 'TEXT')
|
|
65
69
|
|
|
66
70
|
return dbInstance
|
|
67
71
|
}
|
|
68
72
|
|
|
73
|
+
function ensureColumn(db, tableName, columnName, definition) {
|
|
74
|
+
const columns = db.prepare(`PRAGMA table_info(${tableName})`).all()
|
|
75
|
+
if (columns.some((column) => column?.name === columnName)) {
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
db.exec(`ALTER TABLE ${tableName} ADD COLUMN ${columnName} ${definition}`)
|
|
80
|
+
}
|
|
81
|
+
|
|
69
82
|
function getDbPath() {
|
|
70
83
|
return DB_PATH
|
|
71
84
|
}
|
|
@@ -102,6 +115,193 @@ function toOptionalText(value) {
|
|
|
102
115
|
return normalized || undefined
|
|
103
116
|
}
|
|
104
117
|
|
|
118
|
+
function normalizeLoadoutImportSource(value) {
|
|
119
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
120
|
+
return undefined
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const repoWebUrl = String(value.repoWebUrl || '').trim()
|
|
124
|
+
const repoUrl = String(value.repoUrl || '').trim()
|
|
125
|
+
const originalUrl = String(value.originalUrl || '').trim()
|
|
126
|
+
const branch = toOptionalText(value.branch)
|
|
127
|
+
const rootSubdir = String(value.rootSubdir || '').trim() || '/'
|
|
128
|
+
const importedAt = String(value.importedAt || '').trim()
|
|
129
|
+
const lastSourceUpdatedAt = String(value.lastSourceUpdatedAt || '').trim()
|
|
130
|
+
const lastSafetyCheck = normalizeKitSafetyCheck(value.lastSafetyCheck)
|
|
131
|
+
|
|
132
|
+
if (!repoWebUrl || !repoUrl || !originalUrl || !importedAt || !lastSourceUpdatedAt) {
|
|
133
|
+
return undefined
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
repoWebUrl,
|
|
138
|
+
repoUrl,
|
|
139
|
+
originalUrl,
|
|
140
|
+
branch,
|
|
141
|
+
rootSubdir,
|
|
142
|
+
importedAt,
|
|
143
|
+
lastSourceUpdatedAt,
|
|
144
|
+
lastSafetyCheck,
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function normalizeKitSafetyCheck(value) {
|
|
149
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
150
|
+
return undefined
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const checkedAt = Number(value.checkedAt)
|
|
154
|
+
const status = value.status === 'warn' ? 'warn' : 'pass'
|
|
155
|
+
const scannedFiles = Number(value.scannedFiles)
|
|
156
|
+
const warnings = Array.isArray(value.warnings)
|
|
157
|
+
? value.warnings.map((entry) => String(entry || '').trim()).filter(Boolean)
|
|
158
|
+
: []
|
|
159
|
+
const flaggedFiles = Array.isArray(value.flaggedFiles)
|
|
160
|
+
? value.flaggedFiles.map((entry) => String(entry || '').trim()).filter(Boolean)
|
|
161
|
+
: []
|
|
162
|
+
|
|
163
|
+
if (!Number.isFinite(checkedAt) || !Number.isFinite(scannedFiles)) {
|
|
164
|
+
return undefined
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
checkedAt,
|
|
169
|
+
status,
|
|
170
|
+
scannedFiles,
|
|
171
|
+
warnings,
|
|
172
|
+
flaggedFiles,
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function normalizeManagedPolicyBaseline(value) {
|
|
177
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
178
|
+
return undefined
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const id = String(value.id || '').trim()
|
|
182
|
+
const name = String(value.name || '').trim()
|
|
183
|
+
const content = String(value.content || '').trim()
|
|
184
|
+
const description = toOptionalText(value.description)
|
|
185
|
+
|
|
186
|
+
if (!id || !name || !content) {
|
|
187
|
+
return undefined
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
id,
|
|
192
|
+
name,
|
|
193
|
+
description,
|
|
194
|
+
content,
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function normalizeManagedLoadoutBaseline(value) {
|
|
199
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
200
|
+
return undefined
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const id = String(value.id || '').trim()
|
|
204
|
+
const name = String(value.name || '').trim()
|
|
205
|
+
const description = toOptionalText(value.description)
|
|
206
|
+
const items = normalizeLoadoutItems(value.items || [])
|
|
207
|
+
|
|
208
|
+
if (!id || !name || items.length === 0) {
|
|
209
|
+
return undefined
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
id,
|
|
214
|
+
name,
|
|
215
|
+
description,
|
|
216
|
+
items,
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function normalizeManagedSecurityChecks(value) {
|
|
221
|
+
if (!Array.isArray(value)) {
|
|
222
|
+
return []
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return value
|
|
226
|
+
.map((entry) => {
|
|
227
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
|
|
228
|
+
return null
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const sourceId = String(entry.sourceId || '').trim()
|
|
232
|
+
const sourceName = String(entry.sourceName || '').trim()
|
|
233
|
+
const check = normalizeKitSafetyCheck(entry.check)
|
|
234
|
+
if (!sourceId || !sourceName || !check) {
|
|
235
|
+
return null
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
sourceId,
|
|
240
|
+
sourceName,
|
|
241
|
+
check,
|
|
242
|
+
}
|
|
243
|
+
})
|
|
244
|
+
.filter(Boolean)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function normalizeManagedSource(value) {
|
|
248
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
249
|
+
return undefined
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (String(value.kind || '').trim() !== 'official_preset') {
|
|
253
|
+
return undefined
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const presetId = String(value.presetId || '').trim()
|
|
257
|
+
const presetName = String(value.presetName || '').trim()
|
|
258
|
+
const catalogVersion = Number(value.catalogVersion)
|
|
259
|
+
const installedAt = Number(value.installedAt)
|
|
260
|
+
const lastRestoredAt = value.lastRestoredAt == null ? undefined : Number(value.lastRestoredAt)
|
|
261
|
+
const restoreCount = Number(value.restoreCount)
|
|
262
|
+
const baseline = value.baseline
|
|
263
|
+
|
|
264
|
+
if (!baseline || typeof baseline !== 'object' || Array.isArray(baseline)) {
|
|
265
|
+
return undefined
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const name = String(baseline.name || '').trim()
|
|
269
|
+
const description = toOptionalText(baseline.description)
|
|
270
|
+
const policy = normalizeManagedPolicyBaseline(baseline.policy)
|
|
271
|
+
const loadout = normalizeManagedLoadoutBaseline(baseline.loadout)
|
|
272
|
+
const securityChecks = normalizeManagedSecurityChecks(value.securityChecks)
|
|
273
|
+
|
|
274
|
+
if (
|
|
275
|
+
!presetId ||
|
|
276
|
+
!presetName ||
|
|
277
|
+
!name ||
|
|
278
|
+
!policy ||
|
|
279
|
+
!loadout ||
|
|
280
|
+
!Number.isFinite(catalogVersion) ||
|
|
281
|
+
!Number.isFinite(installedAt) ||
|
|
282
|
+
!Number.isFinite(restoreCount)
|
|
283
|
+
) {
|
|
284
|
+
return undefined
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return {
|
|
288
|
+
kind: 'official_preset',
|
|
289
|
+
presetId,
|
|
290
|
+
presetName,
|
|
291
|
+
catalogVersion,
|
|
292
|
+
installedAt,
|
|
293
|
+
lastRestoredAt: Number.isFinite(lastRestoredAt) ? lastRestoredAt : undefined,
|
|
294
|
+
restoreCount,
|
|
295
|
+
baseline: {
|
|
296
|
+
name,
|
|
297
|
+
description,
|
|
298
|
+
policy,
|
|
299
|
+
loadout,
|
|
300
|
+
},
|
|
301
|
+
securityChecks,
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
105
305
|
function normalizeLoadoutItems(items) {
|
|
106
306
|
if (!Array.isArray(items)) {
|
|
107
307
|
throw new Error('Skills package items must be an array')
|
|
@@ -157,6 +357,7 @@ function parseLoadoutRow(row, items) {
|
|
|
157
357
|
name: row.name,
|
|
158
358
|
description: row.description || undefined,
|
|
159
359
|
items,
|
|
360
|
+
importSource: normalizeLoadoutImportSource(parseJsonSafe(row.import_source_json, undefined)),
|
|
160
361
|
createdAt: row.created_at,
|
|
161
362
|
updatedAt: row.updated_at,
|
|
162
363
|
}
|
|
@@ -168,12 +369,13 @@ function parseKitRow(row) {
|
|
|
168
369
|
id: row.id,
|
|
169
370
|
name: row.name,
|
|
170
371
|
description: row.description || undefined,
|
|
171
|
-
policyId: row.policy_id,
|
|
172
|
-
loadoutId: row.loadout_id,
|
|
372
|
+
policyId: row.policy_id || undefined,
|
|
373
|
+
loadoutId: row.loadout_id || undefined,
|
|
173
374
|
lastAppliedAt: row.last_applied_at || undefined,
|
|
174
375
|
lastAppliedTarget: row.last_applied_target_json
|
|
175
376
|
? parseJsonSafe(row.last_applied_target_json, undefined)
|
|
176
377
|
: undefined,
|
|
378
|
+
managedSource: normalizeManagedSource(parseJsonSafe(row.managed_source_json, undefined)),
|
|
177
379
|
createdAt: row.created_at,
|
|
178
380
|
updatedAt: row.updated_at,
|
|
179
381
|
}
|
|
@@ -302,7 +504,7 @@ function listKitLoadouts() {
|
|
|
302
504
|
const db = ensureDb()
|
|
303
505
|
const rows = db
|
|
304
506
|
.prepare(
|
|
305
|
-
`SELECT id, name, description, created_at, updated_at
|
|
507
|
+
`SELECT id, name, description, import_source_json, created_at, updated_at
|
|
306
508
|
FROM kit_loadouts
|
|
307
509
|
ORDER BY updated_at DESC, name ASC`
|
|
308
510
|
)
|
|
@@ -316,7 +518,7 @@ function getKitLoadoutById(id) {
|
|
|
316
518
|
const db = ensureDb()
|
|
317
519
|
const row = db
|
|
318
520
|
.prepare(
|
|
319
|
-
`SELECT id, name, description, created_at, updated_at
|
|
521
|
+
`SELECT id, name, description, import_source_json, created_at, updated_at
|
|
320
522
|
FROM kit_loadouts
|
|
321
523
|
WHERE id = ?`
|
|
322
524
|
)
|
|
@@ -335,12 +537,13 @@ function addKitLoadout(input) {
|
|
|
335
537
|
const items = normalizeLoadoutItems(input?.items || [])
|
|
336
538
|
|
|
337
539
|
db.prepare(
|
|
338
|
-
`INSERT INTO kit_loadouts (id, name, description, created_at, updated_at)
|
|
339
|
-
VALUES (@id, @name, @description, @createdAt, @updatedAt)`
|
|
540
|
+
`INSERT INTO kit_loadouts (id, name, description, import_source_json, created_at, updated_at)
|
|
541
|
+
VALUES (@id, @name, @description, @importSourceJson, @createdAt, @updatedAt)`
|
|
340
542
|
).run({
|
|
341
543
|
id,
|
|
342
544
|
name,
|
|
343
545
|
description: toOptionalText(input?.description) || null,
|
|
546
|
+
importSourceJson: JSON.stringify(normalizeLoadoutImportSource(input?.importSource) || null),
|
|
344
547
|
createdAt: ts,
|
|
345
548
|
updatedAt: ts,
|
|
346
549
|
})
|
|
@@ -384,12 +587,17 @@ function updateKitLoadout(input) {
|
|
|
384
587
|
? existing.description || null
|
|
385
588
|
: toOptionalText(input.description) || null
|
|
386
589
|
const nextItems = input?.items === undefined ? existing.items : normalizeLoadoutItems(input.items)
|
|
590
|
+
const nextImportSource =
|
|
591
|
+
input?.importSource === undefined
|
|
592
|
+
? existing.importSource || null
|
|
593
|
+
: normalizeLoadoutImportSource(input.importSource) || null
|
|
387
594
|
|
|
388
595
|
const db = ensureDb()
|
|
389
596
|
const updateLoadout = db.prepare(
|
|
390
597
|
`UPDATE kit_loadouts
|
|
391
598
|
SET name = @name,
|
|
392
599
|
description = @description,
|
|
600
|
+
import_source_json = @importSourceJson,
|
|
393
601
|
updated_at = @updatedAt
|
|
394
602
|
WHERE id = @id`
|
|
395
603
|
)
|
|
@@ -404,6 +612,7 @@ function updateKitLoadout(input) {
|
|
|
404
612
|
id,
|
|
405
613
|
name: nextName,
|
|
406
614
|
description: nextDescription,
|
|
615
|
+
importSourceJson: JSON.stringify(nextImportSource),
|
|
407
616
|
updatedAt: nowTs(),
|
|
408
617
|
})
|
|
409
618
|
|
|
@@ -448,7 +657,7 @@ function listKits() {
|
|
|
448
657
|
const db = ensureDb()
|
|
449
658
|
const rows = db
|
|
450
659
|
.prepare(
|
|
451
|
-
`SELECT id, name, description, policy_id, loadout_id, last_applied_at, last_applied_target_json,
|
|
660
|
+
`SELECT id, name, description, policy_id, loadout_id, managed_source_json, last_applied_at, last_applied_target_json,
|
|
452
661
|
created_at, updated_at
|
|
453
662
|
FROM kit_presets
|
|
454
663
|
ORDER BY updated_at DESC, name ASC`
|
|
@@ -462,7 +671,7 @@ function getKitById(id) {
|
|
|
462
671
|
const db = ensureDb()
|
|
463
672
|
const row = db
|
|
464
673
|
.prepare(
|
|
465
|
-
`SELECT id, name, description, policy_id, loadout_id, last_applied_at, last_applied_target_json,
|
|
674
|
+
`SELECT id, name, description, policy_id, loadout_id, managed_source_json, last_applied_at, last_applied_target_json,
|
|
466
675
|
created_at, updated_at
|
|
467
676
|
FROM kit_presets
|
|
468
677
|
WHERE id = ?`
|
|
@@ -472,11 +681,11 @@ function getKitById(id) {
|
|
|
472
681
|
}
|
|
473
682
|
|
|
474
683
|
function ensureKitRefsExist(policyId, loadoutId) {
|
|
475
|
-
if (!getKitPolicyById(policyId)) {
|
|
684
|
+
if (policyId && !getKitPolicyById(policyId)) {
|
|
476
685
|
throw new Error(`AGENTS.md not found: ${policyId}`)
|
|
477
686
|
}
|
|
478
687
|
|
|
479
|
-
if (!getKitLoadoutById(loadoutId)) {
|
|
688
|
+
if (loadoutId && !getKitLoadoutById(loadoutId)) {
|
|
480
689
|
throw new Error(`Skills package not found: ${loadoutId}`)
|
|
481
690
|
}
|
|
482
691
|
}
|
|
@@ -489,24 +698,25 @@ function addKit(input) {
|
|
|
489
698
|
const name = requireName(input?.name, 'kit name')
|
|
490
699
|
const policyId = String(input?.policyId || '').trim()
|
|
491
700
|
const loadoutId = String(input?.loadoutId || '').trim()
|
|
492
|
-
if (!policyId
|
|
493
|
-
throw new Error('Kit must include
|
|
701
|
+
if (!policyId && !loadoutId) {
|
|
702
|
+
throw new Error('Kit must include at least AGENTS.md or Skills package')
|
|
494
703
|
}
|
|
495
704
|
|
|
496
705
|
ensureKitRefsExist(policyId, loadoutId)
|
|
497
706
|
|
|
498
707
|
db.prepare(
|
|
499
708
|
`INSERT INTO kit_presets (
|
|
500
|
-
id, name, description, policy_id, loadout_id, created_at, updated_at
|
|
709
|
+
id, name, description, policy_id, loadout_id, managed_source_json, created_at, updated_at
|
|
501
710
|
) VALUES (
|
|
502
|
-
@id, @name, @description, @policyId, @loadoutId, @createdAt, @updatedAt
|
|
711
|
+
@id, @name, @description, @policyId, @loadoutId, @managedSourceJson, @createdAt, @updatedAt
|
|
503
712
|
)`
|
|
504
713
|
).run({
|
|
505
714
|
id,
|
|
506
715
|
name,
|
|
507
716
|
description: toOptionalText(input?.description) || null,
|
|
508
|
-
policyId,
|
|
509
|
-
loadoutId,
|
|
717
|
+
policyId: policyId || '',
|
|
718
|
+
loadoutId: loadoutId || '',
|
|
719
|
+
managedSourceJson: JSON.stringify(normalizeManagedSource(input?.managedSource) || null),
|
|
510
720
|
createdAt: ts,
|
|
511
721
|
updatedAt: ts,
|
|
512
722
|
})
|
|
@@ -534,9 +744,13 @@ function updateKit(input) {
|
|
|
534
744
|
input?.policyId === undefined ? existing.policyId : String(input.policyId || '').trim()
|
|
535
745
|
const nextLoadoutId =
|
|
536
746
|
input?.loadoutId === undefined ? existing.loadoutId : String(input.loadoutId || '').trim()
|
|
747
|
+
const nextManagedSource =
|
|
748
|
+
input?.managedSource === undefined
|
|
749
|
+
? existing.managedSource || null
|
|
750
|
+
: normalizeManagedSource(input.managedSource) || null
|
|
537
751
|
|
|
538
|
-
if (!nextPolicyId
|
|
539
|
-
throw new Error('Kit must include
|
|
752
|
+
if (!nextPolicyId && !nextLoadoutId) {
|
|
753
|
+
throw new Error('Kit must include at least AGENTS.md or Skills package')
|
|
540
754
|
}
|
|
541
755
|
|
|
542
756
|
ensureKitRefsExist(nextPolicyId, nextLoadoutId)
|
|
@@ -548,14 +762,16 @@ function updateKit(input) {
|
|
|
548
762
|
description = @description,
|
|
549
763
|
policy_id = @policyId,
|
|
550
764
|
loadout_id = @loadoutId,
|
|
765
|
+
managed_source_json = @managedSourceJson,
|
|
551
766
|
updated_at = @updatedAt
|
|
552
767
|
WHERE id = @id`
|
|
553
768
|
).run({
|
|
554
769
|
id,
|
|
555
770
|
name: nextName,
|
|
556
771
|
description: nextDescription,
|
|
557
|
-
policyId: nextPolicyId,
|
|
558
|
-
loadoutId: nextLoadoutId,
|
|
772
|
+
policyId: nextPolicyId || '',
|
|
773
|
+
loadoutId: nextLoadoutId || '',
|
|
774
|
+
managedSourceJson: JSON.stringify(nextManagedSource),
|
|
559
775
|
updatedAt: nowTs(),
|
|
560
776
|
})
|
|
561
777
|
|
|
@@ -569,6 +785,96 @@ function deleteKit(id) {
|
|
|
569
785
|
return result.changes > 0
|
|
570
786
|
}
|
|
571
787
|
|
|
788
|
+
function upsertPolicyBaseline(db, baseline, updatedAt) {
|
|
789
|
+
const existing = getKitPolicyById(baseline.id)
|
|
790
|
+
db.prepare(
|
|
791
|
+
`INSERT OR REPLACE INTO kit_policies (id, name, description, content, created_at, updated_at)
|
|
792
|
+
VALUES (@id, @name, @description, @content, @createdAt, @updatedAt)`
|
|
793
|
+
).run({
|
|
794
|
+
id: baseline.id,
|
|
795
|
+
name: baseline.name,
|
|
796
|
+
description: baseline.description || null,
|
|
797
|
+
content: baseline.content,
|
|
798
|
+
createdAt: existing?.createdAt || updatedAt,
|
|
799
|
+
updatedAt,
|
|
800
|
+
})
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
function upsertLoadoutBaseline(db, baseline, updatedAt) {
|
|
804
|
+
const existing = getKitLoadoutById(baseline.id)
|
|
805
|
+
db.prepare(
|
|
806
|
+
`INSERT OR REPLACE INTO kit_loadouts (id, name, description, import_source_json, created_at, updated_at)
|
|
807
|
+
VALUES (@id, @name, @description, @importSourceJson, @createdAt, @updatedAt)`
|
|
808
|
+
).run({
|
|
809
|
+
id: baseline.id,
|
|
810
|
+
name: baseline.name,
|
|
811
|
+
description: baseline.description || null,
|
|
812
|
+
importSourceJson: JSON.stringify(null),
|
|
813
|
+
createdAt: existing?.createdAt || updatedAt,
|
|
814
|
+
updatedAt,
|
|
815
|
+
})
|
|
816
|
+
|
|
817
|
+
db.prepare(`DELETE FROM kit_loadout_items WHERE loadout_id = ?`).run(baseline.id)
|
|
818
|
+
const insertItem = db.prepare(
|
|
819
|
+
`INSERT INTO kit_loadout_items (loadout_id, skill_path, mode, sort_order)
|
|
820
|
+
VALUES (@loadoutId, @skillPath, @mode, @sortOrder)`
|
|
821
|
+
)
|
|
822
|
+
|
|
823
|
+
for (const item of baseline.items) {
|
|
824
|
+
insertItem.run({
|
|
825
|
+
loadoutId: baseline.id,
|
|
826
|
+
skillPath: item.skillPath,
|
|
827
|
+
mode: item.mode,
|
|
828
|
+
sortOrder: item.sortOrder,
|
|
829
|
+
})
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
function restoreManagedKitBaseline(id) {
|
|
834
|
+
const existing = getKitById(id)
|
|
835
|
+
if (!existing) {
|
|
836
|
+
throw new Error(`Kit not found: ${id}`)
|
|
837
|
+
}
|
|
838
|
+
if (!existing.managedSource || existing.managedSource.kind !== 'official_preset') {
|
|
839
|
+
throw new Error('Only managed official kits can be restored')
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
const db = ensureDb()
|
|
843
|
+
const ts = nowTs()
|
|
844
|
+
const { baseline } = existing.managedSource
|
|
845
|
+
const nextManagedSource = {
|
|
846
|
+
...existing.managedSource,
|
|
847
|
+
lastRestoredAt: ts,
|
|
848
|
+
restoreCount: Number(existing.managedSource.restoreCount || 0) + 1,
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
const tx = db.transaction(() => {
|
|
852
|
+
upsertPolicyBaseline(db, baseline.policy, ts)
|
|
853
|
+
upsertLoadoutBaseline(db, baseline.loadout, ts)
|
|
854
|
+
db.prepare(
|
|
855
|
+
`UPDATE kit_presets
|
|
856
|
+
SET name = @name,
|
|
857
|
+
description = @description,
|
|
858
|
+
policy_id = @policyId,
|
|
859
|
+
loadout_id = @loadoutId,
|
|
860
|
+
managed_source_json = @managedSourceJson,
|
|
861
|
+
updated_at = @updatedAt
|
|
862
|
+
WHERE id = @id`
|
|
863
|
+
).run({
|
|
864
|
+
id,
|
|
865
|
+
name: baseline.name,
|
|
866
|
+
description: baseline.description || null,
|
|
867
|
+
policyId: baseline.policy.id,
|
|
868
|
+
loadoutId: baseline.loadout.id,
|
|
869
|
+
managedSourceJson: JSON.stringify(nextManagedSource),
|
|
870
|
+
updatedAt: ts,
|
|
871
|
+
})
|
|
872
|
+
})
|
|
873
|
+
|
|
874
|
+
tx()
|
|
875
|
+
return getKitById(id)
|
|
876
|
+
}
|
|
877
|
+
|
|
572
878
|
function markKitApplied(input) {
|
|
573
879
|
const id = String(input?.id || '').trim()
|
|
574
880
|
if (!id) {
|
|
@@ -622,5 +928,6 @@ export {
|
|
|
622
928
|
addKit,
|
|
623
929
|
updateKit,
|
|
624
930
|
deleteKit,
|
|
931
|
+
restoreManagedKitBaseline,
|
|
625
932
|
markKitApplied,
|
|
626
933
|
}
|
package/lib/core/kit-types.ts
CHANGED
|
@@ -6,11 +6,23 @@ export interface KitLoadoutItem {
|
|
|
6
6
|
sortOrder: number
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
export interface KitLoadoutImportSource {
|
|
10
|
+
repoWebUrl: string
|
|
11
|
+
repoUrl: string
|
|
12
|
+
originalUrl: string
|
|
13
|
+
branch?: string
|
|
14
|
+
rootSubdir: string
|
|
15
|
+
importedAt: string
|
|
16
|
+
lastSourceUpdatedAt: string
|
|
17
|
+
lastSafetyCheck?: KitSafetyCheck
|
|
18
|
+
}
|
|
19
|
+
|
|
9
20
|
export interface KitLoadoutRecord {
|
|
10
21
|
id: string
|
|
11
22
|
name: string
|
|
12
23
|
description?: string
|
|
13
24
|
items: KitLoadoutItem[]
|
|
25
|
+
importSource?: KitLoadoutImportSource
|
|
14
26
|
createdAt: number
|
|
15
27
|
updatedAt: number
|
|
16
28
|
}
|
|
@@ -24,17 +36,63 @@ export interface KitPolicyRecord {
|
|
|
24
36
|
updatedAt: number
|
|
25
37
|
}
|
|
26
38
|
|
|
39
|
+
export interface KitSafetyCheck {
|
|
40
|
+
checkedAt: number
|
|
41
|
+
status: 'pass' | 'warn'
|
|
42
|
+
scannedFiles: number
|
|
43
|
+
warnings: string[]
|
|
44
|
+
flaggedFiles: string[]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface ManagedKitPolicyBaseline {
|
|
48
|
+
id: string
|
|
49
|
+
name: string
|
|
50
|
+
description?: string
|
|
51
|
+
content: string
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface ManagedKitLoadoutBaseline {
|
|
55
|
+
id: string
|
|
56
|
+
name: string
|
|
57
|
+
description?: string
|
|
58
|
+
items: KitLoadoutItem[]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface ManagedKitSecurityCheck {
|
|
62
|
+
sourceId: string
|
|
63
|
+
sourceName: string
|
|
64
|
+
check: KitSafetyCheck
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface ManagedKitSource {
|
|
68
|
+
kind: 'official_preset'
|
|
69
|
+
presetId: string
|
|
70
|
+
presetName: string
|
|
71
|
+
catalogVersion: number
|
|
72
|
+
installedAt: number
|
|
73
|
+
lastRestoredAt?: number
|
|
74
|
+
restoreCount: number
|
|
75
|
+
baseline: {
|
|
76
|
+
name: string
|
|
77
|
+
description?: string
|
|
78
|
+
policy: ManagedKitPolicyBaseline
|
|
79
|
+
loadout: ManagedKitLoadoutBaseline
|
|
80
|
+
}
|
|
81
|
+
securityChecks: ManagedKitSecurityCheck[]
|
|
82
|
+
}
|
|
83
|
+
|
|
27
84
|
export interface KitRecord {
|
|
28
85
|
id: string
|
|
29
86
|
name: string
|
|
30
87
|
description?: string
|
|
31
|
-
policyId
|
|
32
|
-
loadoutId
|
|
88
|
+
policyId?: string
|
|
89
|
+
loadoutId?: string
|
|
33
90
|
lastAppliedAt?: number
|
|
34
91
|
lastAppliedTarget?: {
|
|
35
92
|
projectPath: string
|
|
36
93
|
agentName: string
|
|
37
94
|
}
|
|
95
|
+
managedSource?: ManagedKitSource
|
|
38
96
|
createdAt: number
|
|
39
97
|
updatedAt: number
|
|
40
98
|
}
|
|
@@ -50,10 +108,77 @@ export interface KitApplySkillResult {
|
|
|
50
108
|
export interface KitApplyResult {
|
|
51
109
|
kitId: string
|
|
52
110
|
kitName: string
|
|
53
|
-
policyPath
|
|
111
|
+
policyPath?: string
|
|
112
|
+
policyFileName?: string
|
|
54
113
|
projectPath: string
|
|
55
114
|
agentName: string
|
|
56
115
|
appliedAt: number
|
|
57
116
|
overwroteAgentsMd?: boolean
|
|
58
117
|
loadoutResults: KitApplySkillResult[]
|
|
59
118
|
}
|
|
119
|
+
|
|
120
|
+
export interface KitLoadoutImportResult {
|
|
121
|
+
loadout: KitLoadoutRecord
|
|
122
|
+
loadoutStatus: 'created' | 'updated'
|
|
123
|
+
importedSkillPaths: string[]
|
|
124
|
+
overwrittenCount: number
|
|
125
|
+
removedCount: number
|
|
126
|
+
discoveredCount: number
|
|
127
|
+
source: KitLoadoutImportSource
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export interface OfficialPresetSummary {
|
|
131
|
+
id: string
|
|
132
|
+
name: string
|
|
133
|
+
description?: string
|
|
134
|
+
policyName: string
|
|
135
|
+
sourceCount: number
|
|
136
|
+
skillCount: number
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface OfficialPresetSource {
|
|
140
|
+
id: string
|
|
141
|
+
name: string
|
|
142
|
+
url: string
|
|
143
|
+
description?: string
|
|
144
|
+
selectedSkillDetails?: Array<{
|
|
145
|
+
name: string
|
|
146
|
+
description?: string
|
|
147
|
+
}>
|
|
148
|
+
selectedSkills: string[]
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export interface OfficialPresetDetail {
|
|
152
|
+
id: string
|
|
153
|
+
name: string
|
|
154
|
+
description?: string
|
|
155
|
+
policy: {
|
|
156
|
+
name: string
|
|
157
|
+
description?: string
|
|
158
|
+
template: string
|
|
159
|
+
}
|
|
160
|
+
sources: OfficialPresetSource[]
|
|
161
|
+
skillCount: number
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export interface OfficialPresetInstallResult {
|
|
165
|
+
preset: {
|
|
166
|
+
id: string
|
|
167
|
+
name: string
|
|
168
|
+
description?: string
|
|
169
|
+
}
|
|
170
|
+
policy: KitPolicyRecord
|
|
171
|
+
loadout: KitLoadoutRecord
|
|
172
|
+
kit: KitRecord
|
|
173
|
+
importedSources: Array<{
|
|
174
|
+
id: string
|
|
175
|
+
name: string
|
|
176
|
+
loadoutId: string
|
|
177
|
+
importedSkillCount: number
|
|
178
|
+
selectedSkillCount: number
|
|
179
|
+
}>
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export interface OfficialPresetBatchInstallResult {
|
|
183
|
+
installed: OfficialPresetInstallResult[]
|
|
184
|
+
}
|