skillex 0.2.4 → 0.3.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.
- package/CHANGELOG.md +29 -0
- package/README.md +45 -23
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +67 -25
- package/dist/install.js +148 -35
- package/dist/markdown.d.ts +7 -0
- package/dist/markdown.js +193 -0
- package/dist/sync.js +90 -4
- package/dist/types.d.ts +16 -0
- package/dist/ui.d.ts +10 -5
- package/dist/ui.js +7 -4
- package/dist/web-ui.d.ts +31 -0
- package/dist/web-ui.js +461 -0
- package/dist-ui/assets/CatalogPage-BVGkKQWg.js +1 -0
- package/dist-ui/assets/SkillDetailPage-CbIWg9lz.js +1 -0
- package/dist-ui/assets/index-DXrW27jF.js +25 -0
- package/dist-ui/assets/index-K8hfZs7_.css +1 -0
- package/dist-ui/index.html +16 -0
- package/package.json +15 -5
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.3.0] - 2026-04-08
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `skillex ui` now launches a local HTTP server serving the interactive Vue/Vite SPA instead of a terminal TUI
|
|
14
|
+
- New Vue 3 + Vite single-page application under `ui/` with catalog browser, skill detail pages, and router-based navigation
|
|
15
|
+
- `src/web-ui.ts`: self-contained HTTP server with `/api/skills` and `/api/catalog` JSON endpoints and graceful shutdown
|
|
16
|
+
- `src/markdown.ts`: shared Markdown-to-HTML renderer used by the web API
|
|
17
|
+
- Terminal browser detection: `skillex ui` opens the local server in the platform default browser or a detected terminal viewer
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- `src/cli.ts`: `ui` command now delegates to the local web-UI server
|
|
21
|
+
- `src/ui.ts`: updated to detect and delegate to terminal browser when available
|
|
22
|
+
- Build: `npm run build` now runs `build:ui` (Vite) followed by `tsc`
|
|
23
|
+
|
|
24
|
+
## [0.2.5] - 2026-04-08
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
- `technical-writing-pro` first-party skill for structured technical writing and documentation
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
- Auto-sync now enabled by default on `skillex init` (was `false`)
|
|
31
|
+
- Auto-sync syncs **all detected adapters**, not only the active one — workspaces with multiple agents (e.g. `.claude/` and `.codex/`) are kept in sync automatically
|
|
32
|
+
- `sync` and auto-sync output now lists each adapter individually with its target path, sync mode, and whether anything changed
|
|
33
|
+
- Removed the restriction that required an active adapter to enable auto-sync
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
- Skill removal now cleans up the synced adapter target file and its generated source when the last skill is removed
|
|
37
|
+
- `remove` correctly iterates all resolved adapters when propagating the removal
|
|
38
|
+
|
|
10
39
|
## [0.2.4] - 2026-04-08
|
|
11
40
|
|
|
12
41
|
### Changed
|
package/README.md
CHANGED
|
@@ -41,13 +41,9 @@ npx skillex@latest list
|
|
|
41
41
|
|
|
42
42
|
# 3. Install a skill
|
|
43
43
|
npx skillex@latest install create-skills
|
|
44
|
-
|
|
45
|
-
# 4. Write the installed skills into your agent's config file
|
|
46
|
-
# ⚠️ This step is required — without it, your agent cannot see the skills.
|
|
47
|
-
npx skillex@latest sync
|
|
48
44
|
```
|
|
49
45
|
|
|
50
|
-
> **Important:**
|
|
46
|
+
> **Important:** auto-sync is enabled by default. After `init`, `install`, `update`, and `remove`, Skillex automatically synchronizes skills into every detected adapter target. For directory-native adapters such as Codex, Claude, and Gemini, this materializes one folder per skill under the agent's `skills/` directory. For file-based adapters such as Copilot, Cursor, Cline, and Windsurf, it updates the adapter config file. Use `skillex sync` when you want to preview, re-run manually, or target a specific adapter.
|
|
51
47
|
|
|
52
48
|
After `init`, Skillex saves the configured source list in the local lockfile. New workspaces start with `lgili/skillex@main` by default, and you can add more sources later with `skillex source add`.
|
|
53
49
|
|
|
@@ -75,6 +71,23 @@ npm install -D skillex
|
|
|
75
71
|
npx skillex <command>
|
|
76
72
|
```
|
|
77
73
|
|
|
74
|
+
### Local Web UI development
|
|
75
|
+
|
|
76
|
+
When working on the repository itself, the browser UI is a standalone Vue 3 + Vite frontend under [`ui/`](/Users/lgili/Documents/01%20-%20Codes/01%20-%20Github/Skill/ui). The CLI serves the built assets from [`dist-ui/`](/Users/lgili/Documents/01%20-%20Codes/01%20-%20Github/Skill/dist-ui) and keeps all install/remove/update/sync logic in the local TypeScript backend.
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npm install
|
|
80
|
+
npm run build:ui
|
|
81
|
+
npm run build
|
|
82
|
+
node ./bin/skillex.js ui
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
For frontend-only iteration:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npm run dev:ui
|
|
89
|
+
```
|
|
90
|
+
|
|
78
91
|
---
|
|
79
92
|
|
|
80
93
|
## Commands
|
|
@@ -88,6 +101,7 @@ skillex init
|
|
|
88
101
|
skillex init --repo <owner/repo>
|
|
89
102
|
skillex init --repo lgili/skillex --adapter cursor
|
|
90
103
|
skillex init --repo lgili/skillex --auto-sync
|
|
104
|
+
skillex init --auto-sync=false
|
|
91
105
|
skillex init --global --adapter codex
|
|
92
106
|
```
|
|
93
107
|
|
|
@@ -95,7 +109,7 @@ skillex init --global --adapter codex
|
|
|
95
109
|
|------|-------------|
|
|
96
110
|
| `--repo <owner/repo>` | Optional. Overrides the default first-party source for this workspace. |
|
|
97
111
|
| `--adapter <id>` | Force a specific adapter instead of auto-detecting. |
|
|
98
|
-
| `--auto-sync` |
|
|
112
|
+
| `--auto-sync` | Enable or disable automatic sync after install, update, and remove. Default: `true`. |
|
|
99
113
|
| `--ref <branch>` | Use a specific branch or tag (default: `main`). |
|
|
100
114
|
| `--scope <local\|global>` | Choose whether Skillex manages workspace or user-global state. |
|
|
101
115
|
| `--global` | Shortcut for `--scope global`. |
|
|
@@ -173,7 +187,7 @@ skillex install create-skills --global
|
|
|
173
187
|
| `--scope <local\|global>` | Choose whether Skillex writes to `.agent-skills/` or `~/.skillex/`. |
|
|
174
188
|
| `--global` | Shortcut for `--scope global`. |
|
|
175
189
|
|
|
176
|
-
> **After installing,**
|
|
190
|
+
> **After installing,** Skillex syncs automatically by default. Run `skillex sync` manually only when you want to preview changes, re-run synchronization, or force a specific adapter.
|
|
177
191
|
|
|
178
192
|
---
|
|
179
193
|
|
|
@@ -208,10 +222,10 @@ skillex remove create-skills --global
|
|
|
208
222
|
|
|
209
223
|
### `sync`
|
|
210
224
|
|
|
211
|
-
Expose all installed skills to the
|
|
225
|
+
Expose all installed skills to the detected adapters. For `codex`, `claude`, and `gemini`, this creates one folder per skill under the adapter's `skills/` directory. For file-based adapters, it updates the adapter's config file. This is the same operation Skillex runs automatically after `install`, `update`, and `remove`.
|
|
212
226
|
|
|
213
227
|
```bash
|
|
214
|
-
# Sync to
|
|
228
|
+
# Sync to all detected adapters
|
|
215
229
|
skillex sync
|
|
216
230
|
|
|
217
231
|
# Preview changes without writing (shows a diff)
|
|
@@ -237,22 +251,17 @@ skillex sync --global --adapter codex
|
|
|
237
251
|
|
|
238
252
|
#### Using multiple agents in the same workspace
|
|
239
253
|
|
|
240
|
-
`sync` writes to
|
|
254
|
+
By default, `sync` writes to every detected adapter in the workspace. If you want to limit the operation to one adapter, pass `--adapter`:
|
|
241
255
|
|
|
242
256
|
```bash
|
|
243
|
-
#
|
|
257
|
+
# Only sync Claude
|
|
244
258
|
skillex sync --adapter claude
|
|
245
259
|
|
|
246
|
-
#
|
|
260
|
+
# Only sync Codex
|
|
247
261
|
skillex sync --adapter codex
|
|
248
262
|
```
|
|
249
263
|
|
|
250
|
-
Each adapter writes to its own target path, so the
|
|
251
|
-
|
|
252
|
-
```bash
|
|
253
|
-
# Sync to all agents at once (shell alias / Makefile target)
|
|
254
|
-
skillex sync --adapter claude && skillex sync --adapter codex
|
|
255
|
-
```
|
|
264
|
+
Each adapter writes to its own target path, so the syncs are independent and non-destructive. Automatic sync uses the same multi-adapter behavior.
|
|
256
265
|
|
|
257
266
|
---
|
|
258
267
|
|
|
@@ -267,12 +276,25 @@ skillex run git-master:cleanup --yes # skip confirmation
|
|
|
267
276
|
|
|
268
277
|
---
|
|
269
278
|
|
|
279
|
+
### Default terminal browser
|
|
280
|
+
|
|
281
|
+
Running `skillex` with no subcommand now opens the interactive terminal browser by default.
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
skillex
|
|
285
|
+
skillex browse
|
|
286
|
+
skillex tui
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
270
291
|
### `ui`
|
|
271
292
|
|
|
272
|
-
Open
|
|
293
|
+
Open the local Web UI in your browser.
|
|
273
294
|
|
|
274
295
|
```bash
|
|
275
296
|
skillex ui
|
|
297
|
+
skillex ui --global
|
|
276
298
|
```
|
|
277
299
|
|
|
278
300
|
---
|
|
@@ -580,14 +602,14 @@ For directory-native adapters, `sync` creates per-skill directories such as:
|
|
|
580
602
|
|
|
581
603
|
## Auto-sync
|
|
582
604
|
|
|
583
|
-
|
|
605
|
+
Auto-sync is enabled by default. After `init`, Skillex automatically runs sync after every `install`, `update`, and `remove`, targeting every detected adapter unless you explicitly override one with `--adapter`.
|
|
584
606
|
|
|
585
607
|
```bash
|
|
586
|
-
#
|
|
608
|
+
# Explicitly enable at init time
|
|
587
609
|
skillex init --auto-sync
|
|
588
610
|
|
|
589
|
-
#
|
|
590
|
-
skillex init --
|
|
611
|
+
# Explicitly disable it
|
|
612
|
+
skillex init --auto-sync=false
|
|
591
613
|
|
|
592
614
|
# Enable it for global installs
|
|
593
615
|
skillex init --global --adapter codex --auto-sync
|
package/dist/cli.d.ts
CHANGED
package/dist/cli.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import { listAdapters } from "./adapters.js";
|
|
3
|
-
import { computeCatalogCacheKey,
|
|
3
|
+
import { computeCatalogCacheKey, readCatalogCache, searchCatalogSkills, } from "./catalog.js";
|
|
4
4
|
import { DEFAULT_INSTALL_SCOPE, getScopedStatePaths } from "./config.js";
|
|
5
5
|
import { addProjectSource, getInstalledSkills, initProject, installSkills, listProjectSources, loadProjectCatalogs, removeProjectSource, removeSkills, resolveProjectSource, syncInstalledSkills, updateInstalledSkills, } from "./install.js";
|
|
6
6
|
import * as output from "./output.js";
|
|
7
7
|
import { setVerbose } from "./output.js";
|
|
8
8
|
import { parseSkillCommandReference, runSkillScript } from "./runner.js";
|
|
9
9
|
import { runInteractiveUi } from "./ui.js";
|
|
10
|
+
import { startWebUiServer } from "./web-ui.js";
|
|
10
11
|
import { CliError } from "./types.js";
|
|
11
12
|
import { VALID_CONFIG_KEYS, readUserConfig, writeUserConfig } from "./user-config.js";
|
|
12
13
|
// ---------------------------------------------------------------------------
|
|
@@ -21,7 +22,7 @@ Options:
|
|
|
21
22
|
--repo <owner/repo> GitHub repository with skills (default: lgili/skillex)
|
|
22
23
|
--ref <ref> Branch, tag, or commit (default: main)
|
|
23
24
|
--adapter <id> Force a specific adapter
|
|
24
|
-
--auto-sync Enable auto-sync
|
|
25
|
+
--auto-sync Enable or disable auto-sync (default: on)
|
|
25
26
|
--scope <scope> local or global (default: local)
|
|
26
27
|
--global Shortcut for --scope global
|
|
27
28
|
--cwd <path> Target project directory (default: current directory)
|
|
@@ -124,7 +125,9 @@ Options:
|
|
|
124
125
|
|
|
125
126
|
Example:
|
|
126
127
|
skillex run git-master:cleanup --yes`,
|
|
127
|
-
|
|
128
|
+
browse: `Usage: skillex browse [options]
|
|
129
|
+
skillex tui [options]
|
|
130
|
+
skillex [options]
|
|
128
131
|
|
|
129
132
|
Open the interactive terminal browser to browse and install skills.
|
|
130
133
|
|
|
@@ -133,7 +136,21 @@ Options:
|
|
|
133
136
|
--no-cache Bypass local catalog cache
|
|
134
137
|
|
|
135
138
|
Example:
|
|
136
|
-
skillex
|
|
139
|
+
skillex
|
|
140
|
+
skillex browse`,
|
|
141
|
+
ui: `Usage: skillex ui [options]
|
|
142
|
+
|
|
143
|
+
Open the local Web UI in your browser.
|
|
144
|
+
|
|
145
|
+
Options:
|
|
146
|
+
--repo <owner/repo> GitHub repository
|
|
147
|
+
--scope <scope> local or global (default: local)
|
|
148
|
+
--global Shortcut for --scope global
|
|
149
|
+
--no-cache Bypass local catalog cache
|
|
150
|
+
|
|
151
|
+
Example:
|
|
152
|
+
skillex ui
|
|
153
|
+
skillex ui --global`,
|
|
137
154
|
status: `Usage: skillex status [options]
|
|
138
155
|
|
|
139
156
|
Show the installation status of the selected scope.
|
|
@@ -206,9 +223,14 @@ export async function main(argv) {
|
|
|
206
223
|
if (userConfig.githubToken && !process.env.GITHUB_TOKEN) {
|
|
207
224
|
process.env.GITHUB_TOKEN = userConfig.githubToken;
|
|
208
225
|
}
|
|
226
|
+
const resolvedCommand = resolveCommandRoute(command);
|
|
227
|
+
if (flags.help === true && !command) {
|
|
228
|
+
printHelp();
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
209
231
|
// Per-command --help
|
|
210
|
-
if (flags.help === true &&
|
|
211
|
-
const helpText = COMMAND_HELP[
|
|
232
|
+
if (flags.help === true && resolvedCommand && resolvedCommand !== "help") {
|
|
233
|
+
const helpText = COMMAND_HELP[resolvedCommand];
|
|
212
234
|
if (helpText) {
|
|
213
235
|
output.info(helpText);
|
|
214
236
|
}
|
|
@@ -217,13 +239,13 @@ export async function main(argv) {
|
|
|
217
239
|
}
|
|
218
240
|
return;
|
|
219
241
|
}
|
|
220
|
-
// Resolve command aliases
|
|
221
|
-
const resolvedCommand = resolveAlias(command);
|
|
222
242
|
switch (resolvedCommand) {
|
|
223
243
|
case "help":
|
|
224
|
-
case undefined:
|
|
225
244
|
printHelp();
|
|
226
245
|
return;
|
|
246
|
+
case "browse":
|
|
247
|
+
await handleBrowse(flags, userConfig);
|
|
248
|
+
return;
|
|
227
249
|
case "init":
|
|
228
250
|
await handleInit(flags, userConfig);
|
|
229
251
|
return;
|
|
@@ -249,7 +271,7 @@ export async function main(argv) {
|
|
|
249
271
|
await handleRun(positionals, flags, userConfig);
|
|
250
272
|
return;
|
|
251
273
|
case "ui":
|
|
252
|
-
await
|
|
274
|
+
await handleWebUi(flags, userConfig);
|
|
253
275
|
return;
|
|
254
276
|
case "status":
|
|
255
277
|
await handleStatus(flags, userConfig);
|
|
@@ -296,6 +318,9 @@ async function handleInit(flags, userConfig) {
|
|
|
296
318
|
` Use --adapter <id> to specify one. Available: ${listAdapters().map((a) => a.id).join(", ")}`);
|
|
297
319
|
}
|
|
298
320
|
output.info(` Auto-sync: ${result.lockfile.settings.autoSync ? "enabled" : "disabled"}`);
|
|
321
|
+
if (result.lockfile.adapters.detected.length > 0) {
|
|
322
|
+
output.info(` Detected : ${result.lockfile.adapters.detected.join(", ")}`);
|
|
323
|
+
}
|
|
299
324
|
output.info("\nNext: run 'skillex list' to browse available skills");
|
|
300
325
|
}
|
|
301
326
|
async function handleList(flags, userConfig) {
|
|
@@ -406,17 +431,19 @@ async function handleRemove(positionals, flags, userConfig) {
|
|
|
406
431
|
async function handleSync(flags, userConfig) {
|
|
407
432
|
const result = await syncInstalledSkills(commonOptions(flags, userConfig));
|
|
408
433
|
if (result.dryRun) {
|
|
409
|
-
output.info(`Preview: ${result.skillCount} skill(s)
|
|
410
|
-
|
|
411
|
-
|
|
434
|
+
output.info(`Preview: ${result.skillCount} skill(s)`);
|
|
435
|
+
for (const entry of result.syncs) {
|
|
436
|
+
output.info(` ${entry.adapter} → ${entry.targetPath} [${entry.syncMode}]${entry.changed ? "" : " (no changes)"}`);
|
|
437
|
+
}
|
|
412
438
|
process.stdout.write(result.diff);
|
|
413
439
|
return;
|
|
414
440
|
}
|
|
415
|
-
output.success(`Synced ${result.skillCount} skill(s)
|
|
416
|
-
|
|
417
|
-
|
|
441
|
+
output.success(`Synced ${result.skillCount} skill(s)`);
|
|
442
|
+
for (const entry of result.syncs) {
|
|
443
|
+
output.info(` ${entry.adapter} → ${entry.targetPath} [${entry.syncMode}]${entry.changed ? "" : " (no changes)"}`);
|
|
444
|
+
}
|
|
418
445
|
if (!result.changed) {
|
|
419
|
-
output.info("No changes to the target
|
|
446
|
+
output.info("No changes to the target paths.");
|
|
420
447
|
}
|
|
421
448
|
}
|
|
422
449
|
async function handleRun(positionals, flags, userConfig) {
|
|
@@ -434,12 +461,11 @@ async function handleRun(positionals, flags, userConfig) {
|
|
|
434
461
|
process.exitCode = exitCode;
|
|
435
462
|
}
|
|
436
463
|
}
|
|
437
|
-
async function
|
|
464
|
+
async function handleBrowse(flags, userConfig) {
|
|
438
465
|
const options = commonOptions(flags, userConfig);
|
|
439
466
|
const state = await getInstalledSkills(options);
|
|
440
|
-
const source = await resolveProjectSource(options);
|
|
441
467
|
output.statusLine("Fetching catalog...");
|
|
442
|
-
const catalog = await
|
|
468
|
+
const catalog = await loadProjectCatalogs({ ...options, ...cacheOptions(options) });
|
|
443
469
|
output.clearStatus();
|
|
444
470
|
if (catalog.skills.length === 0) {
|
|
445
471
|
output.info("No skills available in the catalog.");
|
|
@@ -472,6 +498,15 @@ async function handleUi(flags, userConfig) {
|
|
|
472
498
|
}
|
|
473
499
|
printAutoSyncResult(installResult?.autoSync ?? removeResult?.autoSync ?? null);
|
|
474
500
|
}
|
|
501
|
+
async function handleWebUi(flags, userConfig) {
|
|
502
|
+
const options = commonOptions(flags, userConfig);
|
|
503
|
+
const session = await startWebUiServer(options);
|
|
504
|
+
output.success(`Skillex Web UI running at ${session.url}`);
|
|
505
|
+
if (!session.opened) {
|
|
506
|
+
output.warn("Could not open the browser automatically. Open the URL above manually.");
|
|
507
|
+
}
|
|
508
|
+
output.info("Press Ctrl+C to stop the local server.");
|
|
509
|
+
}
|
|
475
510
|
async function handleStatus(flags, userConfig) {
|
|
476
511
|
const options = commonOptions(flags, userConfig);
|
|
477
512
|
const state = await getInstalledSkills(options);
|
|
@@ -732,13 +767,17 @@ async function handleConfig(positionals, flags) {
|
|
|
732
767
|
// ---------------------------------------------------------------------------
|
|
733
768
|
// Helpers
|
|
734
769
|
// ---------------------------------------------------------------------------
|
|
735
|
-
function
|
|
770
|
+
export function resolveCommandRoute(command) {
|
|
736
771
|
const ALIASES = {
|
|
737
772
|
ls: "list",
|
|
738
773
|
rm: "remove",
|
|
739
774
|
uninstall: "remove",
|
|
775
|
+
tui: "browse",
|
|
740
776
|
};
|
|
741
|
-
|
|
777
|
+
if (command === undefined) {
|
|
778
|
+
return "browse";
|
|
779
|
+
}
|
|
780
|
+
return ALIASES[command] ?? command;
|
|
742
781
|
}
|
|
743
782
|
function commonOptions(flags, userConfig = {}) {
|
|
744
783
|
const options = {
|
|
@@ -856,6 +895,7 @@ function printHelp() {
|
|
|
856
895
|
output.info(`skillex — AI agent skill manager
|
|
857
896
|
|
|
858
897
|
Commands:
|
|
898
|
+
skillex open the terminal browser
|
|
859
899
|
skillex init [--repo owner/repo] [--ref main]
|
|
860
900
|
skillex list [--json]
|
|
861
901
|
skillex search [query] [--compatibility claude] [--tag git]
|
|
@@ -863,10 +903,11 @@ Commands:
|
|
|
863
903
|
skillex install --all
|
|
864
904
|
skillex update [skill-id...]
|
|
865
905
|
skillex remove <skill-id...> aliases: rm, uninstall
|
|
906
|
+
skillex browse aliases: tui
|
|
866
907
|
skillex source <add|remove|list> [...]
|
|
867
908
|
skillex sync [--adapter id] [--dry-run] [--mode copy]
|
|
868
909
|
skillex run <skill-id:command> [--yes] [--timeout 30]
|
|
869
|
-
skillex ui
|
|
910
|
+
skillex ui open local Web UI
|
|
870
911
|
skillex status [--json]
|
|
871
912
|
skillex doctor [--json]
|
|
872
913
|
skillex config set <key> <value>
|
|
@@ -933,8 +974,9 @@ function parseSyncMode(value) {
|
|
|
933
974
|
function printAutoSyncResult(result) {
|
|
934
975
|
if (!result)
|
|
935
976
|
return;
|
|
936
|
-
const
|
|
937
|
-
|
|
977
|
+
for (const entry of result.syncs) {
|
|
978
|
+
output.info(`Sync: ${entry.adapter} → ${entry.targetPath} [${entry.syncMode}]${entry.changed ? "" : " (no changes)"}`);
|
|
979
|
+
}
|
|
938
980
|
}
|
|
939
981
|
function asOptionalString(value) {
|
|
940
982
|
return typeof value === "string" ? value : undefined;
|