@skill-map/cli 0.15.0 → 0.16.1
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 -0
- package/dist/cli/tutorial/sm-tutorial.md +235 -50
- package/dist/cli.js +133 -42
- package/dist/cli.js.map +1 -1
- package/dist/index.js +8 -5
- package/dist/index.js.map +1 -1
- package/dist/kernel/index.d.ts +23 -7
- package/dist/kernel/index.js +8 -5
- package/dist/kernel/index.js.map +1 -1
- package/dist/ui/{chunk-E2ZFWQW6.js → chunk-3NHGIRPN.js} +3 -3
- package/dist/ui/index.html +1 -1
- package/dist/ui/{main-JI2DDER5.js → main-ZNVY5M44.js} +1 -1
- package/package.json +2 -2
- /package/dist/config/defaults/{skill-mapignore → skillmapignore} +0 -0
package/dist/cli.js
CHANGED
|
@@ -225,7 +225,7 @@ var JOBS_DIRNAME = "jobs";
|
|
|
225
225
|
var PLUGINS_DIRNAME = "plugins";
|
|
226
226
|
var SETTINGS_FILENAME = "settings.json";
|
|
227
227
|
var LOCAL_SETTINGS_FILENAME = "settings.local.json";
|
|
228
|
-
var IGNORE_FILENAME = ".
|
|
228
|
+
var IGNORE_FILENAME = ".skillmapignore";
|
|
229
229
|
var DEFAULT_DB_REL = `${SKILL_MAP_DIR}/${DB_FILENAME}`;
|
|
230
230
|
var GITIGNORE_ENTRIES = [
|
|
231
231
|
`${SKILL_MAP_DIR}/${LOCAL_SETTINGS_FILENAME}`,
|
|
@@ -609,7 +609,7 @@ function loadBundledIgnoreText() {
|
|
|
609
609
|
return loadDefaultsText();
|
|
610
610
|
}
|
|
611
611
|
function readIgnoreFileText(scopeRoot) {
|
|
612
|
-
const path = resolve2(scopeRoot, ".
|
|
612
|
+
const path = resolve2(scopeRoot, ".skillmapignore");
|
|
613
613
|
if (!existsSync2(path)) return void 0;
|
|
614
614
|
try {
|
|
615
615
|
return readFileSync(path, "utf8");
|
|
@@ -626,11 +626,11 @@ function loadDefaultsText() {
|
|
|
626
626
|
function readDefaultsFromDisk() {
|
|
627
627
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
628
628
|
const candidates = [
|
|
629
|
-
resolve2(here, "../../config/defaults/
|
|
629
|
+
resolve2(here, "../../config/defaults/skillmapignore"),
|
|
630
630
|
// src/kernel/scan/ → src/config/defaults/
|
|
631
|
-
resolve2(here, "../config/defaults/
|
|
631
|
+
resolve2(here, "../config/defaults/skillmapignore"),
|
|
632
632
|
// dist/cli.js → dist/config/defaults/ (siblings)
|
|
633
|
-
resolve2(here, "config/defaults/
|
|
633
|
+
resolve2(here, "config/defaults/skillmapignore")
|
|
634
634
|
];
|
|
635
635
|
for (const candidate of candidates) {
|
|
636
636
|
if (existsSync2(candidate)) {
|
|
@@ -2958,7 +2958,7 @@ var AsyncMutex = class {
|
|
|
2958
2958
|
this.#locked = true;
|
|
2959
2959
|
return;
|
|
2960
2960
|
}
|
|
2961
|
-
await new Promise((
|
|
2961
|
+
await new Promise((resolve23) => this.#waiters.push(resolve23));
|
|
2962
2962
|
this.#locked = true;
|
|
2963
2963
|
}
|
|
2964
2964
|
unlock() {
|
|
@@ -7362,7 +7362,7 @@ import { Command as Command8, Option as Option8 } from "clipanion";
|
|
|
7362
7362
|
// package.json
|
|
7363
7363
|
var package_default = {
|
|
7364
7364
|
name: "@skill-map/cli",
|
|
7365
|
-
version: "0.
|
|
7365
|
+
version: "0.16.1",
|
|
7366
7366
|
description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
|
|
7367
7367
|
license: "MIT",
|
|
7368
7368
|
type: "module",
|
|
@@ -7421,7 +7421,7 @@ var package_default = {
|
|
|
7421
7421
|
},
|
|
7422
7422
|
dependencies: {
|
|
7423
7423
|
"@hono/node-server": "2.0.1",
|
|
7424
|
-
"@skill-map/spec": "0.
|
|
7424
|
+
"@skill-map/spec": "0.16.0",
|
|
7425
7425
|
ajv: "8.18.0",
|
|
7426
7426
|
"ajv-formats": "3.0.1",
|
|
7427
7427
|
chokidar: "5.0.0",
|
|
@@ -8850,11 +8850,14 @@ import { resolve as resolve14, relative as relative4, sep as sep2 } from "path";
|
|
|
8850
8850
|
import chokidar from "chokidar";
|
|
8851
8851
|
function createChokidarWatcher(opts) {
|
|
8852
8852
|
const absRoots = opts.roots.map((r) => resolve14(opts.cwd, r));
|
|
8853
|
-
const
|
|
8854
|
-
const
|
|
8853
|
+
const ignoreFilterOpt = opts.ignoreFilter;
|
|
8854
|
+
const getFilter = ignoreFilterOpt === void 0 ? void 0 : typeof ignoreFilterOpt === "function" ? ignoreFilterOpt : () => ignoreFilterOpt;
|
|
8855
|
+
const ignored = getFilter ? (path) => {
|
|
8856
|
+
const filter = getFilter();
|
|
8857
|
+
if (!filter) return false;
|
|
8855
8858
|
const rel = relativePathFromRoots(path, absRoots);
|
|
8856
8859
|
if (rel === null) return false;
|
|
8857
|
-
return
|
|
8860
|
+
return filter.ignores(rel);
|
|
8858
8861
|
} : void 0;
|
|
8859
8862
|
const watcher = chokidar.watch(absRoots, {
|
|
8860
8863
|
ignoreInitial: true,
|
|
@@ -9101,7 +9104,7 @@ var InitCommand = class extends SmCommand {
|
|
|
9101
9104
|
details: `
|
|
9102
9105
|
Project scope (default): creates ./.skill-map/ with settings.json,
|
|
9103
9106
|
settings.local.json, and skill-map.db. Drops a starter
|
|
9104
|
-
.
|
|
9107
|
+
.skillmapignore at the scope root and appends the DB + local
|
|
9105
9108
|
settings to .gitignore.
|
|
9106
9109
|
|
|
9107
9110
|
Global scope (-g): same scaffolding under ~/.skill-map/. No
|
|
@@ -9123,7 +9126,7 @@ var InitCommand = class extends SmCommand {
|
|
|
9123
9126
|
description: "Skip the first scan after scaffolding."
|
|
9124
9127
|
});
|
|
9125
9128
|
force = Option9.Boolean("--force", false, {
|
|
9126
|
-
description: "Overwrite an existing settings.json / settings.local.json / .
|
|
9129
|
+
description: "Overwrite an existing settings.json / settings.local.json / .skillmapignore."
|
|
9127
9130
|
});
|
|
9128
9131
|
strict = Option9.Boolean("--strict", false, {
|
|
9129
9132
|
description: "Strict mode: fail on any layered-loader warning AND promote frontmatter warnings to errors during the first scan. Same flag as sm scan / sm config."
|
|
@@ -11563,6 +11566,7 @@ async function runEphemeralPath(opts, dbPath, strict, loadPrior, runScanWith) {
|
|
|
11563
11566
|
}
|
|
11564
11567
|
|
|
11565
11568
|
// cli/commands/watch.ts
|
|
11569
|
+
import { resolve as resolve19 } from "path";
|
|
11566
11570
|
import { Command as Command16, Option as Option16 } from "clipanion";
|
|
11567
11571
|
|
|
11568
11572
|
// cli/i18n/watch.texts.ts
|
|
@@ -11586,20 +11590,24 @@ async function runWatchLoop(opts) {
|
|
|
11586
11590
|
const { context } = opts;
|
|
11587
11591
|
const runtimeCtx = defaultRuntimeContext();
|
|
11588
11592
|
const { cwd } = runtimeCtx;
|
|
11593
|
+
const loadEffectiveConfig = () => loadConfig({ scope: "project", strict: opts.strict, ...runtimeCtx }).effective;
|
|
11594
|
+
const buildCurrentIgnoreFilter = (cfgIn) => {
|
|
11595
|
+
const text = readIgnoreFileText(cwd);
|
|
11596
|
+
const filterOpts = {};
|
|
11597
|
+
if (cfgIn.ignore.length > 0) filterOpts.configIgnore = cfgIn.ignore;
|
|
11598
|
+
if (text !== void 0) filterOpts.ignoreFileText = text;
|
|
11599
|
+
return buildIgnoreFilter(filterOpts);
|
|
11600
|
+
};
|
|
11589
11601
|
let cfg;
|
|
11590
11602
|
try {
|
|
11591
|
-
cfg =
|
|
11603
|
+
cfg = loadEffectiveConfig();
|
|
11592
11604
|
} catch (err) {
|
|
11593
11605
|
const message = formatErrorMessage(err);
|
|
11594
11606
|
context.stderr.write(tx(WATCH_TEXTS.configLoadFailure, { message }));
|
|
11595
11607
|
return ExitCode.Error;
|
|
11596
11608
|
}
|
|
11597
|
-
|
|
11598
|
-
|
|
11599
|
-
if (cfg.ignore.length > 0) ignoreFilterOpts.configIgnore = cfg.ignore;
|
|
11600
|
-
if (ignoreFileText !== void 0) ignoreFilterOpts.ignoreFileText = ignoreFileText;
|
|
11601
|
-
const ignoreFilter = buildIgnoreFilter(ignoreFilterOpts);
|
|
11602
|
-
const strict = opts.strict || cfg.scan.strict === true;
|
|
11609
|
+
let ignoreFilter = buildCurrentIgnoreFilter(cfg);
|
|
11610
|
+
let strict = opts.strict || cfg.scan.strict === true;
|
|
11603
11611
|
const debounceMs = cfg.scan.watch.debounceMs;
|
|
11604
11612
|
const dbPath = defaultProjectDbPath(runtimeCtx);
|
|
11605
11613
|
const pluginRuntime = opts.noPlugins ? emptyPluginRuntime() : await loadPluginRuntime({ scope: "project" });
|
|
@@ -11708,7 +11716,11 @@ async function runWatchLoop(opts) {
|
|
|
11708
11716
|
roots: opts.roots,
|
|
11709
11717
|
cwd,
|
|
11710
11718
|
debounceMs,
|
|
11711
|
-
|
|
11719
|
+
// Pass a getter, NOT the filter directly: the meta-file watcher
|
|
11720
|
+
// below mutates `ignoreFilter` after a `.skillmapignore` /
|
|
11721
|
+
// `.skill-map/settings.json` edit, and chokidar's `ignored`
|
|
11722
|
+
// predicate must read the current value on every event.
|
|
11723
|
+
ignoreFilter: () => ignoreFilter,
|
|
11712
11724
|
onBatch: async () => {
|
|
11713
11725
|
const next = await handleBatch();
|
|
11714
11726
|
if (next === "stop") {
|
|
@@ -11720,6 +11732,29 @@ async function runWatchLoop(opts) {
|
|
|
11720
11732
|
context.stderr.write(tx(WATCH_TEXTS.watcherError, { message: err.message }));
|
|
11721
11733
|
}
|
|
11722
11734
|
});
|
|
11735
|
+
const metaWatcher = createChokidarWatcher({
|
|
11736
|
+
roots: [
|
|
11737
|
+
resolve19(cwd, ".skillmapignore"),
|
|
11738
|
+
resolve19(cwd, ".skill-map", "settings.json")
|
|
11739
|
+
],
|
|
11740
|
+
cwd,
|
|
11741
|
+
debounceMs,
|
|
11742
|
+
onBatch: async () => {
|
|
11743
|
+
if (stopRequested) return;
|
|
11744
|
+
try {
|
|
11745
|
+
cfg = loadEffectiveConfig();
|
|
11746
|
+
ignoreFilter = buildCurrentIgnoreFilter(cfg);
|
|
11747
|
+
strict = opts.strict || cfg.scan.strict === true;
|
|
11748
|
+
await handleBatch();
|
|
11749
|
+
} catch (err) {
|
|
11750
|
+
const message = formatErrorMessage(err);
|
|
11751
|
+
context.stderr.write(tx(WATCH_TEXTS.batchFailed, { message }));
|
|
11752
|
+
}
|
|
11753
|
+
},
|
|
11754
|
+
onError: (err) => {
|
|
11755
|
+
context.stderr.write(tx(WATCH_TEXTS.watcherError, { message: err.message }));
|
|
11756
|
+
}
|
|
11757
|
+
});
|
|
11723
11758
|
const onSignal = () => {
|
|
11724
11759
|
if (stopRequested) return;
|
|
11725
11760
|
stopRequested = true;
|
|
@@ -11728,12 +11763,14 @@ async function runWatchLoop(opts) {
|
|
|
11728
11763
|
process.once("SIGINT", onSignal);
|
|
11729
11764
|
process.once("SIGTERM", onSignal);
|
|
11730
11765
|
await watcher.ready;
|
|
11766
|
+
await metaWatcher.ready;
|
|
11731
11767
|
if (!opts.json) {
|
|
11732
11768
|
context.stderr.write(WATCH_TEXTS.ready);
|
|
11733
11769
|
}
|
|
11734
11770
|
await stopped;
|
|
11735
11771
|
process.removeListener("SIGINT", onSignal);
|
|
11736
11772
|
process.removeListener("SIGTERM", onSignal);
|
|
11773
|
+
await metaWatcher.close();
|
|
11737
11774
|
await watcher.close();
|
|
11738
11775
|
if (!opts.json) {
|
|
11739
11776
|
context.stderr.write(tx(WATCH_TEXTS.stopped, { batchCount }));
|
|
@@ -11748,7 +11785,7 @@ var WatchCommand = class extends SmCommand {
|
|
|
11748
11785
|
details: `
|
|
11749
11786
|
Long-running version of 'sm scan --changed'. Subscribes to the
|
|
11750
11787
|
given roots via chokidar, applies the same ignore chain
|
|
11751
|
-
(.
|
|
11788
|
+
(.skillmapignore + config.ignore + bundled defaults), and
|
|
11752
11789
|
triggers an incremental scan after each debounced batch.
|
|
11753
11790
|
|
|
11754
11791
|
Default debounce is 300ms; configure via 'scan.watch.debounceMs'
|
|
@@ -13166,6 +13203,9 @@ function buildKindRegistry(providers) {
|
|
|
13166
13203
|
return registry;
|
|
13167
13204
|
}
|
|
13168
13205
|
|
|
13206
|
+
// server/watcher.ts
|
|
13207
|
+
import { resolve as resolve20 } from "path";
|
|
13208
|
+
|
|
13169
13209
|
// server/events.ts
|
|
13170
13210
|
function buildWatcherStartedEvent(data) {
|
|
13171
13211
|
return {
|
|
@@ -13188,18 +13228,24 @@ function buildWatcherErrorEvent(data) {
|
|
|
13188
13228
|
var WATCH_ROOT = ".";
|
|
13189
13229
|
function createWatcherService(opts) {
|
|
13190
13230
|
let chokidarHandle = null;
|
|
13231
|
+
let metaHandle = null;
|
|
13191
13232
|
let stopped = false;
|
|
13192
13233
|
const start = async () => {
|
|
13193
|
-
const
|
|
13234
|
+
const cwd = opts.runtimeContext.cwd;
|
|
13235
|
+
const loadEffectiveConfig = () => loadConfig({
|
|
13194
13236
|
scope: opts.options.scope,
|
|
13195
|
-
cwd
|
|
13237
|
+
cwd,
|
|
13196
13238
|
homedir: opts.runtimeContext.homedir
|
|
13197
13239
|
}).effective;
|
|
13198
|
-
const
|
|
13199
|
-
|
|
13200
|
-
|
|
13201
|
-
|
|
13202
|
-
|
|
13240
|
+
const buildCurrentIgnoreFilter = (cfgIn) => {
|
|
13241
|
+
const ignoreFileText = readIgnoreFileText(cwd);
|
|
13242
|
+
const filterOpts = {};
|
|
13243
|
+
if (cfgIn.ignore.length > 0) filterOpts.configIgnore = cfgIn.ignore;
|
|
13244
|
+
if (ignoreFileText !== void 0) filterOpts.ignoreFileText = ignoreFileText;
|
|
13245
|
+
return buildIgnoreFilter(filterOpts);
|
|
13246
|
+
};
|
|
13247
|
+
let cfg = loadEffectiveConfig();
|
|
13248
|
+
let ignoreFilter = buildCurrentIgnoreFilter(cfg);
|
|
13203
13249
|
const debounceMs = opts.debounceMsOverride ?? cfg.scan.watch.debounceMs;
|
|
13204
13250
|
const pluginRuntime = opts.options.noPlugins ? emptyPluginRuntime() : await loadPluginRuntime({ scope: opts.options.scope });
|
|
13205
13251
|
for (const warn of pluginRuntime.warnings) {
|
|
@@ -13230,7 +13276,12 @@ function createWatcherService(opts) {
|
|
|
13230
13276
|
roots: [WATCH_ROOT],
|
|
13231
13277
|
cwd: opts.runtimeContext.cwd,
|
|
13232
13278
|
debounceMs,
|
|
13233
|
-
|
|
13279
|
+
// Pass a getter, NOT the filter directly: the meta-file watcher
|
|
13280
|
+
// below mutates `ignoreFilter` after a `.skillmapignore` /
|
|
13281
|
+
// `.skill-map/settings.json` edit, and chokidar's `ignored`
|
|
13282
|
+
// predicate must read the current value on every event. See
|
|
13283
|
+
// `kernel/scan/watcher.ts` for the supported shapes.
|
|
13284
|
+
ignoreFilter: () => ignoreFilter,
|
|
13234
13285
|
onBatch: async () => {
|
|
13235
13286
|
if (stopped) return;
|
|
13236
13287
|
try {
|
|
@@ -13257,6 +13308,42 @@ function createWatcherService(opts) {
|
|
|
13257
13308
|
if ("ready" in chokidarHandle && chokidarHandle.ready instanceof Promise) {
|
|
13258
13309
|
await chokidarHandle.ready;
|
|
13259
13310
|
}
|
|
13311
|
+
metaHandle = createChokidarWatcher({
|
|
13312
|
+
roots: [
|
|
13313
|
+
resolve20(cwd, ".skillmapignore"),
|
|
13314
|
+
resolve20(cwd, ".skill-map", "settings.json")
|
|
13315
|
+
],
|
|
13316
|
+
cwd,
|
|
13317
|
+
debounceMs,
|
|
13318
|
+
// No `ignoreFilter` — these specific paths must always be observed,
|
|
13319
|
+
// regardless of any user pattern.
|
|
13320
|
+
onBatch: async () => {
|
|
13321
|
+
if (stopped) return;
|
|
13322
|
+
try {
|
|
13323
|
+
cfg = loadEffectiveConfig();
|
|
13324
|
+
ignoreFilter = buildCurrentIgnoreFilter(cfg);
|
|
13325
|
+
await runOneBatch();
|
|
13326
|
+
} catch (err) {
|
|
13327
|
+
const message = formatErrorMessage(err);
|
|
13328
|
+
log.warn(
|
|
13329
|
+
tx(SERVER_TEXTS.watcherBatchFailed, {
|
|
13330
|
+
message: sanitizeForTerminal(message)
|
|
13331
|
+
})
|
|
13332
|
+
);
|
|
13333
|
+
}
|
|
13334
|
+
},
|
|
13335
|
+
onError: (err) => {
|
|
13336
|
+
const message = err.message;
|
|
13337
|
+
log.warn(
|
|
13338
|
+
tx(SERVER_TEXTS.watcherError, {
|
|
13339
|
+
message: sanitizeForTerminal(message)
|
|
13340
|
+
})
|
|
13341
|
+
);
|
|
13342
|
+
}
|
|
13343
|
+
});
|
|
13344
|
+
if ("ready" in metaHandle && metaHandle.ready instanceof Promise) {
|
|
13345
|
+
await metaHandle.ready;
|
|
13346
|
+
}
|
|
13260
13347
|
await runInitialBatch({ isStopped: () => stopped, runOneBatch });
|
|
13261
13348
|
opts.broadcaster.broadcast(
|
|
13262
13349
|
buildWatcherStartedEvent({ roots: [WATCH_ROOT], debounceMs })
|
|
@@ -13271,19 +13358,23 @@ function createWatcherService(opts) {
|
|
|
13271
13358
|
const stop = async () => {
|
|
13272
13359
|
if (stopped) return;
|
|
13273
13360
|
stopped = true;
|
|
13274
|
-
|
|
13361
|
+
const closeQuietly = async (handle, label) => {
|
|
13362
|
+
if (!handle) return;
|
|
13275
13363
|
try {
|
|
13276
|
-
await
|
|
13364
|
+
await handle.close();
|
|
13277
13365
|
} catch (err) {
|
|
13278
13366
|
const message = err instanceof Error ? err.message : String(err);
|
|
13279
13367
|
log.warn(
|
|
13280
13368
|
tx(SERVER_TEXTS.watcherCloseFailed, {
|
|
13281
|
-
message: sanitizeForTerminal(message)
|
|
13369
|
+
message: sanitizeForTerminal(`${label}: ${message}`)
|
|
13282
13370
|
})
|
|
13283
13371
|
);
|
|
13284
13372
|
}
|
|
13285
|
-
|
|
13286
|
-
|
|
13373
|
+
};
|
|
13374
|
+
await closeQuietly(metaHandle, "meta-watcher");
|
|
13375
|
+
metaHandle = null;
|
|
13376
|
+
await closeQuietly(chokidarHandle, "primary");
|
|
13377
|
+
chokidarHandle = null;
|
|
13287
13378
|
};
|
|
13288
13379
|
return { start, stop };
|
|
13289
13380
|
}
|
|
@@ -13458,7 +13549,7 @@ function validateWatcherDebounce(value) {
|
|
|
13458
13549
|
|
|
13459
13550
|
// server/paths.ts
|
|
13460
13551
|
import { existsSync as existsSync17, statSync as statSync5 } from "fs";
|
|
13461
|
-
import { dirname as dirname9, isAbsolute as isAbsolute5, join as join14, resolve as
|
|
13552
|
+
import { dirname as dirname9, isAbsolute as isAbsolute5, join as join14, resolve as resolve21 } from "path";
|
|
13462
13553
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
13463
13554
|
var DEFAULT_UI_REL = join14("ui", "dist", "ui", "browser");
|
|
13464
13555
|
var PACKAGE_UI_REL = "ui";
|
|
@@ -13469,7 +13560,7 @@ function resolveDefaultUiDist(ctx) {
|
|
|
13469
13560
|
return walkUpForUi(ctx.cwd);
|
|
13470
13561
|
}
|
|
13471
13562
|
function resolveExplicitUiDist(ctx, raw) {
|
|
13472
|
-
return isAbsolute5(raw) ? raw :
|
|
13563
|
+
return isAbsolute5(raw) ? raw : resolve21(ctx.cwd, raw);
|
|
13473
13564
|
}
|
|
13474
13565
|
function isUiBundleDir(path) {
|
|
13475
13566
|
if (!existsSync17(path)) return false;
|
|
@@ -13503,7 +13594,7 @@ function resolvePackageBundledUiFrom(here) {
|
|
|
13503
13594
|
return null;
|
|
13504
13595
|
}
|
|
13505
13596
|
function walkUpForUi(startDir) {
|
|
13506
|
-
let current =
|
|
13597
|
+
let current = resolve21(startDir);
|
|
13507
13598
|
for (let i = 0; i < 64; i++) {
|
|
13508
13599
|
const candidate = join14(current, DEFAULT_UI_REL);
|
|
13509
13600
|
if (isUiBundleDir(candidate)) return candidate;
|
|
@@ -14467,7 +14558,7 @@ var STUB_COMMANDS = [
|
|
|
14467
14558
|
// cli/commands/tutorial.ts
|
|
14468
14559
|
import { existsSync as existsSync19, readFileSync as readFileSync12 } from "fs";
|
|
14469
14560
|
import { writeFile as writeFile2 } from "fs/promises";
|
|
14470
|
-
import { dirname as dirname10, join as join15, resolve as
|
|
14561
|
+
import { dirname as dirname10, join as join15, resolve as resolve22 } from "path";
|
|
14471
14562
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
14472
14563
|
import { Command as Command22, Option as Option22 } from "clipanion";
|
|
14473
14564
|
|
|
@@ -14543,11 +14634,11 @@ function readTutorialFromDisk() {
|
|
|
14543
14634
|
const here = dirname10(fileURLToPath6(import.meta.url));
|
|
14544
14635
|
const candidates = [
|
|
14545
14636
|
// dev: src/cli/commands/ → repo-root .claude/skills/sm-tutorial/SKILL.md
|
|
14546
|
-
|
|
14637
|
+
resolve22(here, "../../../.claude/skills/sm-tutorial/SKILL.md"),
|
|
14547
14638
|
// bundled: dist/cli.js → dist/cli/tutorial/sm-tutorial.md (sibling)
|
|
14548
|
-
|
|
14639
|
+
resolve22(here, "cli/tutorial/sm-tutorial.md"),
|
|
14549
14640
|
// bundled fallback: any-depth → cli/tutorial/sm-tutorial.md
|
|
14550
|
-
|
|
14641
|
+
resolve22(here, "../cli/tutorial/sm-tutorial.md")
|
|
14551
14642
|
];
|
|
14552
14643
|
for (const candidate of candidates) {
|
|
14553
14644
|
if (existsSync19(candidate)) {
|