agent-skills-standard 2.7.2 → 2.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/dist/index.js +511 -42
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -40,20 +40,20 @@ js_yaml = __toESM(js_yaml);
|
|
|
40
40
|
let zod = require("zod");
|
|
41
41
|
let node_path = require("node:path");
|
|
42
42
|
node_path = __toESM(node_path);
|
|
43
|
+
let child_process = require("child_process");
|
|
43
44
|
let os = require("os");
|
|
44
45
|
os = __toESM(os);
|
|
45
46
|
let node_stream = require("node:stream");
|
|
46
47
|
let node_child_process = require("node:child_process");
|
|
47
48
|
let node_os = require("node:os");
|
|
48
49
|
node_os = __toESM(node_os);
|
|
49
|
-
let child_process = require("child_process");
|
|
50
50
|
let fs = require("fs");
|
|
51
51
|
fs = __toESM(fs);
|
|
52
52
|
let crypto = require("crypto");
|
|
53
53
|
crypto = __toESM(crypto);
|
|
54
54
|
var package_default = {
|
|
55
55
|
name: "agent-skills-standard",
|
|
56
|
-
version: "2.7.
|
|
56
|
+
version: "2.7.4",
|
|
57
57
|
description: "A CLI to manage and sync AI agent skills standard for Cursor, Claude, Copilot, Windsurf, and more.",
|
|
58
58
|
main: "dist/index.js",
|
|
59
59
|
bin: {
|
|
@@ -1436,6 +1436,11 @@ var McpConfigSchema = zod.z.object({
|
|
|
1436
1436
|
scope: McpScopeSchema,
|
|
1437
1437
|
prompted: zod.z.boolean(),
|
|
1438
1438
|
version: zod.z.string().optional(),
|
|
1439
|
+
via: zod.z.enum([
|
|
1440
|
+
"auto",
|
|
1441
|
+
"npx",
|
|
1442
|
+
"global"
|
|
1443
|
+
]).optional(),
|
|
1439
1444
|
snippets: zod.z.boolean().optional()
|
|
1440
1445
|
});
|
|
1441
1446
|
var SkillConfigSchema = zod.z.object({
|
|
@@ -1479,6 +1484,7 @@ var MCP_FIELD_KEYS = [
|
|
|
1479
1484
|
"scope",
|
|
1480
1485
|
"prompted",
|
|
1481
1486
|
"version",
|
|
1487
|
+
"via",
|
|
1482
1488
|
"snippets"
|
|
1483
1489
|
];
|
|
1484
1490
|
var DEFAULT_SDLC_SUPPORT_CATEGORIES = ["quality-engineering"];
|
|
@@ -3629,11 +3635,10 @@ var LockfileService = class {
|
|
|
3629
3635
|
* agent-skills-standard-mcp`, which would silently pull whatever is latest
|
|
3630
3636
|
* on npm at the moment the agent starts.
|
|
3631
3637
|
*
|
|
3632
|
-
*
|
|
3633
|
-
*
|
|
3634
|
-
* category versions are maintained today).
|
|
3638
|
+
* Bumped automatically by `scripts/release-mcp.ts` when the MCP package is
|
|
3639
|
+
* released and gated in CI, so this pin always matches `mcp/package.json`.
|
|
3635
3640
|
*/
|
|
3636
|
-
var MCP_COMPATIBLE_VERSION = "0.
|
|
3641
|
+
var MCP_COMPATIBLE_VERSION = "0.8.1";
|
|
3637
3642
|
/**
|
|
3638
3643
|
* Minimum `agent-skills-standard-mcp` version required for session retrospectives.
|
|
3639
3644
|
*/
|
|
@@ -3655,6 +3660,10 @@ function escapeTomlString(value) {
|
|
|
3655
3660
|
}
|
|
3656
3661
|
//#endregion
|
|
3657
3662
|
//#region src/services/utils/codexTomlMcp.ts
|
|
3663
|
+
/** Structural equality for the server entry we generate and re-read. */
|
|
3664
|
+
function sameMcpEntry(a, b) {
|
|
3665
|
+
return a.command === b.command && a.args.length === b.args.length && a.args.every((arg, index) => arg === b.args[index]);
|
|
3666
|
+
}
|
|
3658
3667
|
var HEADER_RE = /^\s*\[\[?\s*([^\]]+?)\s*\]\]?\s*(#.*)?$/;
|
|
3659
3668
|
function normalizeKey(raw) {
|
|
3660
3669
|
return raw.split(".").map((s) => s.trim().replace(/^"(.*)"$/, "$1").replace(/^'(.*)'$/, "$1")).join(".");
|
|
@@ -3692,6 +3701,61 @@ function renderTomlMcpServer(name, entry) {
|
|
|
3692
3701
|
function hasTomlMcpServer(content, name) {
|
|
3693
3702
|
return findOurRanges(content.split("\n"), name).length > 0;
|
|
3694
3703
|
}
|
|
3704
|
+
function parseTomlStringLiteral(raw) {
|
|
3705
|
+
const value = raw.trim();
|
|
3706
|
+
if (value.length < 2) return null;
|
|
3707
|
+
if (value.startsWith("\"") && value.endsWith("\"")) return value.slice(1, -1).replace(/\\(["\\])/g, "$1");
|
|
3708
|
+
if (value.startsWith("'") && value.endsWith("'")) return value.slice(1, -1);
|
|
3709
|
+
return null;
|
|
3710
|
+
}
|
|
3711
|
+
var STRING_TOKEN_RE = /("(?:\\.|[^"\\])*"|'[^']*')/g;
|
|
3712
|
+
/**
|
|
3713
|
+
* Reads the `[mcp_servers.<name>]` table emitted by `renderTomlMcpServer`.
|
|
3714
|
+
* Returns null when the table is absent or either `command` / `args` is
|
|
3715
|
+
* missing or malformed. Only the exact table block is inspected, so
|
|
3716
|
+
* unrelated tables (and subtables) are ignored.
|
|
3717
|
+
*/
|
|
3718
|
+
function readTomlMcpServer(content, name) {
|
|
3719
|
+
const lines = content.split("\n");
|
|
3720
|
+
const base = `mcp_servers.${name}`;
|
|
3721
|
+
let start = -1;
|
|
3722
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
3723
|
+
const m = HEADER_RE.exec(lines[i]);
|
|
3724
|
+
if (m && normalizeKey(m[1]) === base) {
|
|
3725
|
+
start = i;
|
|
3726
|
+
break;
|
|
3727
|
+
}
|
|
3728
|
+
}
|
|
3729
|
+
if (start === -1) return null;
|
|
3730
|
+
let command = null;
|
|
3731
|
+
let args = null;
|
|
3732
|
+
for (let i = start + 1; i < lines.length; i += 1) {
|
|
3733
|
+
const line = lines[i];
|
|
3734
|
+
if (HEADER_RE.test(line)) break;
|
|
3735
|
+
const commandMatch = /^\s*command\s*=\s*((?:"(?:\\.|[^"\\])*")|(?:'[^']*'))\s*(?:#.*)?$/.exec(line);
|
|
3736
|
+
if (commandMatch) {
|
|
3737
|
+
command = parseTomlStringLiteral(commandMatch[1]);
|
|
3738
|
+
continue;
|
|
3739
|
+
}
|
|
3740
|
+
const argsMatch = /^\s*args\s*=\s*\[(.*)\]\s*(?:#.*)?$/.exec(line);
|
|
3741
|
+
if (argsMatch) {
|
|
3742
|
+
const tokens = argsMatch[1].match(STRING_TOKEN_RE) ?? [];
|
|
3743
|
+
if (argsMatch[1].replace(STRING_TOKEN_RE, "").replace(/[\s,]/g, "").length > 0) return null;
|
|
3744
|
+
const parsed = [];
|
|
3745
|
+
for (const token of tokens) {
|
|
3746
|
+
const value = parseTomlStringLiteral(token);
|
|
3747
|
+
if (value === null) return null;
|
|
3748
|
+
parsed.push(value);
|
|
3749
|
+
}
|
|
3750
|
+
args = parsed;
|
|
3751
|
+
}
|
|
3752
|
+
}
|
|
3753
|
+
if (command === null || args === null) return null;
|
|
3754
|
+
return {
|
|
3755
|
+
command,
|
|
3756
|
+
args
|
|
3757
|
+
};
|
|
3758
|
+
}
|
|
3695
3759
|
function removeTomlMcpServer(content, name) {
|
|
3696
3760
|
const lines = content.split("\n");
|
|
3697
3761
|
const ranges = findOurRanges(lines, name);
|
|
@@ -3727,6 +3791,10 @@ function upsertTomlMcpServer(content, name, entry) {
|
|
|
3727
3791
|
//#region src/services/McpConfigService.ts
|
|
3728
3792
|
var SERVER_NAME = "agent-skills-standard";
|
|
3729
3793
|
var PACKAGE = "agent-skills-standard-mcp";
|
|
3794
|
+
/** Sentinel: the `ags-mcp` command is not on PATH (no manifest fallback allowed). */
|
|
3795
|
+
var GLOBAL_BINARY_MISSING = Symbol("ags-mcp-missing");
|
|
3796
|
+
var SEMVER_PREFIX = /^\d+\.\d+\.\d+/;
|
|
3797
|
+
var SEMVER_EXACT = /^\d+\.\d+\.\d+$/;
|
|
3730
3798
|
var getTargets = (home = os.default.homedir(), platform = process.platform) => {
|
|
3731
3799
|
const out = {};
|
|
3732
3800
|
for (const agent of Object.values(Agent)) {
|
|
@@ -3749,6 +3817,27 @@ var getTargets = (home = os.default.homedir(), platform = process.platform) => {
|
|
|
3749
3817
|
return out;
|
|
3750
3818
|
};
|
|
3751
3819
|
/**
|
|
3820
|
+
* Command used to run `ags-mcp --version`.
|
|
3821
|
+
*
|
|
3822
|
+
* On Windows npm exposes the binary as a `.cmd` shim, which `execFileSync`
|
|
3823
|
+
* cannot spawn directly (ENOENT), so the probe goes through `cmd /c` — the
|
|
3824
|
+
* same shape the generated global launch entry uses. POSIX keeps the bare
|
|
3825
|
+
* binary with no shell.
|
|
3826
|
+
*/
|
|
3827
|
+
function globalVersionProbeCommand(platform = process.platform) {
|
|
3828
|
+
return platform === "win32" ? {
|
|
3829
|
+
command: "cmd",
|
|
3830
|
+
args: [
|
|
3831
|
+
"/c",
|
|
3832
|
+
"ags-mcp",
|
|
3833
|
+
"--version"
|
|
3834
|
+
]
|
|
3835
|
+
} : {
|
|
3836
|
+
command: "ags-mcp",
|
|
3837
|
+
args: ["--version"]
|
|
3838
|
+
};
|
|
3839
|
+
}
|
|
3840
|
+
/**
|
|
3752
3841
|
* Owns all MCP-config writes. Pure logic — never prompts the user directly.
|
|
3753
3842
|
* The caller (sync command, mcp subcommand) supplies a `userScopePrompt`
|
|
3754
3843
|
* callback that is invoked ONLY when the user has chosen `scope: 'user'` AND
|
|
@@ -3761,18 +3850,143 @@ var McpConfigService = class {
|
|
|
3761
3850
|
* <package>`, which would otherwise pull whatever is latest on npm the
|
|
3762
3851
|
* moment the agent starts — an unpinned update-drift surface (AST07).
|
|
3763
3852
|
* Pass an explicit `version` (from `mcp.version` in .skillsrc) to override.
|
|
3853
|
+
*
|
|
3854
|
+
* `via` selects the launch strategy (default `auto`). A global install
|
|
3855
|
+
* avoids the ~0.6s npx resolution overhead on every client spawn, but a
|
|
3856
|
+
* stale global must never be silently preferred over the pin.
|
|
3764
3857
|
*/
|
|
3765
|
-
buildEntry(
|
|
3858
|
+
buildEntry(opts = {}) {
|
|
3859
|
+
const version = opts.version || "0.8.1";
|
|
3860
|
+
const via = opts.via ?? "auto";
|
|
3861
|
+
const platform = this.testPlatform ?? process.platform;
|
|
3862
|
+
if (via === "global" || via === "auto" && platform !== "win32" && this.detectGlobalVersion() === version) return platform === "win32" ? {
|
|
3863
|
+
command: "cmd",
|
|
3864
|
+
args: ["/c", "ags-mcp"]
|
|
3865
|
+
} : {
|
|
3866
|
+
command: "ags-mcp",
|
|
3867
|
+
args: []
|
|
3868
|
+
};
|
|
3766
3869
|
return {
|
|
3767
3870
|
command: "npx",
|
|
3768
|
-
args: [
|
|
3871
|
+
args: [
|
|
3872
|
+
"-y",
|
|
3873
|
+
"--prefer-offline",
|
|
3874
|
+
`${PACKAGE}@${version}`
|
|
3875
|
+
]
|
|
3769
3876
|
};
|
|
3770
3877
|
}
|
|
3878
|
+
/**
|
|
3879
|
+
* Version reported by a globally installed `ags-mcp`, or `null` when the
|
|
3880
|
+
* binary is missing/unusable.
|
|
3881
|
+
*
|
|
3882
|
+
* Two probes, in order:
|
|
3883
|
+
* 1. `ags-mcp --version` — authoritative and install-manager agnostic.
|
|
3884
|
+
* 2. the manifest under `npm root -g` — required for servers published
|
|
3885
|
+
* before `--version` existed, where probe 1 either prints nothing or
|
|
3886
|
+
* times out because the stdio server starts normally instead.
|
|
3887
|
+
* A missing binary never falls through: an installed manifest alone does
|
|
3888
|
+
* not prove the command is on the client's PATH. POSIX surfaces that as
|
|
3889
|
+
* ENOENT; win32 (which probes through `cmd /c`) proves it with
|
|
3890
|
+
* `where.exe ags-mcp` before allowing the legacy manifest fallback.
|
|
3891
|
+
*/
|
|
3892
|
+
detectGlobalVersion() {
|
|
3893
|
+
const binaryVersion = this.probeGlobalBinaryVersion();
|
|
3894
|
+
if (binaryVersion === GLOBAL_BINARY_MISSING) return null;
|
|
3895
|
+
if (binaryVersion) return binaryVersion;
|
|
3896
|
+
if (!this.probeGlobalBinaryOnPath()) return null;
|
|
3897
|
+
return this.probeGlobalManifestVersion();
|
|
3898
|
+
}
|
|
3899
|
+
/**
|
|
3900
|
+
* Whether `ags-mcp` resolves on PATH. POSIX misses already surface as
|
|
3901
|
+
* ENOENT in the version probe, so only win32 needs the explicit check.
|
|
3902
|
+
*/
|
|
3903
|
+
probeGlobalBinaryOnPath() {
|
|
3904
|
+
if ((this.testPlatform ?? process.platform) !== "win32") return true;
|
|
3905
|
+
if (this.testGlobalPathProbe) return this.testGlobalPathProbe();
|
|
3906
|
+
try {
|
|
3907
|
+
const output = (0, child_process.execFileSync)("where.exe", ["ags-mcp"], {
|
|
3908
|
+
encoding: "utf8",
|
|
3909
|
+
timeout: 2e3,
|
|
3910
|
+
stdio: [
|
|
3911
|
+
"ignore",
|
|
3912
|
+
"pipe",
|
|
3913
|
+
"ignore"
|
|
3914
|
+
]
|
|
3915
|
+
});
|
|
3916
|
+
return String(output).trim().length > 0;
|
|
3917
|
+
} catch {
|
|
3918
|
+
return false;
|
|
3919
|
+
}
|
|
3920
|
+
}
|
|
3921
|
+
/** `ags-mcp --version`, or the sentinel when the command is not on PATH. */
|
|
3922
|
+
probeGlobalBinaryVersion() {
|
|
3923
|
+
try {
|
|
3924
|
+
if (this.testGlobalVersionProbe) return this.testGlobalVersionProbe();
|
|
3925
|
+
const { command, args } = globalVersionProbeCommand(this.testPlatform ?? process.platform);
|
|
3926
|
+
const output = (0, child_process.execFileSync)(command, args, {
|
|
3927
|
+
encoding: "utf8",
|
|
3928
|
+
timeout: 2e3,
|
|
3929
|
+
stdio: [
|
|
3930
|
+
"ignore",
|
|
3931
|
+
"pipe",
|
|
3932
|
+
"ignore"
|
|
3933
|
+
]
|
|
3934
|
+
});
|
|
3935
|
+
const firstLine = String(output).split("\n")[0].trim();
|
|
3936
|
+
return SEMVER_PREFIX.test(firstLine) ? firstLine : null;
|
|
3937
|
+
} catch (err) {
|
|
3938
|
+
return err.code === "ENOENT" ? GLOBAL_BINARY_MISSING : null;
|
|
3939
|
+
}
|
|
3940
|
+
}
|
|
3941
|
+
/** Version field of the globally installed manifest, purely from disk. */
|
|
3942
|
+
probeGlobalManifestVersion() {
|
|
3943
|
+
if (this.testGlobalManifestProbe) return this.testGlobalManifestProbe();
|
|
3944
|
+
const isWindows = (this.testPlatform ?? process.platform) === "win32";
|
|
3945
|
+
try {
|
|
3946
|
+
const globalRoot = String((0, child_process.execFileSync)(isWindows ? "npm.cmd" : "npm", ["root", "-g"], {
|
|
3947
|
+
encoding: "utf8",
|
|
3948
|
+
timeout: 5e3,
|
|
3949
|
+
stdio: [
|
|
3950
|
+
"ignore",
|
|
3951
|
+
"pipe",
|
|
3952
|
+
"ignore"
|
|
3953
|
+
],
|
|
3954
|
+
...isWindows ? { shell: true } : {}
|
|
3955
|
+
})).trim();
|
|
3956
|
+
if (!globalRoot) return null;
|
|
3957
|
+
const manifestPath = path.default.join(globalRoot, PACKAGE, "package.json");
|
|
3958
|
+
if (!fs_extra.default.pathExistsSync(manifestPath)) return null;
|
|
3959
|
+
const manifest = fs_extra.default.readJsonSync(manifestPath);
|
|
3960
|
+
return typeof manifest.version === "string" && SEMVER_EXACT.test(manifest.version) ? manifest.version : null;
|
|
3961
|
+
} catch {
|
|
3962
|
+
return null;
|
|
3963
|
+
}
|
|
3964
|
+
}
|
|
3771
3965
|
/** For testing: allows overriding the home directory. */
|
|
3772
3966
|
testHome = null;
|
|
3773
3967
|
setHomeForTesting(home) {
|
|
3774
3968
|
this.testHome = home;
|
|
3775
3969
|
}
|
|
3970
|
+
/** For testing: allows overriding the detected platform. */
|
|
3971
|
+
testPlatform = null;
|
|
3972
|
+
setPlatformForTesting(platform) {
|
|
3973
|
+
this.testPlatform = platform;
|
|
3974
|
+
}
|
|
3975
|
+
/** For testing: allows overriding the global-version probe. */
|
|
3976
|
+
testGlobalVersionProbe = null;
|
|
3977
|
+
setGlobalVersionProbeForTesting(fn) {
|
|
3978
|
+
this.testGlobalVersionProbe = fn;
|
|
3979
|
+
}
|
|
3980
|
+
/** For testing: allows overriding the `npm root -g` manifest probe. */
|
|
3981
|
+
testGlobalManifestProbe = null;
|
|
3982
|
+
setGlobalManifestProbeForTesting(fn) {
|
|
3983
|
+
this.testGlobalManifestProbe = fn;
|
|
3984
|
+
}
|
|
3985
|
+
/** For testing: allows overriding the win32 PATH-resolution probe. */
|
|
3986
|
+
testGlobalPathProbe = null;
|
|
3987
|
+
setGlobalPathProbeForTesting(fn) {
|
|
3988
|
+
this.testGlobalPathProbe = fn;
|
|
3989
|
+
}
|
|
3776
3990
|
getTargets() {
|
|
3777
3991
|
return getTargets(this.testHome ?? void 0);
|
|
3778
3992
|
}
|
|
@@ -3791,7 +4005,10 @@ var McpConfigService = class {
|
|
|
3791
4005
|
const { rootDir, agents, mcp } = opts;
|
|
3792
4006
|
if (mcp.scope === "disabled" || !mcp.enabled) return report;
|
|
3793
4007
|
const TARGETS = this.getTargets();
|
|
3794
|
-
const entry = this.buildEntry(
|
|
4008
|
+
const entry = this.buildEntry({
|
|
4009
|
+
version: mcp.version,
|
|
4010
|
+
via: mcp.via
|
|
4011
|
+
});
|
|
3795
4012
|
if (mcp.scope === "snippets-only" || mcp.snippets) await this.generateSnippets(rootDir, agents, entry, report);
|
|
3796
4013
|
if (mcp.scope === "snippets-only") return report;
|
|
3797
4014
|
for (const agent of agents) {
|
|
@@ -3868,12 +4085,45 @@ var McpConfigService = class {
|
|
|
3868
4085
|
const target = TARGETS[agent];
|
|
3869
4086
|
if (!target) continue;
|
|
3870
4087
|
const row = { agent };
|
|
3871
|
-
if (target.projectFile)
|
|
3872
|
-
|
|
4088
|
+
if (target.projectFile) {
|
|
4089
|
+
const abs = path.default.join(opts.rootDir, target.projectFile);
|
|
4090
|
+
row.project = await this.hasOurEntry(abs, target);
|
|
4091
|
+
row.projectEntry = await this.readEntry(abs, target);
|
|
4092
|
+
}
|
|
4093
|
+
if (target.userFile) {
|
|
4094
|
+
row.user = await this.hasOurEntry(target.userFile, target);
|
|
4095
|
+
row.userEntry = await this.readEntry(target.userFile, target);
|
|
4096
|
+
}
|
|
3873
4097
|
out.push(row);
|
|
3874
4098
|
}
|
|
3875
4099
|
return out;
|
|
3876
4100
|
}
|
|
4101
|
+
/** Parse the entry this service wrote for `SERVER_NAME`, if present. */
|
|
4102
|
+
async readEntry(abs, target) {
|
|
4103
|
+
if (!await fs_extra.default.pathExists(abs)) return null;
|
|
4104
|
+
if (target.format === "toml") return readTomlMcpServer(await fs_extra.default.readFile(abs, "utf8"), SERVER_NAME);
|
|
4105
|
+
const data = await fs_extra.default.readJson(abs).catch(() => null);
|
|
4106
|
+
if (!data) return null;
|
|
4107
|
+
const container = this.getNestedValue(data, target.key);
|
|
4108
|
+
if (!container) return null;
|
|
4109
|
+
if (target.shape === "map") {
|
|
4110
|
+
if (typeof container !== "object" || Array.isArray(container)) return null;
|
|
4111
|
+
return this.normalizeEntry(container[SERVER_NAME]);
|
|
4112
|
+
}
|
|
4113
|
+
if (!Array.isArray(container)) return null;
|
|
4114
|
+
const item = container.find((entry) => entry.name === SERVER_NAME);
|
|
4115
|
+
return this.normalizeEntry(item?.transport);
|
|
4116
|
+
}
|
|
4117
|
+
normalizeEntry(value) {
|
|
4118
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
|
|
4119
|
+
const record = value;
|
|
4120
|
+
if (typeof record.command !== "string") return null;
|
|
4121
|
+
const args = Array.isArray(record.args) ? record.args.map((arg) => String(arg)) : [];
|
|
4122
|
+
return {
|
|
4123
|
+
command: record.command,
|
|
4124
|
+
args
|
|
4125
|
+
};
|
|
4126
|
+
}
|
|
3877
4127
|
async generateSnippets(rootDir, agents, entry, report) {
|
|
3878
4128
|
const dir = path.default.join(rootDir, "mcp-config-snippets");
|
|
3879
4129
|
await fs_extra.default.ensureDir(dir);
|
|
@@ -3973,7 +4223,11 @@ var McpConfigService = class {
|
|
|
3973
4223
|
if (!data) return false;
|
|
3974
4224
|
const container = this.getNestedValue(data, target.key);
|
|
3975
4225
|
if (!container) return false;
|
|
3976
|
-
if (target.shape === "map")
|
|
4226
|
+
if (target.shape === "map") {
|
|
4227
|
+
if (typeof container !== "object" || Array.isArray(container)) return false;
|
|
4228
|
+
return Boolean(container[SERVER_NAME]);
|
|
4229
|
+
}
|
|
4230
|
+
if (!Array.isArray(container)) return false;
|
|
3977
4231
|
return container.some((item) => item.name === SERVER_NAME);
|
|
3978
4232
|
}
|
|
3979
4233
|
buildFreshDoc(target, entry) {
|
|
@@ -4076,6 +4330,10 @@ function defaultDoctorDeps() {
|
|
|
4076
4330
|
rootDir,
|
|
4077
4331
|
agents
|
|
4078
4332
|
}),
|
|
4333
|
+
buildMcpEntry: (cfg) => mcp.buildEntry({
|
|
4334
|
+
version: cfg.mcp?.version,
|
|
4335
|
+
via: cfg.mcp?.via
|
|
4336
|
+
}),
|
|
4079
4337
|
hookStatus: (rootDir, agents) => hooks.status({
|
|
4080
4338
|
rootDir,
|
|
4081
4339
|
agents
|
|
@@ -4209,13 +4467,48 @@ var DoctorService = class {
|
|
|
4209
4467
|
if (!config.mcp?.enabled || scope !== "project" && scope !== "user") return check("mcp", "skip", "MCP disabled in .skillsrc");
|
|
4210
4468
|
const rows = await this.deps.mcpStatus(rootDir, agents);
|
|
4211
4469
|
const missing = rows.filter((r) => scope === "project" ? !r.project : !(r.project || r.user)).map((r) => r.agent);
|
|
4212
|
-
if (missing.length
|
|
4213
|
-
return check("mcp", "fail", `MCP server not registered for: ${missing.join(", ")}`, {
|
|
4470
|
+
if (missing.length > 0) return check("mcp", "fail", `MCP server not registered for: ${missing.join(", ")}`, {
|
|
4214
4471
|
description: `register MCP for ${missing.join(", ")}`,
|
|
4215
4472
|
apply: async () => {
|
|
4216
4473
|
for (const agent of missing) await this.deps.installMcp(rootDir, agent, config);
|
|
4217
4474
|
}
|
|
4218
4475
|
});
|
|
4476
|
+
const expected = this.deps.buildMcpEntry ? this.deps.buildMcpEntry(config) : new McpConfigService().buildEntry({
|
|
4477
|
+
version: config.mcp.version,
|
|
4478
|
+
via: config.mcp.via
|
|
4479
|
+
});
|
|
4480
|
+
const pin = config.mcp.version ?? "0.8.1";
|
|
4481
|
+
const drifted = [];
|
|
4482
|
+
for (const row of rows) {
|
|
4483
|
+
if (scope === "project") {
|
|
4484
|
+
if (row.project && row.projectEntry && !sameMcpEntry(row.projectEntry, expected)) drifted.push({
|
|
4485
|
+
agent: row.agent,
|
|
4486
|
+
scope: "project",
|
|
4487
|
+
entry: row.projectEntry
|
|
4488
|
+
});
|
|
4489
|
+
continue;
|
|
4490
|
+
}
|
|
4491
|
+
if (row.project && row.projectEntry && !sameMcpEntry(row.projectEntry, expected)) drifted.push({
|
|
4492
|
+
agent: row.agent,
|
|
4493
|
+
scope: "project",
|
|
4494
|
+
entry: row.projectEntry
|
|
4495
|
+
});
|
|
4496
|
+
if (row.user && row.userEntry && !sameMcpEntry(row.userEntry, expected)) drifted.push({
|
|
4497
|
+
agent: row.agent,
|
|
4498
|
+
scope: "user",
|
|
4499
|
+
entry: row.userEntry
|
|
4500
|
+
});
|
|
4501
|
+
}
|
|
4502
|
+
if (drifted.length > 0) {
|
|
4503
|
+
const driftedAgents = [...new Set(drifted.map((item) => item.agent))];
|
|
4504
|
+
return check("mcp", "warn", `MCP pin drift: ${drifted.map((item) => `${item.agent} ${item.scope} pins ${item.entry.command}${item.entry.args.length ? ` ${item.entry.args.join(" ")}` : ""} but this CLI expects agent-skills-standard-mcp@${pin}`).join("; ")}`, {
|
|
4505
|
+
description: `refresh MCP entry for ${driftedAgents.join(", ")}`,
|
|
4506
|
+
apply: async () => {
|
|
4507
|
+
for (const agent of driftedAgents) await this.deps.installMcp(rootDir, agent, config);
|
|
4508
|
+
}
|
|
4509
|
+
});
|
|
4510
|
+
}
|
|
4511
|
+
return check("mcp", "ok", `registered for ${rows.map((r) => r.agent).join(", ") || "no MCP-capable agents"}`);
|
|
4219
4512
|
}
|
|
4220
4513
|
async hooksCheck(rootDir, agents) {
|
|
4221
4514
|
const rows = await this.deps.hookStatus(rootDir, agents);
|
|
@@ -10330,26 +10623,94 @@ var VALID_SCOPES = [
|
|
|
10330
10623
|
"snippets-only",
|
|
10331
10624
|
"disabled"
|
|
10332
10625
|
];
|
|
10626
|
+
var VALID_VIAS = [
|
|
10627
|
+
"auto",
|
|
10628
|
+
"npx",
|
|
10629
|
+
"global"
|
|
10630
|
+
];
|
|
10631
|
+
function parseVia(value) {
|
|
10632
|
+
return typeof value === "string" && VALID_VIAS.includes(value) ? value : null;
|
|
10633
|
+
}
|
|
10634
|
+
function defaultGlobalInstaller(spec) {
|
|
10635
|
+
if (process.platform === "win32") {
|
|
10636
|
+
(0, child_process.execFileSync)("npm.cmd", [
|
|
10637
|
+
"i",
|
|
10638
|
+
"-g",
|
|
10639
|
+
spec
|
|
10640
|
+
], {
|
|
10641
|
+
stdio: "inherit",
|
|
10642
|
+
shell: true
|
|
10643
|
+
});
|
|
10644
|
+
return;
|
|
10645
|
+
}
|
|
10646
|
+
(0, child_process.execFileSync)("npm", [
|
|
10647
|
+
"i",
|
|
10648
|
+
"-g",
|
|
10649
|
+
spec
|
|
10650
|
+
], { stdio: "inherit" });
|
|
10651
|
+
}
|
|
10652
|
+
function formatEntry(entry) {
|
|
10653
|
+
if (!entry) return "(none)";
|
|
10654
|
+
return entry.args.length ? `${entry.command} ${entry.args.join(" ")}` : entry.command;
|
|
10655
|
+
}
|
|
10333
10656
|
/**
|
|
10334
10657
|
* `ags mcp <action>` — manage the optional MCP server integration without
|
|
10335
10658
|
* editing `.skillsrc` by hand.
|
|
10336
10659
|
*
|
|
10337
10660
|
* Sub-actions:
|
|
10338
|
-
* status Show
|
|
10339
|
-
* enable
|
|
10661
|
+
* status Show scope, launch mode, resolved command, and installed configs
|
|
10662
|
+
* enable [--via=...] Set mcp.enabled=true; optionally install configs at the configured scope
|
|
10340
10663
|
* disable Set mcp.enabled=false; existing configs are NOT removed
|
|
10341
10664
|
* scope <scope> Change scope: project | user | snippets-only | disabled
|
|
10342
|
-
*
|
|
10665
|
+
* via <auto|npx|global> Change launch mode (default: auto)
|
|
10666
|
+
* install [--scope=...] [--via=...]
|
|
10667
|
+
* One-shot install at the given (or configured) scope/mode
|
|
10343
10668
|
* uninstall [--from=...] Remove our entry from configs (project | user | all)
|
|
10344
|
-
* snippets
|
|
10669
|
+
* snippets [--via=...] Generate fresh snippet files in ./mcp-config-snippets/
|
|
10670
|
+
*
|
|
10671
|
+
* `enable` also installs `agent-skills-standard-mcp@<pin>` globally and
|
|
10672
|
+
* verifies the reported version whenever the effective launch mode is
|
|
10673
|
+
* `global` (saved mode or `--via global`) — no extra flag required.
|
|
10674
|
+
* `--install-global` remains as an explicit trigger for the same install.
|
|
10345
10675
|
*/
|
|
10346
10676
|
var McpCommand = class {
|
|
10347
10677
|
configService;
|
|
10348
10678
|
mcpService;
|
|
10679
|
+
globalInstaller = defaultGlobalInstaller;
|
|
10349
10680
|
constructor(configService, mcpService) {
|
|
10350
10681
|
this.configService = configService ?? new ConfigService();
|
|
10351
10682
|
this.mcpService = mcpService ?? new McpConfigService();
|
|
10352
10683
|
}
|
|
10684
|
+
/** For testing: replace the global npm installer (never run real npm in unit tests). */
|
|
10685
|
+
setGlobalInstallerForTesting(fn) {
|
|
10686
|
+
this.globalInstaller = fn ?? defaultGlobalInstaller;
|
|
10687
|
+
}
|
|
10688
|
+
/**
|
|
10689
|
+
* Install the pinned package globally and prove the installed binary
|
|
10690
|
+
* reports that exact pin. Returns false (with exit code 1) when either
|
|
10691
|
+
* step fails so callers can abort before persisting or writing configs.
|
|
10692
|
+
*/
|
|
10693
|
+
ensureGlobalPin(pin) {
|
|
10694
|
+
const spec = `${PACKAGE}@${pin}`;
|
|
10695
|
+
try {
|
|
10696
|
+
this.globalInstaller(spec);
|
|
10697
|
+
} catch {
|
|
10698
|
+
console.log(picocolors.default.red(`❌ Global install failed. Run manually:`));
|
|
10699
|
+
console.log(picocolors.default.gray(` npm i -g ${spec}`));
|
|
10700
|
+
process.exitCode = 1;
|
|
10701
|
+
return false;
|
|
10702
|
+
}
|
|
10703
|
+
const detected = this.mcpService.detectGlobalVersion();
|
|
10704
|
+
if (detected !== pin) {
|
|
10705
|
+
console.log(picocolors.default.red(`❌ Global ags-mcp reports ${detected ?? "no version"} but the CLI pin is ${pin}.`));
|
|
10706
|
+
if (!detected) console.log(picocolors.default.gray(" Probe found no usable version: check that npm's global bin directory is on PATH and that `ags-mcp --version` prints the expected pin."));
|
|
10707
|
+
console.log(picocolors.default.gray(` Run manually: npm i -g ${spec}`));
|
|
10708
|
+
process.exitCode = 1;
|
|
10709
|
+
return false;
|
|
10710
|
+
}
|
|
10711
|
+
console.log(picocolors.default.green(`✅ Global ags-mcp ${pin} installed and verified.`));
|
|
10712
|
+
return true;
|
|
10713
|
+
}
|
|
10353
10714
|
async run(action, options = {}) {
|
|
10354
10715
|
const config = await this.configService.loadConfig();
|
|
10355
10716
|
if (!config) {
|
|
@@ -10360,13 +10721,14 @@ var McpCommand = class {
|
|
|
10360
10721
|
const agents = config.agents ?? [];
|
|
10361
10722
|
switch (action) {
|
|
10362
10723
|
case "status": return this.actionStatus(config, cwd);
|
|
10363
|
-
case "enable": return this.actionEnable(config);
|
|
10724
|
+
case "enable": return this.actionEnable(config, agents, cwd, options);
|
|
10364
10725
|
case "disable": return this.actionDisable(config);
|
|
10365
10726
|
case "scope": return this.actionScope(config, options);
|
|
10727
|
+
case "via": return this.actionVia(config, options);
|
|
10366
10728
|
case "install": return this.actionInstall(config, agents, cwd, options);
|
|
10367
10729
|
case "uninstall": return this.actionUninstall(config, agents, cwd, options);
|
|
10368
|
-
case "snippets": return this.actionSnippets(config, agents, cwd);
|
|
10369
|
-
default: console.log(picocolors.default.red(`Unknown action: ${action}`), "\nValid actions: status, enable, disable, scope, install, uninstall, snippets");
|
|
10730
|
+
case "snippets": return this.actionSnippets(config, agents, cwd, options);
|
|
10731
|
+
default: console.log(picocolors.default.red(`Unknown action: ${action}`), "\nValid actions: status, enable, disable, scope, via, install, uninstall, snippets");
|
|
10370
10732
|
}
|
|
10371
10733
|
}
|
|
10372
10734
|
async actionStatus(config, cwd) {
|
|
@@ -10379,20 +10741,50 @@ var McpCommand = class {
|
|
|
10379
10741
|
console.log(picocolors.default.bold("\n🔌 MCP integration status\n"));
|
|
10380
10742
|
console.log(` enabled : ${mcp.enabled ? picocolors.default.green("yes") : picocolors.default.gray("no")}`);
|
|
10381
10743
|
console.log(` scope : ${picocolors.default.cyan(mcp.scope)}`);
|
|
10744
|
+
console.log(` via : ${picocolors.default.cyan(mcp.via ?? "auto")}`);
|
|
10382
10745
|
console.log(` prompted: ${mcp.prompted ? "yes" : "no"}`);
|
|
10383
10746
|
if (mcp.version) console.log(` version : ${mcp.version}`);
|
|
10747
|
+
const pin = mcp.version ?? "0.8.1";
|
|
10748
|
+
const entry = this.mcpService.buildEntry({
|
|
10749
|
+
version: mcp.version,
|
|
10750
|
+
via: mcp.via
|
|
10751
|
+
});
|
|
10752
|
+
const launch = entry.args.length ? `${entry.command} ${entry.args.join(" ")}` : entry.command;
|
|
10753
|
+
console.log(` launch : ${picocolors.default.gray(launch)}`);
|
|
10754
|
+
console.log(` pin : ${pin}`);
|
|
10755
|
+
const detected = this.mcpService.detectGlobalVersion();
|
|
10756
|
+
console.log(` global : ${detected ? picocolors.default.green(detected) : picocolors.default.gray("not found")}`);
|
|
10757
|
+
if (detected && detected !== pin) {
|
|
10758
|
+
console.log(picocolors.default.yellow(`\n⚠️ Global ags-mcp is ${detected} but the CLI pin is ${pin}; "auto" will use npx until they match.`));
|
|
10759
|
+
console.log(picocolors.default.gray(` Update with: npm i -g ${PACKAGE}@${pin}`));
|
|
10760
|
+
}
|
|
10384
10761
|
if (rows.length === 0) {
|
|
10385
10762
|
console.log(picocolors.default.gray("\n (no agents configured)"));
|
|
10386
10763
|
return;
|
|
10387
10764
|
}
|
|
10388
10765
|
console.log("\n Per-agent installation state:");
|
|
10389
10766
|
let installedAnywhere = false;
|
|
10767
|
+
const drift = [];
|
|
10390
10768
|
for (const row of rows) {
|
|
10391
|
-
const project = row.project === void 0 ? "—" : row.project ? picocolors.default.green("✓") : picocolors.default.gray("✗")
|
|
10392
|
-
const user = row.user === void 0 ? "—" : row.user ? picocolors.default.green("✓") : picocolors.default.gray("✗")
|
|
10769
|
+
const project = row.project === void 0 ? "—" : `${row.project ? picocolors.default.green("✓") : picocolors.default.gray("✗")} ${formatEntry(row.projectEntry)}`;
|
|
10770
|
+
const user = row.user === void 0 ? "—" : `${row.user ? picocolors.default.green("✓") : picocolors.default.gray("✗")} ${formatEntry(row.userEntry)}`;
|
|
10393
10771
|
if (row.project === true || row.user === true) installedAnywhere = true;
|
|
10772
|
+
if (row.project === true && row.projectEntry && !sameMcpEntry(row.projectEntry, entry)) drift.push({
|
|
10773
|
+
agent: row.agent,
|
|
10774
|
+
scope: "project",
|
|
10775
|
+
configured: row.projectEntry
|
|
10776
|
+
});
|
|
10777
|
+
if (row.user === true && row.userEntry && !sameMcpEntry(row.userEntry, entry)) drift.push({
|
|
10778
|
+
agent: row.agent,
|
|
10779
|
+
scope: "user",
|
|
10780
|
+
configured: row.userEntry
|
|
10781
|
+
});
|
|
10394
10782
|
console.log(` ${row.agent.padEnd(12)} project: ${project} user: ${user}`);
|
|
10395
10783
|
}
|
|
10784
|
+
if (drift.length > 0) {
|
|
10785
|
+
for (const item of drift) console.log(picocolors.default.yellow(`\n⚠️ MCP pin drift: ${item.agent} ${item.scope} pins ${formatEntry(item.configured)} but this CLI expects ${launch}.`));
|
|
10786
|
+
console.log(picocolors.default.gray(" Run `ags sync` (or `ags mcp install`) to refresh; existing agents pick it up on their next MCP start."));
|
|
10787
|
+
}
|
|
10396
10788
|
if (!mcp.enabled && installedAnywhere) {
|
|
10397
10789
|
console.log(picocolors.default.yellow("\n⚠️ Mismatch detected: .skillsrc says mcp.enabled=false, but the MCP entry IS present in at least one runtime config."));
|
|
10398
10790
|
console.log(picocolors.default.gray(" The MCP will work, but `sync` will not keep its config in step. Consider one of:"));
|
|
@@ -10403,12 +10795,36 @@ var McpCommand = class {
|
|
|
10403
10795
|
console.log(picocolors.default.gray(" Run `ags mcp install` to write the entries, or `ags mcp disable` to clear the consent flag."));
|
|
10404
10796
|
}
|
|
10405
10797
|
}
|
|
10406
|
-
|
|
10798
|
+
/**
|
|
10799
|
+
* Per-file consent for user-home MCP configs. Shared by every install path
|
|
10800
|
+
* so `enable` (including global mode) cannot drift from `actionInstall`.
|
|
10801
|
+
*/
|
|
10802
|
+
async promptUserScopeWrite(agent, file) {
|
|
10803
|
+
const { ok } = await inquirer.default.prompt([{
|
|
10804
|
+
type: "confirm",
|
|
10805
|
+
name: "ok",
|
|
10806
|
+
message: `Write user-scope MCP config for ${picocolors.default.cyan(agent)} at ${picocolors.default.gray(file)}?`,
|
|
10807
|
+
default: false
|
|
10808
|
+
}]);
|
|
10809
|
+
return ok;
|
|
10810
|
+
}
|
|
10811
|
+
async actionEnable(config, agents, cwd, options) {
|
|
10407
10812
|
const mcp = config.mcp ?? defaultMcpConfig();
|
|
10813
|
+
const viaOverrideRaw = options.via;
|
|
10814
|
+
const viaOverride = viaOverrideRaw ? parseVia(viaOverrideRaw) : void 0;
|
|
10815
|
+
if (viaOverrideRaw && !viaOverride) {
|
|
10816
|
+
console.log(picocolors.default.red(`Invalid --via value. Valid: ${VALID_VIAS.join(" | ")}`));
|
|
10817
|
+
return;
|
|
10818
|
+
}
|
|
10408
10819
|
if (mcp.scope === "disabled") {
|
|
10409
10820
|
console.log(picocolors.default.yellow("⚠️ scope is currently \"disabled\". Set a real scope first: `ags mcp scope project`"));
|
|
10410
10821
|
return;
|
|
10411
10822
|
}
|
|
10823
|
+
const effectiveVia = viaOverride ?? mcp.via ?? "auto";
|
|
10824
|
+
if (Boolean(options.installGlobal) || effectiveVia === "global") {
|
|
10825
|
+
const pin = mcp.version ?? "0.8.1";
|
|
10826
|
+
if (!this.ensureGlobalPin(pin)) return;
|
|
10827
|
+
}
|
|
10412
10828
|
config.mcp = {
|
|
10413
10829
|
...mcp,
|
|
10414
10830
|
enabled: true,
|
|
@@ -10416,8 +10832,39 @@ var McpCommand = class {
|
|
|
10416
10832
|
};
|
|
10417
10833
|
await this.configService.saveConfig(config);
|
|
10418
10834
|
console.log(picocolors.default.green("✅ MCP enabled."));
|
|
10835
|
+
if (viaOverride || effectiveVia === "global") {
|
|
10836
|
+
const installVia = viaOverride ?? "global";
|
|
10837
|
+
const report = await this.mcpService.install({
|
|
10838
|
+
rootDir: cwd,
|
|
10839
|
+
agents,
|
|
10840
|
+
mcp: {
|
|
10841
|
+
...config.mcp,
|
|
10842
|
+
via: installVia
|
|
10843
|
+
},
|
|
10844
|
+
userScopePrompt: (agent, file) => this.promptUserScopeWrite(agent, file)
|
|
10845
|
+
});
|
|
10846
|
+
console.log(picocolors.default.gray(` Installed configs using launch mode "${installVia}".`));
|
|
10847
|
+
this.printReport(report, cwd);
|
|
10848
|
+
return;
|
|
10849
|
+
}
|
|
10419
10850
|
console.log(picocolors.default.gray(" Run `ags mcp install` to write configs now, or it happens on next `sync`."));
|
|
10420
10851
|
}
|
|
10852
|
+
async actionVia(config, options) {
|
|
10853
|
+
const via = options.via ?? options._?.[0];
|
|
10854
|
+
if (!parseVia(via)) {
|
|
10855
|
+
console.log(picocolors.default.red(`Invalid via. Valid: ${VALID_VIAS.join(" | ")}`));
|
|
10856
|
+
return;
|
|
10857
|
+
}
|
|
10858
|
+
config.mcp = {
|
|
10859
|
+
...config.mcp ?? defaultMcpConfig(),
|
|
10860
|
+
via,
|
|
10861
|
+
prompted: true
|
|
10862
|
+
};
|
|
10863
|
+
await this.configService.saveConfig(config);
|
|
10864
|
+
console.log(picocolors.default.green(`✅ MCP launch mode set to "${via}".`));
|
|
10865
|
+
if (via === "global") console.log(picocolors.default.yellow("⚠️ \"global\" requires a matching global install: `ags mcp enable` installs and verifies it automatically."));
|
|
10866
|
+
else if (via === "auto") console.log(picocolors.default.gray(" \"auto\" uses a version-matched global ags-mcp when present, otherwise npx."));
|
|
10867
|
+
}
|
|
10421
10868
|
async actionDisable(config) {
|
|
10422
10869
|
config.mcp = {
|
|
10423
10870
|
...config.mcp ?? defaultMcpConfig(),
|
|
@@ -10451,28 +10898,26 @@ var McpCommand = class {
|
|
|
10451
10898
|
console.log(picocolors.default.red(`Invalid --scope value. Valid: ${VALID_SCOPES.join(" | ")}`));
|
|
10452
10899
|
return;
|
|
10453
10900
|
}
|
|
10901
|
+
const viaOverrideRaw = options.via;
|
|
10902
|
+
const viaOverride = viaOverrideRaw ? parseVia(viaOverrideRaw) : void 0;
|
|
10903
|
+
if (viaOverrideRaw && !viaOverride) {
|
|
10904
|
+
console.log(picocolors.default.red(`Invalid --via value. Valid: ${VALID_VIAS.join(" | ")}`));
|
|
10905
|
+
return;
|
|
10906
|
+
}
|
|
10454
10907
|
if (effectiveScope === "disabled") {
|
|
10455
10908
|
console.log(picocolors.default.yellow("Scope is \"disabled\" — nothing to install."));
|
|
10456
10909
|
return;
|
|
10457
10910
|
}
|
|
10458
|
-
const userPrompt = async (agent, file) => {
|
|
10459
|
-
const { ok } = await inquirer.default.prompt([{
|
|
10460
|
-
type: "confirm",
|
|
10461
|
-
name: "ok",
|
|
10462
|
-
message: `Write user-scope MCP config for ${picocolors.default.cyan(agent)} at ${picocolors.default.gray(file)}?`,
|
|
10463
|
-
default: false
|
|
10464
|
-
}]);
|
|
10465
|
-
return ok;
|
|
10466
|
-
};
|
|
10467
10911
|
const report = await this.mcpService.install({
|
|
10468
10912
|
rootDir: cwd,
|
|
10469
10913
|
agents,
|
|
10470
10914
|
mcp: {
|
|
10471
10915
|
...mcp,
|
|
10472
10916
|
enabled: true,
|
|
10473
|
-
scope: effectiveScope
|
|
10917
|
+
scope: effectiveScope,
|
|
10918
|
+
...viaOverride ? { via: viaOverride } : {}
|
|
10474
10919
|
},
|
|
10475
|
-
userScopePrompt:
|
|
10920
|
+
userScopePrompt: (agent, file) => this.promptUserScopeWrite(agent, file)
|
|
10476
10921
|
});
|
|
10477
10922
|
this.printReport(report, cwd);
|
|
10478
10923
|
}
|
|
@@ -10498,15 +10943,22 @@ var McpCommand = class {
|
|
|
10498
10943
|
console.log(picocolors.default.green(`✅ Removed agent-skills MCP entry from:`));
|
|
10499
10944
|
for (const r of removed) console.log(` - ${picocolors.default.cyan(r.agent)} → ${picocolors.default.gray(r.file)}`);
|
|
10500
10945
|
}
|
|
10501
|
-
async actionSnippets(config, agents, cwd) {
|
|
10946
|
+
async actionSnippets(config, agents, cwd, options) {
|
|
10502
10947
|
const mcp = config.mcp ?? defaultMcpConfig();
|
|
10948
|
+
const viaOverrideRaw = options.via;
|
|
10949
|
+
const viaOverride = viaOverrideRaw ? parseVia(viaOverrideRaw) : void 0;
|
|
10950
|
+
if (viaOverrideRaw && !viaOverride) {
|
|
10951
|
+
console.log(picocolors.default.red(`Invalid --via value. Valid: ${VALID_VIAS.join(" | ")}`));
|
|
10952
|
+
return;
|
|
10953
|
+
}
|
|
10503
10954
|
const report = await this.mcpService.install({
|
|
10504
10955
|
rootDir: cwd,
|
|
10505
10956
|
agents,
|
|
10506
10957
|
mcp: {
|
|
10507
10958
|
...mcp,
|
|
10508
10959
|
enabled: true,
|
|
10509
|
-
scope: "snippets-only"
|
|
10960
|
+
scope: "snippets-only",
|
|
10961
|
+
...viaOverride ? { via: viaOverride } : {}
|
|
10510
10962
|
}
|
|
10511
10963
|
});
|
|
10512
10964
|
if (report.snippets.length === 0) {
|
|
@@ -10605,12 +11057,14 @@ var RetrospectiveCommand = class {
|
|
|
10605
11057
|
env;
|
|
10606
11058
|
customExit;
|
|
10607
11059
|
log;
|
|
11060
|
+
mcpVersion;
|
|
10608
11061
|
constructor(deps = {}) {
|
|
10609
11062
|
this.spawnFn = deps.spawnFn ?? defaultSpawn;
|
|
10610
11063
|
this.platform = deps.platform ?? process.platform;
|
|
10611
11064
|
this.env = deps.env ?? process.env;
|
|
10612
11065
|
this.customExit = deps.exit;
|
|
10613
11066
|
this.log = deps.log ?? console.log;
|
|
11067
|
+
this.mcpVersion = deps.mcpVersion ?? "0.8.1";
|
|
10614
11068
|
}
|
|
10615
11069
|
exit(code) {
|
|
10616
11070
|
process.exitCode = code;
|
|
@@ -10639,8 +11093,8 @@ var RetrospectiveCommand = class {
|
|
|
10639
11093
|
}
|
|
10640
11094
|
}
|
|
10641
11095
|
const envSpec = this.env.AGS_MCP_PACKAGE_SPEC;
|
|
10642
|
-
if (!(typeof envSpec === "string" && envSpec.trim() !== "") && !isVersionAtLeast(
|
|
10643
|
-
this.log(`Session retrospectives need agent-skills-standard-mcp ${RETROSPECTIVE_MIN_MCP_VERSION} or newer, but this ags is pinned to ${
|
|
11096
|
+
if (!(typeof envSpec === "string" && envSpec.trim() !== "") && !isVersionAtLeast(this.mcpVersion, "0.8.0")) {
|
|
11097
|
+
this.log(`Session retrospectives need agent-skills-standard-mcp ${RETROSPECTIVE_MIN_MCP_VERSION} or newer, but this ags is pinned to ${this.mcpVersion}. Upgrade ags (ags upgrade), or set AGS_MCP_PACKAGE_SPEC to a newer package for testing.`);
|
|
10644
11098
|
this.exit(1);
|
|
10645
11099
|
return;
|
|
10646
11100
|
}
|
|
@@ -10659,7 +11113,8 @@ var RetrospectiveCommand = class {
|
|
|
10659
11113
|
}
|
|
10660
11114
|
const argsToSpawn = [
|
|
10661
11115
|
"-y",
|
|
10662
|
-
|
|
11116
|
+
"--prefer-offline",
|
|
11117
|
+
typeof envSpec === "string" && envSpec.trim() !== "" ? envSpec : `${PACKAGE}@${this.mcpVersion}`,
|
|
10663
11118
|
"retrospective",
|
|
10664
11119
|
mappedAction,
|
|
10665
11120
|
...forwarded
|
|
@@ -11338,7 +11793,11 @@ var AuditCommand = class {
|
|
|
11338
11793
|
* It checks the latest version on npm and performs the upgrade using the detected package manager.
|
|
11339
11794
|
*/
|
|
11340
11795
|
var UpgradeCommand = class {
|
|
11796
|
+
deps;
|
|
11341
11797
|
currentVersion = package_default.version;
|
|
11798
|
+
constructor(deps = {}) {
|
|
11799
|
+
this.deps = deps;
|
|
11800
|
+
}
|
|
11342
11801
|
/**
|
|
11343
11802
|
* Executes the upgrade flow.
|
|
11344
11803
|
* @param options Command options, including `dryRun` to check for updates without installing.
|
|
@@ -11383,6 +11842,7 @@ var UpgradeCommand = class {
|
|
|
11383
11842
|
const fallbackCmd = this.getUpgradeCommand(pm, "latest");
|
|
11384
11843
|
(0, child_process.execSync)(fallbackCmd, { stdio: "inherit" });
|
|
11385
11844
|
}
|
|
11845
|
+
await this.printMcpRefreshHint();
|
|
11386
11846
|
if (pm === "pnpm") {
|
|
11387
11847
|
const installedVersion = this.getInstalledVersionFromPnpm(latestVersion);
|
|
11388
11848
|
if (installedVersion !== latestVersion) {
|
|
@@ -11434,6 +11894,12 @@ var UpgradeCommand = class {
|
|
|
11434
11894
|
this.printManualInstructions(pm, latestVersion);
|
|
11435
11895
|
}
|
|
11436
11896
|
}
|
|
11897
|
+
async printMcpRefreshHint() {
|
|
11898
|
+
try {
|
|
11899
|
+
if (!(this.deps.loadMcpEnabled ? await this.deps.loadMcpEnabled() : (await new ConfigService().loadConfig())?.mcp?.enabled ?? false)) return;
|
|
11900
|
+
console.log(picocolors.default.gray(`MCP configs are not rewritten by upgrade — run \`ags sync\` (or \`ags mcp install\`) to move them to ${MCP_COMPATIBLE_VERSION}.`));
|
|
11901
|
+
} catch {}
|
|
11902
|
+
}
|
|
11437
11903
|
detectPackageManager() {
|
|
11438
11904
|
const execPath = process.argv[1] || "";
|
|
11439
11905
|
if (execPath.includes("pnpm") || (process.env.npm_config_user_agent || "").includes("pnpm")) return "pnpm";
|
|
@@ -13410,9 +13876,12 @@ program.command("feedback").description("Report skill improvement opportunities
|
|
|
13410
13876
|
program.command("upgrade").description("Upgrade the CLI to the latest version").option("--dry-run", "Check for updates without installing").action(async (options) => {
|
|
13411
13877
|
await new UpgradeCommand().run(options);
|
|
13412
13878
|
});
|
|
13413
|
-
program.command("mcp <action> [scope]").description("Manage the optional MCP server integration. Actions: status | enable | disable | scope <project|user|snippets-only|disabled> | install | uninstall | snippets").option("--scope <scope>", "Override scope for install (project|user|snippets-only|disabled)").option("--from <from>", "For uninstall: project | user | all (default: project)").action(async (action, positional, opts) => {
|
|
13879
|
+
program.command("mcp <action> [scope]").description("Manage the optional MCP server integration. Actions: status | enable | disable | scope <project|user|snippets-only|disabled> | via <auto|npx|global> | install | uninstall | snippets").option("--scope <scope>", "Override scope for install (project|user|snippets-only|disabled)").option("--via <via>", "Override launch mode for this run (auto|npx|global)").option("--install-global", "On enable: install agent-skills-standard-mcp@<pin> globally, then verify its version (automatic when launch mode is global)").option("--from <from>", "For uninstall: project | user | all (default: project)").action(async (action, positional, opts) => {
|
|
13414
13880
|
const merged = { ...opts };
|
|
13415
|
-
if (positional)
|
|
13881
|
+
if (positional) {
|
|
13882
|
+
if (action === "via") merged.via = opts.via ?? positional;
|
|
13883
|
+
else merged.scope = opts.scope ?? positional;
|
|
13884
|
+
}
|
|
13416
13885
|
await new McpCommand().run(action, merged);
|
|
13417
13886
|
});
|
|
13418
13887
|
program.command("retrospective <action> [args...]").description("Enable, disable or inspect local session retrospectives (consent and the cross-session index). Runs the pinned agent-skills-standard-mcp package. Actions: enable | disable | status | index").allowUnknownOption(true).option("--store-root <path>", "Store root directory").option("--source-root <path>", "Additional source root for transcripts (repeatable)", (val, prev) => prev ? [...prev, val] : [val]).option("--since <YYYY-MM-DD>", "Index sessions since date").option("--until <YYYY-MM-DD>", "Index sessions until date").option("--folder <name>", "Index sessions from folder").action(async (action, args = [], options = {}) => {
|