@jaggerxtrm/specialists 2.1.9 → 2.1.11

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.
Files changed (3) hide show
  1. package/README.md +27 -0
  2. package/dist/index.js +341 -40
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -193,6 +193,33 @@ Pre-script output is formatted as `<pre_flight_context>` XML and available in `t
193
193
 
194
194
  ---
195
195
 
196
+ ## CLI
197
+
198
+ Once installed globally, the `specialists` command provides:
199
+
200
+ | Command | Description |
201
+ |---------|-------------|
202
+ | `specialists install` | Full-stack installer: pi, beads, dolt, MCP registration, hooks, scaffold |
203
+ | `specialists list` | List all discovered specialists with model, description, and scope |
204
+ | `specialists version` | Print the installed package version |
205
+ | `specialists` | Start the MCP server (called by Claude Code — not for direct use) |
206
+
207
+ ### specialists list
208
+
209
+ ```
210
+ Specialists (9)
211
+
212
+ auto-remediation google-gemini-cli/gemini-3-flash-preview Autonomous self-healing workflow... [project]
213
+ bug-hunt anthropic/claude-sonnet-4-6 Autonomously investigates bugs... [project]
214
+ codebase-explorer google-gemini-cli/gemini-3-flash-preview Explores codebase structure... [project]
215
+ init-session anthropic/claude-haiku-4-5 Gathers git/commit context... [project]
216
+ overthinker anthropic/claude-sonnet-4-6 Multi-phase deep reasoning... [project]
217
+ ```
218
+
219
+ Scopes: `[project]` = `./specialists/` (or `.claude/specialists/`), `[user]` = `~/.agents/specialists/`
220
+
221
+ ---
222
+
196
223
  ## Development
197
224
 
198
225
  ```bash
package/dist/index.js CHANGED
@@ -17490,11 +17490,6 @@ var init_schema = __esm(() => {
17490
17490
  });
17491
17491
 
17492
17492
  // src/specialist/loader.ts
17493
- var exports_loader = {};
17494
- __export(exports_loader, {
17495
- checkStaleness: () => checkStaleness,
17496
- SpecialistLoader: () => SpecialistLoader
17497
- });
17498
17493
  import { readdir, readFile, stat } from "node:fs/promises";
17499
17494
  import { join } from "node:path";
17500
17495
  import { homedir } from "node:os";
@@ -17592,11 +17587,331 @@ var init_loader = __esm(() => {
17592
17587
  init_schema();
17593
17588
  });
17594
17589
 
17595
- // src/index.ts
17590
+ // src/cli/install.ts
17591
+ var exports_install = {};
17592
+ __export(exports_install, {
17593
+ run: () => run
17594
+ });
17596
17595
  import { execFileSync } from "node:child_process";
17597
17596
  import { fileURLToPath } from "node:url";
17598
17597
  import { dirname as dirname2, join as join4 } from "node:path";
17598
+ async function run() {
17599
+ const installerPath = join4(dirname2(fileURLToPath(import.meta.url)), "..", "..", "bin", "install.js");
17600
+ execFileSync(process.execPath, [installerPath], { stdio: "inherit" });
17601
+ }
17602
+ var init_install = () => {};
17603
+
17604
+ // src/cli/version.ts
17605
+ var exports_version = {};
17606
+ __export(exports_version, {
17607
+ run: () => run2
17608
+ });
17599
17609
  import { createRequire as createRequire2 } from "node:module";
