@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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createSkillServer
3
- } from "./chunk-76UKXVUE.js";
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-ICHBC5FM.js.map
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
- readConfig
7
- } from "./chunk-XTLANVRL.js";
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-ICHBC5FM.js";
18
+ } from "./chunk-FXBLBKP6.js";
11
19
  import {
20
+ kindOf,
12
21
  readCatalog
13
- } from "./chunk-76UKXVUE.js";
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, project) {
917
- let directory = project;
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 project = resolve(projectDirectory);
958
+ const project2 = resolve(projectDirectory);
950
959
  const manifest = JSON.parse(
951
- readFileSync2(join2(project, "package.json"), "utf8")
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, project);
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 project = resolve2(directory);
1038
- const manifest = manifestAt(project);
1039
- let root = project;
1040
- let ancestor = project;
1046
+ const project2 = resolve2(directory);
1047
+ const manifest = manifestAt(project2);
1048
+ let root = project2;
1049
+ let ancestor = project2;
1041
1050
  for (; ; ) {
1042
- if (ancestor !== project && existsSync(join3(ancestor, "package.json"))) {
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 !== project && LOCKS.some((file) => existsSync(join3(project, file)))) {
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(project, "package.json"),
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: project,
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(project, packageName2, expected) {
1705
- const root = resolve4(project, "node_modules", ...packageName2.split("/"));
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 project = projectFrom(context);
1892
- const install = installContext(project);
1893
- const inventory = readInstalledDependencies(project);
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(project, name, digests.get(name) ?? "")
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", 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.5.1";
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
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createSkillServer,
3
3
  readCatalog
4
- } from "./chunk-76UKXVUE.js";
4
+ } from "./chunk-5TX2IAVD.js";
5
5
 
6
6
  // src/http.ts
7
7
  import {
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
- readConfig
8
- } from "./chunk-XTLANVRL.js";
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-76UKXVUE.js";
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,
package/dist/stdio.js CHANGED
@@ -3,8 +3,8 @@ import {
3
3
  main,
4
4
  resolveRoots,
5
5
  serve
6
- } from "./chunk-ICHBC5FM.js";
7
- import "./chunk-76UKXVUE.js";
6
+ } from "./chunk-FXBLBKP6.js";
7
+ import "./chunk-5TX2IAVD.js";
8
8
  export {
9
9
  main,
10
10
  resolveRoots,