@jaggerxtrm/specialists 2.0.2 → 2.1.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.
Files changed (2) hide show
  1. package/dist/index.js +82 -14
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -24568,12 +24568,12 @@ class StdioServerTransport {
24568
24568
  }
24569
24569
 
24570
24570
  // src/server.ts
24571
- import { join as join2 } from "node:path";
24571
+ import { join as join3 } from "node:path";
24572
24572
 
24573
24573
  // src/constants.ts
24574
- var LOG_PREFIX = "[UAI-MCP]";
24574
+ var LOG_PREFIX = "[specialists]";
24575
24575
  var MCP_CONFIG = {
24576
- SERVER_NAME: "unitAI",
24576
+ SERVER_NAME: "specialists",
24577
24577
  VERSION: "1.0.0",
24578
24578
  CAPABILITIES: {
24579
24579
  tools: {},
@@ -24748,6 +24748,7 @@ var SpecialistSchema = objectType({
24748
24748
  capabilities: CapabilitiesSchema,
24749
24749
  communication: CommunicationSchema,
24750
24750
  validation: ValidationSchema,
24751
+ beads_integration: enumType(["auto", "always", "never"]).default("auto"),
24751
24752
  heartbeat: unknownType().optional()
24752
24753
  })
24753
24754
  });
@@ -25078,6 +25079,15 @@ class PiAgentSession {
25078
25079
  }
25079
25080
  }
25080
25081
 
25082
+ // src/specialist/beads.ts
25083
+ function shouldCreateBead(beadsIntegration, permissionRequired) {
25084
+ if (beadsIntegration === "never")
25085
+ return false;
25086
+ if (beadsIntegration === "always")
25087
+ return true;
25088
+ return permissionRequired !== "READ_ONLY";
25089
+ }
25090
+
25081
25091
  // src/specialist/runner.ts
25082
25092
  class SpecialistRunner {
25083
25093
  deps;
@@ -25087,7 +25097,7 @@ class SpecialistRunner {
25087
25097
  this.sessionFactory = deps.sessionFactory ?? PiAgentSession.create.bind(PiAgentSession);
25088
25098
  }
25089
25099
  async run(options, onProgress, onEvent, onMeta, onKillRegistered) {
25090
- const { loader, hooks, circuitBreaker } = this.deps;
25100
+ const { loader, hooks, circuitBreaker, beadsClient } = this.deps;
25091
25101
  const invocationId = crypto.randomUUID();
25092
25102
  const start = Date.now();
25093
25103
  const spec = await loader.get(options.name);
@@ -25142,6 +25152,11 @@ You have access via Bash:
25142
25152
  timeout_ms: execution.timeout_ms,
25143
25153
  permission_level: permissionLevel
25144
25154
  });
25155
+ const beadsIntegration = spec.specialist.beads_integration ?? "auto";
25156
+ let beadId;
25157
+ if (beadsClient && shouldCreateBead(beadsIntegration, execution.permission_required)) {
25158
+ beadId = beadsClient.createBead(metadata.name) ?? undefined;
25159
+ }
25145
25160
  let output;
25146
25161
  let session;
25147
25162
  try {
@@ -25179,6 +25194,8 @@ You have access via Bash:
25179
25194
  circuitBreaker.recordSuccess(model);
25180
25195
  } catch (err) {
25181
25196
  circuitBreaker.recordFailure(model);
25197
+ if (beadId)
25198
+ beadsClient?.closeBead(beadId, "ERROR", Date.now() - start, model);
25182
25199
  await hooks.emit("post_execute", invocationId, metadata.name, metadata.version, {
25183
25200
  status: "ERROR",
25184
25201
  duration_ms: Date.now() - start,
@@ -25198,12 +25215,17 @@ You have access via Bash:
25198
25215
  duration_ms: durationMs,
25199
25216
  output_valid: true
25200
25217
  });
25218
+ if (beadId) {
25219
+ beadsClient?.closeBead(beadId, "COMPLETE", durationMs, model);
25220
+ beadsClient?.auditBead(beadId, metadata.name, model, 0);
25221
+ }
25201
25222
  return {
25202
25223
  output,
25203
25224
  backend: session.meta.backend,
25204
25225
  model,
25205
25226
  durationMs,
25206
- specialistVersion: metadata.version
25227
+ specialistVersion: metadata.version,
25228
+ beadId
25207
25229
  };
25208
25230
  }
