coding-tool-x 3.8.7 → 3.8.8
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/dist/web/assets/{AgentsDrawer-C7ePByZx.js → AgentsDrawer-DRQuwi0m.js} +1 -1
- package/dist/web/assets/Analytics-Cf5KwEdj.js +1 -0
- package/dist/web/assets/AssetPathField-DyniM0pF.js +1 -0
- package/dist/web/assets/{ChatHistoryDrawer-Ccl4rhdz.js → ChatHistoryDrawer-CrfS0KYV.js} +1 -1
- package/dist/web/assets/{CollapseItem-MxG_UD4o.js → CollapseItem-ClMsJuUu.js} +1 -1
- package/dist/web/assets/{CommandsDrawer-DWbaxHRZ.js → CommandsDrawer-CzHelf56.js} +1 -1
- package/dist/web/assets/ConfigExportDrawer-BEZ8wTby.js +1 -0
- package/dist/web/assets/ConfigTemplates-Crg3xWe5.js +1 -0
- package/dist/web/assets/{ConfigTemplatesDrawer-CKMFsfEF.js → ConfigTemplatesDrawer-BqM32Im7.js} +1 -1
- package/dist/web/assets/{ConfigTemplatesPanel-Cvbnkvpt.js → ConfigTemplatesPanel-lTdutC8H.js} +1 -1
- package/dist/web/assets/{Divider-D28g1gwj.js → Divider-DsdbkHrG.js} +1 -1
- package/dist/web/assets/{FavoritesDrawer-DA-dR_vP.js → FavoritesDrawer-DDyWUBJR.js} +1 -1
- package/dist/web/assets/Forward-DLCL7aMY.js +1 -0
- package/dist/web/assets/{GatewayConvertDrawer-402_raGb.js → GatewayConvertDrawer-Rigtt_5J.js} +1 -1
- package/dist/web/assets/Home-tCSKmSHV.js +1 -0
- package/dist/web/assets/{MarkdownEditor-BvkDECwH.js → MarkdownEditor-BMMUufSc.js} +1 -1
- package/dist/web/assets/McpDrawer-CmE-d7VW.js +1 -0
- package/dist/web/assets/{McpFormDrawer-DPMnyt5L.js → McpFormDrawer-ByvS18od.js} +1 -1
- package/dist/web/assets/{PluginManager-FKVeugr4.js → PluginManager-C4kJlpVX.js} +1 -1
- package/dist/web/assets/{PluginsDrawer-MvM1R55d.js → PluginsDrawer-05R4k15x.js} +1 -1
- package/dist/web/assets/PluginsPanel-BCoEs7Og.js +1 -0
- package/dist/web/assets/PluginsPanel-CzGMK-Ap.css +1 -0
- package/dist/web/assets/ProjectList-BJt1OIaI.js +1 -0
- package/dist/web/assets/ProjectList-DG3yu8xZ.css +1 -0
- package/dist/web/assets/PromptsDrawer-DnwBf_oW.js +1 -0
- package/dist/web/assets/RecentSessionsDrawer-B7d-38wH.js +1 -0
- package/dist/web/assets/RecentSessionsDrawer-yrAD4p1A.css +1 -0
- package/dist/web/assets/SessionCard-BUgjHQwX.js +1 -0
- package/dist/web/assets/SessionList-B-bXZRFk.js +1 -0
- package/dist/web/assets/SessionList-qG_XpPBJ.css +1 -0
- package/dist/web/assets/{SettingsDrawer-lMjttbkD.js → SettingsDrawer-BITzjQLl.js} +1 -1
- package/dist/web/assets/SkillManager-D9jkNuRM.js +1 -0
- package/dist/web/assets/{SkillsDrawer-DF6JsSyj.js → SkillsDrawer-0Z4IefxC.js} +1 -1
- package/dist/web/assets/SkillsPanel-BKIVOj7n.js +1 -0
- package/dist/web/assets/{SkillsPanel-B3Nm5j32.css → SkillsPanel-C4ke56nF.css} +1 -1
- package/dist/web/assets/{SpeedTestDrawer-BaZkZqPP.js → SpeedTestDrawer-Bx1bYf5i.js} +1 -1
- package/dist/web/assets/Upload-CzC4rGI4.js +1 -0
- package/dist/web/assets/{WorkspaceDrawer-BFSwZ5yU.js → WorkspaceDrawer-eujtWpoq.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-BHL1uYct.js → WorkspaceManager-CQ-8Ej68.js} +1 -1
- package/dist/web/assets/{WorkspacePanel-B5PooBtg.js → WorkspacePanel-Cql5qE2R.js} +1 -1
- package/dist/web/assets/headers-BqDmkMjy.js +1 -0
- package/dist/web/assets/{icons-B2Vg-IOC.js → icons-DmFe4b5j.js} +1 -1
- package/dist/web/assets/index-Cmmwj3Fx.js +2 -0
- package/dist/web/assets/{index-BohDDAy_.css → index-Cnbe696C.css} +1 -1
- package/dist/web/assets/{light-Cp0t7v32.js → light-0eavxNvt.js} +1 -1
- package/dist/web/assets/platforms-BmzzwnSw.js +1 -0
- package/dist/web/assets/platforms-DWXgmy2w.js +1 -0
- package/dist/web/assets/session-visibility-BAmxh3cK.js +1 -0
- package/dist/web/assets/sessions-D4yQtfKb.js +1 -0
- package/dist/web/assets/sessions-DqlbocLJ.js +1 -0
- package/dist/web/assets/use-message-DqCwefG6.js +1 -0
- package/dist/web/assets/{useResponsiveDrawer-D-DDpbnI.js → useResponsiveDrawer-DZd5rNk5.js} +1 -1
- package/dist/web/index.html +7 -7
- package/package.json +1 -1
- package/src/commands/search.js +17 -2
- package/src/config/paths.js +2 -0
- package/src/platforms/driver-registry.js +9 -1
- package/src/platforms/drivers/claude/session-history-adapter.js +72 -1
- package/src/platforms/drivers/claude/sessions-implementation.js +49 -7
- package/src/platforms/drivers/claude/sessions.js +30 -6
- package/src/platforms/drivers/codex/parser.js +57 -1
- package/src/platforms/drivers/codex/session-history-adapter.js +8 -2
- package/src/platforms/drivers/codex/sessions-implementation.js +25 -3
- package/src/platforms/drivers/codex/sessions.js +18 -1
- package/src/platforms/drivers/dsh/api-operations.js +396 -0
- package/src/platforms/drivers/dsh/channels-implementation.js +458 -0
- package/src/platforms/drivers/dsh/channels.js +191 -0
- package/src/platforms/drivers/dsh/common.js +571 -0
- package/src/platforms/drivers/dsh/native-config.js +93 -0
- package/src/platforms/drivers/dsh/native-logs.js +116 -0
- package/src/platforms/drivers/dsh/plugin-manager.js +117 -0
- package/src/platforms/drivers/dsh/projects.js +23 -0
- package/src/platforms/drivers/dsh/proxy-implementation.js +260 -0
- package/src/platforms/drivers/dsh/proxy.js +23 -0
- package/src/platforms/drivers/dsh/resources.js +346 -0
- package/src/platforms/drivers/dsh/sessions.js +587 -0
- package/src/platforms/drivers/dsh/statistics-implementation.js +238 -0
- package/src/platforms/drivers/dsh/statistics.js +24 -0
- package/src/platforms/drivers/gemini/session-history-adapter.js +49 -1
- package/src/platforms/drivers/gemini/sessions-implementation.js +46 -1
- package/src/platforms/drivers/gemini/sessions.js +2 -1
- package/src/platforms/drivers/omp/session-history-adapter.js +42 -1
- package/src/platforms/drivers/omp/sessions-implementation.js +51 -3
- package/src/platforms/drivers/omp/sessions.js +33 -2
- package/src/platforms/drivers/opencode/sessions-implementation.js +89 -9
- package/src/platforms/drivers/opencode/sessions.js +37 -1
- package/src/platforms/manifest-schema.js +43 -3
- package/src/platforms/manifests/dsh.json +542 -0
- package/src/platforms/path-resolver.js +2 -0
- package/src/platforms/platform-path-context.js +12 -2
- package/src/platforms/registry.js +16 -1
- package/src/platforms/runtime.js +5 -0
- package/src/platforms/session-history-adapters.js +1 -0
- package/src/server/api/platform-api-config.js +23 -2
- package/src/server/api/platform-route-factory.js +9 -2
- package/src/server/api/platforms.js +19 -1
- package/src/server/api/plugins.js +108 -13
- package/src/server/services/config-registry-service.js +10 -1
- package/src/server/services/config-templates-service.js +1 -0
- package/src/server/services/plugins-service.js +474 -103
- package/src/server/services/session-history-index.js +900 -140
- package/src/server/services/session-history-worker.js +3 -1
- package/src/server/services/skill-service.js +214 -7
- package/src/server/services/usage-log-utils.js +1 -0
- package/src/server/services/workspace-service.js +39 -10
- package/src/shared/driver-factories/api.js +22 -6
- package/src/shared/driver-factories/projects.js +8 -0
- package/dist/web/assets/Analytics-BVG6ehFv.js +0 -1
- package/dist/web/assets/AssetPathField-BZI9NvEr.js +0 -1
- package/dist/web/assets/ConfigExportDrawer-DrawKwxE.js +0 -1
- package/dist/web/assets/ConfigTemplates-AiRPv2cd.js +0 -1
- package/dist/web/assets/Home-CTF8ZWKW.js +0 -1
- package/dist/web/assets/McpDrawer-DZGHkuDm.js +0 -1
- package/dist/web/assets/PluginsPanel-CKuBs38B.js +0 -1
- package/dist/web/assets/PluginsPanel-D6ZqITcR.css +0 -1
- package/dist/web/assets/ProjectList-677RLZ8O.css +0 -1
- package/dist/web/assets/ProjectList-tsjwjXV1.js +0 -1
- package/dist/web/assets/PromptsDrawer-B-X9ci8n.js +0 -1
- package/dist/web/assets/RecentSessionsDrawer-CKFsOYDM.css +0 -1
- package/dist/web/assets/RecentSessionsDrawer-wLktMF_c.js +0 -1
- package/dist/web/assets/SessionCard-DkJPy0TY.js +0 -1
- package/dist/web/assets/SessionList-BpYRz1cX.css +0 -1
- package/dist/web/assets/SessionList-Gzhh4FOZ.js +0 -1
- package/dist/web/assets/SkillManager-CzFxWxx-.js +0 -1
- package/dist/web/assets/SkillsPanel--lLEts9y.js +0 -1
- package/dist/web/assets/Upload-BEQ-3Kqq.js +0 -1
- package/dist/web/assets/headers-cf-QapUq.js +0 -1
- package/dist/web/assets/index-BFInlZGI.js +0 -2
- package/dist/web/assets/platforms-CBkB45JP.js +0 -1
- package/dist/web/assets/platforms-FTpjxAT6.js +0 -1
- package/dist/web/assets/sessions-Cmy31slD.js +0 -1
- package/dist/web/assets/sessions-yC_oVVh8.js +0 -1
- package/dist/web/assets/use-message-DoLM8C1C.js +0 -1
|
@@ -157,6 +157,7 @@ function runInventoryWorker(source, indexDbPath, options = {}) {
|
|
|
157
157
|
CC_TOOL_SESSION_HISTORY_SOURCE: source,
|
|
158
158
|
CC_TOOL_SESSION_HISTORY_DB: indexDbPath,
|
|
159
159
|
CC_TOOL_SESSION_HISTORY_FORCE: options.force === true ? '1' : '0',
|
|
160
|
+
CC_TOOL_SESSION_HISTORY_PROJECT_NAME: options.projectName || '',
|
|
160
161
|
CC_TOOL_SESSION_HISTORY_PROJECTS_DIR: options.projectsDir || ''
|
|
161
162
|
},
|
|
162
163
|
silent: true,
|
|
@@ -228,6 +229,7 @@ function attachWorkerHandler() {
|
|
|
228
229
|
const source = process.env.CC_TOOL_SESSION_HISTORY_SOURCE;
|
|
229
230
|
const dbPath = process.env.CC_TOOL_SESSION_HISTORY_DB;
|
|
230
231
|
const projectsDir = process.env.CC_TOOL_SESSION_HISTORY_PROJECTS_DIR || undefined;
|
|
232
|
+
const projectName = process.env.CC_TOOL_SESSION_HISTORY_PROJECT_NAME || undefined;
|
|
231
233
|
const force = process.env.CC_TOOL_SESSION_HISTORY_FORCE === '1';
|
|
232
234
|
if (!source || !dbPath) {
|
|
233
235
|
process.exit(1);
|
|
@@ -246,7 +248,7 @@ function attachWorkerHandler() {
|
|
|
246
248
|
_sendWorkerMessage(message, exitCode);
|
|
247
249
|
};
|
|
248
250
|
|
|
249
|
-
index.ensureSourceIndexed(source, { consistency: 'complete', force })
|
|
251
|
+
index.ensureSourceIndexed(source, { consistency: 'complete', force, projectName })
|
|
250
252
|
.then(() => finishWorker({ type: 'done' }, 0))
|
|
251
253
|
.catch((err) => finishWorker({ type: 'error', error: _serializeWorkerError(err) }, 1));
|
|
252
254
|
}
|
|
@@ -281,10 +281,12 @@ class SkillService {
|
|
|
281
281
|
registry = getPlatformContext().registry,
|
|
282
282
|
controlService = null,
|
|
283
283
|
artifactStore = null,
|
|
284
|
-
formatAdapter = null
|
|
284
|
+
formatAdapter = null,
|
|
285
|
+
runtime = null
|
|
285
286
|
} = {}
|
|
286
287
|
) {
|
|
287
288
|
this.registry = registry;
|
|
289
|
+
this.runtime = runtime || getPlatformContext().runtime;
|
|
288
290
|
this.platform = resolveManagedPlatform(platform).platform;
|
|
289
291
|
if (this.platform === 'omp') {
|
|
290
292
|
const migration = migratePiStorage(PATHS);
|
|
@@ -315,6 +317,10 @@ class SkillService {
|
|
|
315
317
|
this.storageDir = state.localSkills || platformConfig.storageDir;
|
|
316
318
|
this.reposConfigPath = state.skillRepos || platformConfig.reposFile;
|
|
317
319
|
this.cachePath = state.skillCaches || platformConfig.cacheFile;
|
|
320
|
+
this.metadataIndexPath = path.join(
|
|
321
|
+
path.dirname(this.cachePath),
|
|
322
|
+
`${this.platform}-skill-metadata-index.json`
|
|
323
|
+
);
|
|
318
324
|
this.artifactStore = artifactStore || (
|
|
319
325
|
PATHS.skillArtifacts
|
|
320
326
|
? new SkillArtifactStore({ root: PATHS.skillArtifacts })
|
|
@@ -341,6 +347,8 @@ class SkillService {
|
|
|
341
347
|
this._remoteSkillsCache = null;
|
|
342
348
|
this._remoteSkillsFetchedAt = 0;
|
|
343
349
|
this._legacyMigrationChecked = false;
|
|
350
|
+
this._skillMetadataIndex = null;
|
|
351
|
+
this._skillMetadataIndexDirty = false;
|
|
344
352
|
|
|
345
353
|
this.githubTokenCache = new Map();
|
|
346
354
|
|
|
@@ -348,6 +356,95 @@ class SkillService {
|
|
|
348
356
|
this.ensureDirs();
|
|
349
357
|
}
|
|
350
358
|
|
|
359
|
+
_getDshApiDriver() {
|
|
360
|
+
if (this.platform !== 'dsh') return null;
|
|
361
|
+
return this.runtime?.getDriver?.('dsh', 'api') || null;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
_normalizeDshSkill(skill = {}, includeContent = false) {
|
|
365
|
+
const sourceScope = String(skill.source || '').startsWith('project-') ? 'project' : 'user';
|
|
366
|
+
const sourcePath = skill.path || '';
|
|
367
|
+
const controlKey = `dsh:${sourceScope}:${skill.name || sourcePath}`;
|
|
368
|
+
return {
|
|
369
|
+
key: controlKey,
|
|
370
|
+
controlKey,
|
|
371
|
+
name: skill.name || path.basename(skill.resourceBase || sourcePath),
|
|
372
|
+
description: skill.description || '',
|
|
373
|
+
directory: skill.name || path.basename(skill.resourceBase || sourcePath),
|
|
374
|
+
installed: true,
|
|
375
|
+
enabled: true,
|
|
376
|
+
cached: true,
|
|
377
|
+
isLocal: true,
|
|
378
|
+
source: 'native',
|
|
379
|
+
sourceProvider: 'dsh',
|
|
380
|
+
sourceScope,
|
|
381
|
+
scope: sourceScope,
|
|
382
|
+
sourcePath,
|
|
383
|
+
path: sourcePath,
|
|
384
|
+
fullPath: sourcePath,
|
|
385
|
+
installPath: skill.resourceBase || path.dirname(sourcePath),
|
|
386
|
+
trust: 'approved',
|
|
387
|
+
managed: false,
|
|
388
|
+
readonly: true,
|
|
389
|
+
protected: false,
|
|
390
|
+
projection: { mode: 'unsupported', state: 'unsupported' },
|
|
391
|
+
artifactState: 'ready',
|
|
392
|
+
invocation: skill.invocation || null,
|
|
393
|
+
...(skill.whenToUse ? { whenToUse: skill.whenToUse } : {}),
|
|
394
|
+
...(skill.metadata ? { metadata: skill.metadata } : {}),
|
|
395
|
+
...(includeContent && skill.content !== undefined ? { content: skill.content } : {})
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
async _scanDshSkills(options = {}) {
|
|
400
|
+
const driver = this._getDshApiDriver();
|
|
401
|
+
if (!driver?.listSkills) {
|
|
402
|
+
return { skills: [], refresh: { state: 'unsupported', taskId: null, fetchedAt: null, error: null } };
|
|
403
|
+
}
|
|
404
|
+
const result = await driver.listSkills({
|
|
405
|
+
query: {
|
|
406
|
+
...(options.cwd ? { cwd: options.cwd } : {}),
|
|
407
|
+
...(options.profile ? { profile: options.profile } : {}),
|
|
408
|
+
...(options.scope ? { scope: options.scope } : {})
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
if (result?.status !== 'ok') {
|
|
412
|
+
throw new Error(result?.error || 'DSH Skills are unavailable');
|
|
413
|
+
}
|
|
414
|
+
const scope = options.scope || 'user';
|
|
415
|
+
const nativeSkills = (Array.isArray(result.data?.skills) ? result.data.skills : [])
|
|
416
|
+
.filter(skill => {
|
|
417
|
+
const isProject = String(skill.source || '').startsWith('project-');
|
|
418
|
+
return scope === 'project' ? isProject : !isProject;
|
|
419
|
+
})
|
|
420
|
+
.map(skill => this._normalizeDshSkill(skill, false));
|
|
421
|
+
const remoteSkills = [
|
|
422
|
+
...this._artifactSkills({ ...options, scope }),
|
|
423
|
+
...(options.includeRemote === false ? [] : this._legacyCachedSkills({ ...options, scope }))
|
|
424
|
+
];
|
|
425
|
+
const seenNames = new Set(nativeSkills.map(skill => skill.name || skill.directory).filter(Boolean));
|
|
426
|
+
const skills = [
|
|
427
|
+
...nativeSkills,
|
|
428
|
+
...remoteSkills.filter(skill => {
|
|
429
|
+
const name = skill.name || skill.directory;
|
|
430
|
+
if (!name || seenNames.has(name)) return false;
|
|
431
|
+
seenNames.add(name);
|
|
432
|
+
return true;
|
|
433
|
+
})
|
|
434
|
+
];
|
|
435
|
+
return {
|
|
436
|
+
skills: skills.map(skill => this._sanitizeSkillListItem(skill)),
|
|
437
|
+
refresh: this._refreshSnapshot({ ...options, scope })
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
_sanitizeSkillListItem(skill = {}) {
|
|
442
|
+
const sanitized = { ...skill };
|
|
443
|
+
delete sanitized.content;
|
|
444
|
+
delete sanitized.fullContent;
|
|
445
|
+
return sanitized;
|
|
446
|
+
}
|
|
447
|
+
|
|
351
448
|
refreshOmpPaths() {
|
|
352
449
|
if (this.platform !== 'omp') return;
|
|
353
450
|
let nextContext = null;
|
|
@@ -445,6 +542,96 @@ class SkillService {
|
|
|
445
542
|
return value.map(skill => ({ ...skill }));
|
|
446
543
|
}
|
|
447
544
|
|
|
545
|
+
_readSkillFrontmatter(skillPath) {
|
|
546
|
+
const resolvedPath = path.resolve(skillPath);
|
|
547
|
+
let stat;
|
|
548
|
+
try {
|
|
549
|
+
stat = fs.statSync(resolvedPath);
|
|
550
|
+
} catch {
|
|
551
|
+
return {};
|
|
552
|
+
}
|
|
553
|
+
const index = this._loadSkillMetadataIndex();
|
|
554
|
+
const cached = index[resolvedPath];
|
|
555
|
+
if (
|
|
556
|
+
cached
|
|
557
|
+
&& Number(cached.mtimeMs) === Number(stat.mtimeMs)
|
|
558
|
+
&& Number(cached.size) === Number(stat.size)
|
|
559
|
+
&& cached.metadata
|
|
560
|
+
) {
|
|
561
|
+
return { ...cached.metadata };
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
let metadata = {};
|
|
565
|
+
const fd = fs.openSync(skillPath, 'r');
|
|
566
|
+
try {
|
|
567
|
+
const chunks = [];
|
|
568
|
+
const buffer = Buffer.alloc(8192);
|
|
569
|
+
let total = 0;
|
|
570
|
+
const maxBytes = 128 * 1024;
|
|
571
|
+
while (total < maxBytes) {
|
|
572
|
+
const bytesRead = fs.readSync(fd, buffer, 0, buffer.length, total);
|
|
573
|
+
if (!bytesRead) break;
|
|
574
|
+
chunks.push(buffer.subarray(0, bytesRead));
|
|
575
|
+
total += bytesRead;
|
|
576
|
+
const partial = Buffer.concat(chunks).toString('utf8');
|
|
577
|
+
const header = partial.match(/^---\s*\r?\n[\s\S]*?\r?\n---(?:\s*\r?\n|$)/)?.[0];
|
|
578
|
+
if (header) {
|
|
579
|
+
const parsed = this.parseSkillMd(header);
|
|
580
|
+
metadata = {
|
|
581
|
+
...(parsed.name ? { name: parsed.name } : {}),
|
|
582
|
+
...(parsed.description ? { description: parsed.description } : {}),
|
|
583
|
+
...(parsed.shortDescription ? { shortDescription: parsed.shortDescription } : {}),
|
|
584
|
+
...(parsed.allowedTools ? { allowedTools: parsed.allowedTools } : {}),
|
|
585
|
+
...(parsed.license ? { license: parsed.license } : {}),
|
|
586
|
+
...(parsed.metadata ? { metadata: parsed.metadata } : {}),
|
|
587
|
+
...(parsed.format ? { format: parsed.format } : {})
|
|
588
|
+
};
|
|
589
|
+
break;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
} finally {
|
|
593
|
+
fs.closeSync(fd);
|
|
594
|
+
}
|
|
595
|
+
index[resolvedPath] = {
|
|
596
|
+
mtimeMs: stat.mtimeMs,
|
|
597
|
+
size: stat.size,
|
|
598
|
+
metadata
|
|
599
|
+
};
|
|
600
|
+
this._skillMetadataIndexDirty = true;
|
|
601
|
+
return { ...metadata };
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
_loadSkillMetadataIndex() {
|
|
605
|
+
if (this._skillMetadataIndex) return this._skillMetadataIndex;
|
|
606
|
+
this._skillMetadataIndex = {};
|
|
607
|
+
try {
|
|
608
|
+
if (fs.existsSync(this.metadataIndexPath)) {
|
|
609
|
+
const parsed = JSON.parse(fs.readFileSync(this.metadataIndexPath, 'utf8'));
|
|
610
|
+
if (parsed && typeof parsed.entries === 'object' && !Array.isArray(parsed.entries)) {
|
|
611
|
+
this._skillMetadataIndex = parsed.entries;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
} catch {
|
|
615
|
+
this._skillMetadataIndex = {};
|
|
616
|
+
}
|
|
617
|
+
return this._skillMetadataIndex;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
_saveSkillMetadataIndex() {
|
|
621
|
+
if (!this._skillMetadataIndexDirty) return;
|
|
622
|
+
const tempPath = `${this.metadataIndexPath}.tmp-${process.pid}-${Date.now()}`;
|
|
623
|
+
try {
|
|
624
|
+
fs.mkdirSync(path.dirname(this.metadataIndexPath), { recursive: true });
|
|
625
|
+
fs.writeFileSync(tempPath, JSON.stringify({ version: 1, entries: this._loadSkillMetadataIndex() }), 'utf8');
|
|
626
|
+
fs.renameSync(tempPath, this.metadataIndexPath);
|
|
627
|
+
this._skillMetadataIndexDirty = false;
|
|
628
|
+
} catch {
|
|
629
|
+
try {
|
|
630
|
+
if (fs.existsSync(tempPath)) fs.unlinkSync(tempPath);
|
|
631
|
+
} catch (_) {}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
448
635
|
_artifactSkills(options = {}) {
|
|
449
636
|
if (!this.artifactStore || typeof this.artifactStore.list !== 'function') return [];
|
|
450
637
|
const artifacts = this.artifactStore.list({ platform: this.platform });
|
|
@@ -459,7 +646,7 @@ class SkillService {
|
|
|
459
646
|
const skillPath = path.join(artifact.root, 'SKILL.md');
|
|
460
647
|
try {
|
|
461
648
|
if (!fs.existsSync(skillPath) || fs.lstatSync(skillPath).isSymbolicLink()) return null;
|
|
462
|
-
const metadata = this.
|
|
649
|
+
const metadata = this._readSkillFrontmatter(skillPath);
|
|
463
650
|
const rawSourceProvider = artifact.sourceProvider || 'remote';
|
|
464
651
|
const sourceProvider = rawSourceProvider === 'remote'
|
|
465
652
|
? (artifact.repoProvider || 'remote')
|
|
@@ -943,6 +1130,7 @@ class SkillService {
|
|
|
943
1130
|
}
|
|
944
1131
|
|
|
945
1132
|
async scanSkills(options = {}) {
|
|
1133
|
+
if (this.platform === 'dsh') return this._scanDshSkills(options);
|
|
946
1134
|
this._ensureLegacyControlMigration();
|
|
947
1135
|
const scope = options.scope || 'user';
|
|
948
1136
|
const scopeOptions = scope === 'project'
|
|
@@ -968,7 +1156,12 @@ class SkillService {
|
|
|
968
1156
|
const missingControls = this._controlOnlySkills(normalizedOptions, knownControlKeys);
|
|
969
1157
|
const skills = [...controlled, ...missingControls];
|
|
970
1158
|
this.deduplicateSkills(skills);
|
|
971
|
-
const preparedSkills = this._storePrepared(
|
|
1159
|
+
const preparedSkills = this._storePrepared(
|
|
1160
|
+
cacheKey,
|
|
1161
|
+
skills.map(skill => this._sanitizeSkillListItem(skill)),
|
|
1162
|
+
generation
|
|
1163
|
+
);
|
|
1164
|
+
this._saveSkillMetadataIndex();
|
|
972
1165
|
return {
|
|
973
1166
|
skills: preparedSkills,
|
|
974
1167
|
refresh: this._refreshSnapshot(normalizedOptions)
|
|
@@ -2211,8 +2404,7 @@ class SkillService {
|
|
|
2211
2404
|
if (fs.existsSync(skillMdPath)) {
|
|
2212
2405
|
try {
|
|
2213
2406
|
if (fs.lstatSync(skillMdPath).isSymbolicLink()) return;
|
|
2214
|
-
const
|
|
2215
|
-
const metadata = this.parseSkillMd(content);
|
|
2407
|
+
const metadata = this._readSkillFrontmatter(skillMdPath);
|
|
2216
2408
|
const fullDirectory = normalizeRepoPath(path.relative(repoRoot, currentDir));
|
|
2217
2409
|
const directory = this.resolveSkillDirectory(fullDirectory, repo.directory || '', repo);
|
|
2218
2410
|
|
|
@@ -2943,8 +3135,7 @@ class SkillService {
|
|
|
2943
3135
|
}
|
|
2944
3136
|
|
|
2945
3137
|
try {
|
|
2946
|
-
const
|
|
2947
|
-
const metadata = this.parseSkillMd(content);
|
|
3138
|
+
const metadata = this._readSkillFrontmatter(skillMdPath);
|
|
2948
3139
|
const sourceKey = protectedSkill
|
|
2949
3140
|
? `system:${this.platform}:${directory}`
|
|
2950
3141
|
: (sourceProvider === 'native'
|
|
@@ -3447,6 +3638,22 @@ ${content}
|
|
|
3447
3638
|
* 获取本地 Skill 详情。远端内容只能来自已经发布的 artifact。
|
|
3448
3639
|
*/
|
|
3449
3640
|
async getSkillDetail(directory, repoHint = null, fullDirectoryHint = '', options = {}) {
|
|
3641
|
+
if (this.platform === 'dsh') {
|
|
3642
|
+
const driver = this._getDshApiDriver();
|
|
3643
|
+
if (!driver?.getSkill) throw new Error('DSH Skills are unavailable');
|
|
3644
|
+
const result = await driver.getSkill({
|
|
3645
|
+
params: { skillName: directory },
|
|
3646
|
+
query: {
|
|
3647
|
+
...(options.cwd ? { cwd: options.cwd } : {}),
|
|
3648
|
+
...(options.profile ? { profile: options.profile } : {}),
|
|
3649
|
+
...(options.scope ? { scope: options.scope } : {})
|
|
3650
|
+
}
|
|
3651
|
+
});
|
|
3652
|
+
if (result?.status !== 'ok' || !result.data?.skill) {
|
|
3653
|
+
throw new Error(result?.error || `DSH skill not found: ${directory}`);
|
|
3654
|
+
}
|
|
3655
|
+
return this._normalizeDshSkill(result.data.skill, true);
|
|
3656
|
+
}
|
|
3450
3657
|
const safeDirectory = this.normalizeSkillDirectory(directory);
|
|
3451
3658
|
const scope = options.scope || 'user';
|
|
3452
3659
|
|
|
@@ -10,6 +10,7 @@ function toNumber(value) {
|
|
|
10
10
|
|
|
11
11
|
function normalizeToolSource(source = '') {
|
|
12
12
|
const normalized = String(source || '').trim().toLowerCase();
|
|
13
|
+
if (normalized === 'dsh') return 'dsh';
|
|
13
14
|
if (normalized === 'claude' || normalized === 'claude-code') return 'claude';
|
|
14
15
|
if (normalized === 'codex') return 'codex';
|
|
15
16
|
if (normalized === 'gemini') return 'gemini';
|
|
@@ -774,6 +774,34 @@ async function getAllAvailableProjects() {
|
|
|
774
774
|
const allProjects = [];
|
|
775
775
|
const seenKeys = new Set();
|
|
776
776
|
|
|
777
|
+
async function getAllProjectPages(service, {
|
|
778
|
+
pageMethod = 'getProjectsPage',
|
|
779
|
+
fallbackMethod = 'getProjects',
|
|
780
|
+
baseArgs = [],
|
|
781
|
+
fallbackArgs = baseArgs,
|
|
782
|
+
options = {}
|
|
783
|
+
} = {}) {
|
|
784
|
+
if (typeof service?.[pageMethod] !== 'function') {
|
|
785
|
+
if (typeof service?.[fallbackMethod] !== 'function') return [];
|
|
786
|
+
const fallback = await service[fallbackMethod](...fallbackArgs, options);
|
|
787
|
+
return Array.isArray(fallback) ? fallback : [];
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
const projects = [];
|
|
791
|
+
let page = 1;
|
|
792
|
+
while (true) {
|
|
793
|
+
const payload = await service[pageMethod](
|
|
794
|
+
...baseArgs,
|
|
795
|
+
{ ...options, page, limit: 100, force: page === 1 ? options.force === true : false }
|
|
796
|
+
);
|
|
797
|
+
const rows = Array.isArray(payload) ? payload : payload?.projects;
|
|
798
|
+
if (Array.isArray(rows)) projects.push(...rows);
|
|
799
|
+
if (Array.isArray(payload) || !payload?.pagination?.hasMore) break;
|
|
800
|
+
page += 1;
|
|
801
|
+
}
|
|
802
|
+
return projects;
|
|
803
|
+
}
|
|
804
|
+
|
|
777
805
|
function addProject(channel, project) {
|
|
778
806
|
if (!project || !project.name) return;
|
|
779
807
|
|
|
@@ -802,8 +830,13 @@ async function getAllAvailableProjects() {
|
|
|
802
830
|
|
|
803
831
|
try {
|
|
804
832
|
const config = loadConfig();
|
|
805
|
-
const
|
|
806
|
-
|
|
833
|
+
const list = await getAllProjectPages(sessionsService, {
|
|
834
|
+
pageMethod: 'getProjectsPage',
|
|
835
|
+
fallbackMethod: 'getProjectsWithStats',
|
|
836
|
+
baseArgs: [config],
|
|
837
|
+
fallbackArgs: [config],
|
|
838
|
+
options: { force: true }
|
|
839
|
+
});
|
|
807
840
|
list.forEach(project => addProject('claude', project));
|
|
808
841
|
} catch (error) {
|
|
809
842
|
console.error('获取 claude 项目失败:', error.message);
|
|
@@ -811,8 +844,7 @@ async function getAllAvailableProjects() {
|
|
|
811
844
|
|
|
812
845
|
try {
|
|
813
846
|
if (isCodexInstalled()) {
|
|
814
|
-
const
|
|
815
|
-
const list = Array.isArray(codexProjects) ? codexProjects : [];
|
|
847
|
+
const list = await getAllProjectPages(codexSessionsService, { options: { force: true } });
|
|
816
848
|
list.forEach(project => addProject('codex', project));
|
|
817
849
|
}
|
|
818
850
|
} catch (error) {
|
|
@@ -821,8 +853,7 @@ async function getAllAvailableProjects() {
|
|
|
821
853
|
|
|
822
854
|
try {
|
|
823
855
|
if (isGeminiInstalled()) {
|
|
824
|
-
const
|
|
825
|
-
const list = Array.isArray(geminiProjects) ? geminiProjects : [];
|
|
856
|
+
const list = await getAllProjectPages(geminiSessionsService, { options: { force: true } });
|
|
826
857
|
list.forEach(project => addProject('gemini', project));
|
|
827
858
|
}
|
|
828
859
|
} catch (error) {
|
|
@@ -831,8 +862,7 @@ async function getAllAvailableProjects() {
|
|
|
831
862
|
|
|
832
863
|
try {
|
|
833
864
|
if (isOpenCodeInstalled()) {
|
|
834
|
-
const
|
|
835
|
-
const list = Array.isArray(opencodeProjects) ? opencodeProjects : [];
|
|
865
|
+
const list = await getAllProjectPages(opencodeSessionsService, { options: { force: true } });
|
|
836
866
|
list.forEach(project => addProject('opencode', project));
|
|
837
867
|
}
|
|
838
868
|
} catch (error) {
|
|
@@ -840,8 +870,7 @@ async function getAllAvailableProjects() {
|
|
|
840
870
|
}
|
|
841
871
|
|
|
842
872
|
try {
|
|
843
|
-
const
|
|
844
|
-
const list = Array.isArray(ompProjects) ? ompProjects : [];
|
|
873
|
+
const list = await getAllProjectPages(ompSessionsService, { options: { force: true } });
|
|
845
874
|
if (list.length > 0 || ompSessionsService.isOmpInstalled()) {
|
|
846
875
|
list.forEach(project => addProject('omp', project));
|
|
847
876
|
}
|
|
@@ -120,10 +120,10 @@ function createApiOperationsDriver({ platform, runtime, manifest, config, sessio
|
|
|
120
120
|
if (!payload.meta && typeof sessionHistoryIndex?.getSourceIndexMeta === 'function') {
|
|
121
121
|
payload.meta = sessionHistoryIndex.getSourceIndexMeta(platform);
|
|
122
122
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
123
|
+
if (isProjectList && !Object.prototype.hasOwnProperty.call(payload, 'currentProject')) {
|
|
124
|
+
payload.currentProject = platform === 'claude' ? config?.currentProject || null : null;
|
|
125
|
+
}
|
|
126
|
+
return payload;
|
|
127
127
|
};
|
|
128
128
|
|
|
129
129
|
for (const operation of operations) {
|
|
@@ -146,7 +146,7 @@ function createApiOperationsDriver({ platform, runtime, manifest, config, sessio
|
|
|
146
146
|
: operation === 'messages'
|
|
147
147
|
? index.getMessagePage?.(platform, sessionId, {
|
|
148
148
|
page: Number(requestContext.query?.page) || 1,
|
|
149
|
-
limit: Number(requestContext.query?.limit) ||
|
|
149
|
+
limit: Number(requestContext.query?.limit) || 20,
|
|
150
150
|
order: requestContext.query?.order,
|
|
151
151
|
consistency: 'stale-ok'
|
|
152
152
|
})
|
|
@@ -159,10 +159,26 @@ function createApiOperationsDriver({ platform, runtime, manifest, config, sessio
|
|
|
159
159
|
route
|
|
160
160
|
});
|
|
161
161
|
const operationName = targetOperation(capability, operation);
|
|
162
|
+
const pageMethod = capability === 'projects' && operation === 'listProjects'
|
|
163
|
+
? 'listProjectsPage'
|
|
164
|
+
: capability === 'sessions' && operation === 'listSessions'
|
|
165
|
+
? 'listSessionsPage'
|
|
166
|
+
: null;
|
|
167
|
+
const invocationArgs = argsFor(capability, operation, requestContext);
|
|
168
|
+
// Prefer the platform driver's page method so aliases, fork metadata,
|
|
169
|
+
// and platform-specific project normalization remain intact. The
|
|
170
|
+
// shared index is only the compatibility fallback for older drivers.
|
|
171
|
+
if (sessionHistoryIndex && pageMethod && (!target || typeof target[pageMethod] !== 'function')) {
|
|
172
|
+
const value = capability === 'projects'
|
|
173
|
+
? await sessionHistoryIndex.listProjectsPage(platform, invocationArgs[0])
|
|
174
|
+
: await sessionHistoryIndex.listSessionsPage(platform, invocationArgs[0], invocationArgs[1]);
|
|
175
|
+
return resultFor(requestContext, 'ok', await decorateReadModel(capability, operation, value));
|
|
176
|
+
}
|
|
162
177
|
if (!target || typeof target[operationName] !== 'function') {
|
|
163
178
|
return resultFor(requestContext, 'unsupported');
|
|
164
179
|
}
|
|
165
|
-
const
|
|
180
|
+
const method = pageMethod && typeof target[pageMethod] === 'function' ? pageMethod : operationName;
|
|
181
|
+
const value = await target[method](...invocationArgs);
|
|
166
182
|
if (value && typeof value === 'object' && typeof value.status === 'string') {
|
|
167
183
|
if (value.status !== 'ok') return resultFor(requestContext, value.status, undefined, value.error, value.cause);
|
|
168
184
|
return resultFor(requestContext, 'ok', await decorateReadModel(capability, operation, value.data));
|
|
@@ -74,6 +74,14 @@ function createProjectsDriver({
|
|
|
74
74
|
}
|
|
75
75
|
return invoke('listProjects', 'getProjects', [configFor(normalizedOptions)]);
|
|
76
76
|
},
|
|
77
|
+
listProjectsPage(options = {}) {
|
|
78
|
+
const normalizedOptions = options || {};
|
|
79
|
+
const target = loadService();
|
|
80
|
+
if (typeof target?.getProjectsPage === 'function') {
|
|
81
|
+
return invoke('listProjectsPage', 'getProjectsPage', [configFor(normalizedOptions), normalizedOptions]);
|
|
82
|
+
}
|
|
83
|
+
return this.listProjects(normalizedOptions);
|
|
84
|
+
},
|
|
77
85
|
getProjectOrder(options = {}) {
|
|
78
86
|
return invoke('getProjectOrder', 'getProjectOrder', [configFor(options)]);
|
|
79
87
|
},
|