@skill-map/cli 0.33.0 → 0.34.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/dist/cli.js +533 -220
- package/dist/cli.js.map +1 -1
- package/dist/index.js +509 -98
- package/dist/index.js.map +1 -1
- package/dist/kernel/index.d.ts +19 -0
- package/dist/kernel/index.js +509 -98
- package/dist/kernel/index.js.map +1 -1
- package/dist/ui/{chunk-OKFHCQNJ.js → chunk-K3VXQ5PI.js} +4 -4
- package/dist/ui/index.html +1 -1
- package/dist/ui/{main-H7FURBYT.js → main-OQDTVVQA.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.1",
|
|
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.
|
|
13586
|
+
if (!matchesProviderPrecondition(ex, opts.activeProvider)) return false;
|
|
13530
13587
|
return true;
|
|
13531
13588
|
});
|
|
13532
13589
|
const applicableQualifiedIds = new Set(
|
|
@@ -13550,10 +13607,11 @@ function matchesKindPrecondition(ex, kind) {
|
|
|
13550
13607
|
return kindOnly === kind;
|
|
13551
13608
|
});
|
|
13552
13609
|
}
|
|
13553
|
-
function matchesProviderPrecondition(ex,
|
|
13610
|
+
function matchesProviderPrecondition(ex, activeProvider) {
|
|
13554
13611
|
const providers = ex.precondition?.provider;
|
|
13555
13612
|
if (!providers || providers.length === 0) return true;
|
|
13556
|
-
|
|
13613
|
+
if (activeProvider === null) return false;
|
|
13614
|
+
return providers.includes(activeProvider);
|
|
13557
13615
|
}
|
|
13558
13616
|
function splitLegacy(applicableExtractors, applicableQualifiedIds, nodeHashCacheEligible) {
|
|
13559
13617
|
const cachedQualifiedIds = /* @__PURE__ */ new Set();
|
|
@@ -13654,6 +13712,65 @@ function classifyLinkSource(source, shortIdToQualified, cachedQualifiedIds, appl
|
|
|
13654
13712
|
return "obsolete";
|
|
13655
13713
|
}
|
|
13656
13714
|
|
|
13715
|
+
// kernel/orchestrator/lift-mention-confidence.ts
|
|
13716
|
+
function liftMentionConfidence(links, nodes) {
|
|
13717
|
+
if (!links.some((l) => l.kind === "mentions")) return;
|
|
13718
|
+
const byPath3 = /* @__PURE__ */ new Set();
|
|
13719
|
+
for (const node of nodes) byPath3.add(node.path);
|
|
13720
|
+
const byNormalizedName = indexByNormalizedName2(nodes);
|
|
13721
|
+
for (const link2 of links) {
|
|
13722
|
+
if (link2.kind !== "mentions") continue;
|
|
13723
|
+
if (isResolved2(link2, byPath3, byNormalizedName)) {
|
|
13724
|
+
link2.confidence = 1;
|
|
13725
|
+
}
|
|
13726
|
+
}
|
|
13727
|
+
}
|
|
13728
|
+
function isResolved2(link2, byPath3, byNormalizedName) {
|
|
13729
|
+
const normalized = link2.trigger?.normalizedTrigger;
|
|
13730
|
+
if (normalized) {
|
|
13731
|
+
const withoutSigil = normalized.replace(/^[/@]/, "").trim();
|
|
13732
|
+
if (byNormalizedName.has(withoutSigil)) return true;
|
|
13733
|
+
}
|
|
13734
|
+
if (byPath3.has(link2.target)) return true;
|
|
13735
|
+
return false;
|
|
13736
|
+
}
|
|
13737
|
+
function indexByNormalizedName2(nodes) {
|
|
13738
|
+
const out = /* @__PURE__ */ new Map();
|
|
13739
|
+
for (const node of nodes) {
|
|
13740
|
+
const raw = node.frontmatter?.["name"];
|
|
13741
|
+
const name = typeof raw === "string" ? raw : "";
|
|
13742
|
+
if (!name) continue;
|
|
13743
|
+
out.set(normalizeTrigger(name), true);
|
|
13744
|
+
}
|
|
13745
|
+
return out;
|
|
13746
|
+
}
|
|
13747
|
+
|
|
13748
|
+
// kernel/orchestrator/post-walk-transforms.ts
|
|
13749
|
+
var POST_WALK_TRANSFORMS = [
|
|
13750
|
+
{
|
|
13751
|
+
id: "dedupe-links",
|
|
13752
|
+
description: "Collapse identical (source, target, kind, normalizedTrigger) edges across extractors; union sources[] and pick max confidence on merge.",
|
|
13753
|
+
run(links) {
|
|
13754
|
+
return dedupeLinks(links);
|
|
13755
|
+
}
|
|
13756
|
+
},
|
|
13757
|
+
{
|
|
13758
|
+
id: "lift-mention-confidence",
|
|
13759
|
+
description: "Bump resolved `mentions` links to confidence 1.0 once the full node graph is known (post-merge polish).",
|
|
13760
|
+
run(links, nodes) {
|
|
13761
|
+
liftMentionConfidence(links, nodes);
|
|
13762
|
+
}
|
|
13763
|
+
}
|
|
13764
|
+
];
|
|
13765
|
+
function applyPostWalkTransforms(links, nodes, transforms = POST_WALK_TRANSFORMS) {
|
|
13766
|
+
let current = links;
|
|
13767
|
+
for (const transform of transforms) {
|
|
13768
|
+
const next = transform.run(current, nodes);
|
|
13769
|
+
if (next) current = next;
|
|
13770
|
+
}
|
|
13771
|
+
return current;
|
|
13772
|
+
}
|
|
13773
|
+
|
|
13657
13774
|
// kernel/orchestrator/renames.ts
|
|
13658
13775
|
function findHighConfidenceRenames(opts) {
|
|
13659
13776
|
const ops = [];
|
|
@@ -13797,8 +13914,8 @@ function computeDriftStatus(args2) {
|
|
|
13797
13914
|
}
|
|
13798
13915
|
|
|
13799
13916
|
// kernel/sidecar/discover-orphans.ts
|
|
13800
|
-
import { existsSync as
|
|
13801
|
-
import { join as
|
|
13917
|
+
import { existsSync as existsSync20, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
|
|
13918
|
+
import { join as join13, relative as relative4, sep as sep3 } from "path";
|
|
13802
13919
|
function discoverOrphanSidecars(roots, shouldSkip) {
|
|
13803
13920
|
const out = [];
|
|
13804
13921
|
for (const root of roots) {
|
|
@@ -13814,7 +13931,7 @@ function walk(root, current, shouldSkip, out) {
|
|
|
13814
13931
|
return;
|
|
13815
13932
|
}
|
|
13816
13933
|
for (const entry of entries) {
|
|
13817
|
-
const full =
|
|
13934
|
+
const full = join13(current, entry.name);
|
|
13818
13935
|
const rel = relative4(root, full).split(sep3).join("/");
|
|
13819
13936
|
if (shouldSkip(rel)) continue;
|
|
13820
13937
|
if (entry.isSymbolicLink()) continue;
|
|
@@ -13825,7 +13942,7 @@ function walk(root, current, shouldSkip, out) {
|
|
|
13825
13942
|
if (!entry.isFile()) continue;
|
|
13826
13943
|
if (!entry.name.endsWith(".sm")) continue;
|
|
13827
13944
|
const expectedMd = `${full.slice(0, -".sm".length)}.md`;
|
|
13828
|
-
if (
|
|
13945
|
+
if (existsSync20(expectedMd) && safeIsFile(expectedMd)) continue;
|
|
13829
13946
|
out.push({ sidecarPath: full, relativePath: rel, expectedMdPath: expectedMd });
|
|
13830
13947
|
}
|
|
13831
13948
|
}
|
|
@@ -13839,7 +13956,7 @@ function safeIsFile(path) {
|
|
|
13839
13956
|
|
|
13840
13957
|
// kernel/orchestrator/node-build.ts
|
|
13841
13958
|
import { createHash } from "crypto";
|
|
13842
|
-
import { existsSync as
|
|
13959
|
+
import { existsSync as existsSync21 } from "fs";
|
|
13843
13960
|
import { isAbsolute as isAbsolute6, resolve as resolvePath } from "path";
|
|
13844
13961
|
import "js-tiktoken/lite";
|
|
13845
13962
|
import yaml4 from "js-yaml";
|
|
@@ -14003,11 +14120,11 @@ function resolveSidecarOverlay(relativePath2, nodePathForIssue, roots, liveBodyH
|
|
|
14003
14120
|
}
|
|
14004
14121
|
function resolveAbsoluteMdPath(relativePath2, roots) {
|
|
14005
14122
|
if (isAbsolute6(relativePath2)) {
|
|
14006
|
-
return
|
|
14123
|
+
return existsSync21(relativePath2) ? relativePath2 : null;
|
|
14007
14124
|
}
|
|
14008
14125
|
for (const root of roots) {
|
|
14009
14126
|
const candidate = resolvePath(root, relativePath2);
|
|
14010
|
-
if (
|
|
14127
|
+
if (existsSync21(candidate)) return candidate;
|
|
14011
14128
|
}
|
|
14012
14129
|
return null;
|
|
14013
14130
|
}
|
|
@@ -14143,7 +14260,7 @@ async function processRawNode(raw, provider, wctx, accum, claimedPaths, nextInde
|
|
|
14143
14260
|
const cacheDecision = computeCacheDecision({
|
|
14144
14261
|
extractors: wctx.opts.extractors,
|
|
14145
14262
|
kind,
|
|
14146
|
-
|
|
14263
|
+
activeProvider: wctx.opts.activeProvider,
|
|
14147
14264
|
nodePath: raw.path,
|
|
14148
14265
|
bodyHash,
|
|
14149
14266
|
sidecarAnnotationsHash,
|
|
@@ -14357,9 +14474,10 @@ async function runScanInternal(_kernel, options) {
|
|
|
14357
14474
|
priorIndex: setup.priorIndex,
|
|
14358
14475
|
priorExtractorRuns: setup.priorExtractorRuns,
|
|
14359
14476
|
providerFrontmatter: setup.providerFrontmatter,
|
|
14360
|
-
pluginStores: options.pluginStores
|
|
14477
|
+
pluginStores: options.pluginStores,
|
|
14478
|
+
activeProvider: resolveActiveProviderOption(options.activeProvider, options.roots)
|
|
14361
14479
|
});
|
|
14362
|
-
walked.internalLinks =
|
|
14480
|
+
walked.internalLinks = applyPostWalkTransforms(walked.internalLinks, walked.nodes);
|
|
14363
14481
|
recomputeLinkCounts(walked.nodes, walked.internalLinks);
|
|
14364
14482
|
recomputeExternalRefsCount(walked.nodes, walked.externalLinks, walked.cachedPaths);
|
|
14365
14483
|
await dispatchExtractorCompleted(exts.extractors, emitter, hookDispatcher);
|
|
@@ -14475,17 +14593,27 @@ function validateRoots(roots) {
|
|
|
14475
14593
|
throw new Error(ORCHESTRATOR_TEXTS.runScanRootEmptyArray);
|
|
14476
14594
|
}
|
|
14477
14595
|
for (const root of roots) {
|
|
14478
|
-
if (!
|
|
14596
|
+
if (!existsSync22(root) || !statSync7(root).isDirectory()) {
|
|
14479
14597
|
throw new Error(tx(ORCHESTRATOR_TEXTS.runScanRootMissing, { root }));
|
|
14480
14598
|
}
|
|
14481
14599
|
}
|
|
14482
14600
|
}
|
|
14601
|
+
function resolveActiveProviderOption(optionValue, roots) {
|
|
14602
|
+
if (optionValue !== void 0) return optionValue;
|
|
14603
|
+
for (const root of roots) {
|
|
14604
|
+
const absRoot = isAbsolute7(root) ? root : resolve27(root);
|
|
14605
|
+
if (!existsSync22(absRoot)) continue;
|
|
14606
|
+
const detected = resolveActiveProvider(absRoot).resolved;
|
|
14607
|
+
if (detected !== null) return detected;
|
|
14608
|
+
}
|
|
14609
|
+
return null;
|
|
14610
|
+
}
|
|
14483
14611
|
|
|
14484
14612
|
// kernel/scan/watcher.ts
|
|
14485
|
-
import { resolve as
|
|
14613
|
+
import { resolve as resolve28, relative as relative5, sep as sep4 } from "path";
|
|
14486
14614
|
import chokidar from "chokidar";
|
|
14487
14615
|
function createChokidarWatcher(opts) {
|
|
14488
|
-
const absRoots = opts.roots.map((r) =>
|
|
14616
|
+
const absRoots = opts.roots.map((r) => resolve28(opts.cwd, r));
|
|
14489
14617
|
const ignoreFilterOpt = opts.ignoreFilter;
|
|
14490
14618
|
const getFilter = ignoreFilterOpt === void 0 ? void 0 : typeof ignoreFilterOpt === "function" ? ignoreFilterOpt : () => ignoreFilterOpt;
|
|
14491
14619
|
const ignored = getFilter ? (path) => {
|
|
@@ -14704,7 +14832,7 @@ function createKernel() {
|
|
|
14704
14832
|
|
|
14705
14833
|
// kernel/jobs/orphan-files.ts
|
|
14706
14834
|
import { readdirSync as readdirSync8, statSync as statSync8 } from "fs";
|
|
14707
|
-
import { join as
|
|
14835
|
+
import { join as join14, resolve as resolve29 } from "path";
|
|
14708
14836
|
function findOrphanJobFiles(jobsDir, referencedPaths) {
|
|
14709
14837
|
let entries;
|
|
14710
14838
|
try {
|
|
@@ -14722,7 +14850,7 @@ function findOrphanJobFiles(jobsDir, referencedPaths) {
|
|
|
14722
14850
|
if (!entry.isFile()) continue;
|
|
14723
14851
|
const name = entry.name;
|
|
14724
14852
|
if (!name.endsWith(".md")) continue;
|
|
14725
|
-
const abs =
|
|
14853
|
+
const abs = resolve29(join14(jobsDir, name));
|
|
14726
14854
|
if (!referencedPaths.has(abs)) orphans.push(abs);
|
|
14727
14855
|
}
|
|
14728
14856
|
orphans.sort();
|
|
@@ -14786,7 +14914,48 @@ var SCAN_RUNNER_TEXTS = {
|
|
|
14786
14914
|
* does not exist on disk. Surfaced once per missing root so the
|
|
14787
14915
|
* operator notices a typo without the walker silently swallowing it.
|
|
14788
14916
|
*/
|
|
14789
|
-
referenceWalkMissingRoot: 'scan.referencePaths: configured path "{{path}}" does not exist; skipped.'
|
|
14917
|
+
referenceWalkMissingRoot: 'scan.referencePaths: configured path "{{path}}" does not exist; skipped.',
|
|
14918
|
+
/**
|
|
14919
|
+
* Active-provider bootstrap: filesystem auto-detect found no
|
|
14920
|
+
* markers (`.claude/`, `.gemini/`, `.codex/`, `AGENTS.md`, `.cursor/`)
|
|
14921
|
+
* anywhere under cwd or the effective scan roots. Plain-markdown
|
|
14922
|
+
* projects keep scanning fine; provider-specific extractors silently
|
|
14923
|
+
* no-op for this scan.
|
|
14924
|
+
*/
|
|
14925
|
+
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.",
|
|
14926
|
+
/**
|
|
14927
|
+
* Active-provider bootstrap: filesystem auto-detect found exactly
|
|
14928
|
+
* one marker and persisted the detected id to project settings.
|
|
14929
|
+
*/
|
|
14930
|
+
activeProviderAutodetected: "Auto-detected activeProvider = {{id}} from filesystem markers; persisted to .skill-map/settings.json.",
|
|
14931
|
+
/**
|
|
14932
|
+
* Active-provider bootstrap: persistence of the auto-detected id
|
|
14933
|
+
* failed (permission, disk full, etc). Non-fatal; the scan
|
|
14934
|
+
* continues with the value in memory for this run.
|
|
14935
|
+
*/
|
|
14936
|
+
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.",
|
|
14937
|
+
/**
|
|
14938
|
+
* Active-provider bootstrap: ambiguous detection (2+ markers
|
|
14939
|
+
* present), interactive prompt header. Followed by one
|
|
14940
|
+
* `activeProviderPromptOption` per detected provider id.
|
|
14941
|
+
*/
|
|
14942
|
+
activeProviderPromptHeader: "Multiple provider markers detected. Pick the active lens for this project:",
|
|
14943
|
+
activeProviderPromptOption: " {{index}}) {{id}}",
|
|
14944
|
+
activeProviderPromptInput: "Enter the number or provider id: ",
|
|
14945
|
+
/**
|
|
14946
|
+
* Active-provider bootstrap: ambiguous detection under `--yes`. The
|
|
14947
|
+
* caller exits non-zero; this message names the candidates and how
|
|
14948
|
+
* to resolve.
|
|
14949
|
+
*/
|
|
14950
|
+
activeProviderAmbiguousUnderYes: "Multiple provider markers detected ({{candidates}}) and --yes is set. Set the lens explicitly with `sm config set activeProvider <id>` and re-run.",
|
|
14951
|
+
/**
|
|
14952
|
+
* Active lens points at a bundle the operator has disabled (via
|
|
14953
|
+
* `sm plugins disable <id>` or the Settings UI). Classification keeps
|
|
14954
|
+
* running because it's provider-driven, but the lens-gated extractors
|
|
14955
|
+
* for the disabled bundle silently no-op. Without this warning the
|
|
14956
|
+
* graph quietly differs from what the lens implies.
|
|
14957
|
+
*/
|
|
14958
|
+
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
14959
|
};
|
|
14791
14960
|
|
|
14792
14961
|
// core/runtime/scan-roots.ts
|
|
@@ -14800,7 +14969,7 @@ function resolveScanRoots(inputs) {
|
|
|
14800
14969
|
// core/runtime/reference-paths-walker.ts
|
|
14801
14970
|
import { readdirSync as readdirSync9, statSync as statSync9 } from "fs";
|
|
14802
14971
|
import { homedir as osHomedir2 } from "os";
|
|
14803
|
-
import { isAbsolute as
|
|
14972
|
+
import { isAbsolute as isAbsolute8, join as join15, resolve as resolve30 } from "path";
|
|
14804
14973
|
var REFERENCE_WALK_MAX_FILES = 5e4;
|
|
14805
14974
|
var SKIPPED_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
14806
14975
|
"node_modules",
|
|
@@ -14808,10 +14977,10 @@ var SKIPPED_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
|
14808
14977
|
SKILL_MAP_DIR
|
|
14809
14978
|
]);
|
|
14810
14979
|
function resolveScanPath(raw, cwd) {
|
|
14811
|
-
if (raw.startsWith("~/")) return
|
|
14812
|
-
if (raw === "~") return
|
|
14813
|
-
if (
|
|
14814
|
-
return
|
|
14980
|
+
if (raw.startsWith("~/")) return resolve30(join15(osHomedir2(), raw.slice(2)));
|
|
14981
|
+
if (raw === "~") return resolve30(osHomedir2());
|
|
14982
|
+
if (isAbsolute8(raw)) return resolve30(raw);
|
|
14983
|
+
return resolve30(cwd, raw);
|
|
14815
14984
|
}
|
|
14816
14985
|
function walkReferencePaths(rawRoots, cwd) {
|
|
14817
14986
|
const paths = /* @__PURE__ */ new Set();
|
|
@@ -14840,7 +15009,7 @@ function walkInto(dir, out) {
|
|
|
14840
15009
|
for (const entry of entries) {
|
|
14841
15010
|
if (out.size >= REFERENCE_WALK_MAX_FILES) return true;
|
|
14842
15011
|
if (entry.isSymbolicLink()) continue;
|
|
14843
|
-
const full =
|
|
15012
|
+
const full = join15(dir, entry.name);
|
|
14844
15013
|
if (entry.isDirectory()) {
|
|
14845
15014
|
if (SKIPPED_DIR_NAMES.has(entry.name)) continue;
|
|
14846
15015
|
if (walkInto(full, out)) return true;
|
|
@@ -14858,6 +15027,101 @@ function safeStat(path) {
|
|
|
14858
15027
|
}
|
|
14859
15028
|
}
|
|
14860
15029
|
|
|
15030
|
+
// core/runtime/active-provider-bootstrap.ts
|
|
15031
|
+
import { createInterface as createInterface2 } from "readline";
|
|
15032
|
+
import { isAbsolute as isAbsolute9, join as join16 } from "path";
|
|
15033
|
+
async function bootstrapActiveProvider(opts) {
|
|
15034
|
+
const fromCwd = resolveActiveProvider(opts.cwd);
|
|
15035
|
+
if (fromCwd.source === "config") {
|
|
15036
|
+
return { kind: "ok", activeProvider: fromCwd.resolved, source: "config" };
|
|
15037
|
+
}
|
|
15038
|
+
const detected = aggregateDetected(opts.cwd, opts.effectiveRoots, fromCwd.detected);
|
|
15039
|
+
if (detected.length === 0) {
|
|
15040
|
+
opts.printer.warn(SCAN_RUNNER_TEXTS.activeProviderNoMarkerWarning);
|
|
15041
|
+
return { kind: "ok", activeProvider: null, source: "none" };
|
|
15042
|
+
}
|
|
15043
|
+
if (detected.length === 1) {
|
|
15044
|
+
const picked2 = detected[0];
|
|
15045
|
+
persistActiveProvider(opts.cwd, picked2, opts.printer);
|
|
15046
|
+
return { kind: "ok", activeProvider: picked2, source: "autodetect" };
|
|
15047
|
+
}
|
|
15048
|
+
if (opts.yes) {
|
|
15049
|
+
return { kind: "ambiguous", detected };
|
|
15050
|
+
}
|
|
15051
|
+
const picked = await promptForLens(detected, opts.stdin, opts.stderr);
|
|
15052
|
+
if (picked === null) {
|
|
15053
|
+
return { kind: "ambiguous", detected };
|
|
15054
|
+
}
|
|
15055
|
+
persistActiveProvider(opts.cwd, picked, opts.printer);
|
|
15056
|
+
return { kind: "ok", activeProvider: picked, source: "autodetect" };
|
|
15057
|
+
}
|
|
15058
|
+
function aggregateDetected(cwd, effectiveRoots, cwdDetected) {
|
|
15059
|
+
const out = [];
|
|
15060
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15061
|
+
for (const id of cwdDetected) {
|
|
15062
|
+
if (seen.has(id)) continue;
|
|
15063
|
+
seen.add(id);
|
|
15064
|
+
out.push(id);
|
|
15065
|
+
}
|
|
15066
|
+
for (const root of effectiveRoots) {
|
|
15067
|
+
const absRoot = isAbsolute9(root) ? root : join16(cwd, root);
|
|
15068
|
+
const r = resolveActiveProvider(absRoot);
|
|
15069
|
+
for (const id of r.detected) {
|
|
15070
|
+
if (seen.has(id)) continue;
|
|
15071
|
+
seen.add(id);
|
|
15072
|
+
out.push(id);
|
|
15073
|
+
}
|
|
15074
|
+
}
|
|
15075
|
+
return out;
|
|
15076
|
+
}
|
|
15077
|
+
function persistActiveProvider(cwd, id, printer) {
|
|
15078
|
+
try {
|
|
15079
|
+
writeConfigValue("activeProvider", id, { target: "project", cwd });
|
|
15080
|
+
printer.info(tx(SCAN_RUNNER_TEXTS.activeProviderAutodetected, { id }));
|
|
15081
|
+
} catch (err) {
|
|
15082
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
15083
|
+
printer.warn(
|
|
15084
|
+
tx(SCAN_RUNNER_TEXTS.activeProviderPersistFailed, { id, message })
|
|
15085
|
+
);
|
|
15086
|
+
}
|
|
15087
|
+
}
|
|
15088
|
+
function warnIfLensBundleDisabled(args2) {
|
|
15089
|
+
if (args2.activeProvider === null) return;
|
|
15090
|
+
if (args2.resolveEnabled(args2.activeProvider)) return;
|
|
15091
|
+
args2.printer.warn(
|
|
15092
|
+
tx(SCAN_RUNNER_TEXTS.activeProviderBundleDisabledWarning, {
|
|
15093
|
+
id: args2.activeProvider
|
|
15094
|
+
})
|
|
15095
|
+
);
|
|
15096
|
+
}
|
|
15097
|
+
async function promptForLens(detected, stdin, stderr) {
|
|
15098
|
+
const lines = [SCAN_RUNNER_TEXTS.activeProviderPromptHeader];
|
|
15099
|
+
for (let i = 0; i < detected.length; i += 1) {
|
|
15100
|
+
lines.push(
|
|
15101
|
+
tx(SCAN_RUNNER_TEXTS.activeProviderPromptOption, {
|
|
15102
|
+
index: i + 1,
|
|
15103
|
+
id: detected[i]
|
|
15104
|
+
})
|
|
15105
|
+
);
|
|
15106
|
+
}
|
|
15107
|
+
stderr.write(lines.join("\n") + "\n");
|
|
15108
|
+
const rl = createInterface2({ input: stdin, output: stderr });
|
|
15109
|
+
try {
|
|
15110
|
+
const answer = await new Promise(
|
|
15111
|
+
(resolveP) => rl.question(SCAN_RUNNER_TEXTS.activeProviderPromptInput, resolveP)
|
|
15112
|
+
);
|
|
15113
|
+
const trimmed = answer.trim();
|
|
15114
|
+
const asNumber = Number.parseInt(trimmed, 10);
|
|
15115
|
+
if (!Number.isNaN(asNumber) && asNumber >= 1 && asNumber <= detected.length) {
|
|
15116
|
+
return detected[asNumber - 1];
|
|
15117
|
+
}
|
|
15118
|
+
const asId = detected.find((d) => d.toLowerCase() === trimmed.toLowerCase());
|
|
15119
|
+
return asId ?? null;
|
|
15120
|
+
} finally {
|
|
15121
|
+
rl.close();
|
|
15122
|
+
}
|
|
15123
|
+
}
|
|
15124
|
+
|
|
14861
15125
|
// core/runtime/scan-runner.ts
|
|
14862
15126
|
async function runScanForCommand(opts) {
|
|
14863
15127
|
const ctx = opts.ctx ?? defaultRuntimeContext();
|
|
@@ -14865,20 +15129,9 @@ async function runScanForCommand(opts) {
|
|
|
14865
15129
|
const kernel = createKernel();
|
|
14866
15130
|
const pluginRuntime = await preparePluginRuntime(opts, opts.printer);
|
|
14867
15131
|
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
|
-
}
|
|
15132
|
+
const scanInputs = loadScanInputs(opts, ctx);
|
|
15133
|
+
if ("kind" in scanInputs) return scanInputs;
|
|
15134
|
+
const { cfg, ignoreFilter, strict, effectiveRoots } = scanInputs;
|
|
14882
15135
|
let referenceablePaths;
|
|
14883
15136
|
if (cfg.scan.referencePaths.length > 0) {
|
|
14884
15137
|
const walk2 = walkReferencePaths(cfg.scan.referencePaths, ctx.cwd);
|
|
@@ -14887,6 +15140,9 @@ async function runScanForCommand(opts) {
|
|
|
14887
15140
|
}
|
|
14888
15141
|
const loadPrior = makePriorLoader(opts.noBuiltIns, strict);
|
|
14889
15142
|
const jobsDir = defaultProjectJobsDir(ctx);
|
|
15143
|
+
const lens = await resolveActiveLens(opts, ctx, effectiveRoots, pluginRuntime);
|
|
15144
|
+
if (lens.kind === "ambiguous-provider") return lens;
|
|
15145
|
+
const activeProvider = lens.activeProvider;
|
|
14890
15146
|
const runScanWith = makeScanRunner(
|
|
14891
15147
|
kernel,
|
|
14892
15148
|
opts,
|
|
@@ -14895,11 +15151,37 @@ async function runScanForCommand(opts) {
|
|
|
14895
15151
|
strict,
|
|
14896
15152
|
extensions,
|
|
14897
15153
|
referenceablePaths,
|
|
14898
|
-
ctx.cwd
|
|
15154
|
+
ctx.cwd,
|
|
15155
|
+
activeProvider
|
|
14899
15156
|
);
|
|
14900
15157
|
const willPersist = !opts.noBuiltIns && !opts.dryRun;
|
|
14901
15158
|
return willPersist ? runPersistPath(opts, dbPath, jobsDir, strict, loadPrior, runScanWith, extensions) : runEphemeralPath(opts, dbPath, strict, loadPrior, runScanWith);
|
|
14902
15159
|
}
|
|
15160
|
+
async function resolveActiveLens(opts, ctx, effectiveRoots, pluginRuntime) {
|
|
15161
|
+
const bootstrap = await bootstrapActiveProvider({
|
|
15162
|
+
cwd: ctx.cwd,
|
|
15163
|
+
effectiveRoots,
|
|
15164
|
+
yes: opts.yes ?? false,
|
|
15165
|
+
stdin: opts.stdin ?? process.stdin,
|
|
15166
|
+
stderr: opts.stderr,
|
|
15167
|
+
printer: opts.printer
|
|
15168
|
+
});
|
|
15169
|
+
if (bootstrap.kind === "ambiguous") {
|
|
15170
|
+
return {
|
|
15171
|
+
kind: "ambiguous-provider",
|
|
15172
|
+
detected: bootstrap.detected,
|
|
15173
|
+
message: tx(SCAN_RUNNER_TEXTS.activeProviderAmbiguousUnderYes, {
|
|
15174
|
+
candidates: bootstrap.detected.join(", ")
|
|
15175
|
+
})
|
|
15176
|
+
};
|
|
15177
|
+
}
|
|
15178
|
+
warnIfLensBundleDisabled({
|
|
15179
|
+
activeProvider: bootstrap.activeProvider,
|
|
15180
|
+
resolveEnabled: opts.resolveEnabledOverride ?? pluginRuntime.resolveEnabled,
|
|
15181
|
+
printer: opts.printer
|
|
15182
|
+
});
|
|
15183
|
+
return { kind: "ok", activeProvider: bootstrap.activeProvider };
|
|
15184
|
+
}
|
|
14903
15185
|
function emitReferenceWalkAdvisory(walk2, opts) {
|
|
14904
15186
|
if (walk2.truncated) {
|
|
14905
15187
|
opts.printer.warn(SCAN_RUNNER_TEXTS.referenceWalkTruncated);
|
|
@@ -14933,6 +15215,17 @@ function registerExtensions(kernel, pluginRuntime, opts) {
|
|
|
14933
15215
|
registerEnabledExtensions(kernel, pluginRuntime, registerOpts);
|
|
14934
15216
|
return extensions;
|
|
14935
15217
|
}
|
|
15218
|
+
function loadScanInputs(opts, ctx) {
|
|
15219
|
+
try {
|
|
15220
|
+
const cfg = loadConfig({ strict: opts.strict, ...ctx }).effective;
|
|
15221
|
+
const ignoreFilter = buildScanIgnoreFilter(cfg, ctx.cwd);
|
|
15222
|
+
const strict = opts.strict || cfg.scan.strict === true;
|
|
15223
|
+
const effectiveRoots = resolveScanRoots({ positionalRoots: opts.roots });
|
|
15224
|
+
return { cfg, ignoreFilter, strict, effectiveRoots };
|
|
15225
|
+
} catch (err) {
|
|
15226
|
+
return { kind: "config-error", message: formatErrorMessage(err) };
|
|
15227
|
+
}
|
|
15228
|
+
}
|
|
14936
15229
|
function buildScanIgnoreFilter(cfg, cwd) {
|
|
14937
15230
|
const ignoreFileText = readIgnoreFileText(cwd);
|
|
14938
15231
|
const ignoreFilterOpts = {};
|
|
@@ -14955,7 +15248,7 @@ function makePriorLoader(noBuiltIns, strict) {
|
|
|
14955
15248
|
return loaded;
|
|
14956
15249
|
};
|
|
14957
15250
|
}
|
|
14958
|
-
function makeScanRunner(kernel, opts, effectiveRoots, ignoreFilter, strict, extensions, referenceablePaths, scanCwd) {
|
|
15251
|
+
function makeScanRunner(kernel, opts, effectiveRoots, ignoreFilter, strict, extensions, referenceablePaths, scanCwd, activeProvider) {
|
|
14959
15252
|
return async (prior, priorExtractorRuns, orphanJobFiles) => {
|
|
14960
15253
|
if (opts.changed && prior === null) {
|
|
14961
15254
|
opts.stderr.write(SCAN_RUNNER_TEXTS.changedNoPriorWarning);
|
|
@@ -14969,6 +15262,7 @@ function makeScanRunner(kernel, opts, effectiveRoots, ignoreFilter, strict, exte
|
|
|
14969
15262
|
referenceablePaths,
|
|
14970
15263
|
cwd: scanCwd,
|
|
14971
15264
|
prior,
|
|
15265
|
+
activeProvider,
|
|
14972
15266
|
...priorExtractorRuns ? { priorExtractorRuns } : {},
|
|
14973
15267
|
...orphanJobFiles ? { orphanJobFiles } : {}
|
|
14974
15268
|
});
|
|
@@ -14989,7 +15283,8 @@ function buildRunScanOptions(args2) {
|
|
|
14989
15283
|
// visible from this caller" (legacy behaviour). The orchestrator
|
|
14990
15284
|
// defaults to `[]` when the field is absent; we always pass the
|
|
14991
15285
|
// array (possibly empty) to keep the wiring uniform.
|
|
14992
|
-
orphanJobFiles: orphanJobFiles ?? []
|
|
15286
|
+
orphanJobFiles: orphanJobFiles ?? [],
|
|
15287
|
+
activeProvider: args2.activeProvider
|
|
14993
15288
|
};
|
|
14994
15289
|
if (args2.extensions) runOptions.extensions = args2.extensions;
|
|
14995
15290
|
if (prior) {
|
|
@@ -15132,7 +15427,7 @@ var InitCommand = class extends SmCommand {
|
|
|
15132
15427
|
async run() {
|
|
15133
15428
|
const ctx = defaultRuntimeContext();
|
|
15134
15429
|
const scopeRoot = ctx.cwd;
|
|
15135
|
-
const skillMapDir =
|
|
15430
|
+
const skillMapDir = join17(scopeRoot, SKILL_MAP_DIR);
|
|
15136
15431
|
const settingsPath = defaultSettingsPath(scopeRoot);
|
|
15137
15432
|
const localPath = defaultLocalSettingsPath(scopeRoot);
|
|
15138
15433
|
const ignorePath = defaultIgnoreFilePath(scopeRoot);
|
|
@@ -15178,7 +15473,7 @@ var InitCommand = class extends SmCommand {
|
|
|
15178
15473
|
const okGlyph = ansi.green("\u2713");
|
|
15179
15474
|
const updated = await ensureGitignoreEntries(scopeRoot, GITIGNORE_ENTRIES);
|
|
15180
15475
|
if (updated) {
|
|
15181
|
-
const gitignorePath =
|
|
15476
|
+
const gitignorePath = join17(scopeRoot, ".gitignore");
|
|
15182
15477
|
printer.info(
|
|
15183
15478
|
GITIGNORE_ENTRIES.length === 1 ? tx(INIT_TEXTS.gitignoreUpdatedSingular, { glyph: okGlyph, path: gitignorePath }) : tx(INIT_TEXTS.gitignoreUpdatedPlural, {
|
|
15184
15479
|
glyph: okGlyph,
|
|
@@ -15217,7 +15512,7 @@ async function dryRunFileMessage(path) {
|
|
|
15217
15512
|
}
|
|
15218
15513
|
async function writeDryRunGitignorePlan(printer, scopeRoot) {
|
|
15219
15514
|
const wouldAdd = await previewGitignoreEntries(scopeRoot, GITIGNORE_ENTRIES);
|
|
15220
|
-
const gitignorePath =
|
|
15515
|
+
const gitignorePath = join17(scopeRoot, ".gitignore");
|
|
15221
15516
|
if (wouldAdd.length === 0) {
|
|
15222
15517
|
printer.info(tx(INIT_TEXTS.dryRunWouldLeaveGitignoreUnchanged, { path: gitignorePath }));
|
|
15223
15518
|
} else if (wouldAdd.length === 1) {
|
|
@@ -15256,7 +15551,14 @@ async function runFirstScan(scopeRoot, strict, printer, stderr, ansi) {
|
|
|
15256
15551
|
strict,
|
|
15257
15552
|
stderr,
|
|
15258
15553
|
printer,
|
|
15259
|
-
ctx: { cwd: scopeRoot }
|
|
15554
|
+
ctx: { cwd: scopeRoot },
|
|
15555
|
+
// Init's first scan is a provisioning step, not the user's
|
|
15556
|
+
// primary "show me my graph" call. Don't block waiting for the
|
|
15557
|
+
// operator to disambiguate the lens here; let init complete with
|
|
15558
|
+
// `activeProvider` unset and let the FIRST explicit `sm scan`
|
|
15559
|
+
// surface the prompt. Treat the `ambiguous-provider` outcome below
|
|
15560
|
+
// as a soft hint, not a failure.
|
|
15561
|
+
yes: true
|
|
15260
15562
|
});
|
|
15261
15563
|
const errGlyph = ansi.red("\u2715");
|
|
15262
15564
|
if (outcome.kind === "config-error") {
|
|
@@ -15276,6 +15578,10 @@ async function runFirstScan(scopeRoot, strict, printer, stderr, ansi) {
|
|
|
15276
15578
|
);
|
|
15277
15579
|
return ExitCode.Error;
|
|
15278
15580
|
}
|
|
15581
|
+
if (outcome.kind === "ambiguous-provider") {
|
|
15582
|
+
printer.warn(outcome.message);
|
|
15583
|
+
return ExitCode.Ok;
|
|
15584
|
+
}
|
|
15279
15585
|
const result = outcome.result;
|
|
15280
15586
|
const hasErrors = result.issues.some((i) => i.severity === "error");
|
|
15281
15587
|
printer.info(
|
|
@@ -15292,7 +15598,7 @@ async function runFirstScan(scopeRoot, strict, printer, stderr, ansi) {
|
|
|
15292
15598
|
return hasErrors ? ExitCode.Issues : ExitCode.Ok;
|
|
15293
15599
|
}
|
|
15294
15600
|
async function previewGitignoreEntries(scopeRoot, entries) {
|
|
15295
|
-
const path =
|
|
15601
|
+
const path = join17(scopeRoot, ".gitignore");
|
|
15296
15602
|
const body = await pathExists(path) ? await readFile2(path, "utf8") : "";
|
|
15297
15603
|
const present = new Set(
|
|
15298
15604
|
body.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"))
|
|
@@ -15300,7 +15606,7 @@ async function previewGitignoreEntries(scopeRoot, entries) {
|
|
|
15300
15606
|
return entries.filter((entry) => !present.has(entry));
|
|
15301
15607
|
}
|
|
15302
15608
|
async function ensureGitignoreEntries(scopeRoot, entries) {
|
|
15303
|
-
const path =
|
|
15609
|
+
const path = join17(scopeRoot, ".gitignore");
|
|
15304
15610
|
let body = "";
|
|
15305
15611
|
if (await pathExists(path)) {
|
|
15306
15612
|
body = await readFile2(path, "utf8");
|
|
@@ -16865,8 +17171,8 @@ var PLUGINS_TEXTS = {
|
|
|
16865
17171
|
doctorIssueEntry: " {{glyph}} {{id}} {{status}}\n",
|
|
16866
17172
|
doctorIssueBody: " {{line}}\n",
|
|
16867
17173
|
// --- enable / disable -----------------------------------------------
|
|
16868
|
-
toggleBothIdAndAll: "{{glyph}} Pass either
|
|
16869
|
-
toggleNeitherIdNorAll: "{{glyph}} Pass <id> or --all.\n",
|
|
17174
|
+
toggleBothIdAndAll: "{{glyph}} Pass either one or more <id> arguments or --all, not both.\n",
|
|
17175
|
+
toggleNeitherIdNorAll: "{{glyph}} Pass one or more <id> arguments, or --all.\n",
|
|
16870
17176
|
toggleResolveError: "{{error}}",
|
|
16871
17177
|
toggleAppliedSingle: "{{verbPast}}: {{id}}\n",
|
|
16872
17178
|
toggleAppliedManyHeader: "{{verbPast}}: {{count}} plugin(s)\n",
|
|
@@ -16968,9 +17274,9 @@ var PLUGINS_TEXTS = {
|
|
|
16968
17274
|
};
|
|
16969
17275
|
|
|
16970
17276
|
// cli/commands/plugins/shared.ts
|
|
16971
|
-
import { resolve as
|
|
17277
|
+
import { resolve as resolve31 } from "path";
|
|
16972
17278
|
function resolveSearchPaths2(opts, cwd) {
|
|
16973
|
-
if (opts.pluginDir) return [
|
|
17279
|
+
if (opts.pluginDir) return [resolve31(opts.pluginDir)];
|
|
16974
17280
|
return [defaultProjectPluginsDir({ cwd })];
|
|
16975
17281
|
}
|
|
16976
17282
|
async function buildResolver() {
|
|
@@ -17852,7 +18158,7 @@ function buildDoctorJsonEnvelope(args2) {
|
|
|
17852
18158
|
import { Command as Command25, Option as Option24 } from "clipanion";
|
|
17853
18159
|
var TogglePluginsBase = class extends SmCommand {
|
|
17854
18160
|
all = Option24.Boolean("--all", false);
|
|
17855
|
-
|
|
18161
|
+
ids = Option24.Rest({ name: "ids" });
|
|
17856
18162
|
async toggle(enabled) {
|
|
17857
18163
|
const verb = enabled ? "enable" : "disable";
|
|
17858
18164
|
const stderrAnsi = this.ansiFor("stderr");
|
|
@@ -17871,23 +18177,24 @@ var TogglePluginsBase = class extends SmCommand {
|
|
|
17871
18177
|
return ExitCode.Ok;
|
|
17872
18178
|
}
|
|
17873
18179
|
/**
|
|
17874
|
-
* `--all` vs `<id
|
|
17875
|
-
* one must be present; surfaces a directed error on misuse.
|
|
18180
|
+
* `--all` vs `<id>...` mutex check. The two are mutually exclusive
|
|
18181
|
+
* and one must be present; surfaces a directed error on misuse.
|
|
18182
|
+
* Variadic positional accepts one or more ids.
|
|
17876
18183
|
*/
|
|
17877
18184
|
#validateArgs(ansi) {
|
|
17878
18185
|
const errGlyph = ansi.red("\u2715");
|
|
17879
|
-
if (this.all && this.
|
|
18186
|
+
if (this.all && this.ids.length > 0) {
|
|
17880
18187
|
this.printer.error(tx(PLUGINS_TEXTS.toggleBothIdAndAll, { glyph: errGlyph }));
|
|
17881
18188
|
return ExitCode.Error;
|
|
17882
18189
|
}
|
|
17883
|
-
if (!this.all &&
|
|
18190
|
+
if (!this.all && this.ids.length === 0) {
|
|
17884
18191
|
this.printer.error(tx(PLUGINS_TEXTS.toggleNeitherIdNorAll, { glyph: errGlyph }));
|
|
17885
18192
|
return ExitCode.Error;
|
|
17886
18193
|
}
|
|
17887
18194
|
return null;
|
|
17888
18195
|
}
|
|
17889
18196
|
/**
|
|
17890
|
-
* Resolve `<id
|
|
18197
|
+
* Resolve `<id>...` against the catalogue or fan out via `--all`.
|
|
17891
18198
|
* Returns the target list on success, or the exit code on a
|
|
17892
18199
|
* directed-error path (unknown id, granularity mismatch).
|
|
17893
18200
|
*
|
|
@@ -17898,25 +18205,35 @@ var TogglePluginsBase = class extends SmCommand {
|
|
|
17898
18205
|
* the directed error message when they try the bundle id directly,
|
|
17899
18206
|
* so `--all` skips them here too and the real "disable every core
|
|
17900
18207
|
* extension" intent is served by `--no-built-ins` on `sm scan`.
|
|
18208
|
+
*
|
|
18209
|
+
* Variadic mode is all-or-nothing: the first bad id aborts the
|
|
18210
|
+
* batch before any DB write, so the user never lands in a partial
|
|
18211
|
+
* state. Repeated ids in the same call are deduped.
|
|
17901
18212
|
*/
|
|
17902
18213
|
#pickTargets(catalogue, verb, ansi) {
|
|
17903
18214
|
if (this.all) {
|
|
17904
18215
|
return catalogue.filter((b) => b.granularity === "bundle").map((b) => b.id);
|
|
17905
18216
|
}
|
|
17906
|
-
const
|
|
17907
|
-
|
|
17908
|
-
|
|
17909
|
-
|
|
18217
|
+
const keys = [];
|
|
18218
|
+
for (const rawId of this.ids) {
|
|
18219
|
+
const resolved = resolveToggleTarget(rawId, catalogue, verb, ansi);
|
|
18220
|
+
if ("error" in resolved) {
|
|
18221
|
+
this.printer.error(tx(PLUGINS_TEXTS.toggleResolveError, { error: resolved.error }));
|
|
18222
|
+
return ExitCode.NotFound;
|
|
18223
|
+
}
|
|
18224
|
+
keys.push(resolved.key);
|
|
17910
18225
|
}
|
|
17911
|
-
return [
|
|
18226
|
+
return [...new Set(keys)];
|
|
17912
18227
|
}
|
|
17913
18228
|
/**
|
|
17914
|
-
* Host lock, see `src/kernel/config/locked-plugins.ts`.
|
|
17915
|
-
*
|
|
17916
|
-
* rest. Single-id mode
|
|
18229
|
+
* Host lock, see `src/kernel/config/locked-plugins.ts`. Bulk modes
|
|
18230
|
+
* (`--all` or an explicit batch of >1 ids) silently skip locked
|
|
18231
|
+
* targets so the user can still toggle the rest. Single-id mode
|
|
18232
|
+
* surfaces a directed exit-5 message so the user knows their one
|
|
18233
|
+
* intended target was refused.
|
|
17917
18234
|
*/
|
|
17918
18235
|
#applyLockGate(targets, ansi) {
|
|
17919
|
-
if (this.all) return targets.filter((id) => !isPluginLocked(id));
|
|
18236
|
+
if (this.all || this.ids.length > 1) return targets.filter((id) => !isPluginLocked(id));
|
|
17920
18237
|
const lockedHit = targets.find((id) => isPluginLocked(id));
|
|
17921
18238
|
if (!lockedHit) return targets;
|
|
17922
18239
|
this.printer.error(
|
|
@@ -17972,12 +18289,19 @@ var PluginsEnableCommand = class extends TogglePluginsBase {
|
|
|
17972
18289
|
static paths = [["plugins", "enable"]];
|
|
17973
18290
|
static usage = Command25.Usage({
|
|
17974
18291
|
category: "Plugins",
|
|
17975
|
-
description: "Enable
|
|
18292
|
+
description: "Enable one or more plugins (or --all). Persists in config_plugins.",
|
|
17976
18293
|
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
|
|
18294
|
+
Writes a row to config_plugins with enabled=1 per id. Takes
|
|
18295
|
+
precedence over the team-shared baseline at
|
|
18296
|
+
settings.json#/plugins/<id>/enabled. Use sm plugins disable to
|
|
18297
|
+
flip; sm config reset plugins.<id>.enabled drops the settings.json
|
|
18298
|
+
baseline.
|
|
18299
|
+
|
|
18300
|
+
Accepts one or more ids in one call, e.g.
|
|
18301
|
+
'sm plugins enable claude gemini openai'. Batches are
|
|
18302
|
+
all-or-nothing: a single unknown / mismatched id aborts before
|
|
18303
|
+
any write. Repeated ids are deduped. Locked plugins inside a
|
|
18304
|
+
batch are silently skipped.
|
|
17981
18305
|
|
|
17982
18306
|
Granularity: a bundle-granularity plugin (default for user plugins,
|
|
17983
18307
|
and the built-in 'claude' bundle) accepts only the bundle id. An
|
|
@@ -17994,12 +18318,18 @@ var PluginsDisableCommand = class extends TogglePluginsBase {
|
|
|
17994
18318
|
static paths = [["plugins", "disable"]];
|
|
17995
18319
|
static usage = Command25.Usage({
|
|
17996
18320
|
category: "Plugins",
|
|
17997
|
-
description: "Disable
|
|
18321
|
+
description: "Disable one or more plugins (or --all). Persists in config_plugins; does not delete files.",
|
|
17998
18322
|
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.
|
|
18323
|
+
Writes a row to config_plugins with enabled=0 per id. Discovery
|
|
18324
|
+
still surfaces the plugin in sm plugins list, but with
|
|
18325
|
+
status=disabled; its extensions are not imported and the kernel
|
|
18326
|
+
will not run them.
|
|
18327
|
+
|
|
18328
|
+
Accepts one or more ids in one call, e.g.
|
|
18329
|
+
'sm plugins disable gemini openai agent-skills'. Batches are
|
|
18330
|
+
all-or-nothing: a single unknown / mismatched id aborts before
|
|
18331
|
+
any write. Repeated ids are deduped. Locked plugins inside a
|
|
18332
|
+
batch are silently skipped.
|
|
18003
18333
|
|
|
18004
18334
|
Granularity: a bundle-granularity plugin (default for user plugins,
|
|
18005
18335
|
and the built-in 'claude' bundle) accepts only the bundle id. An
|
|
@@ -18105,8 +18435,8 @@ function resolveBareToggle(id, catalogue, verb, ansi) {
|
|
|
18105
18435
|
}
|
|
18106
18436
|
|
|
18107
18437
|
// cli/commands/plugins/create.ts
|
|
18108
|
-
import { existsSync as
|
|
18109
|
-
import { join as
|
|
18438
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync6, writeFileSync as writeFileSync3 } from "fs";
|
|
18439
|
+
import { join as join18, resolve as resolve32 } from "path";
|
|
18110
18440
|
import { Command as Command26, Option as Option25 } from "clipanion";
|
|
18111
18441
|
var PluginsCreateCommand = class extends SmCommand {
|
|
18112
18442
|
static paths = [["plugins", "create"]];
|
|
@@ -18132,8 +18462,8 @@ var PluginsCreateCommand = class extends SmCommand {
|
|
|
18132
18462
|
}
|
|
18133
18463
|
const ctx = defaultRuntimeContext();
|
|
18134
18464
|
const baseDir = defaultProjectPluginsDir(ctx);
|
|
18135
|
-
const targetDir = this.at ?
|
|
18136
|
-
if (
|
|
18465
|
+
const targetDir = this.at ? resolve32(this.at) : join18(baseDir, this.pluginId);
|
|
18466
|
+
if (existsSync23(targetDir) && !this.force) {
|
|
18137
18467
|
this.printer.error(
|
|
18138
18468
|
tx(PLUGINS_TEXTS.createRefuseOverwrite, {
|
|
18139
18469
|
glyph: errGlyph,
|
|
@@ -18143,7 +18473,7 @@ var PluginsCreateCommand = class extends SmCommand {
|
|
|
18143
18473
|
return ExitCode.Error;
|
|
18144
18474
|
}
|
|
18145
18475
|
const extractorName = `${this.pluginId}-extractor`;
|
|
18146
|
-
mkdirSync6(
|
|
18476
|
+
mkdirSync6(join18(targetDir, "extractors", extractorName), { recursive: true });
|
|
18147
18477
|
const specVersion = installedSpecVersion();
|
|
18148
18478
|
const manifest = {
|
|
18149
18479
|
id: this.pluginId,
|
|
@@ -18163,14 +18493,14 @@ var PluginsCreateCommand = class extends SmCommand {
|
|
|
18163
18493
|
}
|
|
18164
18494
|
};
|
|
18165
18495
|
writeFileSync3(
|
|
18166
|
-
|
|
18496
|
+
join18(targetDir, "plugin.json"),
|
|
18167
18497
|
JSON.stringify(manifest, null, 2) + "\n"
|
|
18168
18498
|
);
|
|
18169
18499
|
writeFileSync3(
|
|
18170
|
-
|
|
18500
|
+
join18(targetDir, "extractors", extractorName, "index.js"),
|
|
18171
18501
|
scaffolderExtractorStub(extractorName)
|
|
18172
18502
|
);
|
|
18173
|
-
writeFileSync3(
|
|
18503
|
+
writeFileSync3(join18(targetDir, "README.md"), scaffolderReadme(this.pluginId));
|
|
18174
18504
|
this.printer.data(
|
|
18175
18505
|
tx(PLUGINS_TEXTS.createSuccess, {
|
|
18176
18506
|
targetDir: sanitizeForTerminal(targetDir),
|
|
@@ -18373,7 +18703,7 @@ var PLUGIN_COMMANDS = [
|
|
|
18373
18703
|
|
|
18374
18704
|
// cli/commands/refresh.ts
|
|
18375
18705
|
import { readFile as readFile3 } from "fs/promises";
|
|
18376
|
-
import { resolve as
|
|
18706
|
+
import { resolve as resolve33 } from "path";
|
|
18377
18707
|
import { Command as Command29, Option as Option27 } from "clipanion";
|
|
18378
18708
|
|
|
18379
18709
|
// cli/i18n/refresh.texts.ts
|
|
@@ -18657,7 +18987,7 @@ var RefreshCommand = class extends SmCommand {
|
|
|
18657
18987
|
let body;
|
|
18658
18988
|
try {
|
|
18659
18989
|
assertContained(cwd, node.path);
|
|
18660
|
-
const raw = await readFile3(
|
|
18990
|
+
const raw = await readFile3(resolve33(cwd, node.path), "utf8");
|
|
18661
18991
|
body = stripFrontmatterFence(raw);
|
|
18662
18992
|
} catch (err) {
|
|
18663
18993
|
if (!this.json) {
|
|
@@ -19398,6 +19728,9 @@ var ScanCommand = class extends SmCommand {
|
|
|
19398
19728
|
watch = Option29.Boolean("--watch", false, {
|
|
19399
19729
|
description: "Long-running mode: watch the roots and trigger an incremental scan after each debounced batch of filesystem events. Alias of `sm watch`."
|
|
19400
19730
|
});
|
|
19731
|
+
yes = Option29.Boolean("--yes", false, {
|
|
19732
|
+
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."
|
|
19733
|
+
});
|
|
19401
19734
|
// Each branch in the orchestrator maps to one validation gate
|
|
19402
19735
|
// (--watch alias / --changed mutex / -g mutex / dispatch).
|
|
19403
19736
|
// Splitting per branch scatters the gate from the value it gates.
|
|
@@ -19426,9 +19759,11 @@ var ScanCommand = class extends SmCommand {
|
|
|
19426
19759
|
allowEmpty: this.allowEmpty,
|
|
19427
19760
|
strict: this.strict,
|
|
19428
19761
|
stderr: this.context.stderr,
|
|
19762
|
+
stdin: this.context.stdin,
|
|
19429
19763
|
printer: this.printer,
|
|
19430
19764
|
killSwitches: readConformanceKillSwitches(),
|
|
19431
|
-
colorEnabled
|
|
19765
|
+
colorEnabled,
|
|
19766
|
+
yes: this.yes
|
|
19432
19767
|
});
|
|
19433
19768
|
return outcome.kind === "ok" ? this.renderOutcome(outcome.result, outcome.persistedTo, outcome.dbPath, outcome.strict) : this.renderFailure(outcome);
|
|
19434
19769
|
}
|
|
@@ -19499,6 +19834,12 @@ var ScanCommand = class extends SmCommand {
|
|
|
19499
19834
|
);
|
|
19500
19835
|
return ExitCode.Error;
|
|
19501
19836
|
}
|
|
19837
|
+
if (outcome.kind === "ambiguous-provider") {
|
|
19838
|
+
this.printer.info(
|
|
19839
|
+
tx(SCAN_TEXTS.scanFailure, { glyph: errGlyph, message: outcome.message })
|
|
19840
|
+
);
|
|
19841
|
+
return ExitCode.Error;
|
|
19842
|
+
}
|
|
19502
19843
|
this.printer.info(
|
|
19503
19844
|
tx(SCAN_TEXTS.scanFailure, { glyph: errGlyph, message: outcome.message })
|
|
19504
19845
|
);
|
|
@@ -19581,7 +19922,7 @@ function plural(count, word) {
|
|
|
19581
19922
|
}
|
|
19582
19923
|
|
|
19583
19924
|
// cli/commands/scan-compare.ts
|
|
19584
|
-
import { existsSync as
|
|
19925
|
+
import { existsSync as existsSync24, readFileSync as readFileSync18 } from "fs";
|
|
19585
19926
|
import { Command as Command32, Option as Option30 } from "clipanion";
|
|
19586
19927
|
var ScanCompareCommand = class extends SmCommand {
|
|
19587
19928
|
static paths = [["scan", "compare-with"]];
|
|
@@ -19693,7 +20034,7 @@ var ScanCompareCommand = class extends SmCommand {
|
|
|
19693
20034
|
}
|
|
19694
20035
|
};
|
|
19695
20036
|
function loadAndValidateDump(path) {
|
|
19696
|
-
if (!
|
|
20037
|
+
if (!existsSync24(path)) {
|
|
19697
20038
|
throw new Error(tx(SCAN_TEXTS.compareDumpNotFound, { path }));
|
|
19698
20039
|
}
|
|
19699
20040
|
let raw;
|
|
@@ -20536,7 +20877,7 @@ function contentTypeFor(format) {
|
|
|
20536
20877
|
}
|
|
20537
20878
|
|
|
20538
20879
|
// server/health.ts
|
|
20539
|
-
import { existsSync as
|
|
20880
|
+
import { existsSync as existsSync25 } from "fs";
|
|
20540
20881
|
var FALLBACK_SCHEMA_VERSION = "1";
|
|
20541
20882
|
function buildHealth(deps) {
|
|
20542
20883
|
return {
|
|
@@ -20544,7 +20885,7 @@ function buildHealth(deps) {
|
|
|
20544
20885
|
schemaVersion: FALLBACK_SCHEMA_VERSION,
|
|
20545
20886
|
specVersion: deps.specVersion,
|
|
20546
20887
|
implVersion: VERSION,
|
|
20547
|
-
db:
|
|
20888
|
+
db: existsSync25(deps.dbPath) ? "present" : "missing",
|
|
20548
20889
|
cwd: deps.cwd,
|
|
20549
20890
|
dbPath: deps.dbPath
|
|
20550
20891
|
};
|
|
@@ -20657,9 +20998,9 @@ import { HTTPException as HTTPException6 } from "hono/http-exception";
|
|
|
20657
20998
|
|
|
20658
20999
|
// server/node-body.ts
|
|
20659
21000
|
import { readFile as readFile4 } from "fs/promises";
|
|
20660
|
-
import { isAbsolute as
|
|
21001
|
+
import { isAbsolute as isAbsolute10, resolve as resolvePath2, relative as relativePath, sep as sep5 } from "path";
|
|
20661
21002
|
async function readNodeBody(cwd, relPath) {
|
|
20662
|
-
if (
|
|
21003
|
+
if (isAbsolute10(relPath)) return null;
|
|
20663
21004
|
const absRoot = resolvePath2(cwd);
|
|
20664
21005
|
const absFile = resolvePath2(absRoot, relPath);
|
|
20665
21006
|
const rel = relativePath(absRoot, absFile);
|
|
@@ -21379,12 +21720,12 @@ var parsePatchBody2 = makeBodyValidator(PATCH_BODY_SCHEMA, {
|
|
|
21379
21720
|
import { HTTPException as HTTPException10 } from "hono/http-exception";
|
|
21380
21721
|
|
|
21381
21722
|
// server/util/skillmapignore-io.ts
|
|
21382
|
-
import { existsSync as
|
|
21383
|
-
import { resolve as
|
|
21723
|
+
import { existsSync as existsSync26, readFileSync as readFileSync19, writeFileSync as writeFileSync4 } from "fs";
|
|
21724
|
+
import { resolve as resolve34 } from "path";
|
|
21384
21725
|
var IGNORE_FILENAME2 = ".skillmapignore";
|
|
21385
21726
|
function readPatterns(cwd) {
|
|
21386
|
-
const path =
|
|
21387
|
-
if (!
|
|
21727
|
+
const path = resolve34(cwd, IGNORE_FILENAME2);
|
|
21728
|
+
if (!existsSync26(path)) return [];
|
|
21388
21729
|
let raw;
|
|
21389
21730
|
try {
|
|
21390
21731
|
raw = readFileSync19(path, "utf8");
|
|
@@ -21394,8 +21735,8 @@ function readPatterns(cwd) {
|
|
|
21394
21735
|
return raw.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#"));
|
|
21395
21736
|
}
|
|
21396
21737
|
function writePatterns(cwd, nextPatterns) {
|
|
21397
|
-
const path =
|
|
21398
|
-
const prior =
|
|
21738
|
+
const path = resolve34(cwd, IGNORE_FILENAME2);
|
|
21739
|
+
const prior = existsSync26(path) ? safeRead(path) : "";
|
|
21399
21740
|
const content = buildContent(prior, nextPatterns);
|
|
21400
21741
|
writeFileSync4(path, content, "utf8");
|
|
21401
21742
|
}
|
|
@@ -21747,41 +22088,6 @@ var parsePatchBody4 = makeBodyValidator(PATCH_BODY_SCHEMA3, {
|
|
|
21747
22088
|
// server/routes/active-provider.ts
|
|
21748
22089
|
import { existsSync as existsSync27 } from "fs";
|
|
21749
22090
|
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
22091
|
function registerActiveProviderRoute(app, deps) {
|
|
21786
22092
|
app.get("/api/active-provider", (c) => {
|
|
21787
22093
|
return c.json(buildEnvelope4(deps));
|
|
@@ -21850,14 +22156,14 @@ async function withScanMutex(fn) {
|
|
|
21850
22156
|
if (inFlight !== null) {
|
|
21851
22157
|
throw new ScanBusyError();
|
|
21852
22158
|
}
|
|
21853
|
-
let
|
|
22159
|
+
let resolve39;
|
|
21854
22160
|
inFlight = new Promise((r) => {
|
|
21855
|
-
|
|
22161
|
+
resolve39 = r;
|
|
21856
22162
|
});
|
|
21857
22163
|
try {
|
|
21858
22164
|
return await fn();
|
|
21859
22165
|
} finally {
|
|
21860
|
-
|
|
22166
|
+
resolve39();
|
|
21861
22167
|
inFlight = null;
|
|
21862
22168
|
}
|
|
21863
22169
|
}
|
|
@@ -22023,7 +22329,11 @@ async function runPersistedScan(c, deps) {
|
|
|
22023
22329
|
pluginRuntime: deps.pluginRuntime,
|
|
22024
22330
|
resolveEnabledOverride,
|
|
22025
22331
|
printer: bffScanRunnerPrinter,
|
|
22026
|
-
emitterFactory: () => buildBroadcasterEmitter(deps.broadcaster)
|
|
22332
|
+
emitterFactory: () => buildBroadcasterEmitter(deps.broadcaster),
|
|
22333
|
+
// BFF has no TTY; ambiguous activeProvider must be resolved by
|
|
22334
|
+
// the operator via the Settings UI (PATCH /api/active-provider)
|
|
22335
|
+
// before the scan, not via interactive prompt here.
|
|
22336
|
+
yes: true
|
|
22027
22337
|
});
|
|
22028
22338
|
if (outcome.kind !== "ok") {
|
|
22029
22339
|
throw new HTTPException13(500, {
|
|
@@ -22124,7 +22434,10 @@ async function runFreshScan(deps) {
|
|
|
22124
22434
|
// fallback. The fresh-scan response body IS the ScanResult JSON,
|
|
22125
22435
|
// so `data` is never used here; warn/info/error route through
|
|
22126
22436
|
// `log.warn` (same surface the rest of the BFF uses).
|
|
22127
|
-
printer: bffScanRunnerPrinter
|
|
22437
|
+
printer: bffScanRunnerPrinter,
|
|
22438
|
+
// BFF has no TTY; ambiguous activeProvider is the operator's
|
|
22439
|
+
// problem to resolve via the Settings UI, not via prompt here.
|
|
22440
|
+
yes: true
|
|
22128
22441
|
});
|
|
22129
22442
|
if (outcome.kind !== "ok") {
|
|
22130
22443
|
throw new HTTPException13(500, {
|
|
@@ -22162,7 +22475,7 @@ function emptyScanResult() {
|
|
|
22162
22475
|
|
|
22163
22476
|
// server/routes/sidecar.ts
|
|
22164
22477
|
import { HTTPException as HTTPException14 } from "hono/http-exception";
|
|
22165
|
-
import { resolve as
|
|
22478
|
+
import { resolve as resolve35 } from "path";
|
|
22166
22479
|
var STATUS_FRESH = "fresh";
|
|
22167
22480
|
var ENVELOPE_KIND2 = "sidecar.bumped";
|
|
22168
22481
|
var BUMP_BODY_SCHEMA = {
|
|
@@ -22196,7 +22509,7 @@ function registerSidecarRoutes(app, deps) {
|
|
|
22196
22509
|
let absPath;
|
|
22197
22510
|
try {
|
|
22198
22511
|
assertContained(deps.runtimeContext.cwd, node.path);
|
|
22199
|
-
absPath =
|
|
22512
|
+
absPath = resolve35(deps.runtimeContext.cwd, node.path);
|
|
22200
22513
|
} catch (err) {
|
|
22201
22514
|
throw new HTTPException14(500, { message: formatErrorMessage(err) });
|
|
22202
22515
|
}
|
|
@@ -22322,7 +22635,7 @@ function registerUpdateStatusRoute(app, deps) {
|
|
|
22322
22635
|
// server/static.ts
|
|
22323
22636
|
import { existsSync as existsSync28 } from "fs";
|
|
22324
22637
|
import { readFile as readFile5 } from "fs/promises";
|
|
22325
|
-
import { extname, join as
|
|
22638
|
+
import { extname, join as join19 } from "path";
|
|
22326
22639
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
22327
22640
|
var INDEX_HTML = "index.html";
|
|
22328
22641
|
var PLACEHOLDER_HTML = `<!doctype html>
|
|
@@ -22374,7 +22687,7 @@ function createSpaFallback(opts) {
|
|
|
22374
22687
|
return async (c, _next) => {
|
|
22375
22688
|
if (c.req.method !== "GET" && c.req.method !== "HEAD") return c.notFound();
|
|
22376
22689
|
if (opts.uiDist === null) return htmlResponse(c, placeholder);
|
|
22377
|
-
const indexPath =
|
|
22690
|
+
const indexPath = join19(opts.uiDist, INDEX_HTML);
|
|
22378
22691
|
if (!existsSync28(indexPath)) return htmlResponse(c, placeholder);
|
|
22379
22692
|
return fileResponse(c, indexPath);
|
|
22380
22693
|
};
|
|
@@ -22948,9 +23261,9 @@ function validateNoUi(noUi, uiDist) {
|
|
|
22948
23261
|
|
|
22949
23262
|
// server/paths.ts
|
|
22950
23263
|
import { existsSync as existsSync29, statSync as statSync11 } from "fs";
|
|
22951
|
-
import { dirname as dirname18, isAbsolute as
|
|
23264
|
+
import { dirname as dirname18, isAbsolute as isAbsolute11, join as join20, resolve as resolve36 } from "path";
|
|
22952
23265
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
22953
|
-
var DEFAULT_UI_REL =
|
|
23266
|
+
var DEFAULT_UI_REL = join20("ui", "dist", "ui", "browser");
|
|
22954
23267
|
var PACKAGE_UI_REL = "ui";
|
|
22955
23268
|
var INDEX_HTML2 = "index.html";
|
|
22956
23269
|
function resolveDefaultUiDist(ctx) {
|
|
@@ -22959,13 +23272,13 @@ function resolveDefaultUiDist(ctx) {
|
|
|
22959
23272
|
return walkUpForUi(ctx.cwd);
|
|
22960
23273
|
}
|
|
22961
23274
|
function resolveExplicitUiDist(ctx, raw) {
|
|
22962
|
-
return
|
|
23275
|
+
return isAbsolute11(raw) ? raw : resolve36(ctx.cwd, raw);
|
|
22963
23276
|
}
|
|
22964
23277
|
function isUiBundleDir(path) {
|
|
22965
23278
|
if (!existsSync29(path)) return false;
|
|
22966
23279
|
try {
|
|
22967
23280
|
if (!statSync11(path).isDirectory()) return false;
|
|
22968
|
-
return existsSync29(
|
|
23281
|
+
return existsSync29(join20(path, INDEX_HTML2));
|
|
22969
23282
|
} catch {
|
|
22970
23283
|
return false;
|
|
22971
23284
|
}
|
|
@@ -22982,9 +23295,9 @@ function resolvePackageBundledUi() {
|
|
|
22982
23295
|
function resolvePackageBundledUiFrom(here) {
|
|
22983
23296
|
let current = here;
|
|
22984
23297
|
for (let i = 0; i < 8; i++) {
|
|
22985
|
-
const candidate =
|
|
23298
|
+
const candidate = join20(current, PACKAGE_UI_REL);
|
|
22986
23299
|
if (isUiBundleDir(candidate)) return candidate;
|
|
22987
|
-
const distHere =
|
|
23300
|
+
const distHere = join20(current, "dist", PACKAGE_UI_REL);
|
|
22988
23301
|
if (isUiBundleDir(distHere)) return distHere;
|
|
22989
23302
|
const parent = dirname18(current);
|
|
22990
23303
|
if (parent === current) return null;
|
|
@@ -22993,9 +23306,9 @@ function resolvePackageBundledUiFrom(here) {
|
|
|
22993
23306
|
return null;
|
|
22994
23307
|
}
|
|
22995
23308
|
function walkUpForUi(startDir) {
|
|
22996
|
-
let current =
|
|
23309
|
+
let current = resolve36(startDir);
|
|
22997
23310
|
for (let i = 0; i < 64; i++) {
|
|
22998
|
-
const candidate =
|
|
23311
|
+
const candidate = join20(current, DEFAULT_UI_REL);
|
|
22999
23312
|
if (isUiBundleDir(candidate)) return candidate;
|
|
23000
23313
|
const parent = dirname18(current);
|
|
23001
23314
|
if (parent === current) return null;
|
|
@@ -23197,7 +23510,7 @@ var SERVE_TEXTS = {
|
|
|
23197
23510
|
};
|
|
23198
23511
|
|
|
23199
23512
|
// cli/util/serve-banner.ts
|
|
23200
|
-
import { relative as relative7, isAbsolute as
|
|
23513
|
+
import { relative as relative7, isAbsolute as isAbsolute12 } from "path";
|
|
23201
23514
|
var ESC2 = {
|
|
23202
23515
|
reset: "\x1B[0m",
|
|
23203
23516
|
bold: "\x1B[1m",
|
|
@@ -23331,9 +23644,9 @@ function resolveAnsi(colorEnabled) {
|
|
|
23331
23644
|
}
|
|
23332
23645
|
function formatDbPath(dbPath, cwd) {
|
|
23333
23646
|
const safe = sanitizeForTerminal(dbPath);
|
|
23334
|
-
if (!
|
|
23647
|
+
if (!isAbsolute12(safe)) return safe;
|
|
23335
23648
|
const rel = relative7(cwd, safe);
|
|
23336
|
-
if (rel === "" || rel.startsWith("..") ||
|
|
23649
|
+
if (rel === "" || rel.startsWith("..") || isAbsolute12(rel)) {
|
|
23337
23650
|
return safe;
|
|
23338
23651
|
}
|
|
23339
23652
|
return rel;
|
|
@@ -23933,7 +24246,7 @@ function rankConfidenceForGrouping(c) {
|
|
|
23933
24246
|
|
|
23934
24247
|
// cli/commands/sidecar.ts
|
|
23935
24248
|
import { existsSync as existsSync31, unlinkSync as unlinkSync2 } from "fs";
|
|
23936
|
-
import { resolve as
|
|
24249
|
+
import { resolve as resolve37 } from "path";
|
|
23937
24250
|
import { Command as Command35, Option as Option33 } from "clipanion";
|
|
23938
24251
|
|
|
23939
24252
|
// cli/i18n/sidecar.texts.ts
|
|
@@ -24084,7 +24397,7 @@ var SidecarRefreshCommand = class extends SmCommand {
|
|
|
24084
24397
|
let absPath;
|
|
24085
24398
|
try {
|
|
24086
24399
|
assertContained(ctx.cwd, node.path);
|
|
24087
|
-
absPath =
|
|
24400
|
+
absPath = resolve37(ctx.cwd, node.path);
|
|
24088
24401
|
} catch (err) {
|
|
24089
24402
|
this.printer.error(
|
|
24090
24403
|
tx(SIDECAR_TEXTS.refreshFailed, { glyph: errGlyph, message: formatErrorMessage(err) })
|
|
@@ -24365,7 +24678,7 @@ var SidecarAnnotateCommand = class extends SmCommand {
|
|
|
24365
24678
|
let absPath;
|
|
24366
24679
|
try {
|
|
24367
24680
|
assertContained(ctx.cwd, node.path);
|
|
24368
|
-
absPath =
|
|
24681
|
+
absPath = resolve37(ctx.cwd, node.path);
|
|
24369
24682
|
} catch (err) {
|
|
24370
24683
|
this.printer.error(
|
|
24371
24684
|
tx(SIDECAR_TEXTS.annotateFailed, { glyph: errGlyph, message: formatErrorMessage(err) })
|
|
@@ -24614,7 +24927,7 @@ var STUB_COMMANDS = [
|
|
|
24614
24927
|
|
|
24615
24928
|
// cli/commands/tutorial.ts
|
|
24616
24929
|
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
|
|
24930
|
+
import { dirname as dirname19, join as join21, resolve as resolve38 } from "path";
|
|
24618
24931
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
24619
24932
|
import { Command as Command37, Option as Option35 } from "clipanion";
|
|
24620
24933
|
|
|
@@ -24710,7 +25023,7 @@ var TutorialCommand = class extends SmCommand {
|
|
|
24710
25023
|
}
|
|
24711
25024
|
const variant = rawVariant ?? DEFAULT_VARIANT;
|
|
24712
25025
|
const spec = VARIANT_SPECS[variant];
|
|
24713
|
-
const targetDir =
|
|
25026
|
+
const targetDir = join21(ctx.cwd, ".claude", "skills", spec.slug);
|
|
24714
25027
|
const targetDisplay = `.claude/skills/${spec.slug}/`;
|
|
24715
25028
|
if (existsSync32(targetDir) && !this.force) {
|
|
24716
25029
|
this.printer.error(
|
|
@@ -24788,11 +25101,11 @@ function resolveSkillSourceDir(variant) {
|
|
|
24788
25101
|
const here = dirname19(fileURLToPath6(import.meta.url));
|
|
24789
25102
|
const candidates = [
|
|
24790
25103
|
// dev: src/cli/commands/ → repo-root .claude/skills/<slug>/
|
|
24791
|
-
|
|
25104
|
+
resolve38(here, "../../..", spec.sourceDir),
|
|
24792
25105
|
// bundled: dist/cli.js → dist/cli/tutorial/<slug> (sibling)
|
|
24793
|
-
|
|
25106
|
+
resolve38(here, "cli/tutorial", spec.slug),
|
|
24794
25107
|
// bundled fallback: any-depth → cli/tutorial/<slug>
|
|
24795
|
-
|
|
25108
|
+
resolve38(here, "../cli/tutorial", spec.slug)
|
|
24796
25109
|
];
|
|
24797
25110
|
for (const candidate of candidates) {
|
|
24798
25111
|
if (existsSync32(candidate) && statSync12(candidate).isDirectory()) {
|