17610
+ async function run2() {
17611
+ const req = createRequire2(import.meta.url);
17612
+ const pkg = req("../package.json");
17613
+ console.log(`${pkg.name} v${pkg.version}`);
17614
+ }
17615
+ var init_version = () => {};
17616
+
17617
+ // src/cli/list.ts
17618
+ var exports_list = {};
17619
+ __export(exports_list, {
17620
+ run: () => run3,
17621
+ parseArgs: () => parseArgs,
17622
+ ArgParseError: () => ArgParseError
17623
+ });
17624
+ function parseArgs(argv) {
17625
+ const result = {};
17626
+ for (let i = 0;i < argv.length; i++) {
17627
+ const token = argv[i];
17628
+ if (token === "--category") {
17629
+ const value = argv[++i];
17630
+ if (!value || value.startsWith("--")) {
17631
+ throw new ArgParseError("--category requires a value");
17632
+ }
17633
+ result.category = value;
17634
+ continue;
17635
+ }
17636
+ if (token === "--scope") {
17637
+ const value = argv[++i];
17638
+ if (value !== "project" && value !== "user") {
17639
+ throw new ArgParseError(`--scope must be "project" or "user", got: "${value ?? ""}"`);
17640
+ }
17641
+ result.scope = value;
17642
+ continue;
17643
+ }
17644
+ }
17645
+ return result;
17646
+ }
17647
+ async function run3() {
17648
+ let args;
17649
+ try {
17650
+ args = parseArgs(process.argv.slice(3));
17651
+ } catch (err) {
17652
+ if (err instanceof ArgParseError) {
17653
+ console.error(`Error: ${err.message}`);
17654
+ process.exit(1);
17655
+ }
17656
+ throw err;
17657
+ }
17658
+ const loader = new SpecialistLoader;
17659
+ let specialists = await loader.list(args.category);
17660
+ if (args.scope) {
17661
+ specialists = specialists.filter((s) => s.scope === args.scope);
17662
+ }
17663
+ if (specialists.length === 0) {
17664
+ console.log("No specialists found.");
17665
+ return;
17666
+ }
17667
+ const nameWidth = Math.max(...specialists.map((s) => s.name.length), 4);
17668
+ const modelWidth = Math.max(...specialists.map((s) => s.model.length), 5);
17669
+ console.log(`
17670
+ ${bold(`Specialists (${specialists.length})`)}
17671
+ `);
17672
+ for (const s of specialists) {
17673
+ const name = cyan(s.name.padEnd(nameWidth));
17674
+ const model = dim(s.model.padEnd(modelWidth));
17675
+ const scopeTag = yellow(`[${s.scope}]`);
17676
+ console.log(` ${name} ${model} ${s.description} ${scopeTag}`);
17677
+ }
17678
+ console.log();
17679
+ }
17680
+ var dim = (s) => `\x1B[2m${s}\x1B[0m`, bold = (s) => `\x1B[1m${s}\x1B[0m`, cyan = (s) => `\x1B[36m${s}\x1B[0m`, yellow = (s) => `\x1B[33m${s}\x1B[0m`, ArgParseError;
17681
+ var init_list = __esm(() => {
17682
+ init_loader();
17683
+ ArgParseError = class ArgParseError extends Error {
17684
+ constructor(message) {
17685
+ super(message);
17686
+ this.name = "ArgParseError";
17687
+ }
17688
+ };
17689
+ });
17690
+
17691
+ // src/cli/init.ts
17692
+ var exports_init = {};
17693
+ __export(exports_init, {
17694
+ run: () => run4
17695
+ });
17696
+ import { existsSync as existsSync3, mkdirSync, readFileSync, writeFileSync } from "node:fs";
17697
+ import { join as join5 } from "node:path";
17698
+ function ok(msg) {
17699
+ console.log(` ${green("✓")} ${msg}`);
17700
+ }
17701
+ function skip(msg) {
17702
+ console.log(` ${yellow2("○")} ${msg}`);
17703
+ }
17704
+ async function run4() {
17705
+ const cwd = process.cwd();
17706
+ console.log(`
17707
+ ${bold2("specialists init")}
17708
+ `);
17709
+ const specialistsDir = join5(cwd, "specialists");
17710
+ if (existsSync3(specialistsDir)) {
17711
+ skip("specialists/ already exists");
17712
+ } else {
17713
+ mkdirSync(specialistsDir, { recursive: true });
17714
+ ok("created specialists/");
17715
+ }
17716
+ const agentsPath = join5(cwd, "AGENTS.md");
17717
+ if (existsSync3(agentsPath)) {
17718
+ const existing = readFileSync(agentsPath, "utf-8");
17719
+ if (existing.includes(AGENTS_MARKER)) {
17720
+ skip("AGENTS.md already has Specialists section");
17721
+ } else {
17722
+ writeFileSync(agentsPath, existing.trimEnd() + `
17723
+
17724
+ ` + AGENTS_BLOCK, "utf-8");
17725
+ ok("appended Specialists section to AGENTS.md");
17726
+ }
17727
+ } else {
17728
+ writeFileSync(agentsPath, AGENTS_BLOCK, "utf-8");
17729
+ ok("created AGENTS.md with Specialists section");
17730
+ }
17731
+ console.log(`
17732
+ ${bold2("Done!")}
17733
+ `);
17734
+ console.log(` ${dim2("Next steps:")}`);
17735
+ console.log(` 1. Add your specialists to ${yellow2("specialists/")}`);
17736
+ console.log(` 2. Run ${yellow2("specialists list")} to verify they are discovered`);
17737
+ console.log(` 3. Restart Claude Code to pick up AGENTS.md changes
17738
+ `);
17739
+ }
17740
+ var bold2 = (s) => `\x1B[1m${s}\x1B[0m`, green = (s) => `\x1B[32m${s}\x1B[0m`, yellow2 = (s) => `\x1B[33m${s}\x1B[0m`, dim2 = (s) => `\x1B[2m${s}\x1B[0m`, AGENTS_BLOCK, AGENTS_MARKER = "## Specialists";
17741
+ var init_init = __esm(() => {
17742
+ AGENTS_BLOCK = `
17743
+ ## Specialists
17744
+
17745
+ Call \`specialist_init\` at the start of every session to bootstrap context and
17746
+ see available specialists. Use \`use_specialist\` or \`start_specialist\` to
17747
+ delegate heavy tasks (code review, bug hunting, deep reasoning) to the right
17748
+ specialist without user intervention.
17749
+ `.trimStart();
17750
+ });
17751
+
17752
+ // src/cli/edit.ts
17753
+ var exports_edit = {};
17754
+ __export(exports_edit, {
17755
+ run: () => run5
17756
+ });
17757
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
17758
+ function parseArgs2(argv) {
17759
+ const name = argv[0];
17760
+ if (!name || name.startsWith("--")) {
17761
+ console.error("Usage: specialists edit <name> --<field> <value> [--dry-run]");
17762
+ console.error(` Fields: ${Object.keys(FIELD_MAP).join(", ")}`);
17763
+ process.exit(1);
17764
+ }
17765
+ let field;
17766
+ let value;
17767
+ let dryRun = false;
17768
+ let scope;
17769
+ for (let i = 1;i < argv.length; i++) {
17770
+ const token = argv[i];
17771
+ if (token === "--dry-run") {
17772
+ dryRun = true;
17773
+ continue;
17774
+ }
17775
+ if (token === "--scope") {
17776
+ const v = argv[++i];
17777
+ if (v !== "project" && v !== "user") {
17778
+ console.error(`Error: --scope must be "project" or "user", got: "${v ?? ""}"`);
17779
+ process.exit(1);
17780
+ }
17781
+ scope = v;
17782
+ continue;
17783
+ }
17784
+ if (token.startsWith("--") && !field) {
17785
+ field = token.slice(2);
17786
+ value = argv[++i];
17787
+ continue;
17788
+ }
17789
+ }
17790
+ if (!field || !FIELD_MAP[field]) {
17791
+ console.error(`Error: unknown or missing field. Valid fields: ${Object.keys(FIELD_MAP).join(", ")}`);
17792
+ process.exit(1);
17793
+ }
17794
+ if (value === undefined || value === "") {
17795
+ console.error(`Error: --${field} requires a value`);
17796
+ process.exit(1);
17797
+ }
17798
+ if (field === "permission" && !VALID_PERMISSIONS.includes(value)) {
17799
+ console.error(`Error: --permission must be one of: ${VALID_PERMISSIONS.join(", ")}`);
17800
+ process.exit(1);
17801
+ }
17802
+ if (field === "timeout" && !/^\d+$/.test(value)) {
17803
+ console.error("Error: --timeout must be a number (milliseconds)");
17804
+ process.exit(1);
17805
+ }
17806
+ return { name, field, value, dryRun, scope };
17807
+ }
17808
+ function setIn(doc2, path, value) {
17809
+ let node = doc2;
17810
+ for (let i = 0;i < path.length - 1; i++) {
17811
+ node = node.get(path[i], true);
17812
+ }
17813
+ const leaf = path[path.length - 1];
17814
+ if (Array.isArray(value)) {
17815
+ node.set(leaf, value);
17816
+ } else {
17817
+ node.set(leaf, value);
17818
+ }
17819
+ }
17820
+ async function run5() {
17821
+ const args = parseArgs2(process.argv.slice(3));
17822
+ const { name, field, value, dryRun, scope } = args;
17823
+ const loader = new SpecialistLoader;
17824
+ const all = await loader.list();
17825
+ const match = all.find((s) => s.name === name && (scope === undefined || s.scope === scope));
17826
+ if (!match) {
17827
+ const hint = scope ? ` (scope: ${scope})` : "";
17828
+ console.error(`Error: specialist "${name}" not found${hint}`);
17829
+ console.error(` Run ${yellow3("specialists list")} to see available specialists`);
17830
+ process.exit(1);
17831
+ }
17832
+ const raw = readFileSync2(match.filePath, "utf-8");
17833
+ const doc2 = $parseDocument(raw);
17834
+ const yamlPath = FIELD_MAP[field];
17835
+ let typedValue = value;
17836
+ if (field === "timeout") {
17837
+ typedValue = parseInt(value, 10);
17838
+ } else if (field === "tags") {
17839
+ typedValue = value.split(",").map((t) => t.trim()).filter(Boolean);
17840
+ }
17841
+ setIn(doc2, yamlPath, typedValue);
17842
+ const updated = doc2.toString();
17843
+ if (dryRun) {
17844
+ console.log(`
17845
+ ${bold3(`[dry-run] ${match.filePath}`)}
17846
+ `);
17847
+ console.log(dim3("--- current"));
17848
+ console.log(dim3(`+++ updated`));
17849
+ const oldLines = raw.split(`
17850
+ `);
17851
+ const newLines = updated.split(`
17852
+ `);
17853
+ newLines.forEach((line, i) => {
17854
+ if (line !== oldLines[i]) {
17855
+ if (oldLines[i] !== undefined)
17856
+ console.log(dim3(`- ${oldLines[i]}`));
17857
+ console.log(green2(`+ ${line}`));
17858
+ }
17859
+ });
17860
+ console.log();
17861
+ return;
17862
+ }
17863
+ writeFileSync2(match.filePath, updated, "utf-8");
17864
+ const displayValue = field === "tags" ? `[${typedValue.join(", ")}]` : String(typedValue);
17865
+ console.log(`${green2("✓")} ${bold3(name)}: ${yellow3(field)} = ${displayValue}` + dim3(` (${match.filePath})`));
17866
+ }
17867
+ var bold3 = (s) => `\x1B[1m${s}\x1B[0m`, green2 = (s) => `\x1B[32m${s}\x1B[0m`, yellow3 = (s) => `\x1B[33m${s}\x1B[0m`, dim3 = (s) => `\x1B[2m${s}\x1B[0m`, FIELD_MAP, VALID_PERMISSIONS;
17868
+ var init_edit = __esm(() => {
17869
+ init_dist();
17870
+ init_loader();
17871
+ FIELD_MAP = {
17872
+ model: ["specialist", "execution", "model"],
17873
+ "fallback-model": ["specialist", "execution", "fallback_model"],
17874
+ description: ["specialist", "metadata", "description"],
17875
+ permission: ["specialist", "execution", "permission_required"],
17876
+ timeout: ["specialist", "execution", "timeout_ms"],
17877
+ tags: ["specialist", "metadata", "tags"]
17878
+ };
17879
+ VALID_PERMISSIONS = ["READ_ONLY", "LOW", "MEDIUM", "HIGH"];
17880
+ });
17881
+
17882
+ // src/cli/help.ts
17883
+ var exports_help = {};
17884
+ __export(exports_help, {
17885
+ run: () => run6
17886
+ });
17887
+ async function run6() {
17888
+ const lines = [
17889
+ "",
17890
+ bold4("specialists <command>"),
17891
+ "",
17892
+ "Commands:",
17893
+ ...COMMANDS.map(([cmd, desc]) => ` ${cmd.padEnd(COL_WIDTH)} ${dim4(desc)}`),
17894
+ "",
17895
+ dim4("Run 'specialists <command> --help' for command-specific options."),
17896
+ ""
17897
+ ];
17898
+ console.log(lines.join(`
17899
+ `));
17900
+ }
17901
+ var bold4 = (s) => `\x1B[1m${s}\x1B[0m`, dim4 = (s) => `\x1B[2m${s}\x1B[0m`, COMMANDS, COL_WIDTH;
17902
+ var init_help = __esm(() => {
17903
+ COMMANDS = [
17904
+ ["install", "Full-stack installer: pi, beads, dolt, MCP registration, hooks"],
17905
+ ["list", "List available specialists with model and description"],
17906
+ ["version", "Print installed version"],
17907
+ ["init", "Initialize specialists in the current project"],
17908
+ ["edit", "Edit a specialist field (e.g. --model, --description)"],
17909
+ ["run", "Run a specialist with a prompt"],
17910
+ ["status", "Show system health (pi, beads, MCP)"],
17911
+ ["help", "Show this help message"]
17912
+ ];
17913
+ COL_WIDTH = Math.max(...COMMANDS.map(([cmd]) => cmd.length));
17914
+ });
17600
17915
 