25209
25231
  async startAsync(options, registry2) {
@@ -25478,6 +25500,12 @@ class JobRegistry {
25478
25500
  if (meta.model)
25479
25501
  job.model = meta.model;
25480
25502
  }
25503
+ setBeadId(id, beadId) {
25504
+ const job = this.jobs.get(id);
25505
+ if (!job)
25506
+ return;
25507
+ job.beadId = beadId;
25508
+ }
25481
25509
  setKillFn(id, killFn) {
25482
25510
  const job = this.jobs.get(id);
25483
25511
  if (!job)
@@ -25499,6 +25527,8 @@ class JobRegistry {
25499
25527
  job.model = result.model;
25500
25528
  job.specialistVersion = result.specialistVersion;
25501
25529
  job.endedAtMs = Date.now();
25530
+ if (result.beadId)
25531
+ job.beadId = result.beadId;
25502
25532
  }
25503
25533
  fail(id, err) {
25504
25534
  const job = this.jobs.get(id);
@@ -25535,7 +25565,8 @@ class JobRegistry {
25535
25565
  model: job.model,
25536
25566
  specialist_version: job.specialistVersion,
25537
25567
  duration_ms: (job.endedAtMs ?? Date.now()) - job.startedAtMs,
25538
- error: job.error
25568
+ error: job.error,
25569
+ beadId: job.beadId
25539
25570
  };
25540
25571
  }
25541
25572
  delete(id) {
@@ -25606,15 +25637,50 @@ function createStopSpecialistTool(registry2) {
25606
25637
  };
25607
25638
  }
25608
25639
 
25640
+ // src/tools/specialist/specialist_init.tool.ts
25641
+ import { spawnSync } from "node:child_process";
25642
+ import { existsSync as existsSync2 } from "node:fs";
25643
+ import { join as join2 } from "node:path";
25644
+ var specialistInitSchema = exports_external.object({});
25645
+ function createSpecialistInitTool(loader, deps) {
25646
+ const resolved = deps ?? {
25647
+ bdAvailable: () => spawnSync("bd", ["--version"], { stdio: "ignore" }).status === 0,
25648
+ beadsExists: () => existsSync2(join2(process.cwd(), ".beads")),
25649
+ bdInit: () => spawnSync("bd", ["init"], { stdio: "ignore" })
25650
+ };
25651
+ return {
25652
+ name: "specialist_init",
25653
+ description: "Session bootstrap: initializes beads in the project if not already set up, " + "then returns available specialists. Call at session start for orientation.",
25654
+ inputSchema: specialistInitSchema,
25655
+ async execute(_input) {
25656
+ const available = resolved.bdAvailable();
25657
+ let initialized = false;
25658
+ if (available) {
25659
+ if (resolved.beadsExists()) {
25660
+ initialized = true;
25661
+ } else {
25662
+ const result = resolved.bdInit();
25663
+ initialized = result.status === 0;
25664
+ }
25665
+ }
25666
+ const specialists = await loader.list();
25667
+ return {
25668
+ specialists,
25669
+ beads: { available, initialized }
25670
+ };
25671
+ }
25672
+ };
25673
+ }
25674
+
25609
25675
  // src/server.ts
25610
- class UnitAIServer {
25676
+ class SpecialistsServer {
25611
25677
  server;
25612
25678
  tools;
25613
25679
  constructor() {
25614
25680
  const circuitBreaker = new CircuitBreaker;
25615
25681
  const loader = new SpecialistLoader;
25616
25682
  const hooks = new HookEmitter({
25617
- tracePath: join2(process.cwd(), ".unitai", "trace.jsonl")
25683
+ tracePath: join3(process.cwd(), ".specialists", "trace.jsonl")
25618
25684
  });
25619
25685
  const runner = new SpecialistRunner({ loader, hooks, circuitBreaker });
25620
25686
  const registry2 = new JobRegistry;
@@ -25625,7 +25691,8 @@ class UnitAIServer {
25625
25691
  createSpecialistStatusTool(loader, circuitBreaker),
25626
25692
  createStartSpecialistTool(runner, registry2),
25627
25693
  createPollSpecialistTool(registry2),
25628
- createStopSpecialistTool(registry2)
25694
+ createStopSpecialistTool(registry2),
25695
+ createSpecialistInitTool(loader)
25629
25696
  ];
25630
25697
  this.server = new Server({ name: MCP_CONFIG.SERVER_NAME, version: MCP_CONFIG.VERSION }, { capabilities: MCP_CONFIG.CAPABILITIES });
25631
25698
  this.setupHandlers();
@@ -25639,7 +25706,8 @@ class UnitAIServer {
25639
25706
  specialist_status: exports_external.object({}),
25640
25707
  start_specialist: startSpecialistSchema,
25641
25708
  poll_specialist: pollSpecialistSchema,
25642
- stop_specialist: stopSpecialistSchema
25709
+ stop_specialist: stopSpecialistSchema,
25710
+ specialist_init: specialistInitSchema
25643
25711
  };
25644
25712
  this.toolSchemas = schemaMap;
25645
25713
  this.server.setRequestHandler(ListToolsRequestSchema, async () => {
@@ -25665,7 +25733,7 @@ class UnitAIServer {
25665
25733
  const onProgress = (msg) => {
25666
25734
  this.server.notification({
25667
25735
  method: "notifications/message",
25668
- params: { level: "info", logger: "unitai", data: msg }
25736
+ params: { level: "info", logger: "specialists", data: msg }
25669
25737
  }).catch(() => {});
25670
25738
  };
25671
25739
  try {
@@ -25689,7 +25757,7 @@ class UnitAIServer {
25689
25757
  try {
25690
25758
  const transport = new StdioServerTransport;
25691
25759
  await this.server.connect(transport);
25692
- logger.info(`UnitAI MCP Server v2 started — ${this.tools.length} tools registered`);
25760
+ logger.info(`Specialists MCP Server v2 started — ${this.tools.length} tools registered`);
25693
25761
  } catch (error2) {
25694
25762
  logger.error("Failed to start server", error2);
25695
25763
  process.exit(1);
@@ -25702,8 +25770,8 @@ class UnitAIServer {
25702
25770
 
25703
25771
  // src/index.ts
25704
25772
  async function main() {
25705
- logger.info("Starting Unified AI MCP Tool server...");
25706
- const server = new UnitAIServer;
25773
+ logger.info("Starting Specialists MCP Server...");
25774
+ const server = new SpecialistsServer;
25707
25775
  await server.start();
25708
25776
  }
25709
25777
  main().catch((error2) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jaggerxtrm/specialists",
3
- "version": "2.0.2",
3
+ "version": "2.1.0",
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",