@skill-map/cli 0.33.0 → 0.34.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/dist/cli.js +534 -219
- package/dist/cli.js.map +1 -1
- package/dist/index.js +510 -97
- package/dist/index.js.map +1 -1
- package/dist/kernel/index.d.ts +19 -0
- package/dist/kernel/index.js +510 -97
- package/dist/kernel/index.js.map +1 -1
- package/dist/ui/{chunk-OKFHCQNJ.js → chunk-MHWM2642.js} +4 -4
- package/dist/ui/index.html +1 -1
- package/dist/ui/{main-H7FURBYT.js → main-4X6AAGKZ.js} +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -531,11 +531,14 @@ var claudeProvider = {
|
|
|
531
531
|
const lower = path.toLowerCase();
|
|
532
532
|
if (lower.startsWith(".claude/agents/")) return "agent";
|
|
533
533
|
if (lower.startsWith(".claude/commands/")) return "command";
|
|
534
|
-
if (
|
|
534
|
+
if (/^\.claude\/skills\/[^/]+\/skill\.md$/.test(lower)) return "skill";
|
|
535
535
|
return null;
|
|
536
536
|
}
|
|
537
537
|
};
|
|
538
538
|
|
|
539
|
+
// plugins/claude/extractors/at-directive/index.ts
|
|
540
|
+
import { posix as pathPosix } from "path";
|
|
541
|
+
|
|
539
542
|
// kernel/util/strip-code-blocks.ts
|
|
540
543
|
var FENCE_RE = /^(?<indent> {0,3})(?<fence>`{3,}|~{3,})/;
|
|
541
544
|
function stripCodeBlocks(input) {
|
|
@@ -609,26 +612,29 @@ var atDirectiveExtractor = {
|
|
|
609
612
|
const seenMentions = /* @__PURE__ */ new Set();
|
|
610
613
|
const seenReferences = /* @__PURE__ */ new Set();
|
|
611
614
|
const body = stripCodeBlocks(ctx.body);
|
|
615
|
+
const sourceDir = pathPosix.dirname(ctx.node.path);
|
|
612
616
|
for (const match of body.matchAll(AT_RE)) {
|
|
613
617
|
const original = match[1];
|
|
614
618
|
const bare = original.slice(1);
|
|
615
|
-
|
|
619
|
+
if (bare.startsWith("/")) continue;
|
|
620
|
+
const isReference = bare.startsWith("./") || bare.startsWith("../") || FILE_EXT_RE.test(bare);
|
|
616
621
|
if (isReference) {
|
|
617
|
-
const target =
|
|
618
|
-
|
|
619
|
-
seenReferences.
|
|
622
|
+
const target = resolveSourceRelative(sourceDir, bare);
|
|
623
|
+
const dedupKey = target.toLowerCase();
|
|
624
|
+
if (seenReferences.has(dedupKey)) continue;
|
|
625
|
+
seenReferences.add(dedupKey);
|
|
620
626
|
ctx.emitLink({
|
|
621
627
|
source: ctx.node.path,
|
|
622
628
|
target,
|
|
623
629
|
kind: "references",
|
|
624
|
-
// 0.85: strong file signal (path prefix `./` / `../`
|
|
630
|
+
// 0.85: strong file signal (path prefix `./` / `../` OR
|
|
625
631
|
// a known file extension on the tail). One degree of inference
|
|
626
632
|
// (the runtime still resolves the path).
|
|
627
633
|
confidence: 0.85,
|
|
628
634
|
sources: [ID],
|
|
629
635
|
trigger: {
|
|
630
636
|
originalTrigger: original,
|
|
631
|
-
normalizedTrigger: target
|
|
637
|
+
normalizedTrigger: target
|
|
632
638
|
}
|
|
633
639
|
});
|
|
634
640
|
continue;
|
|
@@ -654,10 +660,14 @@ var atDirectiveExtractor = {
|
|
|
654
660
|
}
|
|
655
661
|
}
|
|
656
662
|
};
|
|
663
|
+
function resolveSourceRelative(sourceDir, bare) {
|
|
664
|
+
const joined = sourceDir === "." ? bare : `${sourceDir}/${bare}`;
|
|
665
|
+
return pathPosix.normalize(joined);
|
|
666
|
+
}
|
|
657
667
|
|
|
658
668
|
// plugins/claude/extractors/slash/index.ts
|
|
659
669
|
var ID2 = "slash";
|
|
660
|
-
var SLASH_RE = /(?<![A-Za-z0-9_
|
|
670
|
+
var SLASH_RE = /(?<![A-Za-z0-9_/.:?#=&])(\/[a-z0-9][a-z0-9_-]*(?::[a-z0-9][a-z0-9_-]*)?)/gi;
|
|
661
671
|
var slashExtractor = {
|
|
662
672
|
id: ID2,
|
|
663
673
|
pluginId: "claude",
|
|
@@ -809,7 +819,7 @@ var geminiProvider = {
|
|
|
809
819
|
classify(path) {
|
|
810
820
|
const lower = path.toLowerCase();
|
|
811
821
|
if (lower.startsWith(".gemini/agents/")) return "agent";
|
|
812
|
-
if (
|
|
822
|
+
if (/^\.gemini\/skills\/[^/]+\/skill\.md$/.test(lower)) return "skill";
|
|
813
823
|
return null;
|
|
814
824
|
}
|
|
815
825
|
};
|
|
@@ -929,7 +939,7 @@ var agentSkillsProvider = {
|
|
|
929
939
|
}
|
|
930
940
|
},
|
|
931
941
|
classify(path) {
|
|
932
|
-
if (path.toLowerCase()
|
|
942
|
+
if (/^\.agents\/skills\/[^/]+\/skill\.md$/.test(path.toLowerCase())) return "skill";
|
|
933
943
|
return null;
|
|
934
944
|
}
|
|
935
945
|
};
|
|
@@ -1138,7 +1148,7 @@ function lineFor(lineStarts, offset) {
|
|
|
1138
1148
|
}
|
|
1139
1149
|
|
|
1140
1150
|
// plugins/core/extractors/markdown-link/index.ts
|
|
1141
|
-
import { posix as
|
|
1151
|
+
import { posix as pathPosix2 } from "path";
|
|
1142
1152
|
var ID5 = "markdown-link";
|
|
1143
1153
|
var LINK_RE = /(?<!!)\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g;
|
|
1144
1154
|
var URL_SCHEME_RE = /^[a-z][a-z0-9+.-]*:/i;
|
|
@@ -1152,7 +1162,7 @@ var markdownLinkExtractor = {
|
|
|
1152
1162
|
extract(ctx) {
|
|
1153
1163
|
const seen = /* @__PURE__ */ new Set();
|
|
1154
1164
|
const lineStarts = computeLineStarts2(ctx.body);
|
|
1155
|
-
const sourceDir =
|
|
1165
|
+
const sourceDir = pathPosix2.dirname(ctx.node.path);
|
|
1156
1166
|
for (const match of ctx.body.matchAll(LINK_RE)) {
|
|
1157
1167
|
const original = match[2];
|
|
1158
1168
|
const resolved = resolveTarget(sourceDir, original);
|
|
@@ -1184,7 +1194,7 @@ function resolveTarget(sourceDir, raw) {
|
|
|
1184
1194
|
if (URL_SCHEME_RE.test(trimmed)) return null;
|
|
1185
1195
|
if (trimmed.startsWith("/")) return null;
|
|
1186
1196
|
const joined = sourceDir === "." ? trimmed : `${sourceDir}/${trimmed}`;
|
|
1187
|
-
return
|
|
1197
|
+
return pathPosix2.normalize(joined);
|
|
1188
1198
|
}
|
|
1189
1199
|
function computeLineStarts2(body) {
|
|
1190
1200
|
const starts = [0];
|
|
@@ -1417,7 +1427,7 @@ function tooltipFor(status) {
|
|
|
1417
1427
|
}
|
|
1418
1428
|
|
|
1419
1429
|
// plugins/core/analyzers/broken-ref/index.ts
|
|
1420
|
-
import { posix as
|
|
1430
|
+
import { posix as pathPosix3, resolve } from "path";
|
|
1421
1431
|
|
|
1422
1432
|
// plugins/core/analyzers/broken-ref/text.ts
|
|
1423
1433
|
var BROKEN_REF_TEXTS = {
|
|
@@ -1556,8 +1566,8 @@ function indexByNormalizedName(nodes) {
|
|
|
1556
1566
|
return out;
|
|
1557
1567
|
}
|
|
1558
1568
|
function basenameWithoutExt(path) {
|
|
1559
|
-
const base =
|
|
1560
|
-
const ext =
|
|
1569
|
+
const base = pathPosix3.basename(path);
|
|
1570
|
+
const ext = pathPosix3.extname(base);
|
|
1561
1571
|
return ext ? base.slice(0, -ext.length) : base;
|
|
1562
1572
|
}
|
|
1563
1573
|
function indexByBasenameWithoutName(nodes) {
|
|
@@ -3124,7 +3134,7 @@ var UPDATE_CHECK_TEXTS = {
|
|
|
3124
3134
|
// package.json
|
|
3125
3135
|
var package_default = {
|
|
3126
3136
|
name: "@skill-map/cli",
|
|
3127
|
-
version: "0.
|
|
3137
|
+
version: "0.34.0",
|
|
3128
3138
|
description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
|
|
3129
3139
|
license: "MIT",
|
|
3130
3140
|
type: "module",
|
|
@@ -5058,7 +5068,7 @@ var AsyncMutex = class {
|
|
|
5058
5068
|
this.#locked = true;
|
|
5059
5069
|
return;
|
|
5060
5070
|
}
|
|
5061
|
-
await new Promise((
|
|
5071
|
+
await new Promise((resolve39) => this.#waiters.push(resolve39));
|
|
5062
5072
|
this.#locked = true;
|
|
5063
5073
|
}
|
|
5064
5074
|
unlock() {
|
|
@@ -9660,9 +9670,42 @@ function trimRedundantPath(message, primary) {
|
|
|
9660
9670
|
}
|
|
9661
9671
|
|
|
9662
9672
|
// cli/commands/config.ts
|
|
9663
|
-
import { existsSync as
|
|
9673
|
+
import { existsSync as existsSync15 } from "fs";
|
|
9664
9674
|
import { Command as Command4, Option as Option4 } from "clipanion";
|
|
9665
9675
|
|
|
9676
|
+
// core/config/active-provider.ts
|
|
9677
|
+
import { existsSync as existsSync14 } from "fs";
|
|
9678
|
+
import { join as join10 } from "path";
|
|
9679
|
+
var DETECTION_RULES = [
|
|
9680
|
+
{ providerId: "claude", marker: ".claude" },
|
|
9681
|
+
{ providerId: "gemini", marker: ".gemini" },
|
|
9682
|
+
{ providerId: "openai", marker: ".codex" },
|
|
9683
|
+
{ providerId: "openai", marker: "AGENTS.md" },
|
|
9684
|
+
{ providerId: "cursor", marker: ".cursor" }
|
|
9685
|
+
];
|
|
9686
|
+
function resolveActiveProvider(cwd) {
|
|
9687
|
+
const detected = detectProvidersFromFilesystem(cwd);
|
|
9688
|
+
const fromConfig = readConfigValue("activeProvider", { cwd });
|
|
9689
|
+
if (typeof fromConfig === "string" && fromConfig.length > 0) {
|
|
9690
|
+
return { resolved: fromConfig, source: "config", detected };
|
|
9691
|
+
}
|
|
9692
|
+
if (detected.length > 0) {
|
|
9693
|
+
return { resolved: detected[0], source: "autodetect", detected };
|
|
9694
|
+
}
|
|
9695
|
+
return { resolved: null, source: "none", detected };
|
|
9696
|
+
}
|
|
9697
|
+
function detectProvidersFromFilesystem(cwd) {
|
|
9698
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9699
|
+
const out = [];
|
|
9700
|
+
for (const rule of DETECTION_RULES) {
|
|
9701
|
+
if (seen.has(rule.providerId)) continue;
|
|
9702
|
+
if (!existsSync14(join10(cwd, rule.marker))) continue;
|
|
9703
|
+
seen.add(rule.providerId);
|
|
9704
|
+
out.push(rule.providerId);
|
|
9705
|
+
}
|
|
9706
|
+
return out;
|
|
9707
|
+
}
|
|
9708
|
+
|
|
9666
9709
|
// cli/util/path-display.ts
|
|
9667
9710
|
import { isAbsolute as isAbsolute4, relative as pathRelative } from "path";
|
|
9668
9711
|
function relativeIfBelow(path, cwd) {
|
|
@@ -9801,6 +9844,9 @@ function suggestConfigKey(effective, typed, ansi) {
|
|
|
9801
9844
|
hint: ansi.dim(tx(CONFIG_TEXTS.unknownKeySuggestionHint, { suggestions: formatted }))
|
|
9802
9845
|
});
|
|
9803
9846
|
}
|
|
9847
|
+
var KNOWN_DEFAULTLESS_KEY_RESOLVERS = {
|
|
9848
|
+
activeProvider: (cwd) => resolveActiveProvider(cwd).resolved
|
|
9849
|
+
};
|
|
9804
9850
|
function parseCliValue(raw) {
|
|
9805
9851
|
try {
|
|
9806
9852
|
return JSON.parse(raw);
|
|
@@ -9973,6 +10019,11 @@ function formatValueListHuman(value, ansi) {
|
|
|
9973
10019
|
}
|
|
9974
10020
|
return String(value);
|
|
9975
10021
|
}
|
|
10022
|
+
function resolveConfigGetValue(lookupValue, key, cwd) {
|
|
10023
|
+
if (lookupValue !== void 0) return lookupValue;
|
|
10024
|
+
const runtimeResolver = KNOWN_DEFAULTLESS_KEY_RESOLVERS[key];
|
|
10025
|
+
return runtimeResolver ? runtimeResolver(cwd) : void 0;
|
|
10026
|
+
}
|
|
9976
10027
|
var ConfigGetCommand = class extends SmCommand {
|
|
9977
10028
|
static paths = [["config", "get"]];
|
|
9978
10029
|
static usage = Command4.Usage({
|
|
@@ -9987,16 +10038,14 @@ var ConfigGetCommand = class extends SmCommand {
|
|
|
9987
10038
|
strict = Option4.Boolean("--strict", false);
|
|
9988
10039
|
emitElapsed = false;
|
|
9989
10040
|
async run() {
|
|
9990
|
-
const
|
|
9991
|
-
|
|
9992
|
-
this.context.stderr
|
|
9993
|
-
);
|
|
10041
|
+
const ctx = defaultRuntimeContext();
|
|
10042
|
+
const result = tryLoadConfig({ strict: this.strict, ...ctx }, this.context.stderr);
|
|
9994
10043
|
if (!result.ok) return result.exitCode;
|
|
9995
10044
|
const { effective, warnings } = result.loaded;
|
|
9996
10045
|
for (const w of warnings) this.printer.info(w + "\n");
|
|
9997
10046
|
const lookup = safeGetAtPath(effective, this.key, this.context.stderr);
|
|
9998
10047
|
if (!lookup.ok) return lookup.exitCode;
|
|
9999
|
-
const
|
|
10048
|
+
const value = resolveConfigGetValue(lookup.value, this.key, ctx.cwd);
|
|
10000
10049
|
if (value === void 0) {
|
|
10001
10050
|
const ansi = this.ansiFor("stderr");
|
|
10002
10051
|
this.printer.info(
|
|
@@ -10036,10 +10085,8 @@ var ConfigShowCommand = class extends SmCommand {
|
|
|
10036
10085
|
// the value it gates.
|
|
10037
10086
|
// eslint-disable-next-line complexity
|
|
10038
10087
|
async run() {
|
|
10039
|
-
const
|
|
10040
|
-
|
|
10041
|
-
this.context.stderr
|
|
10042
|
-
);
|
|
10088
|
+
const ctx = defaultRuntimeContext();
|
|
10089
|
+
const result = tryLoadConfig({ strict: this.strict, ...ctx }, this.context.stderr);
|
|
10043
10090
|
if (!result.ok) return result.exitCode;
|
|
10044
10091
|
const { effective, sources, warnings } = result.loaded;
|
|
10045
10092
|
for (const w of warnings) this.printer.info(w + "\n");
|
|
@@ -10062,6 +10109,12 @@ var ConfigShowCommand = class extends SmCommand {
|
|
|
10062
10109
|
}
|
|
10063
10110
|
throw err;
|
|
10064
10111
|
}
|
|
10112
|
+
if (value === void 0) {
|
|
10113
|
+
const runtimeResolver = KNOWN_DEFAULTLESS_KEY_RESOLVERS[this.key];
|
|
10114
|
+
if (runtimeResolver) {
|
|
10115
|
+
value = runtimeResolver(ctx.cwd);
|
|
10116
|
+
}
|
|
10117
|
+
}
|
|
10065
10118
|
if (value === void 0) {
|
|
10066
10119
|
this.printer.info(tx(CONFIG_TEXTS.unknownKey, { glyph: errGlyphShow, key: this.key }));
|
|
10067
10120
|
return ExitCode.NotFound;
|
|
@@ -10234,7 +10287,7 @@ var ConfigSetCommand = class extends SmCommand {
|
|
|
10234
10287
|
announceLensSwitch(cwd, ansi) {
|
|
10235
10288
|
const dbPath = resolveDbPath({ db: void 0, cwd });
|
|
10236
10289
|
const okGlyph = ansi.green("\u2713");
|
|
10237
|
-
if (!
|
|
10290
|
+
if (!existsSync15(dbPath)) {
|
|
10238
10291
|
this.printer.info(tx(CONFIG_TEXTS.lensSwitchedNoDb, { glyph: okGlyph }));
|
|
10239
10292
|
return;
|
|
10240
10293
|
}
|
|
@@ -10274,7 +10327,7 @@ var ConfigResetCommand = class extends SmCommand {
|
|
|
10274
10327
|
const path = targetSettingsPath2(target, ctx.cwd);
|
|
10275
10328
|
const ansi = this.ansiFor("stdout");
|
|
10276
10329
|
const okGlyph = ansi.green("\u2713");
|
|
10277
|
-
if (!
|
|
10330
|
+
if (!existsSync15(path)) {
|
|
10278
10331
|
this.printer.data(
|
|
10279
10332
|
tx(CONFIG_TEXTS.unsetNoOverride, {
|
|
10280
10333
|
glyph: okGlyph,
|
|
@@ -10349,16 +10402,16 @@ var CONFIG_COMMANDS = [
|
|
|
10349
10402
|
];
|
|
10350
10403
|
|
|
10351
10404
|
// cli/commands/conformance.ts
|
|
10352
|
-
import { existsSync as
|
|
10405
|
+
import { existsSync as existsSync18, readFileSync as readFileSync15 } from "fs";
|
|
10353
10406
|
import { dirname as dirname12, resolve as resolve21 } from "path";
|
|
10354
10407
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
10355
10408
|
import { Command as Command5, Option as Option5 } from "clipanion";
|
|
10356
10409
|
|
|
10357
10410
|
// conformance/index.ts
|
|
10358
10411
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
10359
|
-
import { cpSync, existsSync as
|
|
10412
|
+
import { cpSync, existsSync as existsSync16, mkdtempSync, readdirSync as readdirSync5, readFileSync as readFileSync14, rmSync, statSync as statSync3 } from "fs";
|
|
10360
10413
|
import { tmpdir } from "os";
|
|
10361
|
-
import { isAbsolute as isAbsolute5, join as
|
|
10414
|
+
import { isAbsolute as isAbsolute5, join as join11, relative as relative3, resolve as resolve19 } from "path";
|
|
10362
10415
|
|
|
10363
10416
|
// conformance/i18n/runner.texts.ts
|
|
10364
10417
|
var CONFORMANCE_RUNNER_TEXTS = {
|
|
@@ -10394,9 +10447,9 @@ function disableEnv(setup) {
|
|
|
10394
10447
|
function runConformanceCase(options) {
|
|
10395
10448
|
const raw = readFileSync14(options.casePath, "utf8");
|
|
10396
10449
|
const c = JSON.parse(raw);
|
|
10397
|
-
const fixturesRoot = options.fixturesRoot ??
|
|
10450
|
+
const fixturesRoot = options.fixturesRoot ?? join11(options.specRoot, "conformance", "fixtures");
|
|
10398
10451
|
const safeId = c.id.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 32);
|
|
10399
|
-
const scope = mkdtempSync(
|
|
10452
|
+
const scope = mkdtempSync(join11(tmpdir(), `sm-conformance-${safeId}-`));
|
|
10400
10453
|
const setupEnv = disableEnv(c.setup);
|
|
10401
10454
|
try {
|
|
10402
10455
|
const priorFailure = runPriorScansSetup(c, options, scope, fixturesRoot, setupEnv);
|
|
@@ -10468,9 +10521,9 @@ function replaceFixture(scope, fixturesRoot, fixture) {
|
|
|
10468
10521
|
assertContained2(fixturesRoot, fixture, "fixture");
|
|
10469
10522
|
for (const entry of readdirSync5(scope)) {
|
|
10470
10523
|
if (entry === KERNEL_SKILL_MAP_DIR) continue;
|
|
10471
|
-
rmSync(
|
|
10524
|
+
rmSync(join11(scope, entry), { recursive: true, force: true });
|
|
10472
10525
|
}
|
|
10473
|
-
const src =
|
|
10526
|
+
const src = join11(fixturesRoot, fixture);
|
|
10474
10527
|
cpSync(src, scope, { recursive: true });
|
|
10475
10528
|
}
|
|
10476
10529
|
function assertContained2(root, rel, label) {
|
|
@@ -10507,7 +10560,7 @@ function evaluateAssertion(a, ctx) {
|
|
|
10507
10560
|
return { ok: false, type: a.type, reason: formatErrorMessage(err) };
|
|
10508
10561
|
}
|
|
10509
10562
|
const abs = resolve19(ctx.scope, a.path);
|
|
10510
|
-
return
|
|
10563
|
+
return existsSync16(abs) ? { ok: true, type: a.type } : {
|
|
10511
10564
|
ok: false,
|
|
10512
10565
|
type: a.type,
|
|
10513
10566
|
reason: tx(CONFORMANCE_RUNNER_TEXTS.fileNotFound, { path: a.path })
|
|
@@ -10520,9 +10573,9 @@ function evaluateAssertion(a, ctx) {
|
|
|
10520
10573
|
} catch (err) {
|
|
10521
10574
|
return { ok: false, type: a.type, reason: formatErrorMessage(err) };
|
|
10522
10575
|
}
|
|
10523
|
-
const fixturePath =
|
|
10576
|
+
const fixturePath = join11(ctx.fixturesRoot, a.fixture);
|
|
10524
10577
|
const targetPath = resolve19(ctx.scope, a.path);
|
|
10525
|
-
if (!
|
|
10578
|
+
if (!existsSync16(targetPath)) {
|
|
10526
10579
|
return {
|
|
10527
10580
|
ok: false,
|
|
10528
10581
|
type: a.type,
|
|
@@ -10703,7 +10756,7 @@ var CONFORMANCE_TEXTS = {
|
|
|
10703
10756
|
};
|
|
10704
10757
|
|
|
10705
10758
|
// cli/util/conformance-scopes.ts
|
|
10706
|
-
import { existsSync as
|
|
10759
|
+
import { existsSync as existsSync17, readdirSync as readdirSync6, statSync as statSync4 } from "fs";
|
|
10707
10760
|
import { dirname as dirname11, resolve as resolve20 } from "path";
|
|
10708
10761
|
import { createRequire as createRequire6 } from "module";
|
|
10709
10762
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
@@ -10723,7 +10776,7 @@ function resolveCliWorkspaceRoot() {
|
|
|
10723
10776
|
let cursor = here;
|
|
10724
10777
|
for (let depth = 0; depth < 6; depth += 1) {
|
|
10725
10778
|
const candidate = resolve20(cursor, "plugins");
|
|
10726
|
-
if (
|
|
10779
|
+
if (existsSync17(candidate) && statSync4(candidate).isDirectory()) {
|
|
10727
10780
|
return cursor;
|
|
10728
10781
|
}
|
|
10729
10782
|
const parent = dirname11(cursor);
|
|
@@ -10743,7 +10796,7 @@ function collectProviderScopes(specRoot) {
|
|
|
10743
10796
|
return out;
|
|
10744
10797
|
}
|
|
10745
10798
|
const pluginsRoot = resolve20(workspaceRoot, "plugins");
|
|
10746
|
-
if (!
|
|
10799
|
+
if (!existsSync17(pluginsRoot)) return out;
|
|
10747
10800
|
for (const bundleEntry of readdirSync6(pluginsRoot)) {
|
|
10748
10801
|
const bundleDir = resolve20(pluginsRoot, bundleEntry);
|
|
10749
10802
|
if (!isDir(bundleDir)) continue;
|
|
@@ -10755,7 +10808,7 @@ function collectProviderScopes(specRoot) {
|
|
|
10755
10808
|
}
|
|
10756
10809
|
function isDir(path) {
|
|
10757
10810
|
try {
|
|
10758
|
-
return
|
|
10811
|
+
return existsSync17(path) && statSync4(path).isDirectory();
|
|
10759
10812
|
} catch {
|
|
10760
10813
|
return false;
|
|
10761
10814
|
}
|
|
@@ -10765,10 +10818,10 @@ function collectBundleProviderScopes(providersRoot, specRoot, out) {
|
|
|
10765
10818
|
const providerDir = resolve20(providersRoot, entry);
|
|
10766
10819
|
if (!isDir(providerDir)) continue;
|
|
10767
10820
|
const conformanceDir = resolve20(providerDir, "conformance");
|
|
10768
|
-
if (!
|
|
10821
|
+
if (!existsSync17(conformanceDir)) continue;
|
|
10769
10822
|
const casesDir = resolve20(conformanceDir, "cases");
|
|
10770
10823
|
const fixturesDir = resolve20(conformanceDir, "fixtures");
|
|
10771
|
-
if (!
|
|
10824
|
+
if (!existsSync17(casesDir) || !existsSync17(fixturesDir)) continue;
|
|
10772
10825
|
out.push({
|
|
10773
10826
|
id: `provider:${entry}`,
|
|
10774
10827
|
kind: "provider",
|
|
@@ -10806,7 +10859,7 @@ function selectConformanceScopes(scope) {
|
|
|
10806
10859
|
return [match];
|
|
10807
10860
|
}
|
|
10808
10861
|
function listCaseFiles(scope) {
|
|
10809
|
-
if (!
|
|
10862
|
+
if (!existsSync17(scope.casesDir)) return [];
|
|
10810
10863
|
return readdirSync6(scope.casesDir).filter((entry) => entry.endsWith(".json")).sort().map((entry) => resolve20(scope.casesDir, entry));
|
|
10811
10864
|
}
|
|
10812
10865
|
|
|
@@ -10825,7 +10878,7 @@ function resolveBinary() {
|
|
|
10825
10878
|
let cursor = here;
|
|
10826
10879
|
for (let depth = 0; depth < 6; depth += 1) {
|
|
10827
10880
|
const candidate = resolve21(cursor, "bin", "sm.js");
|
|
10828
|
-
if (
|
|
10881
|
+
if (existsSync18(candidate)) return candidate;
|
|
10829
10882
|
const parent = dirname12(cursor);
|
|
10830
10883
|
if (parent === cursor) break;
|
|
10831
10884
|
cursor = parent;
|
|
@@ -10891,7 +10944,7 @@ var ConformanceRunCommand = class extends SmCommand {
|
|
|
10891
10944
|
return ExitCode.Error;
|
|
10892
10945
|
}
|
|
10893
10946
|
const binary = resolveBinary();
|
|
10894
|
-
if (!
|
|
10947
|
+
if (!existsSync18(binary)) {
|
|
10895
10948
|
if (this.json) {
|
|
10896
10949
|
this.#emitJsonError(
|
|
10897
10950
|
"internal",
|
|
@@ -11083,7 +11136,7 @@ function writeStreamSnippet(stream, header, text) {
|
|
|
11083
11136
|
var CONFORMANCE_COMMANDS = [ConformanceRunCommand];
|
|
11084
11137
|
|
|
11085
11138
|
// cli/commands/db/backup.ts
|
|
11086
|
-
import { dirname as dirname13, join as
|
|
11139
|
+
import { dirname as dirname13, join as join12, resolve as resolve22 } from "path";
|
|
11087
11140
|
import { Command as Command6, Option as Option6 } from "clipanion";
|
|
11088
11141
|
|
|
11089
11142
|
// cli/i18n/db.texts.ts
|
|
@@ -11169,7 +11222,7 @@ var DbBackupCommand = class extends SmCommand {
|
|
|
11169
11222
|
const exit = requireDbOrExit(path, this.context.stderr);
|
|
11170
11223
|
if (exit !== null) return exit;
|
|
11171
11224
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
11172
|
-
const outPath = this.out ? resolve22(this.out) :
|
|
11225
|
+
const outPath = this.out ? resolve22(this.out) : join12(dirname13(path), "backups", `${ts}.db`);
|
|
11173
11226
|
await withSqlite({ databasePath: path, autoMigrate: false }, async (storage) => {
|
|
11174
11227
|
storage.migrations.writeBackup(outPath);
|
|
11175
11228
|
});
|
|
@@ -12823,7 +12876,7 @@ function registeredVerbPaths(cli2) {
|
|
|
12823
12876
|
// cli/commands/hooks.ts
|
|
12824
12877
|
import {
|
|
12825
12878
|
chmodSync,
|
|
12826
|
-
existsSync as
|
|
12879
|
+
existsSync as existsSync19,
|
|
12827
12880
|
mkdirSync as mkdirSync5,
|
|
12828
12881
|
readFileSync as readFileSync17,
|
|
12829
12882
|
statSync as statSync5,
|
|
@@ -12922,7 +12975,7 @@ var HooksInstallCommand = class extends SmCommand {
|
|
|
12922
12975
|
}
|
|
12923
12976
|
const hooksDir = resolve26(repoRoot, ".git", "hooks");
|
|
12924
12977
|
const hookPath = resolve26(hooksDir, "pre-commit");
|
|
12925
|
-
const existing =
|
|
12978
|
+
const existing = existsSync19(hookPath) ? readFileSync17(hookPath, "utf8") : null;
|
|
12926
12979
|
const planned2 = computePlannedHookContent(existing);
|
|
12927
12980
|
if (planned2.kind === "already-installed") {
|
|
12928
12981
|
this.printer.info(tx(HOOKS_TEXTS.alreadyInstalled, { glyph: okGlyph, hookPath }));
|
|
@@ -12948,7 +13001,7 @@ var HooksInstallCommand = class extends SmCommand {
|
|
|
12948
13001
|
return ExitCode.Ok;
|
|
12949
13002
|
}
|
|
12950
13003
|
try {
|
|
12951
|
-
if (!
|
|
13004
|
+
if (!existsSync19(hooksDir)) mkdirSync5(hooksDir, { recursive: true });
|
|
12952
13005
|
writeFileSync2(hookPath, planned2.content, { encoding: "utf8" });
|
|
12953
13006
|
ensureExecutableBit(hookPath);
|
|
12954
13007
|
} catch (err) {
|
|
@@ -12979,7 +13032,7 @@ var HooksInstallCommand = class extends SmCommand {
|
|
|
12979
13032
|
function findGitRepoRoot(cwd) {
|
|
12980
13033
|
let current = cwd;
|
|
12981
13034
|
while (true) {
|
|
12982
|
-
if (
|
|
13035
|
+
if (existsSync19(resolve26(current, ".git"))) return current;
|
|
12983
13036
|
const parent = dirname16(current);
|
|
12984
13037
|
if (parent === current) return null;
|
|
12985
13038
|
current = parent;
|
|
@@ -13001,11 +13054,12 @@ var HOOKS_COMMANDS = [HooksInstallCommand];
|
|
|
13001
13054
|
|
|
13002
13055
|
// cli/commands/init.ts
|
|
13003
13056
|
import { mkdir as mkdir3, readFile as readFile2, writeFile } from "fs/promises";
|
|
13004
|
-
import { join as
|
|
13057
|
+
import { join as join17 } from "path";
|
|
13005
13058
|
import { Command as Command17, Option as Option16 } from "clipanion";
|
|
13006
13059
|
|
|
13007
13060
|
// kernel/orchestrator/index.ts
|
|
13008
|
-
import { existsSync as
|
|
13061
|
+
import { existsSync as existsSync22, statSync as statSync7 } from "fs";
|
|
13062
|
+
import { isAbsolute as isAbsolute7, resolve as resolve27 } from "path";
|
|
13009
13063
|
import { Tiktoken as Tiktoken2 } from "js-tiktoken/lite";
|
|
13010
13064
|
import cl100k_base from "js-tiktoken/ranks/cl100k_base";
|
|
13011
13065
|
|
|
@@ -13332,6 +13386,9 @@ function dedupeLinks(links) {
|
|
|
13332
13386
|
existing.sources = [...existing.sources, src];
|
|
13333
13387
|
}
|
|
13334
13388
|
}
|
|
13389
|
+
if (link2.confidence > existing.confidence) {
|
|
13390
|
+
existing.confidence = link2.confidence;
|
|
13391
|
+
}
|
|
13335
13392
|
continue;
|
|
13336
13393
|
}
|
|
13337
13394
|
out.set(key, link2);
|
|
@@ -13526,7 +13583,7 @@ function originatingNodeOf(link2, priorNodePaths) {
|
|
|
13526
13583
|
function computeCacheDecision(opts) {
|
|
13527
13584
|
const applicableExtractors = opts.extractors.filter((ex) => {
|
|
13528
13585
|
if (!matchesKindPrecondition(ex, opts.kind)) return false;
|
|
13529
|
-
if (!matchesProviderPrecondition(ex, opts.provider)) return false;
|
|
13586
|
+
if (!matchesProviderPrecondition(ex, opts.provider, opts.activeProvider)) return false;
|
|
13530
13587
|
return true;
|
|
13531
13588
|
});
|
|
13532
13589
|
const applicableQualifiedIds = new Set(
|
|
@@ -13550,10 +13607,12 @@ function matchesKindPrecondition(ex, kind) {
|
|
|
13550
13607
|
return kindOnly === kind;
|
|
13551
13608
|
});
|
|
13552
13609
|
}
|
|
13553
|
-
function matchesProviderPrecondition(ex,
|
|
13610
|
+
function matchesProviderPrecondition(ex, nodeProvider, activeProvider) {
|
|
13554
13611
|
const providers = ex.precondition?.provider;
|
|
13555
13612
|
if (!providers || providers.length === 0) return true;
|
|
13556
|
-
|
|
13613
|
+
if (!providers.includes(nodeProvider)) return false;
|
|
13614
|
+
if (activeProvider === null) return false;
|
|
13615
|
+
return providers.includes(activeProvider);
|
|
13557
13616
|
}
|
|
13558
13617
|
function splitLegacy(applicableExtractors, applicableQualifiedIds, nodeHashCacheEligible) {
|
|
13559
13618
|
const cachedQualifiedIds = /* @__PURE__ */ new Set();
|
|
@@ -13654,6 +13713,65 @@ function classifyLinkSource(source, shortIdToQualified, cachedQualifiedIds, appl
|
|
|
13654
13713
|
return "obsolete";
|
|
13655
13714
|
}
|
|
13656
13715
|
|
|
13716
|
+
// kernel/orchestrator/lift-mention-confidence.ts
|
|
13717
|
+
function liftMentionConfidence(links, nodes) {
|
|
13718
|
+
if (!links.some((l) => l.kind === "mentions")) return;
|
|
13719
|
+
const byPath3 = /* @__PURE__ */ new Set();
|
|
13720
|
+
for (const node of nodes) byPath3.add(node.path);
|
|
13721
|
+
const byNormalizedName = indexByNormalizedName2(nodes);
|
|
13722
|
+
for (const link2 of links) {
|
|
13723
|
+
if (link2.kind !== "mentions") continue;
|
|
13724
|
+
if (isResolved2(link2, byPath3, byNormalizedName)) {
|
|
13725
|
+
link2.confidence = 1;
|
|
13726
|
+
}
|
|
13727
|
+
}
|
|
13728
|
+
}
|
|
13729
|
+
function isResolved2(link2, byPath3, byNormalizedName) {
|
|
13730
|
+
const normalized = link2.trigger?.normalizedTrigger;
|
|
13731
|
+
if (normalized) {
|
|
13732
|
+
const withoutSigil = normalized.replace(/^[/@]/, "").trim();
|
|
13733
|
+
if (byNormalizedName.has(withoutSigil)) return true;
|
|
13734
|
+
}
|
|
13735
|
+
if (byPath3.has(link2.target)) return true;
|
|
13736
|
+
return false;
|
|
13737
|
+
}
|
|
13738
|
+
function indexByNormalizedName2(nodes) {
|
|
13739
|
+
const out = /* @__PURE__ */ new Map();
|
|
13740
|
+
for (const node of nodes) {
|
|
13741
|
+
const raw = node.frontmatter?.["name"];
|
|
13742
|
+
const name = typeof raw === "string" ? raw : "";
|
|
13743
|
+
if (!name) continue;
|
|
13744
|
+
out.set(normalizeTrigger(name), true);
|
|
13745
|
+
}
|
|
13746
|
+
return out;
|
|
13747
|
+
}
|
|
13748
|
+
|
|
13749
|
+
// kernel/orchestrator/post-walk-transforms.ts
|
|
13750
|
+
var POST_WALK_TRANSFORMS = [
|
|
13751
|
+
{
|
|
13752
|
+
id: "dedupe-links",
|
|
13753
|
+
description: "Collapse identical (source, target, kind, normalizedTrigger) edges across extractors; union sources[] and pick max confidence on merge.",
|
|
13754
|
+
run(links) {
|
|
13755
|
+
return dedupeLinks(links);
|
|
13756
|
+
}
|
|
13757
|
+
},
|
|
13758
|
+
{
|
|
13759
|
+
id: "lift-mention-confidence",
|
|
13760
|
+
description: "Bump resolved `mentions` links to confidence 1.0 once the full node graph is known (post-merge polish).",
|
|
13761
|
+
run(links, nodes) {
|
|
13762
|
+
liftMentionConfidence(links, nodes);
|
|
13763
|
+
}
|
|
13764
|
+
}
|
|
13765
|
+
];
|
|
13766
|
+
function applyPostWalkTransforms(links, nodes, transforms = POST_WALK_TRANSFORMS) {
|
|
13767
|
+
let current = links;
|
|
13768
|
+
for (const transform of transforms) {
|
|
13769
|
+
const next = transform.run(current, nodes);
|
|
13770
|
+
if (next) current = next;
|
|
13771
|
+
}
|
|
13772
|
+
return current;
|
|
13773
|
+
}
|
|
13774
|
+
|
|
13657
13775
|
// kernel/orchestrator/renames.ts
|
|
13658
13776
|
function findHighConfidenceRenames(opts) {
|
|
13659
13777
|
const ops = [];
|
|
@@ -13797,8 +13915,8 @@ function computeDriftStatus(args2) {
|
|
|
13797
13915
|
}
|
|
13798
13916
|
|
|
13799
13917
|
// kernel/sidecar/discover-orphans.ts
|
|
13800
|
-
import { existsSync as
|
|
13801
|
-
import { join as
|
|
13918
|
+
import { existsSync as existsSync20, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
|
|
13919
|
+
import { join as join13, relative as relative4, sep as sep3 } from "path";
|
|
13802
13920
|
function discoverOrphanSidecars(roots, shouldSkip) {
|
|
13803
13921
|
const out = [];
|
|
13804
13922
|
for (const root of roots) {
|
|
@@ -13814,7 +13932,7 @@ function walk(root, current, shouldSkip, out) {
|
|
|
13814
13932
|
return;
|
|
13815
13933
|
}
|
|
13816
13934
|
for (const entry of entries) {
|
|
13817
|
-
const full =
|
|
13935
|
+
const full = join13(current, entry.name);
|
|
13818
13936
|
const rel = relative4(root, full).split(sep3).join("/");
|
|
13819
13937
|
if (shouldSkip(rel)) continue;
|
|
13820
13938
|
if (entry.isSymbolicLink()) continue;
|
|
@@ -13825,7 +13943,7 @@ function walk(root, current, shouldSkip, out) {
|
|
|
13825
13943
|
if (!entry.isFile()) continue;
|
|
13826
13944
|
if (!entry.name.endsWith(".sm")) continue;
|
|
13827
13945
|
const expectedMd = `${full.slice(0, -".sm".length)}.md`;
|
|
13828
|
-
if (
|
|
13946
|
+
if (existsSync20(expectedMd) && safeIsFile(expectedMd)) continue;
|
|
13829
13947
|
out.push({ sidecarPath: full, relativePath: rel, expectedMdPath: expectedMd });
|
|
13830
13948
|
}
|
|
13831
13949
|
}
|
|
@@ -13839,7 +13957,7 @@ function safeIsFile(path) {
|
|
|
13839
13957
|
|
|
13840
13958
|
// kernel/orchestrator/node-build.ts
|
|
13841
13959
|
import { createHash } from "crypto";
|
|
13842
|
-
import { existsSync as
|
|
13960
|
+
import { existsSync as existsSync21 } from "fs";
|
|
13843
13961
|
import { isAbsolute as isAbsolute6, resolve as resolvePath } from "path";
|
|
13844
13962
|
import "js-tiktoken/lite";
|
|
13845
13963
|
import yaml4 from "js-yaml";
|
|
@@ -14003,11 +14121,11 @@ function resolveSidecarOverlay(relativePath2, nodePathForIssue, roots, liveBodyH
|
|
|
14003
14121
|
}
|
|
14004
14122
|
function resolveAbsoluteMdPath(relativePath2, roots) {
|
|
14005
14123
|
if (isAbsolute6(relativePath2)) {
|
|
14006
|
-
return
|
|
14124
|
+
return existsSync21(relativePath2) ? relativePath2 : null;
|
|
14007
14125
|
}
|
|
14008
14126
|
for (const root of roots) {
|
|
14009
14127
|
const candidate = resolvePath(root, relativePath2);
|
|
14010
|
-
if (
|
|
14128
|
+
if (existsSync21(candidate)) return candidate;
|
|
14011
14129
|
}
|
|
14012
14130
|
return null;
|
|
14013
14131
|
}
|
|
@@ -14144,6 +14262,7 @@ async function processRawNode(raw, provider, wctx, accum, claimedPaths, nextInde
|
|
|
14144
14262
|
extractors: wctx.opts.extractors,
|
|
14145
14263
|
kind,
|
|
14146
14264
|
provider: provider.id,
|
|
14265
|
+
activeProvider: wctx.opts.activeProvider,
|
|
14147
14266
|
nodePath: raw.path,
|
|
14148
14267
|
bodyHash,
|
|
14149
14268
|
sidecarAnnotationsHash,
|
|
@@ -14357,9 +14476,10 @@ async function runScanInternal(_kernel, options) {
|
|
|
14357
14476
|
priorIndex: setup.priorIndex,
|
|
14358
14477
|
priorExtractorRuns: setup.priorExtractorRuns,
|
|
14359
14478
|
providerFrontmatter: setup.providerFrontmatter,
|
|
14360
|
-
pluginStores: options.pluginStores
|
|
14479
|
+
pluginStores: options.pluginStores,
|
|
14480
|
+
activeProvider: resolveActiveProviderOption(options.activeProvider, options.roots)
|
|
14361
14481
|
});
|
|
14362
|
-
walked.internalLinks =
|
|
14482
|
+
walked.internalLinks = applyPostWalkTransforms(walked.internalLinks, walked.nodes);
|
|
14363
14483
|
recomputeLinkCounts(walked.nodes, walked.internalLinks);
|
|
14364
14484
|
recomputeExternalRefsCount(walked.nodes, walked.externalLinks, walked.cachedPaths);
|
|
14365
14485
|
await dispatchExtractorCompleted(exts.extractors, emitter, hookDispatcher);
|
|
@@ -14475,17 +14595,27 @@ function validateRoots(roots) {
|
|
|
14475
14595
|
throw new Error(ORCHESTRATOR_TEXTS.runScanRootEmptyArray);
|
|
14476
14596
|
}
|
|
14477
14597
|
for (const root of roots) {
|
|
14478
|
-
if (!
|
|
14598
|
+
if (!existsSync22(root) || !statSync7(root).isDirectory()) {
|
|
14479
14599
|
throw new Error(tx(ORCHESTRATOR_TEXTS.runScanRootMissing, { root }));
|
|
14480
14600
|
}
|
|
14481
14601
|
}
|
|
14482
14602
|
}
|
|
14603
|
+
function resolveActiveProviderOption(optionValue, roots) {
|
|
14604
|
+
if (optionValue !== void 0) return optionValue;
|
|
14605
|
+
for (const root of roots) {
|
|
14606
|
+
const absRoot = isAbsolute7(root) ? root : resolve27(root);
|
|
14607
|
+
if (!existsSync22(absRoot)) continue;
|
|
14608
|
+
const detected = resolveActiveProvider(absRoot).resolved;
|
|
14609
|
+
if (detected !== null) return detected;
|
|
14610
|
+
}
|
|
14611
|
+
return null;
|
|
14612
|
+
}
|
|
14483
14613
|
|
|
14484
14614
|
// kernel/scan/watcher.ts
|
|
14485
|
-
import { resolve as
|
|
14615
|
+
import { resolve as resolve28, relative as relative5, sep as sep4 } from "path";
|
|
14486
14616
|
import chokidar from "chokidar";
|
|
14487
14617
|
function createChokidarWatcher(opts) {
|
|
14488
|
-
const absRoots = opts.roots.map((r) =>
|
|
14618
|
+
const absRoots = opts.roots.map((r) => resolve28(opts.cwd, r));
|
|
14489
14619
|
const ignoreFilterOpt = opts.ignoreFilter;
|
|
14490
14620
|
const getFilter = ignoreFilterOpt === void 0 ? void 0 : typeof ignoreFilterOpt === "function" ? ignoreFilterOpt : () => ignoreFilterOpt;
|
|
14491
14621
|
const ignored = getFilter ? (path) => {
|
|
@@ -14704,7 +14834,7 @@ function createKernel() {
|
|
|
14704
14834
|
|
|
14705
14835
|
// kernel/jobs/orphan-files.ts
|
|
14706
14836
|
import { readdirSync as readdirSync8, statSync as statSync8 } from "fs";
|
|
14707
|
-
import { join as
|
|
14837
|
+
import { join as join14, resolve as resolve29 } from "path";
|
|
14708
14838
|
function findOrphanJobFiles(jobsDir, referencedPaths) {
|
|
14709
14839
|
let entries;
|
|
14710
14840
|
try {
|
|
@@ -14722,7 +14852,7 @@ function findOrphanJobFiles(jobsDir, referencedPaths) {
|
|
|
14722
14852
|
if (!entry.isFile()) continue;
|
|
14723
14853
|
const name = entry.name;
|
|
14724
14854
|
if (!name.endsWith(".md")) continue;
|
|
14725
|
-
const abs =
|
|
14855
|
+
const abs = resolve29(join14(jobsDir, name));
|
|
14726
14856
|
if (!referencedPaths.has(abs)) orphans.push(abs);
|
|
14727
14857
|
}
|
|
14728
14858
|
orphans.sort();
|
|
@@ -14786,7 +14916,48 @@ var SCAN_RUNNER_TEXTS = {
|
|
|
14786
14916
|
* does not exist on disk. Surfaced once per missing root so the
|
|
14787
14917
|
* operator notices a typo without the walker silently swallowing it.
|
|
14788
14918
|
*/
|
|
14789
|
-
referenceWalkMissingRoot: 'scan.referencePaths: configured path "{{path}}" does not exist; skipped.'
|
|
14919
|
+
referenceWalkMissingRoot: 'scan.referencePaths: configured path "{{path}}" does not exist; skipped.',
|
|
14920
|
+
/**
|
|
14921
|
+
* Active-provider bootstrap: filesystem auto-detect found no
|
|
14922
|
+
* markers (`.claude/`, `.gemini/`, `.codex/`, `AGENTS.md`, `.cursor/`)
|
|
14923
|
+
* anywhere under cwd or the effective scan roots. Plain-markdown
|
|
14924
|
+
* projects keep scanning fine; provider-specific extractors silently
|
|
14925
|
+
* no-op for this scan.
|
|
14926
|
+
*/
|
|
14927
|
+
activeProviderNoMarkerWarning: "No provider markers detected (.claude/, .gemini/, .codex/, AGENTS.md, .cursor/). Scanning as universal markdown only; provider-specific link types (e.g. claude @-directives, /-commands) will not appear in the graph. Set `activeProvider` in .skill-map/settings.json or install a provider plugin to enable them.",
|
|
14928
|
+
/**
|
|
14929
|
+
* Active-provider bootstrap: filesystem auto-detect found exactly
|
|
14930
|
+
* one marker and persisted the detected id to project settings.
|
|
14931
|
+
*/
|
|
14932
|
+
activeProviderAutodetected: "Auto-detected activeProvider = {{id}} from filesystem markers; persisted to .skill-map/settings.json.",
|
|
14933
|
+
/**
|
|
14934
|
+
* Active-provider bootstrap: persistence of the auto-detected id
|
|
14935
|
+
* failed (permission, disk full, etc). Non-fatal; the scan
|
|
14936
|
+
* continues with the value in memory for this run.
|
|
14937
|
+
*/
|
|
14938
|
+
activeProviderPersistFailed: "Auto-detected activeProvider = {{id}}, but persisting to .skill-map/settings.json failed: {{message}}. Run `sm config set activeProvider {{id}}` manually to make the choice sticky.",
|
|
14939
|
+
/**
|
|
14940
|
+
* Active-provider bootstrap: ambiguous detection (2+ markers
|
|
14941
|
+
* present), interactive prompt header. Followed by one
|
|
14942
|
+
* `activeProviderPromptOption` per detected provider id.
|
|
14943
|
+
*/
|
|
14944
|
+
activeProviderPromptHeader: "Multiple provider markers detected. Pick the active lens for this project:",
|
|
14945
|
+
activeProviderPromptOption: " {{index}}) {{id}}",
|
|
14946
|
+
activeProviderPromptInput: "Enter the number or provider id: ",
|
|
14947
|
+
/**
|
|
14948
|
+
* Active-provider bootstrap: ambiguous detection under `--yes`. The
|
|
14949
|
+
* caller exits non-zero; this message names the candidates and how
|
|
14950
|
+
* to resolve.
|
|
14951
|
+
*/
|
|
14952
|
+
activeProviderAmbiguousUnderYes: "Multiple provider markers detected ({{candidates}}) and --yes is set. Set the lens explicitly with `sm config set activeProvider <id>` and re-run.",
|
|
14953
|
+
/**
|
|
14954
|
+
* Active lens points at a bundle the operator has disabled (via
|
|
14955
|
+
* `sm plugins disable <id>` or the Settings UI). Classification keeps
|
|
14956
|
+
* running because it's provider-driven, but the lens-gated extractors
|
|
14957
|
+
* for the disabled bundle silently no-op. Without this warning the
|
|
14958
|
+
* graph quietly differs from what the lens implies.
|
|
14959
|
+
*/
|
|
14960
|
+
activeProviderBundleDisabledWarning: 'activeProvider = "{{id}}" but the "{{id}}" plugin bundle is currently disabled; provider-specific extractors will not run. Re-enable the bundle with `sm plugins enable {{id}}` or switch the lens with `sm config set activeProvider <id>` to silence this warning.'
|
|
14790
14961
|
};
|
|
14791
14962
|
|
|
14792
14963
|
// core/runtime/scan-roots.ts
|
|
@@ -14800,7 +14971,7 @@ function resolveScanRoots(inputs) {
|
|
|
14800
14971
|
// core/runtime/reference-paths-walker.ts
|
|
14801
14972
|
import { readdirSync as readdirSync9, statSync as statSync9 } from "fs";
|
|
14802
14973
|
import { homedir as osHomedir2 } from "os";
|
|
14803
|
-
import { isAbsolute as
|
|
14974
|
+
import { isAbsolute as isAbsolute8, join as join15, resolve as resolve30 } from "path";
|
|
14804
14975
|
var REFERENCE_WALK_MAX_FILES = 5e4;
|
|
14805
14976
|
var SKIPPED_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
14806
14977
|
"node_modules",
|
|
@@ -14808,10 +14979,10 @@ var SKIPPED_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
|
14808
14979
|
SKILL_MAP_DIR
|
|
14809
14980
|
]);
|
|
14810
14981
|
function resolveScanPath(raw, cwd) {
|
|
14811
|
-
if (raw.startsWith("~/")) return
|
|
14812
|
-
if (raw === "~") return
|
|
14813
|
-
if (
|
|
14814
|
-
return
|
|
14982
|
+
if (raw.startsWith("~/")) return resolve30(join15(osHomedir2(), raw.slice(2)));
|
|
14983
|
+
if (raw === "~") return resolve30(osHomedir2());
|
|
14984
|
+
if (isAbsolute8(raw)) return resolve30(raw);
|
|
14985
|
+
return resolve30(cwd, raw);
|
|
14815
14986
|
}
|
|
14816
14987
|
function walkReferencePaths(rawRoots, cwd) {
|
|
14817
14988
|
const paths = /* @__PURE__ */ new Set();
|
|
@@ -14840,7 +15011,7 @@ function walkInto(dir, out) {
|
|
|
14840
15011
|
for (const entry of entries) {
|
|
14841
15012
|
if (out.size >= REFERENCE_WALK_MAX_FILES) return true;
|
|
14842
15013
|
if (entry.isSymbolicLink()) continue;
|
|
14843
|
-
const full =
|
|
15014
|
+
const full = join15(dir, entry.name);
|
|
14844
15015
|
if (entry.isDirectory()) {
|
|
14845
15016
|
if (SKIPPED_DIR_NAMES.has(entry.name)) continue;
|
|
14846
15017
|
if (walkInto(full, out)) return true;
|
|
@@ -14858,6 +15029,101 @@ function safeStat(path) {
|
|
|
14858
15029
|
}
|
|
14859
15030
|
}
|
|
14860
15031
|
|
|
15032
|
+
// core/runtime/active-provider-bootstrap.ts
|
|
15033
|
+
import { createInterface as createInterface2 } from "readline";
|
|
15034
|
+
import { isAbsolute as isAbsolute9, join as join16 } from "path";
|
|
15035
|
+
async function bootstrapActiveProvider(opts) {
|
|
15036
|
+
const fromCwd = resolveActiveProvider(opts.cwd);
|
|
15037
|
+
if (fromCwd.source === "config") {
|
|
15038
|
+
return { kind: "ok", activeProvider: fromCwd.resolved, source: "config" };
|
|
15039
|
+
}
|
|
15040
|
+
const detected = aggregateDetected(opts.cwd, opts.effectiveRoots, fromCwd.detected);
|
|
15041
|
+
if (detected.length === 0) {
|
|
15042
|
+
opts.printer.warn(SCAN_RUNNER_TEXTS.activeProviderNoMarkerWarning);
|
|
15043
|
+
return { kind: "ok", activeProvider: null, source: "none" };
|
|
15044
|
+
}
|
|
15045
|
+
if (detected.length === 1) {
|
|
15046
|
+
const picked2 = detected[0];
|
|
15047
|
+
persistActiveProvider(opts.cwd, picked2, opts.printer);
|
|
15048
|
+
return { kind: "ok", activeProvider: picked2, source: "autodetect" };
|
|
15049
|
+
}
|
|
15050
|
+
if (opts.yes) {
|
|
15051
|
+
return { kind: "ambiguous", detected };
|
|
15052
|
+
}
|
|
15053
|
+
const picked = await promptForLens(detected, opts.stdin, opts.stderr);
|
|
15054
|
+
if (picked === null) {
|
|
15055
|
+
return { kind: "ambiguous", detected };
|
|
15056
|
+
}
|
|
15057
|
+
persistActiveProvider(opts.cwd, picked, opts.printer);
|
|
15058
|
+
return { kind: "ok", activeProvider: picked, source: "autodetect" };
|
|
15059
|
+
}
|
|
15060
|
+
function aggregateDetected(cwd, effectiveRoots, cwdDetected) {
|
|
15061
|
+
const out = [];
|
|
15062
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15063
|
+
for (const id of cwdDetected) {
|
|
15064
|
+
if (seen.has(id)) continue;
|
|
15065
|
+
seen.add(id);
|
|
15066
|
+
out.push(id);
|
|
15067
|
+
}
|
|
15068
|
+
for (const root of effectiveRoots) {
|
|
15069
|
+
const absRoot = isAbsolute9(root) ? root : join16(cwd, root);
|
|
15070
|
+
const r = resolveActiveProvider(absRoot);
|
|
15071
|
+
for (const id of r.detected) {
|
|
15072
|
+
if (seen.has(id)) continue;
|
|
15073
|
+
seen.add(id);
|
|
15074
|
+
out.push(id);
|
|
15075
|
+
}
|
|
15076
|
+
}
|
|
15077
|
+
return out;
|
|
15078
|
+
}
|
|
15079
|
+
function persistActiveProvider(cwd, id, printer) {
|
|
15080
|
+
try {
|
|
15081
|
+
writeConfigValue("activeProvider", id, { target: "project", cwd });
|
|
15082
|
+
printer.info(tx(SCAN_RUNNER_TEXTS.activeProviderAutodetected, { id }));
|
|
15083
|
+
} catch (err) {
|
|
15084
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
15085
|
+
printer.warn(
|
|
15086
|
+
tx(SCAN_RUNNER_TEXTS.activeProviderPersistFailed, { id, message })
|
|
15087
|
+
);
|
|
15088
|
+
}
|
|
15089
|
+
}
|
|
15090
|
+
function warnIfLensBundleDisabled(args2) {
|
|
15091
|
+
if (args2.activeProvider === null) return;
|
|
15092
|
+
if (args2.resolveEnabled(args2.activeProvider)) return;
|
|
15093
|
+
args2.printer.warn(
|
|
15094
|
+
tx(SCAN_RUNNER_TEXTS.activeProviderBundleDisabledWarning, {
|
|
15095
|
+
id: args2.activeProvider
|
|
15096
|
+
})
|
|
15097
|
+
);
|
|
15098
|
+
}
|
|
15099
|
+
async function promptForLens(detected, stdin, stderr) {
|
|
15100
|
+
const lines = [SCAN_RUNNER_TEXTS.activeProviderPromptHeader];
|
|
15101
|
+
for (let i = 0; i < detected.length; i += 1) {
|
|
15102
|
+
lines.push(
|
|
15103
|
+
tx(SCAN_RUNNER_TEXTS.activeProviderPromptOption, {
|
|
15104
|
+
index: i + 1,
|
|
15105
|
+
id: detected[i]
|
|
15106
|
+
})
|
|
15107
|
+
);
|
|
15108
|
+
}
|
|
15109
|
+
stderr.write(lines.join("\n") + "\n");
|
|
15110
|
+
const rl = createInterface2({ input: stdin, output: stderr });
|
|
15111
|
+
try {
|
|
15112
|
+
const answer = await new Promise(
|
|
15113
|
+
(resolveP) => rl.question(SCAN_RUNNER_TEXTS.activeProviderPromptInput, resolveP)
|
|
15114
|
+
);
|
|
15115
|
+
const trimmed = answer.trim();
|
|
15116
|
+
const asNumber = Number.parseInt(trimmed, 10);
|
|
15117
|
+
if (!Number.isNaN(asNumber) && asNumber >= 1 && asNumber <= detected.length) {
|
|
15118
|
+
return detected[asNumber - 1];
|
|
15119
|
+
}
|
|
15120
|
+
const asId = detected.find((d) => d.toLowerCase() === trimmed.toLowerCase());
|
|
15121
|
+
return asId ?? null;
|
|
15122
|
+
} finally {
|
|
15123
|
+
rl.close();
|
|
15124
|
+
}
|
|
15125
|
+
}
|
|
15126
|
+
|
|
14861
15127
|
// core/runtime/scan-runner.ts
|
|
14862
15128
|
async function runScanForCommand(opts) {
|
|
14863
15129
|
const ctx = opts.ctx ?? defaultRuntimeContext();
|
|
@@ -14865,20 +15131,9 @@ async function runScanForCommand(opts) {
|
|
|
14865
15131
|
const kernel = createKernel();
|
|
14866
15132
|
const pluginRuntime = await preparePluginRuntime(opts, opts.printer);
|
|
14867
15133
|
const extensions = registerExtensions(kernel, pluginRuntime, opts);
|
|
14868
|
-
|
|
14869
|
-
|
|
14870
|
-
|
|
14871
|
-
} catch (err) {
|
|
14872
|
-
return { kind: "config-error", message: formatErrorMessage(err) };
|
|
14873
|
-
}
|
|
14874
|
-
const ignoreFilter = buildScanIgnoreFilter(cfg, ctx.cwd);
|
|
14875
|
-
const strict = opts.strict || cfg.scan.strict === true;
|
|
14876
|
-
let effectiveRoots;
|
|
14877
|
-
try {
|
|
14878
|
-
effectiveRoots = resolveScanRoots({ positionalRoots: opts.roots });
|
|
14879
|
-
} catch (err) {
|
|
14880
|
-
return { kind: "config-error", message: formatErrorMessage(err) };
|
|
14881
|
-
}
|
|
15134
|
+
const scanInputs = loadScanInputs(opts, ctx);
|
|
15135
|
+
if ("kind" in scanInputs) return scanInputs;
|
|
15136
|
+
const { cfg, ignoreFilter, strict, effectiveRoots } = scanInputs;
|
|
14882
15137
|
let referenceablePaths;
|
|
14883
15138
|
if (cfg.scan.referencePaths.length > 0) {
|
|
14884
15139
|
const walk2 = walkReferencePaths(cfg.scan.referencePaths, ctx.cwd);
|
|
@@ -14887,6 +15142,9 @@ async function runScanForCommand(opts) {
|
|
|
14887
15142
|
}
|
|
14888
15143
|
const loadPrior = makePriorLoader(opts.noBuiltIns, strict);
|
|
14889
15144
|
const jobsDir = defaultProjectJobsDir(ctx);
|
|
15145
|
+
const lens = await resolveActiveLens(opts, ctx, effectiveRoots, pluginRuntime);
|
|
15146
|
+
if (lens.kind === "ambiguous-provider") return lens;
|
|
15147
|
+
const activeProvider = lens.activeProvider;
|
|
14890
15148
|
const runScanWith = makeScanRunner(
|
|
14891
15149
|
kernel,
|
|
14892
15150
|
opts,
|
|
@@ -14895,11 +15153,37 @@ async function runScanForCommand(opts) {
|
|
|
14895
15153
|
strict,
|
|
14896
15154
|
extensions,
|
|
14897
15155
|
referenceablePaths,
|
|
14898
|
-
ctx.cwd
|
|
15156
|
+
ctx.cwd,
|
|
15157
|
+
activeProvider
|
|
14899
15158
|
);
|
|
14900
15159
|
const willPersist = !opts.noBuiltIns && !opts.dryRun;
|
|
14901
15160
|
return willPersist ? runPersistPath(opts, dbPath, jobsDir, strict, loadPrior, runScanWith, extensions) : runEphemeralPath(opts, dbPath, strict, loadPrior, runScanWith);
|
|
14902
15161
|
}
|
|
15162
|
+
async function resolveActiveLens(opts, ctx, effectiveRoots, pluginRuntime) {
|
|
15163
|
+
const bootstrap = await bootstrapActiveProvider({
|
|
15164
|
+
cwd: ctx.cwd,
|
|
15165
|
+
effectiveRoots,
|
|
15166
|
+
yes: opts.yes ?? false,
|
|
15167
|
+
stdin: opts.stdin ?? process.stdin,
|
|
15168
|
+
stderr: opts.stderr,
|
|
15169
|
+
printer: opts.printer
|
|
15170
|
+
});
|
|
15171
|
+
if (bootstrap.kind === "ambiguous") {
|
|
15172
|
+
return {
|
|
15173
|
+
kind: "ambiguous-provider",
|
|
15174
|
+
detected: bootstrap.detected,
|
|
15175
|
+
message: tx(SCAN_RUNNER_TEXTS.activeProviderAmbiguousUnderYes, {
|
|
15176
|
+
candidates: bootstrap.detected.join(", ")
|
|
15177
|
+
})
|
|
15178
|
+
};
|
|
15179
|
+
}
|
|
15180
|
+
warnIfLensBundleDisabled({
|
|
15181
|
+
activeProvider: bootstrap.activeProvider,
|
|
15182
|
+
resolveEnabled: opts.resolveEnabledOverride ?? pluginRuntime.resolveEnabled,
|
|
15183
|
+
printer: opts.printer
|
|
15184
|
+
});
|
|
15185
|
+
return { kind: "ok", activeProvider: bootstrap.activeProvider };
|
|
15186
|
+
}
|
|
14903
15187
|
function emitReferenceWalkAdvisory(walk2, opts) {
|
|
14904
15188
|
if (walk2.truncated) {
|
|
14905
15189
|
opts.printer.warn(SCAN_RUNNER_TEXTS.referenceWalkTruncated);
|
|
@@ -14933,6 +15217,17 @@ function registerExtensions(kernel, pluginRuntime, opts) {
|
|
|
14933
15217
|
registerEnabledExtensions(kernel, pluginRuntime, registerOpts);
|
|
14934
15218
|
return extensions;
|
|
14935
15219
|
}
|
|
15220
|
+
function loadScanInputs(opts, ctx) {
|
|
15221
|
+
try {
|
|
15222
|
+
const cfg = loadConfig({ strict: opts.strict, ...ctx }).effective;
|
|
15223
|
+
const ignoreFilter = buildScanIgnoreFilter(cfg, ctx.cwd);
|
|
15224
|
+
const strict = opts.strict || cfg.scan.strict === true;
|
|
15225
|
+
const effectiveRoots = resolveScanRoots({ positionalRoots: opts.roots });
|
|
15226
|
+
return { cfg, ignoreFilter, strict, effectiveRoots };
|
|
15227
|
+
} catch (err) {
|
|
15228
|
+
return { kind: "config-error", message: formatErrorMessage(err) };
|
|
15229
|
+
}
|
|
15230
|
+
}
|
|
14936
15231
|
function buildScanIgnoreFilter(cfg, cwd) {
|
|
14937
15232
|
const ignoreFileText = readIgnoreFileText(cwd);
|
|
14938
15233
|
const ignoreFilterOpts = {};
|
|
@@ -14955,7 +15250,7 @@ function makePriorLoader(noBuiltIns, strict) {
|
|
|
14955
15250
|
return loaded;
|
|
14956
15251
|
};
|
|
14957
15252
|
}
|
|
14958
|
-
function makeScanRunner(kernel, opts, effectiveRoots, ignoreFilter, strict, extensions, referenceablePaths, scanCwd) {
|
|
15253
|
+
function makeScanRunner(kernel, opts, effectiveRoots, ignoreFilter, strict, extensions, referenceablePaths, scanCwd, activeProvider) {
|
|
14959
15254
|
return async (prior, priorExtractorRuns, orphanJobFiles) => {
|
|
14960
15255
|
if (opts.changed && prior === null) {
|
|
14961
15256
|
opts.stderr.write(SCAN_RUNNER_TEXTS.changedNoPriorWarning);
|
|
@@ -14969,6 +15264,7 @@ function makeScanRunner(kernel, opts, effectiveRoots, ignoreFilter, strict, exte
|
|
|
14969
15264
|
referenceablePaths,
|
|
14970
15265
|
cwd: scanCwd,
|
|
14971
15266
|
prior,
|
|
15267
|
+
activeProvider,
|
|
14972
15268
|
...priorExtractorRuns ? { priorExtractorRuns } : {},
|
|
14973
15269
|
...orphanJobFiles ? { orphanJobFiles } : {}
|
|
14974
15270
|
});
|
|
@@ -14989,7 +15285,8 @@ function buildRunScanOptions(args2) {
|
|
|
14989
15285
|
// visible from this caller" (legacy behaviour). The orchestrator
|
|
14990
15286
|
// defaults to `[]` when the field is absent; we always pass the
|
|
14991
15287
|
// array (possibly empty) to keep the wiring uniform.
|
|
14992
|
-
orphanJobFiles: orphanJobFiles ?? []
|
|
15288
|
+
orphanJobFiles: orphanJobFiles ?? [],
|
|
15289
|
+
activeProvider: args2.activeProvider
|
|
14993
15290
|
};
|
|
14994
15291
|
if (args2.extensions) runOptions.extensions = args2.extensions;
|
|
14995
15292
|
if (prior) {
|
|
@@ -15132,7 +15429,7 @@ var InitCommand = class extends SmCommand {
|
|
|
15132
15429
|
async run() {
|
|
15133
15430
|
const ctx = defaultRuntimeContext();
|
|
15134
15431
|
const scopeRoot = ctx.cwd;
|
|
15135
|
-
const skillMapDir =
|
|
15432
|
+
const skillMapDir = join17(scopeRoot, SKILL_MAP_DIR);
|
|
15136
15433
|
const settingsPath = defaultSettingsPath(scopeRoot);
|
|
15137
15434
|
const localPath = defaultLocalSettingsPath(scopeRoot);
|
|
15138
15435
|
const ignorePath = defaultIgnoreFilePath(scopeRoot);
|
|
@@ -15178,7 +15475,7 @@ var InitCommand = class extends SmCommand {
|
|
|
15178
15475
|
const okGlyph = ansi.green("\u2713");
|
|
15179
15476
|
const updated = await ensureGitignoreEntries(scopeRoot, GITIGNORE_ENTRIES);
|
|
15180
15477
|
if (updated) {
|
|
15181
|
-
const gitignorePath =
|
|
15478
|
+
const gitignorePath = join17(scopeRoot, ".gitignore");
|
|
15182
15479
|
printer.info(
|
|
15183
15480
|
GITIGNORE_ENTRIES.length === 1 ? tx(INIT_TEXTS.gitignoreUpdatedSingular, { glyph: okGlyph, path: gitignorePath }) : tx(INIT_TEXTS.gitignoreUpdatedPlural, {
|
|
15184
15481
|
glyph: okGlyph,
|
|
@@ -15217,7 +15514,7 @@ async function dryRunFileMessage(path) {
|
|
|
15217
15514
|
}
|
|
15218
15515
|
async function writeDryRunGitignorePlan(printer, scopeRoot) {
|
|
15219
15516
|
const wouldAdd = await previewGitignoreEntries(scopeRoot, GITIGNORE_ENTRIES);
|
|
15220
|
-
const gitignorePath =
|
|
15517
|
+
const gitignorePath = join17(scopeRoot, ".gitignore");
|
|
15221
15518
|
if (wouldAdd.length === 0) {
|
|
15222
15519
|
printer.info(tx(INIT_TEXTS.dryRunWouldLeaveGitignoreUnchanged, { path: gitignorePath }));
|
|
15223
15520
|
} else if (wouldAdd.length === 1) {
|
|
@@ -15256,7 +15553,14 @@ async function runFirstScan(scopeRoot, strict, printer, stderr, ansi) {
|
|
|
15256
15553
|
strict,
|
|
15257
15554
|
stderr,
|
|
15258
15555
|
printer,
|
|
15259
|
-
ctx: { cwd: scopeRoot }
|
|
15556
|
+
ctx: { cwd: scopeRoot },
|
|
15557
|
+
// Init's first scan is a provisioning step, not the user's
|
|
15558
|
+
// primary "show me my graph" call. Don't block waiting for the
|
|
15559
|
+
// operator to disambiguate the lens here; let init complete with
|
|
15560
|
+
// `activeProvider` unset and let the FIRST explicit `sm scan`
|
|
15561
|
+
// surface the prompt. Treat the `ambiguous-provider` outcome below
|
|
15562
|
+
// as a soft hint, not a failure.
|
|
15563
|
+
yes: true
|
|
15260
15564
|
});
|
|
15261
15565
|
const errGlyph = ansi.red("\u2715");
|
|
15262
15566
|
if (outcome.kind === "config-error") {
|
|
@@ -15276,6 +15580,10 @@ async function runFirstScan(scopeRoot, strict, printer, stderr, ansi) {
|
|
|
15276
15580
|
);
|
|
15277
15581
|
return ExitCode.Error;
|
|
15278
15582
|
}
|
|
15583
|
+
if (outcome.kind === "ambiguous-provider") {
|
|
15584
|
+
printer.warn(outcome.message);
|
|
15585
|
+
return ExitCode.Ok;
|
|
15586
|
+
}
|
|
15279
15587
|
const result = outcome.result;
|
|
15280
15588
|
const hasErrors = result.issues.some((i) => i.severity === "error");
|
|
15281
15589
|
printer.info(
|
|
@@ -15292,7 +15600,7 @@ async function runFirstScan(scopeRoot, strict, printer, stderr, ansi) {
|
|
|
15292
15600
|
return hasErrors ? ExitCode.Issues : ExitCode.Ok;
|
|
15293
15601
|
}
|
|
15294
15602
|
async function previewGitignoreEntries(scopeRoot, entries) {
|
|
15295
|
-
const path =
|
|
15603
|
+
const path = join17(scopeRoot, ".gitignore");
|
|
15296
15604
|
const body = await pathExists(path) ? await readFile2(path, "utf8") : "";
|
|
15297
15605
|
const present = new Set(
|
|
15298
15606
|
body.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"))
|
|
@@ -15300,7 +15608,7 @@ async function previewGitignoreEntries(scopeRoot, entries) {
|
|
|
15300
15608
|
return entries.filter((entry) => !present.has(entry));
|
|
15301
15609
|
}
|
|
15302
15610
|
async function ensureGitignoreEntries(scopeRoot, entries) {
|
|
15303
|
-
const path =
|
|
15611
|
+
const path = join17(scopeRoot, ".gitignore");
|
|
15304
15612
|
let body = "";
|
|
15305
15613
|
if (await pathExists(path)) {
|
|
15306
15614
|
body = await readFile2(path, "utf8");
|
|
@@ -16865,8 +17173,8 @@ var PLUGINS_TEXTS = {
|
|
|
16865
17173
|
doctorIssueEntry: " {{glyph}} {{id}} {{status}}\n",
|
|
16866
17174
|
doctorIssueBody: " {{line}}\n",
|
|
16867
17175
|
// --- enable / disable -----------------------------------------------
|
|
16868
|
-
toggleBothIdAndAll: "{{glyph}} Pass either
|
|
16869
|
-
toggleNeitherIdNorAll: "{{glyph}} Pass <id> or --all.\n",
|
|
17176
|
+
toggleBothIdAndAll: "{{glyph}} Pass either one or more <id> arguments or --all, not both.\n",
|
|
17177
|
+
toggleNeitherIdNorAll: "{{glyph}} Pass one or more <id> arguments, or --all.\n",
|
|
16870
17178
|
toggleResolveError: "{{error}}",
|
|
16871
17179
|
toggleAppliedSingle: "{{verbPast}}: {{id}}\n",
|
|
16872
17180
|
toggleAppliedManyHeader: "{{verbPast}}: {{count}} plugin(s)\n",
|
|
@@ -16968,9 +17276,9 @@ var PLUGINS_TEXTS = {
|
|
|
16968
17276
|
};
|
|
16969
17277
|
|
|
16970
17278
|
// cli/commands/plugins/shared.ts
|
|
16971
|
-
import { resolve as
|
|
17279
|
+
import { resolve as resolve31 } from "path";
|
|
16972
17280
|
function resolveSearchPaths2(opts, cwd) {
|
|
16973
|
-
if (opts.pluginDir) return [
|
|
17281
|
+
if (opts.pluginDir) return [resolve31(opts.pluginDir)];
|
|
16974
17282
|
return [defaultProjectPluginsDir({ cwd })];
|
|
16975
17283
|
}
|
|
16976
17284
|
async function buildResolver() {
|
|
@@ -17852,7 +18160,7 @@ function buildDoctorJsonEnvelope(args2) {
|
|
|
17852
18160
|
import { Command as Command25, Option as Option24 } from "clipanion";
|
|
17853
18161
|
var TogglePluginsBase = class extends SmCommand {
|
|
17854
18162
|
all = Option24.Boolean("--all", false);
|
|
17855
|
-
|
|
18163
|
+
ids = Option24.Rest({ name: "ids" });
|
|
17856
18164
|
async toggle(enabled) {
|
|
17857
18165
|
const verb = enabled ? "enable" : "disable";
|
|
17858
18166
|
const stderrAnsi = this.ansiFor("stderr");
|
|
@@ -17871,23 +18179,24 @@ var TogglePluginsBase = class extends SmCommand {
|
|
|
17871
18179
|
return ExitCode.Ok;
|
|
17872
18180
|
}
|
|
17873
18181
|
/**
|
|
17874
|
-
* `--all` vs `<id
|
|
17875
|
-
* one must be present; surfaces a directed error on misuse.
|
|
18182
|
+
* `--all` vs `<id>...` mutex check. The two are mutually exclusive
|
|
18183
|
+
* and one must be present; surfaces a directed error on misuse.
|
|
18184
|
+
* Variadic positional accepts one or more ids.
|
|
17876
18185
|
*/
|
|
17877
18186
|
#validateArgs(ansi) {
|
|
17878
18187
|
const errGlyph = ansi.red("\u2715");
|
|
17879
|
-
if (this.all && this.
|
|
18188
|
+
if (this.all && this.ids.length > 0) {
|
|
17880
18189
|
this.printer.error(tx(PLUGINS_TEXTS.toggleBothIdAndAll, { glyph: errGlyph }));
|
|
17881
18190
|
return ExitCode.Error;
|
|
17882
18191
|
}
|
|
17883
|
-
if (!this.all &&
|
|
18192
|
+
if (!this.all && this.ids.length === 0) {
|
|
17884
18193
|
this.printer.error(tx(PLUGINS_TEXTS.toggleNeitherIdNorAll, { glyph: errGlyph }));
|
|
17885
18194
|
return ExitCode.Error;
|
|
17886
18195
|
}
|
|
17887
18196
|
return null;
|
|
17888
18197
|
}
|
|
17889
18198
|
/**
|
|
17890
|
-
* Resolve `<id
|
|
18199
|
+
* Resolve `<id>...` against the catalogue or fan out via `--all`.
|
|
17891
18200
|
* Returns the target list on success, or the exit code on a
|
|
17892
18201
|
* directed-error path (unknown id, granularity mismatch).
|
|
17893
18202
|
*
|
|
@@ -17898,25 +18207,35 @@ var TogglePluginsBase = class extends SmCommand {
|
|
|
17898
18207
|
* the directed error message when they try the bundle id directly,
|
|
17899
18208
|
* so `--all` skips them here too and the real "disable every core
|
|
17900
18209
|
* extension" intent is served by `--no-built-ins` on `sm scan`.
|
|
18210
|
+
*
|
|
18211
|
+
* Variadic mode is all-or-nothing: the first bad id aborts the
|
|
18212
|
+
* batch before any DB write, so the user never lands in a partial
|
|
18213
|
+
* state. Repeated ids in the same call are deduped.
|
|
17901
18214
|
*/
|
|
17902
18215
|
#pickTargets(catalogue, verb, ansi) {
|
|
17903
18216
|
if (this.all) {
|
|
17904
18217
|
return catalogue.filter((b) => b.granularity === "bundle").map((b) => b.id);
|
|
17905
18218
|
}
|
|
17906
|
-
const
|
|
17907
|
-
|
|
17908
|
-
|
|
17909
|
-
|
|
18219
|
+
const keys = [];
|
|
18220
|
+
for (const rawId of this.ids) {
|
|
18221
|
+
const resolved = resolveToggleTarget(rawId, catalogue, verb, ansi);
|
|
18222
|
+
if ("error" in resolved) {
|
|
18223
|
+
this.printer.error(tx(PLUGINS_TEXTS.toggleResolveError, { error: resolved.error }));
|
|
18224
|
+
return ExitCode.NotFound;
|
|
18225
|
+
}
|
|
18226
|
+
keys.push(resolved.key);
|
|
17910
18227
|
}
|
|
17911
|
-
return [
|
|
18228
|
+
return [...new Set(keys)];
|
|
17912
18229
|
}
|
|
17913
18230
|
/**
|
|
17914
|
-
* Host lock, see `src/kernel/config/locked-plugins.ts`.
|
|
17915
|
-
*
|
|
17916
|
-
* rest. Single-id mode
|
|
18231
|
+
* Host lock, see `src/kernel/config/locked-plugins.ts`. Bulk modes
|
|
18232
|
+
* (`--all` or an explicit batch of >1 ids) silently skip locked
|
|
18233
|
+
* targets so the user can still toggle the rest. Single-id mode
|
|
18234
|
+
* surfaces a directed exit-5 message so the user knows their one
|
|
18235
|
+
* intended target was refused.
|
|
17917
18236
|
*/
|
|
17918
18237
|
#applyLockGate(targets, ansi) {
|
|
17919
|
-
if (this.all) return targets.filter((id) => !isPluginLocked(id));
|
|
18238
|
+
if (this.all || this.ids.length > 1) return targets.filter((id) => !isPluginLocked(id));
|
|
17920
18239
|
const lockedHit = targets.find((id) => isPluginLocked(id));
|
|
17921
18240
|
if (!lockedHit) return targets;
|
|
17922
18241
|
this.printer.error(
|
|
@@ -17972,12 +18291,19 @@ var PluginsEnableCommand = class extends TogglePluginsBase {
|
|
|
17972
18291
|
static paths = [["plugins", "enable"]];
|
|
17973
18292
|
static usage = Command25.Usage({
|
|
17974
18293
|
category: "Plugins",
|
|
17975
|
-
description: "Enable
|
|
18294
|
+
description: "Enable one or more plugins (or --all). Persists in config_plugins.",
|
|
17976
18295
|
details: `
|
|
17977
|
-
Writes a row to config_plugins with enabled=1. Takes
|
|
17978
|
-
over the team-shared baseline at
|
|
17979
|
-
Use sm plugins disable to
|
|
17980
|
-
drops the settings.json
|
|
18296
|
+
Writes a row to config_plugins with enabled=1 per id. Takes
|
|
18297
|
+
precedence over the team-shared baseline at
|
|
18298
|
+
settings.json#/plugins/<id>/enabled. Use sm plugins disable to
|
|
18299
|
+
flip; sm config reset plugins.<id>.enabled drops the settings.json
|
|
18300
|
+
baseline.
|
|
18301
|
+
|
|
18302
|
+
Accepts one or more ids in one call, e.g.
|
|
18303
|
+
'sm plugins enable claude gemini openai'. Batches are
|
|
18304
|
+
all-or-nothing: a single unknown / mismatched id aborts before
|
|
18305
|
+
any write. Repeated ids are deduped. Locked plugins inside a
|
|
18306
|
+
batch are silently skipped.
|
|
17981
18307
|
|
|
17982
18308
|
Granularity: a bundle-granularity plugin (default for user plugins,
|
|
17983
18309
|
and the built-in 'claude' bundle) accepts only the bundle id. An
|
|
@@ -17994,12 +18320,18 @@ var PluginsDisableCommand = class extends TogglePluginsBase {
|
|
|
17994
18320
|
static paths = [["plugins", "disable"]];
|
|
17995
18321
|
static usage = Command25.Usage({
|
|
17996
18322
|
category: "Plugins",
|
|
17997
|
-
description: "Disable
|
|
18323
|
+
description: "Disable one or more plugins (or --all). Persists in config_plugins; does not delete files.",
|
|
17998
18324
|
details: `
|
|
17999
|
-
Writes a row to config_plugins with enabled=0. Discovery
|
|
18000
|
-
surfaces the plugin in sm plugins list, but with
|
|
18001
|
-
its extensions are not imported and the kernel
|
|
18002
|
-
them.
|
|
18325
|
+
Writes a row to config_plugins with enabled=0 per id. Discovery
|
|
18326
|
+
still surfaces the plugin in sm plugins list, but with
|
|
18327
|
+
status=disabled; its extensions are not imported and the kernel
|
|
18328
|
+
will not run them.
|
|
18329
|
+
|
|
18330
|
+
Accepts one or more ids in one call, e.g.
|
|
18331
|
+
'sm plugins disable gemini openai agent-skills'. Batches are
|
|
18332
|
+
all-or-nothing: a single unknown / mismatched id aborts before
|
|
18333
|
+
any write. Repeated ids are deduped. Locked plugins inside a
|
|
18334
|
+
batch are silently skipped.
|
|
18003
18335
|
|
|
18004
18336
|
Granularity: a bundle-granularity plugin (default for user plugins,
|
|
18005
18337
|
and the built-in 'claude' bundle) accepts only the bundle id. An
|
|
@@ -18105,8 +18437,8 @@ function resolveBareToggle(id, catalogue, verb, ansi) {
|
|
|
18105
18437
|
}
|
|
18106
18438
|
|
|
18107
18439
|
// cli/commands/plugins/create.ts
|
|
18108
|
-
import { existsSync as
|
|
18109
|
-
import { join as
|
|
18440
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync6, writeFileSync as writeFileSync3 } from "fs";
|
|
18441
|
+
import { join as join18, resolve as resolve32 } from "path";
|
|
18110
18442
|
import { Command as Command26, Option as Option25 } from "clipanion";
|
|
18111
18443
|
var PluginsCreateCommand = class extends SmCommand {
|
|
18112
18444
|
static paths = [["plugins", "create"]];
|
|
@@ -18132,8 +18464,8 @@ var PluginsCreateCommand = class extends SmCommand {
|
|
|
18132
18464
|
}
|
|
18133
18465
|
const ctx = defaultRuntimeContext();
|
|
18134
18466
|
const baseDir = defaultProjectPluginsDir(ctx);
|
|
18135
|
-
const targetDir = this.at ?
|
|
18136
|
-
if (
|
|
18467
|
+
const targetDir = this.at ? resolve32(this.at) : join18(baseDir, this.pluginId);
|
|
18468
|
+
if (existsSync23(targetDir) && !this.force) {
|
|
18137
18469
|
this.printer.error(
|
|
18138
18470
|
tx(PLUGINS_TEXTS.createRefuseOverwrite, {
|
|
18139
18471
|
glyph: errGlyph,
|
|
@@ -18143,7 +18475,7 @@ var PluginsCreateCommand = class extends SmCommand {
|
|
|
18143
18475
|
return ExitCode.Error;
|
|
18144
18476
|
}
|
|
18145
18477
|
const extractorName = `${this.pluginId}-extractor`;
|
|
18146
|
-
mkdirSync6(
|
|
18478
|
+
mkdirSync6(join18(targetDir, "extractors", extractorName), { recursive: true });
|
|
18147
18479
|
const specVersion = installedSpecVersion();
|
|
18148
18480
|
const manifest = {
|
|
18149
18481
|
id: this.pluginId,
|
|
@@ -18163,14 +18495,14 @@ var PluginsCreateCommand = class extends SmCommand {
|
|
|
18163
18495
|
}
|
|
18164
18496
|
};
|
|
18165
18497
|
writeFileSync3(
|
|
18166
|
-
|
|
18498
|
+
join18(targetDir, "plugin.json"),
|
|
18167
18499
|
JSON.stringify(manifest, null, 2) + "\n"
|
|
18168
18500
|
);
|
|
18169
18501
|
writeFileSync3(
|
|
18170
|
-
|
|
18502
|
+
join18(targetDir, "extractors", extractorName, "index.js"),
|
|
18171
18503
|
scaffolderExtractorStub(extractorName)
|
|
18172
18504
|
);
|
|
18173
|
-
writeFileSync3(
|
|
18505
|
+
writeFileSync3(join18(targetDir, "README.md"), scaffolderReadme(this.pluginId));
|
|
18174
18506
|
this.printer.data(
|
|
18175
18507
|
tx(PLUGINS_TEXTS.createSuccess, {
|
|
18176
18508
|
targetDir: sanitizeForTerminal(targetDir),
|
|
@@ -18373,7 +18705,7 @@ var PLUGIN_COMMANDS = [
|
|
|
18373
18705
|
|
|
18374
18706
|
// cli/commands/refresh.ts
|
|
18375
18707
|
import { readFile as readFile3 } from "fs/promises";
|
|
18376
|
-
import { resolve as
|
|
18708
|
+
import { resolve as resolve33 } from "path";
|
|
18377
18709
|
import { Command as Command29, Option as Option27 } from "clipanion";
|
|
18378
18710
|
|
|
18379
18711
|
// cli/i18n/refresh.texts.ts
|
|
@@ -18657,7 +18989,7 @@ var RefreshCommand = class extends SmCommand {
|
|
|
18657
18989
|
let body;
|
|
18658
18990
|
try {
|
|
18659
18991
|
assertContained(cwd, node.path);
|
|
18660
|
-
const raw = await readFile3(
|
|
18992
|
+
const raw = await readFile3(resolve33(cwd, node.path), "utf8");
|
|
18661
18993
|
body = stripFrontmatterFence(raw);
|
|
18662
18994
|
} catch (err) {
|
|
18663
18995
|
if (!this.json) {
|
|
@@ -19398,6 +19730,9 @@ var ScanCommand = class extends SmCommand {
|
|
|
19398
19730
|
watch = Option29.Boolean("--watch", false, {
|
|
19399
19731
|
description: "Long-running mode: watch the roots and trigger an incremental scan after each debounced batch of filesystem events. Alias of `sm watch`."
|
|
19400
19732
|
});
|
|
19733
|
+
yes = Option29.Boolean("--yes", false, {
|
|
19734
|
+
description: "Non-interactive mode for ambiguous activeProvider auto-detect. With `--yes`, multiple provider markers (.claude/, .gemini/, .codex/, AGENTS.md, .cursor/) under the scan tree exit non-zero instead of prompting the operator. Set the lens manually via `sm config set activeProvider <id>` and re-run."
|
|
19735
|
+
});
|
|
19401
19736
|
// Each branch in the orchestrator maps to one validation gate
|
|
19402
19737
|
// (--watch alias / --changed mutex / -g mutex / dispatch).
|
|
19403
19738
|
// Splitting per branch scatters the gate from the value it gates.
|
|
@@ -19426,9 +19761,11 @@ var ScanCommand = class extends SmCommand {
|
|
|
19426
19761
|
allowEmpty: this.allowEmpty,
|
|
19427
19762
|
strict: this.strict,
|
|
19428
19763
|
stderr: this.context.stderr,
|
|
19764
|
+
stdin: this.context.stdin,
|
|
19429
19765
|
printer: this.printer,
|
|
19430
19766
|
killSwitches: readConformanceKillSwitches(),
|
|
19431
|
-
colorEnabled
|
|
19767
|
+
colorEnabled,
|
|
19768
|
+
yes: this.yes
|
|
19432
19769
|
});
|
|
19433
19770
|
return outcome.kind === "ok" ? this.renderOutcome(outcome.result, outcome.persistedTo, outcome.dbPath, outcome.strict) : this.renderFailure(outcome);
|
|
19434
19771
|
}
|
|
@@ -19499,6 +19836,12 @@ var ScanCommand = class extends SmCommand {
|
|
|
19499
19836
|
);
|
|
19500
19837
|
return ExitCode.Error;
|
|
19501
19838
|
}
|
|
19839
|
+
if (outcome.kind === "ambiguous-provider") {
|
|
19840
|
+
this.printer.info(
|
|
19841
|
+
tx(SCAN_TEXTS.scanFailure, { glyph: errGlyph, message: outcome.message })
|
|
19842
|
+
);
|
|
19843
|
+
return ExitCode.Error;
|
|
19844
|
+
}
|
|
19502
19845
|
this.printer.info(
|
|
19503
19846
|
tx(SCAN_TEXTS.scanFailure, { glyph: errGlyph, message: outcome.message })
|
|
19504
19847
|
);
|
|
@@ -19581,7 +19924,7 @@ function plural(count, word) {
|
|
|
19581
19924
|
}
|
|
19582
19925
|
|
|
19583
19926
|
// cli/commands/scan-compare.ts
|
|
19584
|
-
import { existsSync as
|
|
19927
|
+
import { existsSync as existsSync24, readFileSync as readFileSync18 } from "fs";
|
|
19585
19928
|
import { Command as Command32, Option as Option30 } from "clipanion";
|
|
19586
19929
|
var ScanCompareCommand = class extends SmCommand {
|
|
19587
19930
|
static paths = [["scan", "compare-with"]];
|
|
@@ -19693,7 +20036,7 @@ var ScanCompareCommand = class extends SmCommand {
|
|
|
19693
20036
|
}
|
|
19694
20037
|
};
|
|
19695
20038
|
function loadAndValidateDump(path) {
|
|
19696
|
-
if (!
|
|
20039
|
+
if (!existsSync24(path)) {
|
|
19697
20040
|
throw new Error(tx(SCAN_TEXTS.compareDumpNotFound, { path }));
|
|
19698
20041
|
}
|
|
19699
20042
|
let raw;
|
|
@@ -20536,7 +20879,7 @@ function contentTypeFor(format) {
|
|
|
20536
20879
|
}
|
|
20537
20880
|
|
|
20538
20881
|
// server/health.ts
|
|
20539
|
-
import { existsSync as
|
|
20882
|
+
import { existsSync as existsSync25 } from "fs";
|
|
20540
20883
|
var FALLBACK_SCHEMA_VERSION = "1";
|
|
20541
20884
|
function buildHealth(deps) {
|
|
20542
20885
|
return {
|
|
@@ -20544,7 +20887,7 @@ function buildHealth(deps) {
|
|
|
20544
20887
|
schemaVersion: FALLBACK_SCHEMA_VERSION,
|
|
20545
20888
|
specVersion: deps.specVersion,
|
|
20546
20889
|
implVersion: VERSION,
|
|
20547
|
-
db:
|
|
20890
|
+
db: existsSync25(deps.dbPath) ? "present" : "missing",
|
|
20548
20891
|
cwd: deps.cwd,
|
|
20549
20892
|
dbPath: deps.dbPath
|
|
20550
20893
|
};
|
|
@@ -20657,9 +21000,9 @@ import { HTTPException as HTTPException6 } from "hono/http-exception";
|
|
|
20657
21000
|
|
|
20658
21001
|
// server/node-body.ts
|
|
20659
21002
|
import { readFile as readFile4 } from "fs/promises";
|
|
20660
|
-
import { isAbsolute as
|
|
21003
|
+
import { isAbsolute as isAbsolute10, resolve as resolvePath2, relative as relativePath, sep as sep5 } from "path";
|
|
20661
21004
|
async function readNodeBody(cwd, relPath) {
|
|
20662
|
-
if (
|
|
21005
|
+
if (isAbsolute10(relPath)) return null;
|
|
20663
21006
|
const absRoot = resolvePath2(cwd);
|
|
20664
21007
|
const absFile = resolvePath2(absRoot, relPath);
|
|
20665
21008
|
const rel = relativePath(absRoot, absFile);
|
|
@@ -21379,12 +21722,12 @@ var parsePatchBody2 = makeBodyValidator(PATCH_BODY_SCHEMA, {
|
|
|
21379
21722
|
import { HTTPException as HTTPException10 } from "hono/http-exception";
|
|
21380
21723
|
|
|
21381
21724
|
// server/util/skillmapignore-io.ts
|
|
21382
|
-
import { existsSync as
|
|
21383
|
-
import { resolve as
|
|
21725
|
+
import { existsSync as existsSync26, readFileSync as readFileSync19, writeFileSync as writeFileSync4 } from "fs";
|
|
21726
|
+
import { resolve as resolve34 } from "path";
|
|
21384
21727
|
var IGNORE_FILENAME2 = ".skillmapignore";
|
|
21385
21728
|
function readPatterns(cwd) {
|
|
21386
|
-
const path =
|
|
21387
|
-
if (!
|
|
21729
|
+
const path = resolve34(cwd, IGNORE_FILENAME2);
|
|
21730
|
+
if (!existsSync26(path)) return [];
|
|
21388
21731
|
let raw;
|
|
21389
21732
|
try {
|
|
21390
21733
|
raw = readFileSync19(path, "utf8");
|
|
@@ -21394,8 +21737,8 @@ function readPatterns(cwd) {
|
|
|
21394
21737
|
return raw.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#"));
|
|
21395
21738
|
}
|
|
21396
21739
|
function writePatterns(cwd, nextPatterns) {
|
|
21397
|
-
const path =
|
|
21398
|
-
const prior =
|
|
21740
|
+
const path = resolve34(cwd, IGNORE_FILENAME2);
|
|
21741
|
+
const prior = existsSync26(path) ? safeRead(path) : "";
|
|
21399
21742
|
const content = buildContent(prior, nextPatterns);
|
|
21400
21743
|
writeFileSync4(path, content, "utf8");
|
|
21401
21744
|
}
|
|
@@ -21747,41 +22090,6 @@ var parsePatchBody4 = makeBodyValidator(PATCH_BODY_SCHEMA3, {
|
|
|
21747
22090
|
// server/routes/active-provider.ts
|
|
21748
22091
|
import { existsSync as existsSync27 } from "fs";
|
|
21749
22092
|
import { HTTPException as HTTPException12 } from "hono/http-exception";
|
|
21750
|
-
|
|
21751
|
-
// core/config/active-provider.ts
|
|
21752
|
-
import { existsSync as existsSync26 } from "fs";
|
|
21753
|
-
import { join as join17 } from "path";
|
|
21754
|
-
var DETECTION_RULES = [
|
|
21755
|
-
{ providerId: "claude", marker: ".claude" },
|
|
21756
|
-
{ providerId: "gemini", marker: ".gemini" },
|
|
21757
|
-
{ providerId: "openai", marker: ".codex" },
|
|
21758
|
-
{ providerId: "openai", marker: "AGENTS.md" },
|
|
21759
|
-
{ providerId: "cursor", marker: ".cursor" }
|
|
21760
|
-
];
|
|
21761
|
-
function resolveActiveProvider(cwd) {
|
|
21762
|
-
const detected = detectProvidersFromFilesystem(cwd);
|
|
21763
|
-
const fromConfig = readConfigValue("activeProvider", { cwd });
|
|
21764
|
-
if (typeof fromConfig === "string" && fromConfig.length > 0) {
|
|
21765
|
-
return { resolved: fromConfig, source: "config", detected };
|
|
21766
|
-
}
|
|
21767
|
-
if (detected.length > 0) {
|
|
21768
|
-
return { resolved: detected[0], source: "autodetect", detected };
|
|
21769
|
-
}
|
|
21770
|
-
return { resolved: null, source: "none", detected };
|
|
21771
|
-
}
|
|
21772
|
-
function detectProvidersFromFilesystem(cwd) {
|
|
21773
|
-
const seen = /* @__PURE__ */ new Set();
|
|
21774
|
-
const out = [];
|
|
21775
|
-
for (const rule of DETECTION_RULES) {
|
|
21776
|
-
if (seen.has(rule.providerId)) continue;
|
|
21777
|
-
if (!existsSync26(join17(cwd, rule.marker))) continue;
|
|
21778
|
-
seen.add(rule.providerId);
|
|
21779
|
-
out.push(rule.providerId);
|
|
21780
|
-
}
|
|
21781
|
-
return out;
|
|
21782
|
-
}
|
|
21783
|
-
|
|
21784
|
-
// server/routes/active-provider.ts
|
|
21785
22093
|
function registerActiveProviderRoute(app, deps) {
|
|
21786
22094
|
app.get("/api/active-provider", (c) => {
|
|
21787
22095
|
return c.json(buildEnvelope4(deps));
|
|
@@ -21850,14 +22158,14 @@ async function withScanMutex(fn) {
|
|
|
21850
22158
|
if (inFlight !== null) {
|
|
21851
22159
|
throw new ScanBusyError();
|
|
21852
22160
|
}
|
|
21853
|
-
let
|
|
22161
|
+
let resolve39;
|
|
21854
22162
|
inFlight = new Promise((r) => {
|
|
21855
|
-
|
|
22163
|
+
resolve39 = r;
|
|
21856
22164
|
});
|
|
21857
22165
|
try {
|
|
21858
22166
|
return await fn();
|
|
21859
22167
|
} finally {
|
|
21860
|
-
|
|
22168
|
+
resolve39();
|
|
21861
22169
|
inFlight = null;
|
|
21862
22170
|
}
|
|
21863
22171
|
}
|
|
@@ -22023,7 +22331,11 @@ async function runPersistedScan(c, deps) {
|
|
|
22023
22331
|
pluginRuntime: deps.pluginRuntime,
|
|
22024
22332
|
resolveEnabledOverride,
|
|
22025
22333
|
printer: bffScanRunnerPrinter,
|
|
22026
|
-
emitterFactory: () => buildBroadcasterEmitter(deps.broadcaster)
|
|
22334
|
+
emitterFactory: () => buildBroadcasterEmitter(deps.broadcaster),
|
|
22335
|
+
// BFF has no TTY; ambiguous activeProvider must be resolved by
|
|
22336
|
+
// the operator via the Settings UI (PATCH /api/active-provider)
|
|
22337
|
+
// before the scan, not via interactive prompt here.
|
|
22338
|
+
yes: true
|
|
22027
22339
|
});
|
|
22028
22340
|
if (outcome.kind !== "ok") {
|
|
22029
22341
|
throw new HTTPException13(500, {
|
|
@@ -22124,7 +22436,10 @@ async function runFreshScan(deps) {
|
|
|
22124
22436
|
// fallback. The fresh-scan response body IS the ScanResult JSON,
|
|
22125
22437
|
// so `data` is never used here; warn/info/error route through
|
|
22126
22438
|
// `log.warn` (same surface the rest of the BFF uses).
|
|
22127
|
-
printer: bffScanRunnerPrinter
|
|
22439
|
+
printer: bffScanRunnerPrinter,
|
|
22440
|
+
// BFF has no TTY; ambiguous activeProvider is the operator's
|
|
22441
|
+
// problem to resolve via the Settings UI, not via prompt here.
|
|
22442
|
+
yes: true
|
|
22128
22443
|
});
|
|
22129
22444
|
if (outcome.kind !== "ok") {
|
|
22130
22445
|
throw new HTTPException13(500, {
|
|
@@ -22162,7 +22477,7 @@ function emptyScanResult() {
|
|
|
22162
22477
|
|
|
22163
22478
|
// server/routes/sidecar.ts
|
|
22164
22479
|
import { HTTPException as HTTPException14 } from "hono/http-exception";
|
|
22165
|
-
import { resolve as
|
|
22480
|
+
import { resolve as resolve35 } from "path";
|
|
22166
22481
|
var STATUS_FRESH = "fresh";
|
|
22167
22482
|
var ENVELOPE_KIND2 = "sidecar.bumped";
|
|
22168
22483
|
var BUMP_BODY_SCHEMA = {
|
|
@@ -22196,7 +22511,7 @@ function registerSidecarRoutes(app, deps) {
|
|
|
22196
22511
|
let absPath;
|
|
22197
22512
|
try {
|
|
22198
22513
|
assertContained(deps.runtimeContext.cwd, node.path);
|
|
22199
|
-
absPath =
|
|
22514
|
+
absPath = resolve35(deps.runtimeContext.cwd, node.path);
|
|
22200
22515
|
} catch (err) {
|
|
22201
22516
|
throw new HTTPException14(500, { message: formatErrorMessage(err) });
|
|
22202
22517
|
}
|
|
@@ -22322,7 +22637,7 @@ function registerUpdateStatusRoute(app, deps) {
|
|
|
22322
22637
|
// server/static.ts
|
|
22323
22638
|
import { existsSync as existsSync28 } from "fs";
|
|
22324
22639
|
import { readFile as readFile5 } from "fs/promises";
|
|
22325
|
-
import { extname, join as
|
|
22640
|
+
import { extname, join as join19 } from "path";
|
|
22326
22641
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
22327
22642
|
var INDEX_HTML = "index.html";
|
|
22328
22643
|
var PLACEHOLDER_HTML = `<!doctype html>
|
|
@@ -22374,7 +22689,7 @@ function createSpaFallback(opts) {
|
|
|
22374
22689
|
return async (c, _next) => {
|
|
22375
22690
|
if (c.req.method !== "GET" && c.req.method !== "HEAD") return c.notFound();
|
|
22376
22691
|
if (opts.uiDist === null) return htmlResponse(c, placeholder);
|
|
22377
|
-
const indexPath =
|
|
22692
|
+
const indexPath = join19(opts.uiDist, INDEX_HTML);
|
|
22378
22693
|
if (!existsSync28(indexPath)) return htmlResponse(c, placeholder);
|
|
22379
22694
|
return fileResponse(c, indexPath);
|
|
22380
22695
|
};
|
|
@@ -22948,9 +23263,9 @@ function validateNoUi(noUi, uiDist) {
|
|
|
22948
23263
|
|
|
22949
23264
|
// server/paths.ts
|
|
22950
23265
|
import { existsSync as existsSync29, statSync as statSync11 } from "fs";
|
|
22951
|
-
import { dirname as dirname18, isAbsolute as
|
|
23266
|
+
import { dirname as dirname18, isAbsolute as isAbsolute11, join as join20, resolve as resolve36 } from "path";
|
|
22952
23267
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
22953
|
-
var DEFAULT_UI_REL =
|
|
23268
|
+
var DEFAULT_UI_REL = join20("ui", "dist", "ui", "browser");
|
|
22954
23269
|
var PACKAGE_UI_REL = "ui";
|
|
22955
23270
|
var INDEX_HTML2 = "index.html";
|
|
22956
23271
|
function resolveDefaultUiDist(ctx) {
|
|
@@ -22959,13 +23274,13 @@ function resolveDefaultUiDist(ctx) {
|
|
|
22959
23274
|
return walkUpForUi(ctx.cwd);
|
|
22960
23275
|
}
|
|
22961
23276
|
function resolveExplicitUiDist(ctx, raw) {
|
|
22962
|
-
return
|
|
23277
|
+
return isAbsolute11(raw) ? raw : resolve36(ctx.cwd, raw);
|
|
22963
23278
|
}
|
|
22964
23279
|
function isUiBundleDir(path) {
|
|
22965
23280
|
if (!existsSync29(path)) return false;
|
|
22966
23281
|
try {
|
|
22967
23282
|
if (!statSync11(path).isDirectory()) return false;
|
|
22968
|
-
return existsSync29(
|
|
23283
|
+
return existsSync29(join20(path, INDEX_HTML2));
|
|
22969
23284
|
} catch {
|
|
22970
23285
|
return false;
|
|
22971
23286
|
}
|
|
@@ -22982,9 +23297,9 @@ function resolvePackageBundledUi() {
|
|
|
22982
23297
|
function resolvePackageBundledUiFrom(here) {
|
|
22983
23298
|
let current = here;
|
|
22984
23299
|
for (let i = 0; i < 8; i++) {
|
|
22985
|
-
const candidate =
|
|
23300
|
+
const candidate = join20(current, PACKAGE_UI_REL);
|
|
22986
23301
|
if (isUiBundleDir(candidate)) return candidate;
|
|
22987
|
-
const distHere =
|
|
23302
|
+
const distHere = join20(current, "dist", PACKAGE_UI_REL);
|
|
22988
23303
|
if (isUiBundleDir(distHere)) return distHere;
|
|
22989
23304
|
const parent = dirname18(current);
|
|
22990
23305
|
if (parent === current) return null;
|
|
@@ -22993,9 +23308,9 @@ function resolvePackageBundledUiFrom(here) {
|
|
|
22993
23308
|
return null;
|
|
22994
23309
|
}
|
|
22995
23310
|
function walkUpForUi(startDir) {
|
|
22996
|
-
let current =
|
|
23311
|
+
let current = resolve36(startDir);
|
|
22997
23312
|
for (let i = 0; i < 64; i++) {
|
|
22998
|
-
const candidate =
|
|
23313
|
+
const candidate = join20(current, DEFAULT_UI_REL);
|
|
22999
23314
|
if (isUiBundleDir(candidate)) return candidate;
|
|
23000
23315
|
const parent = dirname18(current);
|
|
23001
23316
|
if (parent === current) return null;
|
|
@@ -23197,7 +23512,7 @@ var SERVE_TEXTS = {
|
|
|
23197
23512
|
};
|
|
23198
23513
|
|
|
23199
23514
|
// cli/util/serve-banner.ts
|
|
23200
|
-
import { relative as relative7, isAbsolute as
|
|
23515
|
+
import { relative as relative7, isAbsolute as isAbsolute12 } from "path";
|
|
23201
23516
|
var ESC2 = {
|
|
23202
23517
|
reset: "\x1B[0m",
|
|
23203
23518
|
bold: "\x1B[1m",
|
|
@@ -23331,9 +23646,9 @@ function resolveAnsi(colorEnabled) {
|
|
|
23331
23646
|
}
|
|
23332
23647
|
function formatDbPath(dbPath, cwd) {
|
|
23333
23648
|
const safe = sanitizeForTerminal(dbPath);
|
|
23334
|
-
if (!
|
|
23649
|
+
if (!isAbsolute12(safe)) return safe;
|
|
23335
23650
|
const rel = relative7(cwd, safe);
|
|
23336
|
-
if (rel === "" || rel.startsWith("..") ||
|
|
23651
|
+
if (rel === "" || rel.startsWith("..") || isAbsolute12(rel)) {
|
|
23337
23652
|
return safe;
|
|
23338
23653
|
}
|
|
23339
23654
|
return rel;
|
|
@@ -23933,7 +24248,7 @@ function rankConfidenceForGrouping(c) {
|
|
|
23933
24248
|
|
|
23934
24249
|
// cli/commands/sidecar.ts
|
|
23935
24250
|
import { existsSync as existsSync31, unlinkSync as unlinkSync2 } from "fs";
|
|
23936
|
-
import { resolve as
|
|
24251
|
+
import { resolve as resolve37 } from "path";
|
|
23937
24252
|
import { Command as Command35, Option as Option33 } from "clipanion";
|
|
23938
24253
|
|
|
23939
24254
|
// cli/i18n/sidecar.texts.ts
|
|
@@ -24084,7 +24399,7 @@ var SidecarRefreshCommand = class extends SmCommand {
|
|
|
24084
24399
|
let absPath;
|
|
24085
24400
|
try {
|
|
24086
24401
|
assertContained(ctx.cwd, node.path);
|
|
24087
|
-
absPath =
|
|
24402
|
+
absPath = resolve37(ctx.cwd, node.path);
|
|
24088
24403
|
} catch (err) {
|
|
24089
24404
|
this.printer.error(
|
|
24090
24405
|
tx(SIDECAR_TEXTS.refreshFailed, { glyph: errGlyph, message: formatErrorMessage(err) })
|
|
@@ -24365,7 +24680,7 @@ var SidecarAnnotateCommand = class extends SmCommand {
|
|
|
24365
24680
|
let absPath;
|
|
24366
24681
|
try {
|
|
24367
24682
|
assertContained(ctx.cwd, node.path);
|
|
24368
|
-
absPath =
|
|
24683
|
+
absPath = resolve37(ctx.cwd, node.path);
|
|
24369
24684
|
} catch (err) {
|
|
24370
24685
|
this.printer.error(
|
|
24371
24686
|
tx(SIDECAR_TEXTS.annotateFailed, { glyph: errGlyph, message: formatErrorMessage(err) })
|
|
@@ -24614,7 +24929,7 @@ var STUB_COMMANDS = [
|
|
|
24614
24929
|
|
|
24615
24930
|
// cli/commands/tutorial.ts
|
|
24616
24931
|
import { cpSync as cpSync2, existsSync as existsSync32, mkdirSync as mkdirSync7, rmSync as rmSync2, statSync as statSync12 } from "fs";
|
|
24617
|
-
import { dirname as dirname19, join as
|
|
24932
|
+
import { dirname as dirname19, join as join21, resolve as resolve38 } from "path";
|
|
24618
24933
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
24619
24934
|
import { Command as Command37, Option as Option35 } from "clipanion";
|
|
24620
24935
|
|
|
@@ -24710,7 +25025,7 @@ var TutorialCommand = class extends SmCommand {
|
|
|
24710
25025
|
}
|
|
24711
25026
|
const variant = rawVariant ?? DEFAULT_VARIANT;
|
|
24712
25027
|
const spec = VARIANT_SPECS[variant];
|
|
24713
|
-
const targetDir =
|
|
25028
|
+
const targetDir = join21(ctx.cwd, ".claude", "skills", spec.slug);
|
|
24714
25029
|
const targetDisplay = `.claude/skills/${spec.slug}/`;
|
|
24715
25030
|
if (existsSync32(targetDir) && !this.force) {
|
|
24716
25031
|
this.printer.error(
|
|
@@ -24788,11 +25103,11 @@ function resolveSkillSourceDir(variant) {
|
|
|
24788
25103
|
const here = dirname19(fileURLToPath6(import.meta.url));
|
|
24789
25104
|
const candidates = [
|
|
24790
25105
|
// dev: src/cli/commands/ → repo-root .claude/skills/<slug>/
|
|
24791
|
-
|
|
25106
|
+
resolve38(here, "../../..", spec.sourceDir),
|
|
24792
25107
|
// bundled: dist/cli.js → dist/cli/tutorial/<slug> (sibling)
|
|
24793
|
-
|
|
25108
|
+
resolve38(here, "cli/tutorial", spec.slug),
|
|
24794
25109
|
// bundled fallback: any-depth → cli/tutorial/<slug>
|
|
24795
|
-
|
|
25110
|
+
resolve38(here, "../cli/tutorial", spec.slug)
|
|
24796
25111
|
];
|
|
24797
25112
|
for (const candidate of candidates) {
|
|
24798
25113
|
if (existsSync32(candidate) && statSync12(candidate).isDirectory()) {
|