@modootoday/pmcp 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -0
- package/dist/{chunk-76UKXVUE.js → chunk-5TX2IAVD.js} +2 -2
- package/dist/chunk-6TSHDCUY.js +1238 -0
- package/dist/{chunk-ICHBC5FM.js → chunk-FXBLBKP6.js} +2 -2
- package/dist/cli.js +301 -29
- package/dist/http.js +1 -1
- package/dist/index.d.ts +126 -1
- package/dist/index.js +21 -3
- package/dist/stdio.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-XTLANVRL.js +0 -193
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createSkillServer
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-5TX2IAVD.js";
|
|
4
4
|
|
|
5
5
|
// src/stdio.ts
|
|
6
6
|
import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
@@ -35,4 +35,4 @@ export {
|
|
|
35
35
|
serve,
|
|
36
36
|
main
|
|
37
37
|
};
|
|
38
|
-
//# sourceMappingURL=chunk-
|
|
38
|
+
//# sourceMappingURL=chunk-FXBLBKP6.js.map
|
package/dist/cli.js
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
ConfigError,
|
|
4
|
+
ProjectError,
|
|
5
|
+
TOOLS,
|
|
6
|
+
doctor,
|
|
4
7
|
findConfig,
|
|
5
8
|
openDatabase,
|
|
6
|
-
|
|
7
|
-
|
|
9
|
+
project,
|
|
10
|
+
readConfig,
|
|
11
|
+
readSpec,
|
|
12
|
+
renderFrontmatter,
|
|
13
|
+
spawnRunner,
|
|
14
|
+
splitFrontmatter
|
|
15
|
+
} from "./chunk-6TSHDCUY.js";
|
|
8
16
|
import {
|
|
9
17
|
serve
|
|
10
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-FXBLBKP6.js";
|
|
11
19
|
import {
|
|
20
|
+
kindOf,
|
|
12
21
|
readCatalog
|
|
13
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-5TX2IAVD.js";
|
|
14
23
|
|
|
15
24
|
// src/cli/command.ts
|
|
16
25
|
var ArgumentError = class extends Error {
|
|
@@ -913,8 +922,8 @@ function absent(error) {
|
|
|
913
922
|
function packageName(name) {
|
|
914
923
|
return /^(?:@[a-z0-9._-]+\/)?[a-z0-9][a-z0-9._-]*$/u.test(name);
|
|
915
924
|
}
|
|
916
|
-
function locate(name,
|
|
917
|
-
let directory =
|
|
925
|
+
function locate(name, project2) {
|
|
926
|
+
let directory = project2;
|
|
918
927
|
for (; ; ) {
|
|
919
928
|
let source;
|
|
920
929
|
try {
|
|
@@ -946,9 +955,9 @@ function locate(name, project) {
|
|
|
946
955
|
}
|
|
947
956
|
}
|
|
948
957
|
function readInstalledDependencies(projectDirectory) {
|
|
949
|
-
const
|
|
958
|
+
const project2 = resolve(projectDirectory);
|
|
950
959
|
const manifest = JSON.parse(
|
|
951
|
-
readFileSync2(join2(
|
|
960
|
+
readFileSync2(join2(project2, "package.json"), "utf8")
|
|
952
961
|
);
|
|
953
962
|
if (!object(manifest))
|
|
954
963
|
throw new Error("project package.json must contain an object");
|
|
@@ -972,7 +981,7 @@ function readInstalledDependencies(projectDirectory) {
|
|
|
972
981
|
const dependencies = [];
|
|
973
982
|
const issues = [];
|
|
974
983
|
for (const name of [...names].sort()) {
|
|
975
|
-
const result = locate(name,
|
|
984
|
+
const result = locate(name, project2);
|
|
976
985
|
if ("reason" in result) issues.push(result);
|
|
977
986
|
else dependencies.push(result);
|
|
978
987
|
}
|
|
@@ -1034,12 +1043,12 @@ function manifestAt(directory) {
|
|
|
1034
1043
|
return value;
|
|
1035
1044
|
}
|
|
1036
1045
|
function installContext(directory) {
|
|
1037
|
-
const
|
|
1038
|
-
const manifest = manifestAt(
|
|
1039
|
-
let root =
|
|
1040
|
-
let ancestor =
|
|
1046
|
+
const project2 = resolve2(directory);
|
|
1047
|
+
const manifest = manifestAt(project2);
|
|
1048
|
+
let root = project2;
|
|
1049
|
+
let ancestor = project2;
|
|
1041
1050
|
for (; ; ) {
|
|
1042
|
-
if (ancestor !==
|
|
1051
|
+
if (ancestor !== project2 && existsSync(join3(ancestor, "package.json"))) {
|
|
1043
1052
|
const candidate = manifestAt(ancestor);
|
|
1044
1053
|
if (candidate.workspaces !== void 0) {
|
|
1045
1054
|
root = ancestor;
|
|
@@ -1076,7 +1085,7 @@ function installContext(directory) {
|
|
|
1076
1085
|
throw new Error(
|
|
1077
1086
|
"declare packageManager or install project dependencies first"
|
|
1078
1087
|
);
|
|
1079
|
-
if (root !==
|
|
1088
|
+
if (root !== project2 && LOCKS.some((file) => existsSync(join3(project2, file)))) {
|
|
1080
1089
|
throw new Error(
|
|
1081
1090
|
"nested project has its own lockfile; resolve the workspace boundary first"
|
|
1082
1091
|
);
|
|
@@ -1084,12 +1093,12 @@ function installContext(directory) {
|
|
|
1084
1093
|
const files = [
|
|
1085
1094
|
.../* @__PURE__ */ new Set([
|
|
1086
1095
|
join3(root, "package.json"),
|
|
1087
|
-
join3(
|
|
1096
|
+
join3(project2, "package.json"),
|
|
1088
1097
|
...LOCKS.map((file) => join3(root, file))
|
|
1089
1098
|
])
|
|
1090
1099
|
];
|
|
1091
1100
|
return {
|
|
1092
|
-
project,
|
|
1101
|
+
project: project2,
|
|
1093
1102
|
root,
|
|
1094
1103
|
manager,
|
|
1095
1104
|
manifest,
|
|
@@ -1584,10 +1593,10 @@ function createPackageManagerRunner(userConfig, baseEnv = process.env, verifiedI
|
|
|
1584
1593
|
integrity(packageSpec, plan) {
|
|
1585
1594
|
const alreadyVerified = verifiedIntegrity?.get(packageSpec);
|
|
1586
1595
|
if (alreadyVerified) return alreadyVerified;
|
|
1587
|
-
const { manager, project } = plan.context;
|
|
1596
|
+
const { manager, project: project2 } = plan.context;
|
|
1588
1597
|
const args = manager === "npm" ? ["view", packageSpec, "dist.integrity", "--json"] : ["info", packageSpec, "dist.integrity", "--json"];
|
|
1589
1598
|
const result = spawnSync(manager, args, {
|
|
1590
|
-
cwd:
|
|
1599
|
+
cwd: project2,
|
|
1591
1600
|
env: environment(userConfig, baseEnv),
|
|
1592
1601
|
encoding: "utf8",
|
|
1593
1602
|
shell: false,
|
|
@@ -1701,8 +1710,8 @@ function contentDigest(files) {
|
|
|
1701
1710
|
}
|
|
1702
1711
|
return `sha256:${hash.digest("hex")}`;
|
|
1703
1712
|
}
|
|
1704
|
-
function verifyInstalledContent(
|
|
1705
|
-
const root = resolve4(
|
|
1713
|
+
function verifyInstalledContent(project2, packageName2, expected) {
|
|
1714
|
+
const root = resolve4(project2, "node_modules", ...packageName2.split("/"));
|
|
1706
1715
|
const files = skillFiles(root);
|
|
1707
1716
|
const actual = contentDigest(files);
|
|
1708
1717
|
return {
|
|
@@ -1888,9 +1897,9 @@ async function manage(context, sync) {
|
|
|
1888
1897
|
if (!sync && !context.args.positional.length && !context.args.flags.has("all")) {
|
|
1889
1898
|
throw new ArgumentError("select a skill package or pass --all");
|
|
1890
1899
|
}
|
|
1891
|
-
const
|
|
1892
|
-
const install = installContext(
|
|
1893
|
-
const inventory = readInstalledDependencies(
|
|
1900
|
+
const project2 = projectFrom(context);
|
|
1901
|
+
const install = installContext(project2);
|
|
1902
|
+
const inventory = readInstalledDependencies(project2);
|
|
1894
1903
|
const catalog = await remoteCatalog(context);
|
|
1895
1904
|
const plan = planInstall({
|
|
1896
1905
|
context: install,
|
|
@@ -1907,7 +1916,7 @@ async function manage(context, sync) {
|
|
|
1907
1916
|
])
|
|
1908
1917
|
);
|
|
1909
1918
|
const mismatched = (packages) => packages.map(
|
|
1910
|
-
(name) => verifyInstalledContent(
|
|
1919
|
+
(name) => verifyInstalledContent(project2, name, digests.get(name) ?? "")
|
|
1911
1920
|
).filter((check) => !check.matched);
|
|
1912
1921
|
const reportMismatch = (checks) => {
|
|
1913
1922
|
for (const check of checks) {
|
|
@@ -1919,12 +1928,12 @@ async function manage(context, sync) {
|
|
|
1919
1928
|
};
|
|
1920
1929
|
const report = {
|
|
1921
1930
|
catalogRevision: plan.catalogRevision,
|
|
1922
|
-
project,
|
|
1931
|
+
project: project2,
|
|
1923
1932
|
changes: plan.changes,
|
|
1924
1933
|
command: plan.command
|
|
1925
1934
|
};
|
|
1926
1935
|
if (!context.args.flags.has("json")) {
|
|
1927
|
-
context.ui.info("project",
|
|
1936
|
+
context.ui.info("project", project2);
|
|
1928
1937
|
for (const entry2 of plan.changes)
|
|
1929
1938
|
context.ui.line(` ${entry2.packageName}@${entry2.version}`);
|
|
1930
1939
|
if (plan.changes.length)
|
|
@@ -2078,6 +2087,267 @@ var syncCommand = {
|
|
|
2078
2087
|
run: (context) => manage(context, true)
|
|
2079
2088
|
};
|
|
2080
2089
|
|
|
2090
|
+
// src/commands/project.ts
|
|
2091
|
+
import { homedir as homedir2 } from "os";
|
|
2092
|
+
|
|
2093
|
+
// src/project/edit.ts
|
|
2094
|
+
import {
|
|
2095
|
+
cpSync,
|
|
2096
|
+
existsSync as existsSync2,
|
|
2097
|
+
mkdirSync as mkdirSync3,
|
|
2098
|
+
readFileSync as readFileSync7,
|
|
2099
|
+
rmSync as rmSync3,
|
|
2100
|
+
statSync as statSync3,
|
|
2101
|
+
writeFileSync as writeFileSync3
|
|
2102
|
+
} from "fs";
|
|
2103
|
+
import { basename, dirname as dirname3, join as join7 } from "path";
|
|
2104
|
+
var CROSS_TOOL = ["name", "description"];
|
|
2105
|
+
var tomlValue = (value) => JSON.stringify(value);
|
|
2106
|
+
function findEntry(entries, wanted) {
|
|
2107
|
+
const exact = entries.filter((e) => e.name === wanted);
|
|
2108
|
+
if (exact.length === 1 && exact[0]) return exact[0];
|
|
2109
|
+
const bySlug = entries.filter(
|
|
2110
|
+
(e) => e.slug === wanted || e.slug.endsWith(`/${wanted}`)
|
|
2111
|
+
);
|
|
2112
|
+
if (bySlug.length === 1 && bySlug[0]) return bySlug[0];
|
|
2113
|
+
if (bySlug.length > 1) {
|
|
2114
|
+
throw new ProjectError(
|
|
2115
|
+
`${wanted} is ambiguous: ${bySlug.map((e) => e.name).join(", ")}`
|
|
2116
|
+
);
|
|
2117
|
+
}
|
|
2118
|
+
throw new ProjectError(`no catalog skill or agent named ${wanted}`);
|
|
2119
|
+
}
|
|
2120
|
+
function addEntry(root, entry2) {
|
|
2121
|
+
const kind = kindOf(entry2);
|
|
2122
|
+
if (kind !== "skill" && kind !== "agent") {
|
|
2123
|
+
throw new ProjectError(
|
|
2124
|
+
`${entry2.name} is a ${kind}; only skills and agents are added to .agents/`
|
|
2125
|
+
);
|
|
2126
|
+
}
|
|
2127
|
+
const { meta, body } = splitFrontmatter(readFileSync7(entry2.path, "utf8"));
|
|
2128
|
+
const name = basename(entry2.slug);
|
|
2129
|
+
if (!/^[a-z0-9][a-z0-9-]*$/u.test(name)) {
|
|
2130
|
+
throw new ProjectError(
|
|
2131
|
+
`${entry2.name}: ${name} is not a usable directory name`
|
|
2132
|
+
);
|
|
2133
|
+
}
|
|
2134
|
+
if (kind === "skill") {
|
|
2135
|
+
const dest2 = join7(root, ".agents/skills", name);
|
|
2136
|
+
if (existsSync2(dest2))
|
|
2137
|
+
throw new ProjectError(`.agents/skills/${name} already exists`);
|
|
2138
|
+
const source = entry2.root ?? dirname3(entry2.path);
|
|
2139
|
+
mkdirSync3(dirname3(dest2), { recursive: true });
|
|
2140
|
+
if (statSync3(source).isDirectory()) {
|
|
2141
|
+
cpSync(source, dest2, { recursive: true });
|
|
2142
|
+
} else {
|
|
2143
|
+
mkdirSync3(dest2);
|
|
2144
|
+
}
|
|
2145
|
+
const head = renderFrontmatter({
|
|
2146
|
+
...meta,
|
|
2147
|
+
name,
|
|
2148
|
+
description: meta["description"] ?? entry2.description
|
|
2149
|
+
});
|
|
2150
|
+
writeFileSync3(
|
|
2151
|
+
join7(dest2, "SKILL.md"),
|
|
2152
|
+
`${head}
|
|
2153
|
+
${body.replace(/^\n/u, "")}`
|
|
2154
|
+
);
|
|
2155
|
+
return { kind, name, path: `.agents/skills/${name}` };
|
|
2156
|
+
}
|
|
2157
|
+
const dest = join7(root, ".agents/agents", name);
|
|
2158
|
+
if (existsSync2(dest))
|
|
2159
|
+
throw new ProjectError(`.agents/agents/${name} already exists`);
|
|
2160
|
+
const kept = {};
|
|
2161
|
+
const moved = {};
|
|
2162
|
+
for (const [key, value] of Object.entries({ ...meta, name })) {
|
|
2163
|
+
if (CROSS_TOOL.includes(key)) kept[key] = value;
|
|
2164
|
+
else moved[key] = value;
|
|
2165
|
+
}
|
|
2166
|
+
mkdirSync3(dest, { recursive: true });
|
|
2167
|
+
writeFileSync3(
|
|
2168
|
+
join7(dest, "agent.md"),
|
|
2169
|
+
`${renderFrontmatter(kept)}
|
|
2170
|
+
${body.replace(/^\n/u, "")}`
|
|
2171
|
+
);
|
|
2172
|
+
if (Object.keys(moved).length > 0) {
|
|
2173
|
+
const config = join7(root, "pmcp.toml");
|
|
2174
|
+
const lines = [
|
|
2175
|
+
`[agents.${name}.claude]`,
|
|
2176
|
+
...Object.entries(moved).map(([k, v]) => `${k} = ${tomlValue(v)}`)
|
|
2177
|
+
];
|
|
2178
|
+
const before = existsSync2(config) ? readFileSync7(config, "utf8") : "";
|
|
2179
|
+
const sep3 = before === "" || before.endsWith("\n\n") ? "" : "\n";
|
|
2180
|
+
writeFileSync3(config, `${before}${sep3}${lines.join("\n")}
|
|
2181
|
+
`);
|
|
2182
|
+
}
|
|
2183
|
+
return { kind, name, path: `.agents/agents/${name}/agent.md` };
|
|
2184
|
+
}
|
|
2185
|
+
function withoutTables(toml, name) {
|
|
2186
|
+
const own = new RegExp(
|
|
2187
|
+
`^\\[(skills\\.${name}|agents\\.${name}(\\.[a-z]+)?)\\]\\s*$`,
|
|
2188
|
+
"u"
|
|
2189
|
+
);
|
|
2190
|
+
const out = [];
|
|
2191
|
+
let skipping = false;
|
|
2192
|
+
for (const line of toml.split("\n")) {
|
|
2193
|
+
if (/^\[/u.test(line)) skipping = own.test(line);
|
|
2194
|
+
if (!skipping) out.push(line);
|
|
2195
|
+
}
|
|
2196
|
+
return out.join("\n").replace(/\n{3,}/gu, "\n\n");
|
|
2197
|
+
}
|
|
2198
|
+
function removeAsset(root, name) {
|
|
2199
|
+
const removed = [];
|
|
2200
|
+
for (const rel of [`.agents/skills/${name}`, `.agents/agents/${name}`]) {
|
|
2201
|
+
if (!existsSync2(join7(root, rel))) continue;
|
|
2202
|
+
rmSync3(join7(root, rel), { recursive: true, force: true });
|
|
2203
|
+
removed.push(rel);
|
|
2204
|
+
}
|
|
2205
|
+
if (removed.length === 0)
|
|
2206
|
+
throw new ProjectError(`no skill or agent named ${name} in .agents/`);
|
|
2207
|
+
const config = join7(root, "pmcp.toml");
|
|
2208
|
+
if (existsSync2(config)) {
|
|
2209
|
+
const before = readFileSync7(config, "utf8");
|
|
2210
|
+
const after = withoutTables(before, name);
|
|
2211
|
+
if (after !== before) writeFileSync3(config, after);
|
|
2212
|
+
}
|
|
2213
|
+
return removed;
|
|
2214
|
+
}
|
|
2215
|
+
|
|
2216
|
+
// src/commands/project.ts
|
|
2217
|
+
function specFrom(context) {
|
|
2218
|
+
const config = configFrom(context);
|
|
2219
|
+
if (config === null) {
|
|
2220
|
+
throw new ArgumentError(
|
|
2221
|
+
"no pmcp.toml at or above this directory; pass --config <file>"
|
|
2222
|
+
);
|
|
2223
|
+
}
|
|
2224
|
+
try {
|
|
2225
|
+
return readSpec(config);
|
|
2226
|
+
} catch (error) {
|
|
2227
|
+
throw new ArgumentError(
|
|
2228
|
+
error instanceof Error ? error.message : String(error)
|
|
2229
|
+
);
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
function reproject(context, spec) {
|
|
2233
|
+
const result = project(spec, { check: false });
|
|
2234
|
+
for (const problem of result.problems) context.ui.error(problem);
|
|
2235
|
+
if (result.code !== 0) return 1;
|
|
2236
|
+
context.ui.success(
|
|
2237
|
+
`projected ${result.outputs} outputs`,
|
|
2238
|
+
result.pruned > 0 ? `pruned ${result.pruned}` : void 0
|
|
2239
|
+
);
|
|
2240
|
+
return 0;
|
|
2241
|
+
}
|
|
2242
|
+
var projectCommand = {
|
|
2243
|
+
name: "project",
|
|
2244
|
+
describe: "Write each agent tool's native files from .agents/ and pmcp.toml",
|
|
2245
|
+
usage: "pmcp project [--check] | pmcp project add <skill|agent> | pmcp project remove <name>",
|
|
2246
|
+
options: [
|
|
2247
|
+
...CATALOG_OPTIONS,
|
|
2248
|
+
{
|
|
2249
|
+
name: "check",
|
|
2250
|
+
describe: "Write nothing; exit 2 when an output drifted, went stale or is undeclared.",
|
|
2251
|
+
boolean: true
|
|
2252
|
+
}
|
|
2253
|
+
],
|
|
2254
|
+
run(context) {
|
|
2255
|
+
const [verb, name, ...extra] = context.args.positional;
|
|
2256
|
+
if (extra.length > 0)
|
|
2257
|
+
throw new ArgumentError(`unexpected ${extra.join(" ")}`);
|
|
2258
|
+
try {
|
|
2259
|
+
if (verb === void 0) {
|
|
2260
|
+
const spec = specFrom(context);
|
|
2261
|
+
if (!context.args.flags.has("check")) return reproject(context, spec);
|
|
2262
|
+
const result = project(spec, { check: true });
|
|
2263
|
+
for (const problem of result.problems) context.ui.error(problem);
|
|
2264
|
+
if (result.code !== 0) {
|
|
2265
|
+
context.ui.info(
|
|
2266
|
+
"fix",
|
|
2267
|
+
"pmcp project (edit .agents/ or pmcp.toml, never the outputs)"
|
|
2268
|
+
);
|
|
2269
|
+
return 2;
|
|
2270
|
+
}
|
|
2271
|
+
context.ui.success(`${result.outputs} outputs match`);
|
|
2272
|
+
return 0;
|
|
2273
|
+
}
|
|
2274
|
+
if (context.args.flags.has("check"))
|
|
2275
|
+
throw new ArgumentError("--check takes no subcommand");
|
|
2276
|
+
if (name === void 0) throw new ArgumentError(`${verb} needs a name`);
|
|
2277
|
+
if (verb === "add") {
|
|
2278
|
+
const spec = specFrom(context);
|
|
2279
|
+
const added = addEntry(
|
|
2280
|
+
spec.root,
|
|
2281
|
+
findEntry(catalogFrom(context), name)
|
|
2282
|
+
);
|
|
2283
|
+
context.ui.success(`added ${added.kind} ${added.name}`, added.path);
|
|
2284
|
+
return reproject(context, specFrom(context));
|
|
2285
|
+
}
|
|
2286
|
+
if (verb === "remove") {
|
|
2287
|
+
const spec = specFrom(context);
|
|
2288
|
+
for (const path of removeAsset(spec.root, name))
|
|
2289
|
+
context.ui.success("removed", path);
|
|
2290
|
+
return reproject(context, specFrom(context));
|
|
2291
|
+
}
|
|
2292
|
+
throw new ArgumentError(`unknown subcommand ${verb}`);
|
|
2293
|
+
} catch (error) {
|
|
2294
|
+
if (error instanceof ProjectError) {
|
|
2295
|
+
context.ui.error(error.message);
|
|
2296
|
+
return 1;
|
|
2297
|
+
}
|
|
2298
|
+
throw error;
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
};
|
|
2302
|
+
var MARKS = {
|
|
2303
|
+
ok: "ok",
|
|
2304
|
+
fail: "FAIL",
|
|
2305
|
+
skip: "skip"
|
|
2306
|
+
};
|
|
2307
|
+
var doctorCommand = {
|
|
2308
|
+
name: "doctor",
|
|
2309
|
+
describe: "Ask each agent tool, through its own listing commands, whether it loads this project's assets",
|
|
2310
|
+
usage: "pmcp doctor [--tool <name>] [--json]",
|
|
2311
|
+
options: [
|
|
2312
|
+
{
|
|
2313
|
+
name: "config",
|
|
2314
|
+
describe: "A pmcp.toml to read instead of the nearest one.",
|
|
2315
|
+
placeholder: "<file>"
|
|
2316
|
+
},
|
|
2317
|
+
{
|
|
2318
|
+
name: "tool",
|
|
2319
|
+
describe: `Check only this tool (${TOOLS.join(", ")}). Repeatable.`,
|
|
2320
|
+
repeat: true,
|
|
2321
|
+
placeholder: "<name>"
|
|
2322
|
+
},
|
|
2323
|
+
{ name: "json", describe: "Emit JSON rather than a table.", boolean: true }
|
|
2324
|
+
],
|
|
2325
|
+
run(context) {
|
|
2326
|
+
const only = many(context.args, "tool");
|
|
2327
|
+
for (const tool of only) {
|
|
2328
|
+
if (!TOOLS.includes(tool))
|
|
2329
|
+
throw new ArgumentError(`unknown tool ${tool}`);
|
|
2330
|
+
}
|
|
2331
|
+
const findings = doctor(specFrom(context), {
|
|
2332
|
+
run: spawnRunner,
|
|
2333
|
+
home: context.env["HOME"] ?? homedir2(),
|
|
2334
|
+
...only.length > 0 ? { only: new Set(only) } : {}
|
|
2335
|
+
});
|
|
2336
|
+
if (context.args.flags.has("json")) {
|
|
2337
|
+
context.ui.data(`${JSON.stringify({ findings }, null, 2)}
|
|
2338
|
+
`);
|
|
2339
|
+
} else {
|
|
2340
|
+
context.ui.table(
|
|
2341
|
+
findings.map((f) => [
|
|
2342
|
+
`${MARKS[f.status]} ${f.tool}`,
|
|
2343
|
+
f.detail ? `${f.check} (${f.detail})` : f.check
|
|
2344
|
+
])
|
|
2345
|
+
);
|
|
2346
|
+
}
|
|
2347
|
+
return findings.some((f) => f.status === "fail") ? 1 : 0;
|
|
2348
|
+
}
|
|
2349
|
+
};
|
|
2350
|
+
|
|
2081
2351
|
// src/commands/index.ts
|
|
2082
2352
|
var COMMANDS = [
|
|
2083
2353
|
serveCommand,
|
|
@@ -2089,7 +2359,9 @@ var COMMANDS = [
|
|
|
2089
2359
|
availableCommand,
|
|
2090
2360
|
previewCommand,
|
|
2091
2361
|
installCommand,
|
|
2092
|
-
syncCommand
|
|
2362
|
+
syncCommand,
|
|
2363
|
+
projectCommand,
|
|
2364
|
+
doctorCommand
|
|
2093
2365
|
];
|
|
2094
2366
|
var PLANNED = [];
|
|
2095
2367
|
var GLOBAL = /* @__PURE__ */ new Set(["-q", "--quiet", "--no-color", "--color"]);
|
|
@@ -2135,7 +2407,7 @@ function dispatch(argv, options = {}) {
|
|
|
2135
2407
|
}
|
|
2136
2408
|
return run(command, rest, { ui, env, cwd });
|
|
2137
2409
|
}
|
|
2138
|
-
var VERSION = "0.
|
|
2410
|
+
var VERSION = "0.6.0";
|
|
2139
2411
|
async function run(command, argv, context) {
|
|
2140
2412
|
try {
|
|
2141
2413
|
return await command.run({
|
package/dist/http.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -99,6 +99,131 @@ declare function findConfig(start: string): string | null;
|
|
|
99
99
|
*/
|
|
100
100
|
declare function readConfig(path: string): ProjectConfig;
|
|
101
101
|
|
|
102
|
+
/** What the projector writes. A path is unique across a plan and is the lock key. */
|
|
103
|
+
type Output = {
|
|
104
|
+
readonly kind: "link";
|
|
105
|
+
readonly path: string;
|
|
106
|
+
readonly target: string;
|
|
107
|
+
} | {
|
|
108
|
+
readonly kind: "file";
|
|
109
|
+
readonly path: string;
|
|
110
|
+
readonly content: string;
|
|
111
|
+
}
|
|
112
|
+
/** One key inside a JSON file other content shares. path = file#a.b */
|
|
113
|
+
| {
|
|
114
|
+
readonly kind: "json";
|
|
115
|
+
readonly path: string;
|
|
116
|
+
readonly file: string;
|
|
117
|
+
readonly key: readonly string[];
|
|
118
|
+
readonly value: unknown;
|
|
119
|
+
}
|
|
120
|
+
/** A marked block inside a text file other content shares. path = file#pmcp */
|
|
121
|
+
| {
|
|
122
|
+
readonly kind: "block";
|
|
123
|
+
readonly path: string;
|
|
124
|
+
readonly file: string;
|
|
125
|
+
readonly content: string;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* The projector's reading of pmcp.toml: which tools to write for, and the
|
|
130
|
+
* per-asset settings that do not belong in the cross-tool sources under .agents/.
|
|
131
|
+
*/
|
|
132
|
+
|
|
133
|
+
declare const TOOLS: readonly ["claude", "codex", "gemini", "grok", "antigravity"];
|
|
134
|
+
type Tool = (typeof TOOLS)[number];
|
|
135
|
+
type Invocation = "any" | "user";
|
|
136
|
+
interface PackageRulesSpec {
|
|
137
|
+
/** Path of the rule file inside each package, e.g. `.agent/rules/PACKAGE.RULE.md`. */
|
|
138
|
+
readonly file: string;
|
|
139
|
+
/** Subdirectory of `.claude/rules` the links go in. */
|
|
140
|
+
readonly dir: string;
|
|
141
|
+
/** Leading path segments dropped when a link name is derived. */
|
|
142
|
+
readonly strip: readonly string[];
|
|
143
|
+
}
|
|
144
|
+
interface McpServerSpec {
|
|
145
|
+
readonly command: string;
|
|
146
|
+
readonly args: readonly string[];
|
|
147
|
+
readonly env: Readonly<Record<string, string>>;
|
|
148
|
+
/** Tools the server should expose where a host can narrow them. */
|
|
149
|
+
readonly tools?: readonly string[];
|
|
150
|
+
}
|
|
151
|
+
interface ProjectSpec {
|
|
152
|
+
readonly root: string;
|
|
153
|
+
readonly tools: ReadonlySet<Tool>;
|
|
154
|
+
readonly skills: Readonly<Record<string, {
|
|
155
|
+
invocation: Invocation;
|
|
156
|
+
}>>;
|
|
157
|
+
readonly agents: Readonly<Record<string, Partial<Record<Tool, Record<string, unknown>>>>>;
|
|
158
|
+
readonly packageRules: PackageRulesSpec | null;
|
|
159
|
+
readonly mcp: Readonly<Record<string, McpServerSpec>>;
|
|
160
|
+
}
|
|
161
|
+
declare function readSpec(config: ProjectConfig): ProjectSpec;
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Every output the sources imply, read from disk but written nowhere. Sources:
|
|
165
|
+
* .agents/skills, .agents/agents, .agents/rules, package rule files, and pmcp.toml.
|
|
166
|
+
*/
|
|
167
|
+
|
|
168
|
+
declare function plan(spec: ProjectSpec, owned?: ReadonlyMap<string, string>): Output[];
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Writes or checks a plan. The lock (pmcp.lock) records what this wrote, so a
|
|
172
|
+
* source removed later takes its outputs with it, and a file someone else wrote
|
|
173
|
+
* is never overwritten.
|
|
174
|
+
*/
|
|
175
|
+
|
|
176
|
+
declare const LOCK_FILE = "pmcp.lock";
|
|
177
|
+
interface ProjectResult {
|
|
178
|
+
/** 0 done or clean, 2 check found problems, 3 refused to overwrite. */
|
|
179
|
+
readonly code: 0 | 2 | 3;
|
|
180
|
+
readonly problems: readonly string[];
|
|
181
|
+
readonly outputs: number;
|
|
182
|
+
readonly pruned: number;
|
|
183
|
+
}
|
|
184
|
+
/** paths: globs whose fixed prefix holds no tracked file: a rule that loads nowhere. */
|
|
185
|
+
declare function deadGlobs(root: string, outputs: readonly Output[]): string[];
|
|
186
|
+
declare function project(spec: ProjectSpec, options: {
|
|
187
|
+
readonly check: boolean;
|
|
188
|
+
}): ProjectResult;
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Asks each tool what it loads, through its own listing commands only: no
|
|
192
|
+
* model is called. A tool whose CLI is not installed is skipped, not failed.
|
|
193
|
+
*/
|
|
194
|
+
|
|
195
|
+
interface RunResult {
|
|
196
|
+
/** The executable was not found. */
|
|
197
|
+
readonly missing: boolean;
|
|
198
|
+
readonly status: number | null;
|
|
199
|
+
readonly stdout: string;
|
|
200
|
+
}
|
|
201
|
+
type Runner = (command: string, args: readonly string[], cwd: string) => RunResult;
|
|
202
|
+
type Status = "ok" | "fail" | "skip";
|
|
203
|
+
interface Finding {
|
|
204
|
+
readonly tool: Tool | "project";
|
|
205
|
+
readonly check: string;
|
|
206
|
+
readonly status: Status;
|
|
207
|
+
readonly detail?: string;
|
|
208
|
+
}
|
|
209
|
+
interface DoctorOptions {
|
|
210
|
+
readonly run: Runner;
|
|
211
|
+
readonly home: string;
|
|
212
|
+
readonly only?: ReadonlySet<Tool>;
|
|
213
|
+
}
|
|
214
|
+
declare function doctor(spec: ProjectSpec, options: DoctorOptions): Finding[];
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Links each package's path-scoped rule file into .claude/rules/<dir>/. One level
|
|
218
|
+
* down because Grok Build loads every top-level .claude/rules/*.md unscoped.
|
|
219
|
+
* Names already in use are kept, so moving the links renames nothing.
|
|
220
|
+
*/
|
|
221
|
+
|
|
222
|
+
declare class ProjectError extends Error {
|
|
223
|
+
constructor(message: string);
|
|
224
|
+
}
|
|
225
|
+
declare function deriveName(dir: string, strip: readonly string[]): string;
|
|
226
|
+
|
|
102
227
|
/**
|
|
103
228
|
* The five tools, spoken over MCP.
|
|
104
229
|
*
|
|
@@ -156,4 +281,4 @@ declare function extensionEntry(entry: SkillEntry): SkillExtensionEntry | null;
|
|
|
156
281
|
*/
|
|
157
282
|
declare function registerSkillsExtension(server: McpServer, skills: () => readonly SkillEntry[]): void;
|
|
158
283
|
|
|
159
|
-
export { CONFIG_FILE, type CatalogSection, ConfigError, DEFAULT_INTENT_CACHE_LIMIT, IntentCache, type IntentCacheOptions, type MetadataValue, type ProjectConfig, type Rejection, SERVER_NAME, SKILLS_EXTENSION, SkillEntry, type SkillExtensionEntry, type SkillResource, SkillServerOptions, SkillTools, assetRoot, createSkillServer, extensionEntry, findConfig, openIntentCache, readConfig, readFrontmatterText, readMarketplace, readMetadata, registerSkillTools, registerSkillsExtension, skillUri };
|
|
284
|
+
export { CONFIG_FILE, type CatalogSection, ConfigError, DEFAULT_INTENT_CACHE_LIMIT, type Finding, IntentCache, type IntentCacheOptions, LOCK_FILE, type McpServerSpec, type MetadataValue, type Output, type PackageRulesSpec, type ProjectConfig, ProjectError, type ProjectResult, type ProjectSpec, type Rejection, type Runner, SERVER_NAME, SKILLS_EXTENSION, SkillEntry, type SkillExtensionEntry, type SkillResource, SkillServerOptions, SkillTools, TOOLS, type Tool, assetRoot, createSkillServer, deadGlobs, deriveName, doctor, extensionEntry, findConfig, openIntentCache, plan, project, readConfig, readFrontmatterText, readMarketplace, readMetadata, readSpec, registerSkillTools, registerSkillsExtension, skillUri };
|
package/dist/index.js
CHANGED
|
@@ -2,10 +2,19 @@ import {
|
|
|
2
2
|
CONFIG_FILE,
|
|
3
3
|
ConfigError,
|
|
4
4
|
DEFAULT_INTENT_CACHE_LIMIT,
|
|
5
|
+
LOCK_FILE,
|
|
6
|
+
ProjectError,
|
|
7
|
+
TOOLS,
|
|
8
|
+
deadGlobs,
|
|
9
|
+
deriveName,
|
|
10
|
+
doctor,
|
|
5
11
|
findConfig,
|
|
6
12
|
openIntentCache,
|
|
7
|
-
|
|
8
|
-
|
|
13
|
+
plan,
|
|
14
|
+
project,
|
|
15
|
+
readConfig,
|
|
16
|
+
readSpec
|
|
17
|
+
} from "./chunk-6TSHDCUY.js";
|
|
9
18
|
import {
|
|
10
19
|
ASSET_KINDS,
|
|
11
20
|
CATALOG_PAGE_MAX,
|
|
@@ -37,7 +46,7 @@ import {
|
|
|
37
46
|
registerSkillsExtension,
|
|
38
47
|
skillUri,
|
|
39
48
|
withoutBody
|
|
40
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-5TX2IAVD.js";
|
|
41
50
|
export {
|
|
42
51
|
ASSET_KINDS,
|
|
43
52
|
CATALOG_PAGE_MAX,
|
|
@@ -47,16 +56,22 @@ export {
|
|
|
47
56
|
DEFAULT_FIND_LIMIT,
|
|
48
57
|
DEFAULT_INTENT_CACHE_LIMIT,
|
|
49
58
|
DESCRIBE_FILE_LIMIT,
|
|
59
|
+
LOCK_FILE,
|
|
50
60
|
MAX_SKILL_DEPTH,
|
|
61
|
+
ProjectError,
|
|
51
62
|
READ_BUDGET_BYTES,
|
|
52
63
|
SERVER_NAME,
|
|
53
64
|
SKILLS_EXTENSION,
|
|
54
65
|
SKIPPED_DIRS,
|
|
66
|
+
TOOLS,
|
|
55
67
|
assetRoot,
|
|
56
68
|
cosine,
|
|
57
69
|
createSkillServer,
|
|
58
70
|
createSkillTools,
|
|
71
|
+
deadGlobs,
|
|
72
|
+
deriveName,
|
|
59
73
|
displayPath,
|
|
74
|
+
doctor,
|
|
60
75
|
extensionEntry,
|
|
61
76
|
find,
|
|
62
77
|
findConfig,
|
|
@@ -64,6 +79,8 @@ export {
|
|
|
64
79
|
lexicalScore,
|
|
65
80
|
normaliseIntent,
|
|
66
81
|
openIntentCache,
|
|
82
|
+
plan,
|
|
83
|
+
project,
|
|
67
84
|
readBody,
|
|
68
85
|
readCatalog,
|
|
69
86
|
readConfig,
|
|
@@ -71,6 +88,7 @@ export {
|
|
|
71
88
|
readFrontmatterText,
|
|
72
89
|
readMarketplace,
|
|
73
90
|
readMetadata,
|
|
91
|
+
readSpec,
|
|
74
92
|
registerSkillTools,
|
|
75
93
|
registerSkillsExtension,
|
|
76
94
|
skillUri,
|