17601
17916
  // node_modules/zod/v4/core/core.js
17602
17917
  var NEVER2 = Object.freeze({
@@ -25880,51 +26195,37 @@ class SpecialistsServer {
25880
26195
  }
25881
26196
 
25882
26197
  // src/index.ts
25883
- var __dirname2 = dirname2(fileURLToPath(import.meta.url));
25884
26198
  var sub = process.argv[2];
25885
- var dim = (s) => `\x1B[2m${s}\x1B[0m`;
25886
- var bold = (s) => `\x1B[1m${s}\x1B[0m`;
25887
- var cyan = (s) => `\x1B[36m${s}\x1B[0m`;
25888
- var yellow = (s) => `\x1B[33m${s}\x1B[0m`;
25889
- async function run() {
26199
+ async function run7() {
25890
26200
  if (sub === "install") {
25891
- const installerPath = join4(__dirname2, "..", "bin", "install.js");
25892
- execFileSync(process.execPath, [installerPath], { stdio: "inherit" });
25893
- return;
26201
+ const { run: handler } = await Promise.resolve().then(() => (init_install(), exports_install));
26202
+ return handler();
25894
26203
  }
25895
26204
  if (sub === "version") {
25896
- const req = createRequire2(import.meta.url);
25897
- const pkg = req("../package.json");
25898
- console.log(`${pkg.name} v${pkg.version}`);
25899
- return;
26205
+ const { run: handler } = await Promise.resolve().then(() => (init_version(), exports_version));
26206
+ return handler();
25900
26207
  }
25901
26208
  if (sub === "list") {
25902
- const { SpecialistLoader: SpecialistLoader2 } = await Promise.resolve().then(() => (init_loader(), exports_loader));
25903
- const loader = new SpecialistLoader2;
25904
- const specialists = await loader.list();
25905
- if (specialists.length === 0) {
25906
- console.log("No specialists found.");
25907
- return;
25908
- }
25909
- const nameWidth = Math.max(...specialists.map((s) => s.name.length), 4);
25910
- const modelWidth = Math.max(...specialists.map((s) => s.model.length), 5);
25911
- console.log(`
25912
- ${bold(`Specialists (${specialists.length})`)}
25913
- `);
25914
- for (const s of specialists) {
25915
- const name = cyan(s.name.padEnd(nameWidth));
25916
- const model = dim(s.model.padEnd(modelWidth));
25917
- const scope = yellow(`[${s.scope}]`);
25918
- console.log(` ${name} ${model} ${s.description} ${scope}`);
25919
- }
25920
- console.log();
25921
- return;
26209
+ const { run: handler } = await Promise.resolve().then(() => (init_list(), exports_list));
26210
+ return handler();
26211
+ }
26212
+ if (sub === "init") {
26213
+ const { run: handler } = await Promise.resolve().then(() => (init_init(), exports_init));
26214
+ return handler();
26215
+ }
26216
+ if (sub === "edit") {
26217
+ const { run: handler } = await Promise.resolve().then(() => (init_edit(), exports_edit));
26218
+ return handler();
26219
+ }
26220
+ if (sub === "help" || sub === "--help" || sub === "-h") {
26221
+ const { run: handler } = await Promise.resolve().then(() => (init_help(), exports_help));
26222
+ return handler();
25922
26223
  }
25923
26224
  logger.info("Starting Specialists MCP Server...");
25924
26225
  const server = new SpecialistsServer;
25925
26226
  await server.start();
25926
26227
  }
25927
- run().catch((error2) => {
26228
+ run7().catch((error2) => {
25928
26229
  logger.error(`Fatal error: ${error2}`);
25929
26230
  process.exit(1);
25930
26231
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jaggerxtrm/specialists",
3
- "version": "2.1.9",
3
+ "version": "2.1.11",
4
4
  "description": "OmniSpecialist — 7-tool MCP orchestration layer powered by the Specialist System. Discover and execute .specialist.yaml files across project/user/system scopes via pi.",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",