@pleri/olam-cli 0.1.170 → 0.1.174

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 (37) hide show
  1. package/dist/agent-stream/driver-runner.js +13 -0
  2. package/dist/commands/auth.d.ts +22 -7
  3. package/dist/commands/auth.d.ts.map +1 -1
  4. package/dist/commands/auth.js +414 -46
  5. package/dist/commands/auth.js.map +1 -1
  6. package/dist/commands/create.d.ts.map +1 -1
  7. package/dist/commands/create.js +45 -1
  8. package/dist/commands/create.js.map +1 -1
  9. package/dist/commands/services.d.ts +39 -0
  10. package/dist/commands/services.d.ts.map +1 -1
  11. package/dist/commands/services.js +64 -9
  12. package/dist/commands/services.js.map +1 -1
  13. package/dist/from-manifest.d.ts +53 -0
  14. package/dist/from-manifest.d.ts.map +1 -0
  15. package/dist/from-manifest.js +95 -0
  16. package/dist/from-manifest.js.map +1 -0
  17. package/dist/image-digests.json +8 -8
  18. package/dist/index.js +911 -137
  19. package/dist/lib/auth-remote.d.ts +130 -0
  20. package/dist/lib/auth-remote.d.ts.map +1 -0
  21. package/dist/lib/auth-remote.js +307 -0
  22. package/dist/lib/auth-remote.js.map +1 -0
  23. package/dist/mcp-server.js +1487 -435
  24. package/hermes-bundle/version.json +1 -1
  25. package/host-cp/k8s/manifests/50-deployment.yaml +1 -1
  26. package/host-cp/k8s/manifests/auth-service/50-deployment.yaml +1 -1
  27. package/host-cp/k8s/manifests/kg-service/50-deployment.yaml +1 -1
  28. package/host-cp/k8s/manifests/mcp-auth-service/50-deployment.yaml +1 -1
  29. package/host-cp/k8s/manifests/memory-service/50-deployment.yaml +1 -1
  30. package/host-cp/observability/ndjson-span-sink.mjs +52 -0
  31. package/host-cp/src/boot-reconciler.mjs +238 -0
  32. package/host-cp/src/linear-sync.mjs +43 -0
  33. package/host-cp/src/plan-chat-service.mjs +129 -1
  34. package/host-cp/src/port-bridge-manager.mjs +116 -10
  35. package/host-cp/src/server.mjs +121 -1
  36. package/host-cp/src/world-activity-tracker.mjs +392 -0
  37. package/package.json +1 -1
@@ -12331,10 +12331,10 @@ var init_runbooks = __esm({
12331
12331
  // ../core/dist/global-config/port-validator.js
12332
12332
  import * as net2 from "node:net";
12333
12333
  import * as childProcess from "node:child_process";
12334
- import { createRequire as createRequire3 } from "node:module";
12334
+ import { createRequire as createRequire4 } from "node:module";
12335
12335
  function getRequire() {
12336
12336
  if (!_require3) {
12337
- _require3 = createRequire3(import.meta.url);
12337
+ _require3 = createRequire4(import.meta.url);
12338
12338
  }
12339
12339
  return _require3;
12340
12340
  }
@@ -14165,9 +14165,9 @@ var init_file_lock = __esm({
14165
14165
  });
14166
14166
 
14167
14167
  // ../core/dist/lib/min-version-filter.js
14168
- import { existsSync as existsSync27, readFileSync as readFileSync23 } from "node:fs";
14168
+ import { existsSync as existsSync28, readFileSync as readFileSync23 } from "node:fs";
14169
14169
  function readOlamMinVersion(filepath) {
14170
- if (!existsSync27(filepath))
14170
+ if (!existsSync28(filepath))
14171
14171
  return void 0;
14172
14172
  let text;
14173
14173
  try {
@@ -14790,7 +14790,7 @@ var init_meta_hook_injector = __esm({
14790
14790
 
14791
14791
  // ../core/dist/lib/markdown-merger.js
14792
14792
  import { createHash as createHash5 } from "node:crypto";
14793
- import { readFileSync as readFileSync27, existsSync as existsSync30, statSync as statSync8 } from "node:fs";
14793
+ import { readFileSync as readFileSync27, existsSync as existsSync31, statSync as statSync8 } from "node:fs";
14794
14794
  function parseFrontmatter(text) {
14795
14795
  const match = FM_RE2.exec(text);
14796
14796
  if (match === null)
@@ -14910,7 +14910,7 @@ function mergeMarkdown(upstreamText, overlayText, labelForError, upstreamPath, o
14910
14910
  return { merged: fmBlock !== "" ? fmBlock + mergedBody : mergedBody };
14911
14911
  }
14912
14912
  function sha256OfPath(p) {
14913
- if (!existsSync30(p) || !statSync8(p).isFile())
14913
+ if (!existsSync31(p) || !statSync8(p).isFile())
14914
14914
  return "MISSING";
14915
14915
  return createHash5("sha256").update(readFileSync27(p)).digest("hex");
14916
14916
  }
@@ -15123,7 +15123,7 @@ var init_prefix_deploy = __esm({
15123
15123
  });
15124
15124
 
15125
15125
  // ../core/dist/skill-sync/resolve-source-config.js
15126
- import { readFileSync as readFileSync29, existsSync as existsSync31 } from "node:fs";
15126
+ import { readFileSync as readFileSync29, existsSync as existsSync32 } from "node:fs";
15127
15127
  import { join as join31 } from "node:path";
15128
15128
  import { parse as parseYaml4 } from "yaml";
15129
15129
  function sourceConfigPath(clonePath) {
@@ -15131,7 +15131,7 @@ function sourceConfigPath(clonePath) {
15131
15131
  }
15132
15132
  function readSourceConfig(clonePath, sourceId) {
15133
15133
  const path53 = sourceConfigPath(clonePath);
15134
- if (!existsSync31(path53))
15134
+ if (!existsSync32(path53))
15135
15135
  return void 0;
15136
15136
  let raw;
15137
15137
  try {
@@ -27600,10 +27600,111 @@ var init_exports = {};
27600
27600
  __export(init_exports, {
27601
27601
  register: () => register
27602
27602
  });
27603
- init_v3();
27604
27603
  import * as fs from "node:fs";
27605
27604
  import * as path from "node:path";
27606
27605
  import { execSync } from "node:child_process";
27606
+
27607
+ // ../skill-runtime/dist/skills/init.js
27608
+ init_v3();
27609
+
27610
+ // ../skill-runtime/dist/define-skill.js
27611
+ init_v3();
27612
+ var KEBAB_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
27613
+ function defineSkill(spec) {
27614
+ if (!KEBAB_RE.test(spec.name)) {
27615
+ throw new Error(`defineSkill: name "${spec.name}" must be kebab-case (lowercase letters, digits, and hyphens only; no leading/trailing/double hyphens).`);
27616
+ }
27617
+ if (!(spec.inputSchema instanceof external_exports.ZodObject)) {
27618
+ throw new Error(`defineSkill: inputSchema for skill "${spec.name}" must be a ZodObject (z.object({...})). Non-object schemas (z.string(), z.array(), etc.) are not supported because McpServer.tool() requires a ZodRawShape (the .shape of a ZodObject).`);
27619
+ }
27620
+ return {
27621
+ asMcpTool() {
27622
+ return {
27623
+ name: spec.name,
27624
+ description: spec.description,
27625
+ inputSchema: spec.inputSchema.shape,
27626
+ async handler(params) {
27627
+ try {
27628
+ const parsed = spec.inputSchema.safeParse(params);
27629
+ if (!parsed.success) {
27630
+ return {
27631
+ content: [
27632
+ {
27633
+ type: "text",
27634
+ text: `Invalid input: ${parsed.error.message}`
27635
+ }
27636
+ ],
27637
+ isError: true
27638
+ };
27639
+ }
27640
+ const output = await spec.handler(parsed.data);
27641
+ return {
27642
+ content: [
27643
+ {
27644
+ type: "text",
27645
+ text: typeof output === "string" ? output : JSON.stringify(output)
27646
+ }
27647
+ ]
27648
+ };
27649
+ } catch (err) {
27650
+ const message = err instanceof Error ? err.message : String(err);
27651
+ return {
27652
+ content: [{ type: "text", text: `Error: ${message}` }],
27653
+ isError: true
27654
+ };
27655
+ }
27656
+ }
27657
+ };
27658
+ },
27659
+ asHttpHandler() {
27660
+ return async (req, res) => {
27661
+ const parsed = spec.inputSchema.safeParse(req.body);
27662
+ if (!parsed.success) {
27663
+ res.status(400).json({
27664
+ error: "Invalid input",
27665
+ issues: parsed.error.issues
27666
+ });
27667
+ return;
27668
+ }
27669
+ try {
27670
+ const output = await spec.handler(parsed.data);
27671
+ res.status(200).json({ result: output });
27672
+ } catch (err) {
27673
+ const message = err instanceof Error ? err.message : String(err);
27674
+ res.status(500).json({ error: message });
27675
+ }
27676
+ };
27677
+ },
27678
+ toJsonSchema() {
27679
+ return zodToJsonSchema(spec.inputSchema, { target: "openApi3" });
27680
+ }
27681
+ };
27682
+ }
27683
+
27684
+ // ../skill-runtime/dist/skills/init.js
27685
+ var initInputSchema = external_exports.object({
27686
+ projectPath: external_exports.string().optional().describe("Path to the project root. Defaults to cwd."),
27687
+ pleriBaseUrl: external_exports.string().optional().describe("Pleri API base URL"),
27688
+ pleriPlaneId: external_exports.string().optional().describe("Pleri plane ID"),
27689
+ pleriApiKey: external_exports.string().optional().describe("Pleri API key from device code flow"),
27690
+ skipPleri: external_exports.boolean().optional().default(false).describe("Skip Pleri setup for now")
27691
+ });
27692
+ var initSkill = defineSkill({
27693
+ name: "init",
27694
+ description: "Initialize Olam in the current project. Creates .olam/ directory with configuration.",
27695
+ inputSchema: initInputSchema,
27696
+ handler: (input) => {
27697
+ const root = input.projectPath ?? process.cwd();
27698
+ const pleriStatus = input.skipPleri ? "skipped" : input.pleriApiKey ? "configured" : "pending";
27699
+ return [
27700
+ `Would initialize Olam at ${root}/.olam/`,
27701
+ ` pleri: ${pleriStatus}`,
27702
+ "Use the MCP tool for actual file system execution."
27703
+ ].join("\n");
27704
+ }
27705
+ });
27706
+
27707
+ // ../mcp-server/src/tools/init.ts
27607
27708
  function detectProjectType(projectRoot) {
27608
27709
  if (fs.existsSync(path.join(projectRoot, "Gemfile")) || fs.existsSync(path.join(projectRoot, "config", "routes.rb"))) {
27609
27710
  return "rails";
@@ -27663,16 +27764,11 @@ function updateGitignore(projectRoot, entries) {
27663
27764
  }
27664
27765
  }
27665
27766
  function register(server, _ctx, _initError) {
27767
+ const { description, inputSchema } = initSkill.asMcpTool();
27666
27768
  server.tool(
27667
27769
  "olam_init",
27668
- "Initialize Olam in the current project. Creates .olam/ directory with configuration.",
27669
- {
27670
- projectPath: external_exports.string().optional().describe("Path to the project root. Defaults to cwd."),
27671
- pleriBaseUrl: external_exports.string().optional().describe("Pleri API base URL"),
27672
- pleriPlaneId: external_exports.string().optional().describe("Pleri plane ID"),
27673
- pleriApiKey: external_exports.string().optional().describe("Pleri API key from device code flow"),
27674
- skipPleri: external_exports.boolean().optional().default(false).describe("Skip Pleri setup for now")
27675
- },
27770
+ description,
27771
+ inputSchema,
27676
27772
  async (params) => {
27677
27773
  try {
27678
27774
  const startDir = params.projectPath ?? process.cwd();
@@ -27799,7 +27895,6 @@ __export(auth_exports, {
27799
27895
  formatAccountFlag: () => formatAccountFlag,
27800
27896
  register: () => register2
27801
27897
  });
27802
- init_v3();
27803
27898
 
27804
27899
  // ../core/dist/auth/secret.js
27805
27900
  import * as crypto from "node:crypto";
@@ -28202,6 +28297,52 @@ function describe2(err) {
28202
28297
  return err instanceof Error ? err.message : "unknown error";
28203
28298
  }
28204
28299
 
28300
+ // ../skill-runtime/dist/skills/auth-status.js
28301
+ init_v3();
28302
+ var authStatusInputSchema = external_exports.object({});
28303
+ var authStatusSkill = defineSkill({
28304
+ name: "auth-status",
28305
+ description: "Show the local auth container state and stored Claude accounts. The container runs the OAuth PKCE dance once and persists tokens across world creations.",
28306
+ inputSchema: authStatusInputSchema,
28307
+ handler: (_input) => "Auth status requires an active AuthClient. Use the MCP tool surface or HTTP endpoint with a configured host."
28308
+ });
28309
+
28310
+ // ../skill-runtime/dist/skills/auth-up.js
28311
+ init_v3();
28312
+ var authUpInputSchema = external_exports.object({});
28313
+ var authUpSkill = defineSkill({
28314
+ name: "auth-up",
28315
+ description: "Start the local auth container (idempotent). Builds the image on first run. After this, run olam_auth_login to store a Claude account.",
28316
+ inputSchema: authUpInputSchema,
28317
+ handler: (_input) => "Auth container control requires an active AuthContainerController. Use the MCP tool surface or HTTP endpoint with a configured host."
28318
+ });
28319
+
28320
+ // ../skill-runtime/dist/skills/auth-login.js
28321
+ init_v3();
28322
+ var authLoginInputSchema = external_exports.object({
28323
+ label: external_exports.string().default("primary").describe("Account label shown in olam_auth_status (e.g. personal, team)."),
28324
+ provider: external_exports.enum(["claude", "codex"]).default("claude")
28325
+ });
28326
+ var authLoginSkill = defineSkill({
28327
+ name: "auth-login",
28328
+ description: "Begin the OAuth PKCE flow. Returns a login URL to open in a browser and a state token. After approving, call olam_auth_complete with the returned authorization code.",
28329
+ inputSchema: authLoginInputSchema,
28330
+ handler: (input) => `OAuth PKCE login requires an active AuthClient. Would initiate login for account "${input.label}" (${input.provider}). Use the MCP tool surface or HTTP endpoint with a configured host.`
28331
+ });
28332
+
28333
+ // ../skill-runtime/dist/skills/auth-complete.js
28334
+ init_v3();
28335
+ var authCompleteInputSchema = external_exports.object({
28336
+ state: external_exports.string().describe("State token returned by olam_auth_login."),
28337
+ code: external_exports.string().describe("Authorization code copied from the Claude redirect page.")
28338
+ });
28339
+ var authCompleteSkill = defineSkill({
28340
+ name: "auth-complete",
28341
+ description: "Complete a PKCE login flow by exchanging the authorization code for tokens. Uses the state returned by olam_auth_login.",
28342
+ inputSchema: authCompleteInputSchema,
28343
+ handler: (input) => `Token exchange requires an active AuthClient. Would complete PKCE flow for state "${input.state}". Use the MCP tool surface or HTTP endpoint with a configured host.`
28344
+ });
28345
+
28205
28346
  // ../mcp-server/src/tools/auth.ts
28206
28347
  function formatAccountFlag(a, now = Date.now()) {
28207
28348
  if (a.state === "disabled") return "disabled";
@@ -28219,10 +28360,11 @@ function formatAccountFlag(a, now = Date.now()) {
28219
28360
  return "valid";
28220
28361
  }
28221
28362
  function register2(server, _ctx, _initError) {
28363
+ const authStatus = authStatusSkill.asMcpTool();
28222
28364
  server.tool(
28223
28365
  "olam_auth_status",
28224
- "Show the local auth container state and stored Claude accounts. The container runs the OAuth PKCE dance once and persists tokens across world creations.",
28225
- {},
28366
+ authStatus.description,
28367
+ authStatus.inputSchema,
28226
28368
  async () => {
28227
28369
  const controller = new AuthContainerController();
28228
28370
  const state = controller.status();
@@ -28247,10 +28389,11 @@ function register2(server, _ctx, _initError) {
28247
28389
  return { content: [{ type: "text", text: lines.join("\n") }] };
28248
28390
  }
28249
28391
  );
28392
+ const authUp = authUpSkill.asMcpTool();
28250
28393
  server.tool(
28251
28394
  "olam_auth_up",
28252
- "Start the local auth container (idempotent). Builds the image on first run. After this, run olam_auth_login to store a Claude account.",
28253
- {},
28395
+ authUp.description,
28396
+ authUp.inputSchema,
28254
28397
  async () => {
28255
28398
  const controller = new AuthContainerController();
28256
28399
  const initial = controller.status();
@@ -28277,13 +28420,11 @@ function register2(server, _ctx, _initError) {
28277
28420
  };
28278
28421
  }
28279
28422
  );
28423
+ const authLogin = authLoginSkill.asMcpTool();
28280
28424
  server.tool(
28281
28425
  "olam_auth_login",
28282
- "Begin the OAuth PKCE flow. Returns a login URL to open in a browser and a state token. After approving, call olam_auth_complete with the returned authorization code.",
28283
- {
28284
- label: external_exports.string().default("primary").describe("Account label shown in olam_auth_status (e.g. personal, team)."),
28285
- provider: external_exports.enum(["claude", "codex"]).default("claude")
28286
- },
28426
+ authLogin.description,
28427
+ authLogin.inputSchema,
28287
28428
  async ({ label, provider }) => {
28288
28429
  const client = new AuthClient();
28289
28430
  const controller = new AuthContainerController();
@@ -28318,13 +28459,11 @@ function register2(server, _ctx, _initError) {
28318
28459
  }
28319
28460
  }
28320
28461
  );
28462
+ const authComplete = authCompleteSkill.asMcpTool();
28321
28463
  server.tool(
28322
28464
  "olam_auth_complete",
28323
- "Complete a PKCE login flow by exchanging the authorization code for tokens. Uses the state returned by olam_auth_login.",
28324
- {
28325
- state: external_exports.string().describe("State token returned by olam_auth_login."),
28326
- code: external_exports.string().describe("Authorization code copied from the Claude redirect page.")
28327
- },
28465
+ authComplete.description,
28466
+ authComplete.inputSchema,
28328
28467
  async ({ state, code }) => {
28329
28468
  const client = new AuthClient();
28330
28469
  try {
@@ -28350,7 +28489,6 @@ var pr_exports = {};
28350
28489
  __export(pr_exports, {
28351
28490
  register: () => register3
28352
28491
  });
28353
- init_v3();
28354
28492
 
28355
28493
  // ../core/dist/world/registry.js
28356
28494
  import { createRequire } from "node:module";
@@ -28580,6 +28718,21 @@ var WorldRegistry = class {
28580
28718
  const row = this.db.prepare("SELECT * FROM worlds WHERE id = ?").get(worldId);
28581
28719
  return row ? rowToMetadata(row) : void 0;
28582
28720
  }
28721
+ /**
28722
+ * Find all worlds with a given `name`. Returns rows of every lifecycle
28723
+ * status — callers filter as needed (e.g. active-only for dedup, error-
28724
+ * only for cleanup).
28725
+ *
28726
+ * The schema has UNIQUE on `id` but NOT on `name`, so a single name can
28727
+ * legitimately have multiple rows (e.g. one destroyed + one new). Issue
28728
+ * #962: `olam create <name>` was generating fresh ids without checking
28729
+ * for an existing row, accumulating orphan error rows under the same
28730
+ * name. This is the registry-level primitive the manager's dedup uses.
28731
+ */
28732
+ findByName(name) {
28733
+ const rows = this.db.prepare("SELECT * FROM worlds WHERE name = ? ORDER BY created_at DESC").all(name);
28734
+ return rows.map(rowToMetadata);
28735
+ }
28583
28736
  list(filter) {
28584
28737
  if (filter?.status) {
28585
28738
  const rows2 = this.db.prepare("SELECT * FROM worlds WHERE status = ? ORDER BY created_at DESC").all(filter.status);
@@ -28685,6 +28838,81 @@ async function decideGate(portOffset, id, payload) {
28685
28838
  return await res.json();
28686
28839
  }
28687
28840
 
28841
+ // ../skill-runtime/dist/skills/pr-list.js
28842
+ init_v3();
28843
+ var prListInputSchema = external_exports.object({});
28844
+ var prListSkill = defineSkill({
28845
+ name: "pr-list",
28846
+ description: "List every PR-gate request across every running world. A gate is opened by the in-world PreToolUse hook whenever Claude is about to run `gh pr create`.",
28847
+ inputSchema: prListInputSchema,
28848
+ handler: (_input) => "PR gate listing requires an active WorldRegistry. Use the MCP tool surface."
28849
+ });
28850
+
28851
+ // ../skill-runtime/dist/skills/pr-show.js
28852
+ init_v3();
28853
+ var prShowInputSchema = external_exports.object({
28854
+ id: external_exports.string().describe("Gate id (full UUID).")
28855
+ });
28856
+ var prShowSkill = defineSkill({
28857
+ name: "pr-show",
28858
+ description: "Show full detail for a gate: diff, commit log, branch, base, and any prior decision.",
28859
+ inputSchema: prShowInputSchema,
28860
+ handler: (input) => `PR gate detail for "${input.id}" requires an active WorldRegistry. Use the MCP tool surface.`
28861
+ });
28862
+
28863
+ // ../skill-runtime/dist/skills/pr-approve.js
28864
+ init_v3();
28865
+ var prApproveInputSchema = external_exports.object({
28866
+ id: external_exports.string().describe("Gate id (full UUID)."),
28867
+ reason: external_exports.string().optional().describe("Short free-text reason surfaced back to Claude on block."),
28868
+ by: external_exports.string().optional().describe('Deciding identity (defaults to "mcp").')
28869
+ });
28870
+ var prApproveSkill = defineSkill({
28871
+ name: "pr-approve",
28872
+ description: "Approve a pending PR-gate so the in-world `gh pr create` proceeds.",
28873
+ inputSchema: prApproveInputSchema,
28874
+ handler: (input) => `Approve gate "${input.id}" by "${input.by ?? "mcp"}" requires an active WorldRegistry. Use the MCP tool surface.`
28875
+ });
28876
+
28877
+ // ../skill-runtime/dist/skills/pr-reject.js
28878
+ init_v3();
28879
+ var prRejectInputSchema = external_exports.object({
28880
+ id: external_exports.string().describe("Gate id (full UUID)."),
28881
+ reason: external_exports.string().optional().describe("Short free-text reason surfaced back to Claude on block."),
28882
+ by: external_exports.string().optional().describe('Deciding identity (defaults to "mcp").')
28883
+ });
28884
+ var prRejectSkill = defineSkill({
28885
+ name: "pr-reject",
28886
+ description: "Block a pending PR-gate. The in-world hook exits non-zero so Claude sees a tool error and can address or retry.",
28887
+ inputSchema: prRejectInputSchema,
28888
+ handler: (input) => `Block gate "${input.id}" by "${input.by ?? "mcp"}" requires an active WorldRegistry. Use the MCP tool surface.`
28889
+ });
28890
+
28891
+ // ../skill-runtime/dist/skills/pr-track.js
28892
+ init_v3();
28893
+ var prTrackInputSchema = external_exports.object({
28894
+ worldId: external_exports.string().describe("World ID"),
28895
+ prUrl: external_exports.string().url().describe("Full GitHub PR URL (e.g. https://github.com/org/repo/pull/123)"),
28896
+ prNumber: external_exports.number().int().positive().optional().describe("PR number"),
28897
+ prRepo: external_exports.string().optional().describe("Repo slug (owner/repo)")
28898
+ });
28899
+ var prTrackSkill = defineSkill({
28900
+ name: "pr-track",
28901
+ description: "Record a PR association for a world after gh pr create succeeds. Call this after the PR is created to enable auto-destroy when the PR merges.",
28902
+ inputSchema: prTrackInputSchema,
28903
+ handler: (input) => {
28904
+ const urlMatch = input.prUrl.match(/github\.com\/([^/]+\/[^/]+)\/pull\/(\d+)/);
28905
+ const resolvedRepo = input.prRepo ?? (urlMatch ? urlMatch[1] : void 0);
28906
+ const resolvedNumber = input.prNumber ?? (urlMatch ? parseInt(urlMatch[2], 10) : void 0);
28907
+ return [
28908
+ `PR tracked for world ${input.worldId}: ${input.prUrl}`,
28909
+ resolvedRepo ? ` repo: ${resolvedRepo}` : "",
28910
+ resolvedNumber ? ` number: ${resolvedNumber}` : "",
28911
+ "Auto-destroy will fire when PR merges (after grace period)."
28912
+ ].filter(Boolean).join("\n");
28913
+ }
28914
+ });
28915
+
28688
28916
  // ../mcp-server/src/tools/pr.ts
28689
28917
  function runningWorlds() {
28690
28918
  const registry2 = new WorldRegistry();
@@ -28709,10 +28937,11 @@ function asMessage2(err) {
28709
28937
  return err instanceof Error ? err.message : String(err);
28710
28938
  }
28711
28939
  function register3(server, _ctx, _initError) {
28940
+ const prListTool = prListSkill.asMcpTool();
28712
28941
  server.tool(
28713
28942
  "olam_pr_list",
28714
- "List every PR-gate request across every running world. A gate is opened by the in-world PreToolUse hook whenever Claude is about to run `gh pr create`.",
28715
- {},
28943
+ prListTool.description,
28944
+ prListTool.inputSchema,
28716
28945
  async () => {
28717
28946
  const worlds = runningWorlds();
28718
28947
  const rows = [];
@@ -28739,10 +28968,11 @@ function register3(server, _ctx, _initError) {
28739
28968
  return { content: [{ type: "text", text: lines.join("\n") }] };
28740
28969
  }
28741
28970
  );
28971
+ const prShowTool = prShowSkill.asMcpTool();
28742
28972
  server.tool(
28743
28973
  "olam_pr_show",
28744
- "Show full detail for a gate: diff, commit log, branch, base, and any prior decision.",
28745
- { id: external_exports.string().describe("Gate id (full UUID).") },
28974
+ prShowTool.description,
28975
+ prShowTool.inputSchema,
28746
28976
  async ({ id }) => {
28747
28977
  const found = await findGate(id);
28748
28978
  if (!found) {
@@ -28771,14 +29001,11 @@ function register3(server, _ctx, _initError) {
28771
29001
  }
28772
29002
  );
28773
29003
  const decideTool = (name, decision) => {
29004
+ const skillTool = decision === "approve" ? prApproveSkill.asMcpTool() : prRejectSkill.asMcpTool();
28774
29005
  server.tool(
28775
29006
  name,
28776
- decision === "approve" ? "Approve a pending PR-gate so the in-world `gh pr create` proceeds." : "Block a pending PR-gate. The in-world hook exits non-zero so Claude sees a tool error and can address or retry.",
28777
- {
28778
- id: external_exports.string().describe("Gate id (full UUID)."),
28779
- reason: external_exports.string().optional().describe("Short free-text reason surfaced back to Claude on block."),
28780
- by: external_exports.string().optional().describe('Deciding identity (defaults to "mcp").')
28781
- },
29007
+ skillTool.description,
29008
+ skillTool.inputSchema,
28782
29009
  async ({ id, reason, by }) => {
28783
29010
  const found = await findGate(id);
28784
29011
  if (!found) {
@@ -28807,15 +29034,11 @@ function register3(server, _ctx, _initError) {
28807
29034
  };
28808
29035
  decideTool("olam_pr_approve", "approve");
28809
29036
  decideTool("olam_pr_reject", "block");
29037
+ const prTrackTool = prTrackSkill.asMcpTool();
28810
29038
  server.tool(
28811
29039
  "olam_pr_track",
28812
- "Record a PR association for a world after gh pr create succeeds. Call this after the PR is created to enable auto-destroy when the PR merges.",
28813
- {
28814
- worldId: external_exports.string().describe("World ID"),
28815
- prUrl: external_exports.string().url().describe("Full GitHub PR URL (e.g. https://github.com/org/repo/pull/123)"),
28816
- prNumber: external_exports.number().int().positive().optional().describe("PR number"),
28817
- prRepo: external_exports.string().optional().describe("Repo slug (owner/repo)")
28818
- },
29040
+ prTrackTool.description,
29041
+ prTrackTool.inputSchema,
28819
29042
  async ({ worldId, prUrl, prNumber, prRepo }) => {
28820
29043
  const urlMatch = prUrl.match(/github\.com\/([^/]+\/[^/]+)\/pull\/(\d+)/);
28821
29044
  const resolvedRepo = prRepo ?? (urlMatch ? urlMatch[1] : void 0);
@@ -28874,7 +29097,6 @@ var workspace_exports = {};
28874
29097
  __export(workspace_exports, {
28875
29098
  register: () => register4
28876
29099
  });
28877
- init_v3();
28878
29100
  import { stringify as stringifyYaml2 } from "yaml";
28879
29101
 
28880
29102
  // ../core/dist/workspace/index.js
@@ -29007,7 +29229,30 @@ function logRuntimeReconciliation(repoName, runtime) {
29007
29229
  console.warn(`[manifest] repo "${repoName}" supersedes central stack: ${validated.join(", ")}`);
29008
29230
  }
29009
29231
 
29010
- // ../mcp-server/src/tools/workspace.ts
29232
+ // ../skill-runtime/dist/skills/workspace-list.js
29233
+ init_v3();
29234
+ var workspaceListInputSchema = external_exports.object({});
29235
+ var workspaceListSkill = defineSkill({
29236
+ name: "workspace-list",
29237
+ description: "List every workspace in the local catalog. A workspace is a named bundle of repos that worlds instantiate from. Returns CF-compatible summaries: { name, repoCount, updatedAt }.",
29238
+ inputSchema: workspaceListInputSchema,
29239
+ handler: (_input) => JSON.stringify({ note: "workspace listing requires local catalog access; use MCP surface" }, null, 2)
29240
+ });
29241
+
29242
+ // ../skill-runtime/dist/skills/workspace-show.js
29243
+ init_v3();
29244
+ var workspaceShowInputSchema = external_exports.object({
29245
+ name: external_exports.string().describe("Workspace name.")
29246
+ });
29247
+ var workspaceShowSkill = defineSkill({
29248
+ name: "workspace-show",
29249
+ description: "Return the full workspace definition as YAML (name, repos, defaults, updatedAt).",
29250
+ inputSchema: workspaceShowInputSchema,
29251
+ handler: ({ name }) => JSON.stringify({ name, note: "workspace lookup requires local catalog access; use MCP surface" }, null, 2)
29252
+ });
29253
+
29254
+ // ../skill-runtime/dist/skills/workspace-add.js
29255
+ init_v3();
29011
29256
  var repoInputSchema = external_exports.object({
29012
29257
  name: external_exports.string().min(1).optional().describe("Repo name; defaults to last URL segment."),
29013
29258
  url: external_exports.string().min(1),
@@ -29015,6 +29260,32 @@ var repoInputSchema = external_exports.object({
29015
29260
  submodules: external_exports.boolean().optional(),
29016
29261
  setupCmd: external_exports.string().optional()
29017
29262
  });
29263
+ var workspaceAddInputSchema = external_exports.object({
29264
+ name: external_exports.string().describe("Workspace name (lowercase alphanumerics + dashes, 1\u201364 chars)."),
29265
+ repos: external_exports.array(repoInputSchema).min(1),
29266
+ defaultBranch: external_exports.string().optional().describe("Fallback branch for repos without an explicit branch."),
29267
+ force: external_exports.boolean().optional().describe("Overwrite if a workspace with this name already exists.")
29268
+ });
29269
+ var workspaceAddSkill = defineSkill({
29270
+ name: "workspace-add",
29271
+ description: "Create a new workspace. Pass an array of repos (each with url + optional branch, submodules, setupCmd). Rejects duplicate names unless force:true.",
29272
+ inputSchema: workspaceAddInputSchema,
29273
+ handler: ({ name, repos }) => JSON.stringify({ name, repoCount: repos.length, note: "workspace creation requires local catalog access; use MCP surface" }, null, 2)
29274
+ });
29275
+
29276
+ // ../skill-runtime/dist/skills/workspace-remove.js
29277
+ init_v3();
29278
+ var workspaceRemoveInputSchema = external_exports.object({
29279
+ name: external_exports.string().describe("Workspace name.")
29280
+ });
29281
+ var workspaceRemoveSkill = defineSkill({
29282
+ name: "workspace-remove",
29283
+ description: "Delete a workspace. Does not affect already-created worlds that referenced it.",
29284
+ inputSchema: workspaceRemoveInputSchema,
29285
+ handler: ({ name }) => JSON.stringify({ name, note: "workspace removal requires local catalog access; use MCP surface" }, null, 2)
29286
+ });
29287
+
29288
+ // ../mcp-server/src/tools/workspace.ts
29018
29289
  function deriveRepoName(url2) {
29019
29290
  return url2.replace(/\.git$/, "").split(/[\/:]/).filter(Boolean).at(-1) ?? "repo";
29020
29291
  }
@@ -29022,10 +29293,11 @@ function asMessage3(err) {
29022
29293
  return err instanceof Error ? err.message : String(err);
29023
29294
  }
29024
29295
  function register4(server, _ctx, _initError) {
29296
+ const listTool = workspaceListSkill.asMcpTool();
29025
29297
  server.tool(
29026
29298
  "olam_workspace_list",
29027
- "List every workspace in the local catalog. A workspace is a named bundle of repos that worlds instantiate from. Returns CF-compatible summaries: { name, repoCount, updatedAt }.",
29028
- {},
29299
+ listTool.description,
29300
+ listTool.inputSchema,
29029
29301
  async () => {
29030
29302
  const all = listWorkspaces();
29031
29303
  if (all.length === 0) {
@@ -29039,10 +29311,11 @@ function register4(server, _ctx, _initError) {
29039
29311
  return { content: [{ type: "text", text: lines.join("\n") }] };
29040
29312
  }
29041
29313
  );
29314
+ const showTool = workspaceShowSkill.asMcpTool();
29042
29315
  server.tool(
29043
29316
  "olam_workspace_show",
29044
- "Return the full workspace definition as YAML (name, repos, defaults, updatedAt).",
29045
- { name: external_exports.string().describe("Workspace name.") },
29317
+ showTool.description,
29318
+ showTool.inputSchema,
29046
29319
  async ({ name }) => {
29047
29320
  try {
29048
29321
  const ws = readWorkspace(name);
@@ -29058,15 +29331,11 @@ function register4(server, _ctx, _initError) {
29058
29331
  }
29059
29332
  }
29060
29333
  );
29334
+ const addTool = workspaceAddSkill.asMcpTool();
29061
29335
  server.tool(
29062
29336
  "olam_workspace_add",
29063
- "Create a new workspace. Pass an array of repos (each with url + optional branch, submodules, setupCmd). Rejects duplicate names unless force:true.",
29064
- {
29065
- name: external_exports.string().describe("Workspace name (lowercase alphanumerics + dashes, 1\u201364 chars)."),
29066
- repos: external_exports.array(repoInputSchema).min(1),
29067
- defaultBranch: external_exports.string().optional().describe("Fallback branch for repos without an explicit branch."),
29068
- force: external_exports.boolean().optional().describe("Overwrite if a workspace with this name already exists.")
29069
- },
29337
+ addTool.description,
29338
+ addTool.inputSchema,
29070
29339
  async ({ name, repos, defaultBranch, force }) => {
29071
29340
  try {
29072
29341
  const filled = repos.map((r) => ({
@@ -29097,10 +29366,11 @@ function register4(server, _ctx, _initError) {
29097
29366
  }
29098
29367
  }
29099
29368
  );
29369
+ const removeTool = workspaceRemoveSkill.asMcpTool();
29100
29370
  server.tool(
29101
29371
  "olam_workspace_remove",
29102
- "Delete a workspace. Does not affect already-created worlds that referenced it.",
29103
- { name: external_exports.string().describe("Workspace name.") },
29372
+ removeTool.description,
29373
+ removeTool.inputSchema,
29104
29374
  async ({ name }) => {
29105
29375
  try {
29106
29376
  const removed = removeWorkspace(name);
@@ -29124,10 +29394,35 @@ __export(role_exports, {
29124
29394
  register: () => register5
29125
29395
  });
29126
29396
  init_v3();
29397
+
29398
+ // ../skill-runtime/dist/skills/role-assign.js
29399
+ init_v3();
29400
+ var ROLE_ARCHETYPES = [
29401
+ "user",
29402
+ "workspace-curator",
29403
+ "policy-admin",
29404
+ "bootstrapper",
29405
+ "admin",
29406
+ "dev"
29407
+ ];
29408
+ var roleAssignInputSchema = external_exports.object({
29409
+ email: external_exports.string().email().describe("Target user email."),
29410
+ archetype: external_exports.enum(ROLE_ARCHETYPES).describe("Named archetype preset."),
29411
+ capabilities: external_exports.array(external_exports.string()).optional().describe("Optional additive capabilities on top of the archetype.")
29412
+ });
29127
29413
  var ROLE_DEPRECATED_MESSAGE = "olam_role_* tools were deprecated in PR #31 \u2014 role management now lives in Pylon. Use `pylon role grant --app=olam --email=<email> --archetype=<name>`, `pylon role list --app=olam`, or `pylon role revoke --email-hash=<sha256>` directly. Install via `npm install -g @pleri/pylon-cli`.";
29414
+ var roleAssignSkill = defineSkill({
29415
+ name: "role-assign",
29416
+ description: "DEPRECATED \u2014 use `pylon role grant --app=olam --email=<email> --archetype=<name>` instead. Role management moved to Pylon in PR #31.",
29417
+ inputSchema: roleAssignInputSchema,
29418
+ handler: (_input) => ROLE_DEPRECATED_MESSAGE
29419
+ });
29420
+
29421
+ // ../mcp-server/src/tools/role.ts
29422
+ var ROLE_DEPRECATED_MESSAGE2 = "olam_role_* tools were deprecated in PR #31 \u2014 role management now lives in Pylon. Use `pylon role grant --app=olam --email=<email> --archetype=<name>`, `pylon role list --app=olam`, or `pylon role revoke --email-hash=<sha256>` directly. Install via `npm install -g @pleri/pylon-cli`.";
29128
29423
  function deprecatedResult() {
29129
29424
  return {
29130
- content: [{ type: "text", text: ROLE_DEPRECATED_MESSAGE }],
29425
+ content: [{ type: "text", text: ROLE_DEPRECATED_MESSAGE2 }],
29131
29426
  isError: true
29132
29427
  };
29133
29428
  }
@@ -29138,15 +29433,14 @@ function register5(server, _ctx, _initError) {
29138
29433
  {},
29139
29434
  async () => deprecatedResult()
29140
29435
  );
29436
+ const roleAssignTool = roleAssignSkill.asMcpTool();
29141
29437
  server.tool(
29142
29438
  "olam_role_assign",
29143
- "DEPRECATED \u2014 use `pylon role grant --app=olam --email=<email> --archetype=<name>` instead. Role management moved to Pylon in PR #31.",
29144
- {
29145
- email: external_exports.string().email().describe("Target user email."),
29146
- archetype: external_exports.enum(["user", "workspace-curator", "policy-admin", "bootstrapper", "admin", "dev"]).describe("Named archetype preset."),
29147
- capabilities: external_exports.array(external_exports.string()).optional().describe("Optional additive capabilities on top of the archetype.")
29148
- },
29149
- async () => deprecatedResult()
29439
+ roleAssignTool.description,
29440
+ roleAssignTool.inputSchema,
29441
+ async (params) => roleAssignTool.handler(
29442
+ params
29443
+ )
29150
29444
  );
29151
29445
  server.tool(
29152
29446
  "olam_role_revoke",
@@ -29163,7 +29457,6 @@ var world_create_exports = {};
29163
29457
  __export(world_create_exports, {
29164
29458
  register: () => register6
29165
29459
  });
29166
- init_v3();
29167
29460
 
29168
29461
  // ../mcp-server/src/utils/logger.ts
29169
29462
  var LEVEL_ORDER = {
@@ -31126,7 +31419,8 @@ function createSshProvider(config2) {
31126
31419
  return new SSHProvider(hosts);
31127
31420
  }
31128
31421
 
31129
- // ../mcp-server/src/tools/world-create.ts
31422
+ // ../skill-runtime/dist/skills/world-create.js
31423
+ init_v3();
31130
31424
  var taskContextSchema = external_exports.object({
31131
31425
  source: external_exports.enum(["linear", "github", "manual"]),
31132
31426
  ticketId: external_exports.string().optional(),
@@ -31138,27 +31432,49 @@ var taskContextSchema = external_exports.object({
31138
31432
  githubRepo: external_exports.string().optional(),
31139
31433
  githubBranch: external_exports.string().optional()
31140
31434
  }).optional();
31435
+ var worldCreateInputSchema = external_exports.object({
31436
+ name: external_exports.string(),
31437
+ repos: external_exports.array(external_exports.string()).optional(),
31438
+ workspace: external_exports.string().optional().describe("Named workspace from the host catalog (~/.olam/workspaces/<name>.yaml). Inline `repos` wins over this."),
31439
+ task: external_exports.string().optional(),
31440
+ linearTicketId: external_exports.string().optional(),
31441
+ branchName: external_exports.string().optional().describe("Override the default branch name (e.g., feat/ATL-1234-payment-webhook)"),
31442
+ planFile: external_exports.string().optional().describe("Path to a plan file on the host to inject into the world"),
31443
+ taskContext: taskContextSchema.describe("Structured task context from Linear, GitHub, or manual entry"),
31444
+ carryUncommitted: external_exports.boolean().optional().describe("Preserve operator's uncommitted edits in the world's worktree (B3); default false"),
31445
+ runbookName: external_exports.string().optional().describe("Named runbook profile from ~/.olam/config.json \u2014 seeds ports, env overrides, and fixture-copy steps")
31446
+ });
31447
+ var worldCreateSkill = defineSkill({
31448
+ name: "world-create",
31449
+ description: "Provision a new isolated development world with optional repository cloning, initial task, and Linear/GitHub task context.",
31450
+ inputSchema: worldCreateInputSchema,
31451
+ handler: (input) => {
31452
+ const hasTask = Boolean(input.task?.trim() || input.linearTicketId?.trim() || input.taskContext?.ticketId?.trim());
31453
+ if (!hasTask) {
31454
+ return "olam_create requires one of: `task`, `linearTicketId`, or `taskContext.ticketId`. Worlds are only created with an actionable input.";
31455
+ }
31456
+ return JSON.stringify({
31457
+ name: input.name,
31458
+ repos: input.repos ?? [],
31459
+ workspace: input.workspace,
31460
+ task: input.task,
31461
+ linearTicketId: input.linearTicketId,
31462
+ branchName: input.branchName,
31463
+ planFile: input.planFile,
31464
+ taskContext: input.taskContext,
31465
+ carryUncommitted: input.carryUncommitted ?? false,
31466
+ runbookName: input.runbookName
31467
+ }, null, 2);
31468
+ }
31469
+ });
31470
+
31471
+ // ../mcp-server/src/tools/world-create.ts
31141
31472
  function register6(server, ctx, initError) {
31473
+ const { description, inputSchema } = worldCreateSkill.asMcpTool();
31142
31474
  server.tool(
31143
31475
  "olam_create",
31144
- "Provision a new isolated development world with optional repository cloning, initial task, and Linear/GitHub task context.",
31145
- {
31146
- name: external_exports.string(),
31147
- repos: external_exports.array(external_exports.string()).optional(),
31148
- workspace: external_exports.string().optional().describe("Named workspace from the host catalog (~/.olam/workspaces/<name>.yaml). Inline `repos` wins over this."),
31149
- task: external_exports.string().optional(),
31150
- linearTicketId: external_exports.string().optional(),
31151
- branchName: external_exports.string().optional().describe("Override the default branch name (e.g., feat/ATL-1234-payment-webhook)"),
31152
- planFile: external_exports.string().optional().describe("Path to a plan file on the host to inject into the world"),
31153
- taskContext: taskContextSchema.describe("Structured task context from Linear, GitHub, or manual entry"),
31154
- // Phase B B3 (olam-session-stability): mirror of CLI's
31155
- // --carry-uncommitted flag. Default OFF (clean tree). Skips B1's
31156
- // strip step so any operator-side uncommitted edits survive into
31157
- // the world's worktree. The baseline-diff snapshot still runs
31158
- // unconditionally (Phase C reaper still needs it).
31159
- carryUncommitted: external_exports.boolean().optional().describe("Preserve operator's uncommitted edits in the world's worktree (B3); default false"),
31160
- runbookName: external_exports.string().optional().describe("Named runbook profile from ~/.olam/config.json \u2014 seeds ports, env overrides, and fixture-copy steps")
31161
- },
31476
+ description,
31477
+ inputSchema,
31162
31478
  async (params) => {
31163
31479
  if (!ctx) {
31164
31480
  const errorMsg = initError?.message ?? "Olam is not configured. Run /olam:init to set up.";
@@ -31314,14 +31630,26 @@ var world_destroy_exports = {};
31314
31630
  __export(world_destroy_exports, {
31315
31631
  register: () => register7
31316
31632
  });
31633
+
31634
+ // ../skill-runtime/dist/skills/world-destroy.js
31317
31635
  init_v3();
31636
+ var worldDestroyInputSchema = external_exports.object({
31637
+ worldId: external_exports.string()
31638
+ });
31639
+ var worldDestroySkill = defineSkill({
31640
+ name: "world-destroy",
31641
+ description: "Destroy an existing world and clean up all associated resources.",
31642
+ inputSchema: worldDestroyInputSchema,
31643
+ handler: ({ worldId }) => `Destroy requested for world: ${worldId}`
31644
+ });
31645
+
31646
+ // ../mcp-server/src/tools/world-destroy.ts
31318
31647
  function register7(server, ctx, initError) {
31648
+ const { description, inputSchema } = worldDestroySkill.asMcpTool();
31319
31649
  server.tool(
31320
31650
  "olam_destroy",
31321
- "Destroy an existing world and clean up all associated resources.",
31322
- {
31323
- worldId: external_exports.string()
31324
- },
31651
+ description,
31652
+ inputSchema,
31325
31653
  async (params) => {
31326
31654
  if (!ctx) {
31327
31655
  const errorMsg = initError?.message ?? "Olam is not configured. Run /olam:init to set up.";
@@ -31355,11 +31683,24 @@ var world_list_exports = {};
31355
31683
  __export(world_list_exports, {
31356
31684
  register: () => register8
31357
31685
  });
31686
+
31687
+ // ../skill-runtime/dist/skills/world-list.js
31688
+ init_v3();
31689
+ var worldListInputSchema = external_exports.object({});
31690
+ var worldListSkill = defineSkill({
31691
+ name: "world-list",
31692
+ description: "List all active development worlds and their current status.",
31693
+ inputSchema: worldListInputSchema,
31694
+ handler: (_input) => "World listing requires an active OlamContext. Use the MCP tool surface or HTTP endpoint with a configured host."
31695
+ });
31696
+
31697
+ // ../mcp-server/src/tools/world-list.ts
31358
31698
  function register8(server, ctx, initError) {
31699
+ const { description, inputSchema } = worldListSkill.asMcpTool();
31359
31700
  server.tool(
31360
31701
  "olam_list",
31361
- "List all active development worlds and their current status.",
31362
- {},
31702
+ description,
31703
+ inputSchema,
31363
31704
  async () => {
31364
31705
  if (!ctx) {
31365
31706
  const errorMsg = initError?.message ?? "Olam is not configured. Run /olam:init to set up.";
@@ -31415,7 +31756,6 @@ __export(world_dispatch_exports, {
31415
31756
  dispatchInputSchema: () => dispatchInputSchema,
31416
31757
  register: () => register9
31417
31758
  });
31418
- init_v3();
31419
31759
  import "node:path";
31420
31760
 
31421
31761
  // ../core/dist/world-paths.js
@@ -31881,7 +32221,8 @@ var MCP_SERVER_ALLOWLIST = /* @__PURE__ */ new Set([
31881
32221
  "command",
31882
32222
  "args",
31883
32223
  "type",
31884
- "url"
32224
+ "url",
32225
+ "alwaysLoad"
31885
32226
  ]);
31886
32227
  function stripMcpServers(mcpServers) {
31887
32228
  const out = {};
@@ -31902,6 +32243,8 @@ function stripMcpServers(mcpServers) {
31902
32243
  stripped.type = value;
31903
32244
  } else if (key === "url" && typeof value === "string") {
31904
32245
  stripped.url = value;
32246
+ } else if (key === "alwaysLoad" && typeof value === "boolean") {
32247
+ stripped.alwaysLoad = value;
31905
32248
  }
31906
32249
  }
31907
32250
  out[svc] = stripped;
@@ -32021,8 +32364,8 @@ function copyDirRecursive(src, dest, depth = 0, skipFiles = /* @__PURE__ */ new
32021
32364
  }
32022
32365
  }
32023
32366
  async function copyClaudeConfigIntoContainer(containerName) {
32024
- const { execSync: execSync7 } = await import("node:child_process");
32025
- const dockerExec = (cmd) => execSync7(`docker exec ${containerName} sh -c '${cmd}'`, { stdio: "pipe" });
32367
+ const { execSync: execSync8 } = await import("node:child_process");
32368
+ const dockerExec = (cmd) => execSync8(`docker exec ${containerName} sh -c '${cmd}'`, { stdio: "pipe" });
32026
32369
  dockerExec("mkdir -p $HOME/.claude");
32027
32370
  dockerExec("test -f /home/olam/workspace/.claude-host-config/settings.json && cp /home/olam/workspace/.claude-host-config/settings.json $HOME/.claude/settings.json || true");
32028
32371
  dockerExec("test -f /home/olam/workspace/.claude-host-config/CLAUDE.md && cp /home/olam/workspace/.claude-host-config/CLAUDE.md $HOME/.claude/CLAUDE.md || true");
@@ -32038,7 +32381,7 @@ async function copyClaudeConfigIntoContainer(containerName) {
32038
32381
  await sanitizeContainerClaudeHooks(containerName);
32039
32382
  }
32040
32383
  async function sanitizeContainerClaudeHooks(containerName) {
32041
- const { execSync: execSync7 } = await import("node:child_process");
32384
+ const { execSync: execSync8 } = await import("node:child_process");
32042
32385
  const script = `
32043
32386
  const fs = require('fs');
32044
32387
  const p = (process.env.HOME || '/home/olam') + '/.claude/settings.json';
@@ -32082,7 +32425,7 @@ if (changed) {
32082
32425
  }
32083
32426
  `;
32084
32427
  try {
32085
- execSync7(`docker exec ${containerName} /usr/local/bin/node -e ${shQuote(script)}`, { stdio: "pipe" });
32428
+ execSync8(`docker exec ${containerName} /usr/local/bin/node -e ${shQuote(script)}`, { stdio: "pipe" });
32086
32429
  } catch {
32087
32430
  }
32088
32431
  }
@@ -32518,28 +32861,59 @@ async function verifyCommentPosted(spec, worldId, execGh) {
32518
32861
  };
32519
32862
  }
32520
32863
 
32521
- // ../mcp-server/src/tools/world-dispatch.ts
32522
- var dispatchInputSchema = {
32864
+ // ../skill-runtime/dist/skills/world-dispatch.js
32865
+ init_v3();
32866
+ var goalSpecSchema = external_exports.object({
32867
+ type: external_exports.enum([
32868
+ "pr-merged",
32869
+ "pr-green",
32870
+ "file-exists",
32871
+ "test-passes",
32872
+ "comment-posted"
32873
+ ]),
32874
+ spec: external_exports.string().min(1).describe("Type-specific spec: PR number, file path, test name, or comment marker"),
32875
+ timeoutMinutes: external_exports.number().int().min(1).max(720).default(90)
32876
+ }).describe("Optional structured completion condition; when set, the world loops between turns checking this until met or timeout");
32877
+ var worldDispatchInputSchema = external_exports.object({
32523
32878
  worldId: external_exports.string(),
32524
32879
  prompt: external_exports.string(),
32525
32880
  maxTurns: external_exports.number().optional(),
32526
- mode: external_exports.enum(["deep", "fast", "auto"]).optional().describe(
32527
- "Execution mode: 'deep' appends ultrathink + sub-agent instructions for complex/generative tasks, 'fast' for simple chores, 'auto' (default) detects from prompt complexity"
32528
- ),
32529
- goal: GoalSpecSchema.optional().describe(
32530
- "Optional structured completion condition. When set, the world's dispatch loop checks this condition after each turn until met or the timeout window closes. v1 ships a single post-dispatch check; v2 will close the loop. /goal-mode parity with Claude Code 2.1.139."
32531
- ),
32532
- escalationTiers: external_exports.array(external_exports.enum(["haiku", "sonnet", "opus"])).optional().default(["sonnet"]).describe(
32533
- "Ordered model-tier escalation chain for dispatch retries. On each retry, host-cp advances to the next tier instead of repeating the failed model. Default ['sonnet'] = no escalation (current behaviour, no cost surprise). Operators opt in via e.g. ['sonnet','opus'] or ['haiku','sonnet','opus']."
32534
- )
32535
- };
32536
- var dispatchInputObject = external_exports.object(dispatchInputSchema);
32881
+ mode: external_exports.enum(["deep", "fast", "auto"]).optional().describe("Execution mode: 'deep' appends ultrathink + sub-agent instructions for complex/generative tasks, 'fast' for simple chores, 'auto' (default) detects from prompt complexity"),
32882
+ goal: goalSpecSchema.optional().describe("Optional structured completion condition. When set, the world's dispatch loop checks this condition after each turn until met or the timeout window closes. v1 ships a single post-dispatch check; v2 will close the loop. /goal-mode parity with Claude Code 2.1.139."),
32883
+ escalationTiers: external_exports.array(external_exports.enum(["haiku", "sonnet", "opus"])).optional().default(["sonnet"]).describe("Ordered model-tier escalation chain for dispatch retries. On each retry, host-cp advances to the next tier instead of repeating the failed model. Default ['sonnet'] = no escalation (current behaviour, no cost surprise). Operators opt in via e.g. ['sonnet','opus'] or ['haiku','sonnet','opus'].")
32884
+ });
32885
+ var worldDispatchSkill = defineSkill({
32886
+ name: "world-dispatch",
32887
+ description: "Send a task prompt to a world for autonomous execution.",
32888
+ inputSchema: worldDispatchInputSchema,
32889
+ handler: (input) => {
32890
+ const lines = [
32891
+ `Dispatch queued.`,
32892
+ ``,
32893
+ ` worldId: ${input.worldId}`,
32894
+ ` mode: ${input.mode ?? "auto"}`,
32895
+ ` prompt: ${input.prompt.slice(0, 120)}${input.prompt.length > 120 ? "\u2026" : ""}`
32896
+ ];
32897
+ if (input.goal) {
32898
+ lines.push(` goal: ${input.goal.type} (${input.goal.spec})`);
32899
+ }
32900
+ if (input.escalationTiers && input.escalationTiers.length > 0) {
32901
+ lines.push(` tiers: ${input.escalationTiers.join(", ")}`);
32902
+ }
32903
+ return lines.join("\n");
32904
+ }
32905
+ });
32906
+
32907
+ // ../mcp-server/src/tools/world-dispatch.ts
32908
+ var dispatchInputObject = worldDispatchInputSchema;
32909
+ var dispatchInputSchema = worldDispatchSkill.asMcpTool().inputSchema;
32537
32910
  function register9(server, ctx, initError, deps) {
32538
32911
  const verify = deps?.verifyGoal ?? verifyGoal;
32912
+ const { description, inputSchema } = worldDispatchSkill.asMcpTool();
32539
32913
  server.tool(
32540
32914
  "olam_dispatch",
32541
- "Send a task prompt to a world for autonomous execution.",
32542
- dispatchInputSchema,
32915
+ description,
32916
+ inputSchema,
32543
32917
  async (params) => {
32544
32918
  if (!ctx) {
32545
32919
  const errorMsg = initError?.message ?? "Olam is not configured. Run /olam:init to set up.";
@@ -32680,17 +33054,116 @@ function register9(server, ctx, initError, deps) {
32680
33054
  // ../mcp-server/src/tools/world-observe.ts
32681
33055
  var world_observe_exports = {};
32682
33056
  __export(world_observe_exports, {
33057
+ readWorldChunks: () => readWorldChunks,
32683
33058
  register: () => register10
32684
33059
  });
33060
+ import { createRequire as createRequire3 } from "node:module";
33061
+ import { existsSync as existsSync11 } from "node:fs";
33062
+
33063
+ // ../skill-runtime/dist/skills/world-observe.js
32685
33064
  init_v3();
33065
+ var worldObserveInputSchema = external_exports.object({
33066
+ worldId: external_exports.string(),
33067
+ sessionId: external_exports.string().optional(),
33068
+ sinceChunkId: external_exports.string().optional(),
33069
+ limit: external_exports.number().int().min(1).max(1e3).optional()
33070
+ });
33071
+ var worldObserveSkill = defineSkill({
33072
+ name: "world-observe",
33073
+ description: "Stream a world's reasoning chunks (thought_nodes feed). Returns JSON `{ chunks: [{ chunkId, sessionId, role, content, createdAt, nodeType }], metadata: { pane_state, count } }`, most-recent-last. Supports pagination via `sinceChunkId` (returns only chunks newer than the given id) and `limit` (default 100, max 1000). Use `olam_list` first to discover worldIds.",
33074
+ inputSchema: worldObserveInputSchema,
33075
+ handler: (_input) => "World observation requires an active OlamContext. Use the MCP tool surface to read a world's chunks."
33076
+ });
33077
+
33078
+ // ../mcp-server/src/tools/world-observe.ts
33079
+ var require2 = createRequire3(import.meta.url);
33080
+ var SYSTEM_NODE_TYPES = /* @__PURE__ */ new Set(["lifecycle", "dispatch_error"]);
33081
+ var DEFAULT_LIMIT = 100;
33082
+ var BUSY_WINDOW_MS = 3e4;
33083
+ function roleFor(nodeType) {
33084
+ return SYSTEM_NODE_TYPES.has(nodeType) ? "system" : "assistant";
33085
+ }
33086
+ function rowToChunk(row) {
33087
+ return {
33088
+ chunkId: row.id,
33089
+ sessionId: row.session_id,
33090
+ role: roleFor(row.node_type),
33091
+ content: row.content,
33092
+ summary: row.summary,
33093
+ createdAt: row.created_at,
33094
+ nodeType: row.node_type,
33095
+ sequenceNum: row.sequence_num
33096
+ };
33097
+ }
33098
+ function readWorldChunks(dbPath, params) {
33099
+ const limit = params.limit ?? DEFAULT_LIMIT;
33100
+ const now = params.now ?? Date.now();
33101
+ const empty = () => ({
33102
+ worldId: params.worldId,
33103
+ chunks: [],
33104
+ metadata: {
33105
+ count: 0,
33106
+ pane_state: "idle",
33107
+ sessionFilter: params.sessionId ?? null,
33108
+ sinceChunkId: params.sinceChunkId ?? null,
33109
+ limit
33110
+ }
33111
+ });
33112
+ if (!existsSync11(dbPath)) return empty();
33113
+ const Sqlite = require2("better-sqlite3");
33114
+ const db = new Sqlite(dbPath, { readonly: true, fileMustExist: true });
33115
+ try {
33116
+ let minSeq = -1;
33117
+ if (params.sinceChunkId) {
33118
+ const seqRow = db.prepare("SELECT sequence_num FROM thought_nodes WHERE id = ?").get(params.sinceChunkId);
33119
+ if (!seqRow) {
33120
+ return empty();
33121
+ }
33122
+ minSeq = seqRow.sequence_num;
33123
+ }
33124
+ const where = ["sequence_num > ?"];
33125
+ const bind = [minSeq];
33126
+ if (params.sessionId !== void 0) {
33127
+ where.push("session_id = ?");
33128
+ bind.push(params.sessionId);
33129
+ }
33130
+ const sql = `SELECT id, node_type, summary, content, source_ref, sequence_num, created_at, session_id, hook_event_name, turn_index, token_usage, duration_ms FROM thought_nodes WHERE ${where.join(" AND ")} ORDER BY sequence_num DESC LIMIT ?`;
33131
+ const rows = db.prepare(sql).all(...bind, limit);
33132
+ rows.reverse();
33133
+ const lastRow = rows[rows.length - 1];
33134
+ let paneState = "idle";
33135
+ if (lastRow) {
33136
+ const ageMs = now - new Date(lastRow.created_at).getTime();
33137
+ paneState = ageMs <= BUSY_WINDOW_MS ? "busy" : "idle";
33138
+ } else {
33139
+ const maxRow = db.prepare("SELECT MAX(created_at) AS max_at FROM thought_nodes").get();
33140
+ if (maxRow?.max_at) {
33141
+ const ageMs = now - new Date(maxRow.max_at).getTime();
33142
+ paneState = ageMs <= BUSY_WINDOW_MS ? "busy" : "idle";
33143
+ }
33144
+ }
33145
+ return {
33146
+ worldId: params.worldId,
33147
+ chunks: rows.map(rowToChunk),
33148
+ metadata: {
33149
+ count: rows.length,
33150
+ pane_state: paneState,
33151
+ sessionFilter: params.sessionId ?? null,
33152
+ sinceChunkId: params.sinceChunkId ?? null,
33153
+ limit
33154
+ }
33155
+ };
33156
+ } finally {
33157
+ db.close();
33158
+ }
33159
+ }
32686
33160
  function register10(server, ctx, initError) {
33161
+ const { description, inputSchema } = worldObserveSkill.asMcpTool();
32687
33162
  server.tool(
32688
33163
  "olam_observe",
32689
- "Watch a world's reasoning process and retrieve its current thought stream.",
32690
- {
32691
- worldId: external_exports.string()
32692
- },
32693
- async (_params) => {
33164
+ description,
33165
+ inputSchema,
33166
+ async (params) => {
32694
33167
  if (!ctx) {
32695
33168
  const errorMsg = initError?.message ?? "Olam is not configured. Run /olam:init to set up.";
32696
33169
  return {
@@ -32698,20 +33171,43 @@ function register10(server, ctx, initError) {
32698
33171
  isError: true
32699
33172
  };
32700
33173
  }
32701
- return {
32702
- content: [{ type: "text", text: "Observation is coming in a future release. This feature will stream a world's reasoning in real-time." }],
32703
- isError: true
32704
- };
32705
- }
32706
- );
32707
- }
32708
-
32709
- // ../mcp-server/src/tools/world-enter.ts
32710
- var world_enter_exports = {};
33174
+ const { worldId, sessionId, sinceChunkId, limit } = params;
33175
+ const world = ctx.worldManager.getWorld(worldId);
33176
+ if (!world) {
33177
+ return {
33178
+ content: [
33179
+ {
33180
+ type: "text",
33181
+ text: `World "${worldId}" not found. Use olam_list to discover available worlds.`
33182
+ }
33183
+ ],
33184
+ isError: true
33185
+ };
33186
+ }
33187
+ try {
33188
+ const dbPath = getWorldDbPath(world.workspacePath);
33189
+ const output = readWorldChunks(dbPath, { worldId, sessionId, sinceChunkId, limit });
33190
+ return {
33191
+ content: [{ type: "text", text: JSON.stringify(output, null, 2) }]
33192
+ };
33193
+ } catch (err) {
33194
+ const message = err instanceof Error ? err.message : String(err);
33195
+ return {
33196
+ content: [
33197
+ { type: "text", text: `Failed to read chunks for world "${worldId}": ${message}` }
33198
+ ],
33199
+ isError: true
33200
+ };
33201
+ }
33202
+ }
33203
+ );
33204
+ }
33205
+
33206
+ // ../mcp-server/src/tools/world-enter.ts
33207
+ var world_enter_exports = {};
32711
33208
  __export(world_enter_exports, {
32712
33209
  register: () => register11
32713
33210
  });
32714
- init_v3();
32715
33211
 
32716
33212
  // ../core/dist/orchestrator/enter.js
32717
33213
  function getEnterCommand(worldId, containerId, provider, sshHost) {
@@ -32747,14 +33243,25 @@ function getEnterCommand(worldId, containerId, provider, sshHost) {
32747
33243
  };
32748
33244
  }
32749
33245
 
33246
+ // ../skill-runtime/dist/skills/world-enter.js
33247
+ init_v3();
33248
+ var worldEnterInputSchema = external_exports.object({
33249
+ worldId: external_exports.string()
33250
+ });
33251
+ var worldEnterSkill = defineSkill({
33252
+ name: "world-enter",
33253
+ description: "Enter a world for interactive collaboration within its isolated environment.",
33254
+ inputSchema: worldEnterInputSchema,
33255
+ handler: ({ worldId }) => `Enter requested for world: ${worldId}`
33256
+ });
33257
+
32750
33258
  // ../mcp-server/src/tools/world-enter.ts
32751
33259
  function register11(server, ctx, initError) {
33260
+ const { description, inputSchema } = worldEnterSkill.asMcpTool();
32752
33261
  server.tool(
32753
33262
  "olam_enter",
32754
- "Enter a world for interactive collaboration within its isolated environment.",
32755
- {
32756
- worldId: external_exports.string()
32757
- },
33263
+ description,
33264
+ inputSchema,
32758
33265
  async ({ worldId }) => {
32759
33266
  if (!ctx) {
32760
33267
  const errorMsg = initError?.message ?? "Olam is not configured. Run /olam:init to set up.";
@@ -32805,7 +33312,6 @@ var world_crystallize_exports = {};
32805
33312
  __export(world_crystallize_exports, {
32806
33313
  register: () => register12
32807
33314
  });
32808
- init_v3();
32809
33315
  import "node:path";
32810
33316
  import * as fs10 from "node:fs";
32811
33317
 
@@ -32818,14 +33324,25 @@ function computeGraphChecksum(nodes, edges) {
32818
33324
  return createHash2("sha256").update(payload).digest("hex");
32819
33325
  }
32820
33326
 
33327
+ // ../skill-runtime/dist/skills/world-crystallize.js
33328
+ init_v3();
33329
+ var worldCrystallizeInputSchema = external_exports.object({
33330
+ worldId: external_exports.string()
33331
+ });
33332
+ var worldCrystallizeSkill = defineSkill({
33333
+ name: "world-crystallize",
33334
+ description: "Save a world's current thoughts and reasoning into a persistent thought graph via Pleri Plane.",
33335
+ inputSchema: worldCrystallizeInputSchema,
33336
+ handler: (_input) => "Crystallization requires an active OlamContext with a configured Pleri connection. Use the MCP tool surface or HTTP endpoint with a configured host."
33337
+ });
33338
+
32821
33339
  // ../mcp-server/src/tools/world-crystallize.ts
32822
33340
  function register12(server, ctx, initError) {
33341
+ const { description, inputSchema } = worldCrystallizeSkill.asMcpTool();
32823
33342
  server.tool(
32824
33343
  "olam_crystallize",
32825
- "Save a world's current thoughts and reasoning into a persistent thought graph via Pleri Plane.",
32826
- {
32827
- worldId: external_exports.string()
32828
- },
33344
+ description,
33345
+ inputSchema,
32829
33346
  async (params) => {
32830
33347
  if (!ctx) {
32831
33348
  const errorMsg = initError?.message ?? "Olam is not configured. Run /olam:init to set up.";
@@ -32953,78 +33470,6 @@ init_v3();
32953
33470
 
32954
33471
  // ../skill-runtime/dist/skills/olam-status.js
32955
33472
  init_v3();
32956
-
32957
- // ../skill-runtime/dist/define-skill.js
32958
- var KEBAB_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
32959
- function defineSkill(spec) {
32960
- if (!KEBAB_RE.test(spec.name)) {
32961
- throw new Error(`defineSkill: name "${spec.name}" must be kebab-case (lowercase letters, digits, and hyphens only; no leading/trailing/double hyphens).`);
32962
- }
32963
- return {
32964
- asMcpTool() {
32965
- return {
32966
- name: spec.name,
32967
- description: spec.description,
32968
- inputSchema: spec.inputSchema,
32969
- async handler(params) {
32970
- try {
32971
- const parsed = spec.inputSchema.safeParse(params);
32972
- if (!parsed.success) {
32973
- return {
32974
- content: [
32975
- {
32976
- type: "text",
32977
- text: `Invalid input: ${parsed.error.message}`
32978
- }
32979
- ],
32980
- isError: true
32981
- };
32982
- }
32983
- const output = await spec.handler(parsed.data);
32984
- return {
32985
- content: [
32986
- {
32987
- type: "text",
32988
- text: typeof output === "string" ? output : JSON.stringify(output)
32989
- }
32990
- ]
32991
- };
32992
- } catch (err) {
32993
- const message = err instanceof Error ? err.message : String(err);
32994
- return {
32995
- content: [{ type: "text", text: `Error: ${message}` }],
32996
- isError: true
32997
- };
32998
- }
32999
- }
33000
- };
33001
- },
33002
- asHttpHandler() {
33003
- return async (req, res) => {
33004
- const parsed = spec.inputSchema.safeParse(req.body);
33005
- if (!parsed.success) {
33006
- res.status(400).json({
33007
- error: "Invalid input",
33008
- issues: parsed.error.issues
33009
- });
33010
- return;
33011
- }
33012
- try {
33013
- const output = await spec.handler(parsed.data);
33014
- res.status(200).json({ result: output });
33015
- } catch (err) {
33016
- const message = err instanceof Error ? err.message : String(err);
33017
- res.status(500).json({ error: message });
33018
- }
33019
- };
33020
- },
33021
- toJsonSchema() {
33022
- return zodToJsonSchema(spec.inputSchema, { target: "openApi3" });
33023
- }
33024
- };
33025
- }
33026
-
33027
- // ../skill-runtime/dist/skills/olam-status.js
33028
33473
  var olamStatusInputSchema = external_exports.object({
33029
33474
  world: external_exports.object({
33030
33475
  id: external_exports.string(),
@@ -33135,11 +33580,24 @@ var control_plane_exports = {};
33135
33580
  __export(control_plane_exports, {
33136
33581
  register: () => register14
33137
33582
  });
33583
+
33584
+ // ../skill-runtime/dist/skills/control-plane.js
33585
+ init_v3();
33586
+ var controlPlaneInputSchema = external_exports.object({});
33587
+ var controlPlaneSkill = defineSkill({
33588
+ name: "control-plane",
33589
+ description: "Access the Olam control plane dashboard URL and status.",
33590
+ inputSchema: controlPlaneInputSchema,
33591
+ handler: (_input) => "Control plane access requires an active OlamContext. Use the MCP tool surface or HTTP endpoint with a configured host."
33592
+ });
33593
+
33594
+ // ../mcp-server/src/tools/control-plane.ts
33138
33595
  function register14(server, ctx, initError) {
33596
+ const { description, inputSchema } = controlPlaneSkill.asMcpTool();
33139
33597
  server.tool(
33140
33598
  "olam_control_plane",
33141
- "Access the Olam control plane dashboard URL and status.",
33142
- {},
33599
+ description,
33600
+ inputSchema,
33143
33601
  async () => {
33144
33602
  if (!ctx) {
33145
33603
  const errorMsg = initError?.message ?? "Olam is not configured. Run /olam:init to set up.";
@@ -33170,18 +33628,36 @@ var lane_create_exports = {};
33170
33628
  __export(lane_create_exports, {
33171
33629
  register: () => register15
33172
33630
  });
33631
+
33632
+ // ../skill-runtime/dist/skills/lane-create.js
33173
33633
  init_v3();
33634
+ var laneCreateInputSchema = external_exports.object({
33635
+ worldId: external_exports.string(),
33636
+ name: external_exports.string().describe("Kebab-case lane name (e.g., auth-fix, add-tests)"),
33637
+ task: external_exports.string().describe("Task description for the lane agent"),
33638
+ scope: external_exports.array(external_exports.string()).describe("File globs this lane is allowed to modify"),
33639
+ avoids: external_exports.array(external_exports.string()).optional().describe("File globs this lane must not touch")
33640
+ });
33641
+ var laneCreateSkill = defineSkill({
33642
+ name: "lane-create",
33643
+ description: "Create a new parallel lane within a world for isolated task execution with file-level locking.",
33644
+ inputSchema: laneCreateInputSchema,
33645
+ handler: (input) => JSON.stringify({
33646
+ worldId: input.worldId,
33647
+ name: input.name,
33648
+ task: input.task,
33649
+ scope: input.scope,
33650
+ avoids: input.avoids ?? []
33651
+ }, null, 2)
33652
+ });
33653
+
33654
+ // ../mcp-server/src/tools/lane-create.ts
33174
33655
  function register15(server, ctx, initError) {
33656
+ const { description, inputSchema } = laneCreateSkill.asMcpTool();
33175
33657
  server.tool(
33176
33658
  "olam_create_lane",
33177
- "Create a new parallel lane within a world for isolated task execution with file-level locking.",
33178
- {
33179
- worldId: external_exports.string(),
33180
- name: external_exports.string().describe("Kebab-case lane name (e.g., auth-fix, add-tests)"),
33181
- task: external_exports.string().describe("Task description for the lane agent"),
33182
- scope: external_exports.array(external_exports.string()).describe("File globs this lane is allowed to modify"),
33183
- avoids: external_exports.array(external_exports.string()).optional().describe("File globs this lane must not touch")
33184
- },
33659
+ description,
33660
+ inputSchema,
33185
33661
  async (params) => {
33186
33662
  if (!ctx) {
33187
33663
  const errorMsg = initError?.message ?? "Olam is not configured. Run /olam:init to set up.";
@@ -33253,14 +33729,26 @@ var lane_list_exports = {};
33253
33729
  __export(lane_list_exports, {
33254
33730
  register: () => register16
33255
33731
  });
33732
+
33733
+ // ../skill-runtime/dist/skills/lane-list.js
33256
33734
  init_v3();
33735
+ var laneListInputSchema = external_exports.object({
33736
+ worldId: external_exports.string()
33737
+ });
33738
+ var laneListSkill = defineSkill({
33739
+ name: "lane-list",
33740
+ description: "List all lanes in a world and their current status.",
33741
+ inputSchema: laneListInputSchema,
33742
+ handler: (_input) => "Lane listing requires an active OlamContext. Use the MCP tool surface or HTTP endpoint with a configured host."
33743
+ });
33744
+
33745
+ // ../mcp-server/src/tools/lane-list.ts
33257
33746
  function register16(server, ctx, initError) {
33747
+ const { description, inputSchema } = laneListSkill.asMcpTool();
33258
33748
  server.tool(
33259
33749
  "olam_list_lanes",
33260
- "List all lanes in a world and their current status.",
33261
- {
33262
- worldId: external_exports.string()
33263
- },
33750
+ description,
33751
+ inputSchema,
33264
33752
  async (params) => {
33265
33753
  if (!ctx) {
33266
33754
  const errorMsg = initError?.message ?? "Olam is not configured. Run /olam:init to set up.";
@@ -33324,16 +33812,37 @@ var lane_dispatch_exports = {};
33324
33812
  __export(lane_dispatch_exports, {
33325
33813
  register: () => register17
33326
33814
  });
33815
+
33816
+ // ../skill-runtime/dist/skills/lane-dispatch.js
33327
33817
  init_v3();
33818
+ var laneDispatchInputSchema = external_exports.object({
33819
+ worldId: external_exports.string(),
33820
+ laneName: external_exports.string(),
33821
+ prompt: external_exports.string()
33822
+ });
33823
+ var laneDispatchSkill = defineSkill({
33824
+ name: "lane-dispatch",
33825
+ description: "Send a task prompt to a specific lane within a world.",
33826
+ inputSchema: laneDispatchInputSchema,
33827
+ handler: (input) => {
33828
+ const lines = [
33829
+ `Dispatch queued for lane.`,
33830
+ ``,
33831
+ ` worldId: ${input.worldId}`,
33832
+ ` lane: ${input.laneName}`,
33833
+ ` prompt: ${input.prompt.slice(0, 100)}${input.prompt.length > 100 ? "..." : ""}`
33834
+ ];
33835
+ return lines.join("\n");
33836
+ }
33837
+ });
33838
+
33839
+ // ../mcp-server/src/tools/lane-dispatch.ts
33328
33840
  function register17(server, ctx, initError) {
33841
+ const { description, inputSchema } = laneDispatchSkill.asMcpTool();
33329
33842
  server.tool(
33330
33843
  "olam_dispatch_lane",
33331
- "Send a task prompt to a specific lane within a world.",
33332
- {
33333
- worldId: external_exports.string(),
33334
- laneName: external_exports.string(),
33335
- prompt: external_exports.string()
33336
- },
33844
+ description,
33845
+ inputSchema,
33337
33846
  async (params) => {
33338
33847
  if (!ctx) {
33339
33848
  const errorMsg = initError?.message ?? "Olam is not configured. Run /olam:init to set up.";
@@ -33399,15 +33908,27 @@ var lane_destroy_exports = {};
33399
33908
  __export(lane_destroy_exports, {
33400
33909
  register: () => register18
33401
33910
  });
33911
+
33912
+ // ../skill-runtime/dist/skills/lane-destroy.js
33402
33913
  init_v3();
33914
+ var laneDestroyInputSchema = external_exports.object({
33915
+ worldId: external_exports.string(),
33916
+ laneName: external_exports.string()
33917
+ });
33918
+ var laneDestroySkill = defineSkill({
33919
+ name: "lane-destroy",
33920
+ description: "Destroy a lane within a world and release its file locks.",
33921
+ inputSchema: laneDestroyInputSchema,
33922
+ handler: (input) => `Lane destruction requires an active OlamContext. Target: world="${input.worldId}", lane="${input.laneName}".`
33923
+ });
33924
+
33925
+ // ../mcp-server/src/tools/lane-destroy.ts
33403
33926
  function register18(server, ctx, initError) {
33927
+ const { description, inputSchema } = laneDestroySkill.asMcpTool();
33404
33928
  server.tool(
33405
33929
  "olam_destroy_lane",
33406
- "Destroy a lane within a world and release its file locks.",
33407
- {
33408
- worldId: external_exports.string(),
33409
- laneName: external_exports.string()
33410
- },
33930
+ description,
33931
+ inputSchema,
33411
33932
  async (params) => {
33412
33933
  if (!ctx) {
33413
33934
  const errorMsg = initError?.message ?? "Olam is not configured. Run /olam:init to set up.";
@@ -33459,15 +33980,32 @@ var lane_merge_exports = {};
33459
33980
  __export(lane_merge_exports, {
33460
33981
  register: () => register19
33461
33982
  });
33983
+
33984
+ // ../skill-runtime/dist/skills/lane-merge.js
33462
33985
  init_v3();
33986
+ var laneMergeInputSchema = external_exports.object({
33987
+ worldId: external_exports.string(),
33988
+ laneName: external_exports.string()
33989
+ });
33990
+ var laneMergeSkill = defineSkill({
33991
+ name: "lane-merge",
33992
+ description: "Merge a lane's changes back into the world's main branch.",
33993
+ inputSchema: laneMergeInputSchema,
33994
+ handler: (input) => {
33995
+ return `Lane merge queued.
33996
+
33997
+ worldId: ${input.worldId}
33998
+ lane: ${input.laneName}`;
33999
+ }
34000
+ });
34001
+
34002
+ // ../mcp-server/src/tools/lane-merge.ts
33463
34003
  function register19(server, ctx, initError) {
34004
+ const { description, inputSchema } = laneMergeSkill.asMcpTool();
33464
34005
  server.tool(
33465
34006
  "olam_merge_lane",
33466
- "Merge a lane's changes back into the world's main branch.",
33467
- {
33468
- worldId: external_exports.string(),
33469
- laneName: external_exports.string()
33470
- },
34007
+ description,
34008
+ inputSchema,
33471
34009
  async (params) => {
33472
34010
  if (!ctx) {
33473
34011
  const errorMsg = initError?.message ?? "Olam is not configured. Run /olam:init to set up.";
@@ -34282,6 +34820,67 @@ function checkLiteral(host, allowLoopback) {
34282
34820
  return null;
34283
34821
  }
34284
34822
 
34823
+ // ../skill-runtime/dist/skills/capture-view.js
34824
+ init_v3();
34825
+ var SHOT_NAME_RE = /^[A-Za-z0-9._-]+$/;
34826
+ var shotSpec = external_exports.object({
34827
+ name: external_exports.string().min(1).max(64).regex(SHOT_NAME_RE, "name must match /^[A-Za-z0-9._-]+$/ \u2014 no slashes, spaces, or path separators").describe("Shot identifier; written as `<name>.png` in `outDir`. Must be unique within a `capture_view` call."),
34828
+ url: external_exports.string().describe("Page URL. May be absolute (`https://\u2026`) or world-relative (`world://<world_id>/<path>`). Rejects `file://`, `chrome-extension://`, `about:`, `javascript:`, `data:`, RFC1918, link-local, cloud-metadata."),
34829
+ viewport: external_exports.object({
34830
+ width: external_exports.number().int().min(320).max(3840),
34831
+ height: external_exports.number().int().min(240).max(2160)
34832
+ }).optional().describe("Browser viewport in CSS pixels. Default `{1280,800}`."),
34833
+ deviceScaleFactor: external_exports.number().int().min(1).max(2).optional().describe("Default 1; cap 2 to keep mobile GPUs sane."),
34834
+ media: external_exports.object({
34835
+ reducedMotion: external_exports.enum(["no-preference", "reduce"]).optional(),
34836
+ colorScheme: external_exports.enum(["light", "dark"]).optional()
34837
+ }).optional().describe("CSS media-feature emulation. Use to capture reduced-motion fallbacks, dark-mode renders, etc."),
34838
+ waitForSelector: external_exports.string().optional().describe("CSS selector to wait for before screenshot."),
34839
+ waitForNetworkIdle: external_exports.boolean().optional().describe("Wait for `networkidle` event before screenshot. Default true."),
34840
+ afterLoadMs: external_exports.number().int().min(0).max(1e4).optional().describe("Sleep N ms after load before screenshot. Use to let WebGL/canvas animations reach a non-zero frame."),
34841
+ fill: external_exports.record(external_exports.string(), external_exports.string()).optional().describe("Selector \u2192 value map. Filled before `submit` (if any). Keys are CSS selectors, values are literal strings."),
34842
+ submit: external_exports.string().optional().describe("Form selector to `requestSubmit()` after `fill`."),
34843
+ afterSubmitMs: external_exports.number().int().min(0).max(1e4).optional().describe("Sleep N ms after submit before screenshot. Use to let success-card render."),
34844
+ mockFetch: external_exports.array(external_exports.object({
34845
+ pattern: external_exports.string().min(1).describe("URL match pattern. Forms: `/regex/` treats inner as RegExp source; otherwise matched as a literal substring."),
34846
+ method: external_exports.enum(["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"]).optional().describe("HTTP method filter. When set, only requests with this method are intercepted."),
34847
+ status: external_exports.number().int().default(200),
34848
+ bodyJson: external_exports.unknown().optional(),
34849
+ bodyText: external_exports.string().optional()
34850
+ })).max(64).optional().describe("Intercept fetch/XHR matching the pattern; return the canned response. Max 64 entries per shot."),
34851
+ setStorage: external_exports.record(external_exports.enum(["local", "session"]), external_exports.record(external_exports.string().min(1).max(128), external_exports.string().max(64 * 1024))).refine((s) => {
34852
+ let total = 0;
34853
+ if (s.local)
34854
+ total += Object.keys(s.local).length;
34855
+ if (s.session)
34856
+ total += Object.keys(s.session).length;
34857
+ return total <= 50;
34858
+ }, { message: "setStorage entries (local + session combined) must not exceed 50 keys" }).optional().describe("Pre-load LocalStorage / SessionStorage entries before navigation. Per scope: keys \u2264 128 chars, values \u2264 64 KB. Combined (local + session) total \u2264 50 entries."),
34859
+ eval: external_exports.string().optional().describe("Raw page-script run after navigation, before screenshot. Gated behind `--allow-eval` on the calling skill.")
34860
+ });
34861
+ var captureViewInputSchema = external_exports.object({
34862
+ shots: external_exports.array(shotSpec).min(1).max(50).refine((shots) => new Set(shots.map((s) => s.name)).size === shots.length, { message: "shot.name must be unique within a single capture_view call" }).describe("Shots to capture. One Chromium boot per call; reused across shots. Names must be unique within the call."),
34863
+ outDir: external_exports.string().describe("Host-side directory to write artifacts. Created if missing. Files are `<name>.png` plus `manifest.json`."),
34864
+ format: external_exports.enum(["png", "jpeg"]).default("png").describe("Output format. Default `png`."),
34865
+ jpegQuality: external_exports.number().int().min(50).max(100).default(85).describe("JPEG quality if `format='jpeg'`."),
34866
+ ttlSeconds: external_exports.number().int().min(30).max(600).default(120).describe("Capability token TTL. Capped at 60 when called from inside a world."),
34867
+ allowEval: external_exports.boolean().default(false).describe("Permit `eval` page-scripts in shots. Default false.")
34868
+ });
34869
+ var captureViewSkill = defineSkill({
34870
+ name: "capture-view",
34871
+ description: "Brokered screenshot primitive: take spec-driven screenshots of a web app via the supervisor's Chromium. Used by the olam:ui-shoot skill (host) and by autonomous in-world agents (which cannot run Playwright themselves). Worlds never get raw browser access; they request artifacts here, the supervisor renders.",
34872
+ inputSchema: captureViewInputSchema,
34873
+ handler: (input) => {
34874
+ const shotNames = input.shots.map((s) => s.name).join(", ");
34875
+ return [
34876
+ `Would capture ${input.shots.length} shot(s) to ${input.outDir}`,
34877
+ ` shots: ${shotNames}`,
34878
+ ` format: ${input.format}`,
34879
+ "Use the MCP tool for actual Chromium execution (requires OlamContext + Playwright)."
34880
+ ].join("\n");
34881
+ }
34882
+ });
34883
+
34285
34884
  // ../mcp-server/src/tools/capture-view.ts
34286
34885
  var CHROMIUM_CONCURRENCY_CAP = Math.max(
34287
34886
  1,
@@ -34400,9 +34999,9 @@ async function rewriteShotUrl(ctx, shotUrl, cache2) {
34400
34999
  url: `http://${endpoint.host}:${endpoint.port}${parsed.pathname}${parsed.search}`
34401
35000
  };
34402
35001
  }
34403
- var SHOT_NAME_RE = /^[A-Za-z0-9._-]+$/;
34404
- var shotSpec = external_exports.object({
34405
- name: external_exports.string().min(1).max(64).regex(SHOT_NAME_RE, "name must match /^[A-Za-z0-9._-]+$/ \u2014 no slashes, spaces, or path separators").describe(
35002
+ var SHOT_NAME_RE2 = /^[A-Za-z0-9._-]+$/;
35003
+ var shotSpec2 = external_exports.object({
35004
+ name: external_exports.string().min(1).max(64).regex(SHOT_NAME_RE2, "name must match /^[A-Za-z0-9._-]+$/ \u2014 no slashes, spaces, or path separators").describe(
34406
35005
  "Shot identifier; written as `<name>.png` in `outDir`. Must be unique within a `capture_view` call. Letters/digits/dots/dashes/underscores only \u2014 schema-level guard against path traversal via filename."
34407
35006
  ),
34408
35007
  url: external_exports.string().describe(
@@ -34472,7 +35071,7 @@ var shotSpec = external_exports.object({
34472
35071
  )
34473
35072
  });
34474
35073
  var captureViewInput = {
34475
- shots: external_exports.array(shotSpec).min(1).max(50).refine(
35074
+ shots: external_exports.array(shotSpec2).min(1).max(50).refine(
34476
35075
  (shots) => new Set(shots.map((s) => s.name)).size === shots.length,
34477
35076
  // Without this refine, two shots with the same name would silently
34478
35077
  // overwrite each other on disk while the success envelope reports
@@ -34623,10 +35222,11 @@ async function runShot(browser, shot, outDir, format, jpegQuality, allowEval, as
34623
35222
  }
34624
35223
  }
34625
35224
  function register20(server, ctx, initError) {
35225
+ const { description, inputSchema } = captureViewSkill.asMcpTool();
34626
35226
  server.tool(
34627
35227
  "olam_capture_view",
34628
- "Brokered screenshot primitive: take spec-driven screenshots of a web app via the supervisor's Chromium. Used by the olam:ui-shoot skill (host) and by autonomous in-world agents (which cannot run Playwright themselves). Worlds never get raw browser access; they request artifacts here, the supervisor renders.",
34629
- captureViewInput,
35228
+ description,
35229
+ inputSchema,
34630
35230
  async (params) => {
34631
35231
  if (!ctx) {
34632
35232
  const errorMsg = initError?.message ?? "Olam is not configured. Run /olam:init to set up.";
@@ -34889,7 +35489,47 @@ var create_from_prompt_exports = {};
34889
35489
  __export(create_from_prompt_exports, {
34890
35490
  register: () => register21
34891
35491
  });
35492
+
35493
+ // ../skill-runtime/dist/skills/create-from-prompt.js
34892
35494
  init_v3();
35495
+ var createFromPromptInputSchema = external_exports.object({
35496
+ prompt: external_exports.string().min(1).describe("Natural-language task. Repo names extracted via heuristic; falls back to picker on ambiguity."),
35497
+ repos: external_exports.array(external_exports.string()).optional().describe("Explicit repos. Bypasses NL inference."),
35498
+ workspace: external_exports.string().optional().describe("Named workspace. Bypasses inference + match."),
35499
+ name: external_exports.string().optional().describe("World name. Defaults to slugified prompt."),
35500
+ autoCodexReview: external_exports.boolean().optional().describe("Spawn parallel codex-review sub-lane (Phase C9)."),
35501
+ noOpen: external_exports.boolean().optional().describe("Suppress browser auto-open after world is up."),
35502
+ carryUncommitted: external_exports.boolean().optional().describe("Preserve operator's uncommitted edits in the world's worktree (B3); default false")
35503
+ });
35504
+ var createFromPromptSkill = defineSkill({
35505
+ name: "create-from-prompt",
35506
+ description: "Phase D one-shot world creator: NL prompt \u2192 workspace match \u2192 world create + auto-dispatch + browser open. Returns picker payload on inference ambiguity.",
35507
+ inputSchema: createFromPromptInputSchema,
35508
+ handler: (input) => {
35509
+ const parts = [
35510
+ `Create-from-prompt requires an active OlamContext with WorldManager.`,
35511
+ ``,
35512
+ ` prompt: ${input.prompt.slice(0, 80)}${input.prompt.length > 80 ? "\u2026" : ""}`
35513
+ ];
35514
+ if (input.repos && input.repos.length > 0) {
35515
+ parts.push(` repos: ${input.repos.join(", ")}`);
35516
+ }
35517
+ if (input.workspace) {
35518
+ parts.push(` workspace: ${input.workspace}`);
35519
+ }
35520
+ if (input.name) {
35521
+ parts.push(` name: ${input.name}`);
35522
+ }
35523
+ if (input.autoCodexReview) {
35524
+ parts.push(` autoCodexReview: true`);
35525
+ }
35526
+ if (input.carryUncommitted) {
35527
+ parts.push(` carryUncommitted: true`);
35528
+ }
35529
+ parts.push(``, `Use the MCP tool surface or HTTP endpoint with a configured host.`);
35530
+ return parts.join("\n");
35531
+ }
35532
+ });
34893
35533
 
34894
35534
  // ../core/dist/world/infer-repos.js
34895
35535
  var PICKER_CONFIDENCE_THRESHOLD = 0.7;
@@ -35106,20 +35746,11 @@ function defaultNameFromPrompt(prompt) {
35106
35746
  return slug || "unnamed";
35107
35747
  }
35108
35748
  function register21(server, ctx, initError) {
35749
+ const { description, inputSchema } = createFromPromptSkill.asMcpTool();
35109
35750
  server.tool(
35110
35751
  "olam_create_from_prompt",
35111
- "Phase D one-shot world creator: NL prompt \u2192 workspace match \u2192 world create + auto-dispatch + browser open. Returns picker payload on inference ambiguity.",
35112
- {
35113
- prompt: external_exports.string().min(1).describe("Natural-language task. Repo names extracted via heuristic; falls back to picker on ambiguity."),
35114
- repos: external_exports.array(external_exports.string()).optional().describe("Explicit repos. Bypasses NL inference."),
35115
- workspace: external_exports.string().optional().describe("Named workspace. Bypasses inference + match."),
35116
- name: external_exports.string().optional().describe("World name. Defaults to slugified prompt."),
35117
- autoCodexReview: external_exports.boolean().optional().describe("Spawn parallel codex-review sub-lane (Phase C9)."),
35118
- noOpen: external_exports.boolean().optional().describe("Suppress browser auto-open after world is up."),
35119
- // Phase B B3 (olam-session-stability): mirror of CLI's
35120
- // --carry-uncommitted flag. Default OFF.
35121
- carryUncommitted: external_exports.boolean().optional().describe("Preserve operator's uncommitted edits in the world's worktree (B3); default false")
35122
- },
35752
+ description,
35753
+ inputSchema,
35123
35754
  async (params) => {
35124
35755
  if (!ctx) {
35125
35756
  const errorMsg = initError?.message ?? "Olam is not configured. Run /olam:init to set up.";
@@ -35297,16 +35928,80 @@ var repo_exports = {};
35297
35928
  __export(repo_exports, {
35298
35929
  register: () => register22
35299
35930
  });
35300
- init_v3();
35301
35931
  init_global_config();
35932
+
35933
+ // ../skill-runtime/dist/skills/repo-list.js
35934
+ init_v3();
35935
+ var repoListInputSchema = external_exports.object({});
35936
+ var repoListSkill = defineSkill({
35937
+ name: "repo-list",
35938
+ description: "List every repo registered in ~/.olam/config.json. Returns { repos: RepoEntry[] }.",
35939
+ inputSchema: repoListInputSchema,
35940
+ handler: (_input) => "Repo listing requires access to ~/.olam/config.json. Use the MCP tool surface."
35941
+ });
35942
+
35943
+ // ../skill-runtime/dist/skills/repo-add.js
35944
+ init_v3();
35945
+ var repoAddInputSchema = external_exports.object({
35946
+ name: external_exports.string().min(1).describe("Repo name (lowercase, digits, dash; 1\u201364 chars)."),
35947
+ path: external_exports.string().min(1).describe("Absolute or ~/... path to the local repo directory."),
35948
+ description: external_exports.string().optional().describe("Optional human-readable description."),
35949
+ defaultBranch: external_exports.string().optional().describe("Default branch name (e.g. main).")
35950
+ });
35951
+ var repoAddSkill = defineSkill({
35952
+ name: "repo-add",
35953
+ description: "Register a local repo path in the global registry. The path must exist on disk. Rejects duplicate names.",
35954
+ inputSchema: repoAddInputSchema,
35955
+ handler: (input) => `Would register repo "${input.name}" at ${input.path}. Use the MCP tool for actual file system execution.`
35956
+ });
35957
+
35958
+ // ../skill-runtime/dist/skills/repo-remove.js
35959
+ init_v3();
35960
+ var repoRemoveInputSchema = external_exports.object({
35961
+ name: external_exports.string().min(1).describe("Repo name to remove.")
35962
+ });
35963
+ var repoRemoveSkill = defineSkill({
35964
+ name: "repo-remove",
35965
+ description: "Remove a repo from the global registry. Does not delete any files on disk.",
35966
+ inputSchema: repoRemoveInputSchema,
35967
+ handler: (input) => `Would remove repo "${input.name}" from the global registry. Use the MCP tool for actual execution.`
35968
+ });
35969
+
35970
+ // ../skill-runtime/dist/skills/repo-update.js
35971
+ init_v3();
35972
+ var repoUpdateInputSchema = external_exports.object({
35973
+ name: external_exports.string().min(1).describe("Repo name to update."),
35974
+ path: external_exports.string().optional().describe("New local path."),
35975
+ description: external_exports.string().optional().describe("New description."),
35976
+ defaultBranch: external_exports.string().optional().describe("New default branch.")
35977
+ });
35978
+ var repoUpdateSkill = defineSkill({
35979
+ name: "repo-update",
35980
+ description: "Update a registered repo's path, description, or default branch.",
35981
+ inputSchema: repoUpdateInputSchema,
35982
+ handler: (input) => {
35983
+ const fields = [];
35984
+ if (input.path)
35985
+ fields.push(`path: ${input.path}`);
35986
+ if (input.description)
35987
+ fields.push(`description: "${input.description}"`);
35988
+ if (input.defaultBranch)
35989
+ fields.push(`defaultBranch: ${input.defaultBranch}`);
35990
+ const changes = fields.length > 0 ? fields.join(", ") : "(no changes specified)";
35991
+ return `Would update repo "${input.name}": ${changes}. Use the MCP tool for actual execution.`;
35992
+ }
35993
+ });
35994
+
35995
+ // ../mcp-server/src/tools/repo.ts
35302
35996
  function asMessage4(err) {
35303
35997
  return err instanceof Error ? err.message : String(err);
35304
35998
  }
35305
35999
  function register22(server, _ctx, _initError) {
36000
+ const repoListTool = repoListSkill.asMcpTool();
35306
36001
  server.tool(
35307
36002
  "olam_repo_list",
35308
- "List every repo registered in ~/.olam/config.json. Returns { repos: RepoEntry[] }.",
35309
- {},
36003
+ repoListTool.description,
36004
+ repoListTool.inputSchema,
35310
36005
  async () => {
35311
36006
  const repos = listRepos();
35312
36007
  return {
@@ -35317,15 +36012,11 @@ function register22(server, _ctx, _initError) {
35317
36012
  };
35318
36013
  }
35319
36014
  );
36015
+ const repoAddTool = repoAddSkill.asMcpTool();
35320
36016
  server.tool(
35321
36017
  "olam_repo_add",
35322
- "Register a local repo path in the global registry. The path must exist on disk. Rejects duplicate names.",
35323
- {
35324
- name: external_exports.string().min(1).describe("Repo name (lowercase, digits, dash; 1\u201364 chars)."),
35325
- path: external_exports.string().min(1).describe("Absolute or ~/... path to the local repo directory."),
35326
- description: external_exports.string().optional().describe("Optional human-readable description."),
35327
- defaultBranch: external_exports.string().optional().describe("Default branch name (e.g. main).")
35328
- },
36018
+ repoAddTool.description,
36019
+ repoAddTool.inputSchema,
35329
36020
  async ({ name, path: path53, description, defaultBranch }) => {
35330
36021
  try {
35331
36022
  const entry = addRepo({ name, path: path53, description, defaultBranch });
@@ -35340,12 +36031,11 @@ function register22(server, _ctx, _initError) {
35340
36031
  }
35341
36032
  }
35342
36033
  );
36034
+ const repoRemoveTool = repoRemoveSkill.asMcpTool();
35343
36035
  server.tool(
35344
36036
  "olam_repo_remove",
35345
- "Remove a repo from the global registry. Does not delete any files on disk.",
35346
- {
35347
- name: external_exports.string().min(1).describe("Repo name to remove.")
35348
- },
36037
+ repoRemoveTool.description,
36038
+ repoRemoveTool.inputSchema,
35349
36039
  async ({ name }) => {
35350
36040
  try {
35351
36041
  removeRepo(name);
@@ -35360,15 +36050,11 @@ function register22(server, _ctx, _initError) {
35360
36050
  }
35361
36051
  }
35362
36052
  );
36053
+ const repoUpdateTool = repoUpdateSkill.asMcpTool();
35363
36054
  server.tool(
35364
36055
  "olam_repo_update",
35365
- "Update a registered repo's path, description, or default branch.",
35366
- {
35367
- name: external_exports.string().min(1).describe("Repo name to update."),
35368
- path: external_exports.string().optional().describe("New local path."),
35369
- description: external_exports.string().optional().describe("New description."),
35370
- defaultBranch: external_exports.string().optional().describe("New default branch.")
35371
- },
36056
+ repoUpdateTool.description,
36057
+ repoUpdateTool.inputSchema,
35372
36058
  async ({ name, path: path53, description, defaultBranch }) => {
35373
36059
  try {
35374
36060
  const entry = updateRepo(name, { path: path53, description, defaultBranch });
@@ -35391,10 +36077,49 @@ __export(process_port_exports, {
35391
36077
  register: () => register23,
35392
36078
  resolveHostCpToken: () => resolveHostCpToken
35393
36079
  });
35394
- init_v3();
35395
36080
  import fs35 from "node:fs";
35396
36081
  import os20 from "node:os";
35397
36082
  import path34 from "node:path";
36083
+
36084
+ // ../skill-runtime/dist/skills/process-list.js
36085
+ init_v3();
36086
+ var processListInputSchema = external_exports.object({
36087
+ world_id: external_exports.string().describe("World ID.")
36088
+ });
36089
+ var processListSkill = defineSkill({
36090
+ name: "process-list",
36091
+ description: "List running processes inside a world container. Returns a process table snapshot via host-cp. Fields per process: pid, user, cpu, mem, started, state, command.",
36092
+ inputSchema: processListInputSchema,
36093
+ handler: (input) => `Process listing requires a host-cp connection. Would fetch processes for world "${input.world_id}". Use the MCP tool surface or HTTP endpoint with a configured host.`
36094
+ });
36095
+
36096
+ // ../skill-runtime/dist/skills/port-expose.js
36097
+ init_v3();
36098
+ var portExposeInputSchema = external_exports.object({
36099
+ world_id: external_exports.string().describe("World ID."),
36100
+ port: external_exports.number().int().min(1).max(65535).describe("Container port to expose on the host.")
36101
+ });
36102
+ var portExposeSkill = defineSkill({
36103
+ name: "port-expose",
36104
+ description: "Expose a port inside a world container to the host by creating a socat bridge. Returns the host-side URL and bridge metadata. The bridge persists until removed with olam_port_unexpose.",
36105
+ inputSchema: portExposeInputSchema,
36106
+ handler: (input) => `Port expose requires a host-cp connection. Would create socat bridge for world "${input.world_id}" port ${input.port}. Use the MCP tool surface or HTTP endpoint with a configured host.`
36107
+ });
36108
+
36109
+ // ../skill-runtime/dist/skills/port-unexpose.js
36110
+ init_v3();
36111
+ var portUnexposeInputSchema = external_exports.object({
36112
+ world_id: external_exports.string().describe("World ID."),
36113
+ port: external_exports.number().int().min(1).max(65535).describe("Container port whose bridge should be removed.")
36114
+ });
36115
+ var portUnexposeSkill = defineSkill({
36116
+ name: "port-unexpose",
36117
+ description: "Tear down a port bridge for a world container. Removes the socat sidecar and frees the host port.",
36118
+ inputSchema: portUnexposeInputSchema,
36119
+ handler: (input) => `Port unexpose requires a host-cp connection. Would remove socat bridge for world "${input.world_id}" port ${input.port}. Use the MCP tool surface or HTTP endpoint with a configured host.`
36120
+ });
36121
+
36122
+ // ../mcp-server/src/tools/process-port.ts
35398
36123
  var HOST_CP_BASE = "http://127.0.0.1:19000";
35399
36124
  function resolveHostCpToken() {
35400
36125
  const envToken = process.env["OLAM_HOST_CP_TOKEN"];
@@ -35416,12 +36141,11 @@ function asMessage5(err) {
35416
36141
  return err instanceof Error ? err.message : String(err);
35417
36142
  }
35418
36143
  function register23(server, _ctx, _initError) {
36144
+ const processList = processListSkill.asMcpTool();
35419
36145
  server.tool(
35420
36146
  "olam_process_list",
35421
- "List running processes inside a world container. Returns a process table snapshot via host-cp. Fields per process: pid, user, cpu, mem, started, state, command.",
35422
- {
35423
- world_id: external_exports.string().describe("World ID.")
35424
- },
36147
+ processList.description,
36148
+ processList.inputSchema,
35425
36149
  async ({ world_id }) => {
35426
36150
  const token = resolveHostCpToken();
35427
36151
  if (!token) return tokenMissingError();
@@ -35446,13 +36170,11 @@ function register23(server, _ctx, _initError) {
35446
36170
  }
35447
36171
  }
35448
36172
  );
36173
+ const portExpose = portExposeSkill.asMcpTool();
35449
36174
  server.tool(
35450
36175
  "olam_port_expose",
35451
- "Expose a port inside a world container to the host by creating a socat bridge. Returns the host-side URL and bridge metadata. The bridge persists until removed with olam_port_unexpose.",
35452
- {
35453
- world_id: external_exports.string().describe("World ID."),
35454
- port: external_exports.number().int().min(1).max(65535).describe("Container port to expose on the host.")
35455
- },
36176
+ portExpose.description,
36177
+ portExpose.inputSchema,
35456
36178
  async ({ world_id, port: port2 }) => {
35457
36179
  const token = resolveHostCpToken();
35458
36180
  if (!token) return tokenMissingError();
@@ -35474,16 +36196,20 @@ function register23(server, _ctx, _initError) {
35474
36196
  };
35475
36197
  }
35476
36198
  const data = JSON.parse(body);
36199
+ const payload = {
36200
+ url: `http://localhost:${data.hostPort}`,
36201
+ host_port: data.hostPort,
36202
+ sidecar_container_id: data.containerId,
36203
+ container_port: data.containerPort,
36204
+ container_name: data.containerName
36205
+ };
36206
+ if (data.pulledImage === true) {
36207
+ payload["pulled_image"] = "alpine/socat:latest (first-call fallback)";
36208
+ }
35477
36209
  return {
35478
36210
  content: [{
35479
36211
  type: "text",
35480
- text: JSON.stringify({
35481
- url: `http://localhost:${data.hostPort}`,
35482
- host_port: data.hostPort,
35483
- sidecar_container_id: data.containerId,
35484
- container_port: data.containerPort,
35485
- container_name: data.containerName
35486
- }, null, 2)
36212
+ text: JSON.stringify(payload, null, 2)
35487
36213
  }]
35488
36214
  };
35489
36215
  } catch (err) {
@@ -35491,13 +36217,11 @@ function register23(server, _ctx, _initError) {
35491
36217
  }
35492
36218
  }
35493
36219
  );
36220
+ const portUnexpose = portUnexposeSkill.asMcpTool();
35494
36221
  server.tool(
35495
36222
  "olam_port_unexpose",
35496
- "Tear down a port bridge for a world container. Removes the socat sidecar and frees the host port.",
35497
- {
35498
- world_id: external_exports.string().describe("World ID."),
35499
- port: external_exports.number().int().min(1).max(65535).describe("Container port whose bridge should be removed.")
35500
- },
36223
+ portUnexpose.description,
36224
+ portUnexpose.inputSchema,
35501
36225
  async ({ world_id, port: port2 }) => {
35502
36226
  const token = resolveHostCpToken();
35503
36227
  if (!token) return tokenMissingError();
@@ -35527,8 +36251,90 @@ var runbook_exports = {};
35527
36251
  __export(runbook_exports, {
35528
36252
  register: () => register24
35529
36253
  });
35530
- init_v3();
35531
36254
  init_global_config();
36255
+
36256
+ // ../skill-runtime/dist/skills/runbook-list.js
36257
+ init_v3();
36258
+ var runbookListInputSchema = external_exports.object({});
36259
+ var runbookListSkill = defineSkill({
36260
+ name: "runbook-list",
36261
+ description: "List all runbooks in ~/.olam/config.json. Returns { runbooks: Runbook[] }.",
36262
+ inputSchema: runbookListInputSchema,
36263
+ handler: (_input) => JSON.stringify({ note: "runbook listing requires global config access; use MCP surface" }, null, 2)
36264
+ });
36265
+
36266
+ // ../skill-runtime/dist/skills/runbook-show.js
36267
+ init_v3();
36268
+ var runbookShowInputSchema = external_exports.object({
36269
+ name: external_exports.string().min(1).describe("Runbook name.")
36270
+ });
36271
+ var runbookShowSkill = defineSkill({
36272
+ name: "runbook-show",
36273
+ description: "Show a single runbook by name. Returns { runbook: Runbook }.",
36274
+ inputSchema: runbookShowInputSchema,
36275
+ handler: ({ name }) => JSON.stringify({ name, note: "runbook lookup requires global config access; use MCP surface" }, null, 2)
36276
+ });
36277
+
36278
+ // ../skill-runtime/dist/skills/runbook-add.js
36279
+ init_v3();
36280
+ var seedSchema = external_exports.union([
36281
+ external_exports.object({
36282
+ type: external_exports.literal("sql-file"),
36283
+ repo: external_exports.string(),
36284
+ service: external_exports.string(),
36285
+ path: external_exports.string()
36286
+ }),
36287
+ external_exports.object({ type: external_exports.literal("command"), repo: external_exports.string(), run: external_exports.string() }),
36288
+ external_exports.object({
36289
+ type: external_exports.literal("fixture-copy"),
36290
+ repo: external_exports.string(),
36291
+ src: external_exports.string(),
36292
+ dest: external_exports.string()
36293
+ })
36294
+ ]);
36295
+ var runbookAddInputSchema = external_exports.object({
36296
+ name: external_exports.string().min(1).describe("Runbook name (lowercase, digits, dash; 1\u201364 chars)."),
36297
+ repos: external_exports.array(external_exports.string().min(1)).min(1).describe("Repo names (must exist in registry)."),
36298
+ description: external_exports.string().optional().describe("Optional human-readable description."),
36299
+ portMap: external_exports.record(external_exports.string().min(1), external_exports.record(external_exports.string().min(1), external_exports.number().int())).optional().describe("Port mappings: { repoName: { serviceName: hostPort } }."),
36300
+ seeds: external_exports.array(seedSchema).optional().describe("Optional seed operations to run when applying the runbook."),
36301
+ env: external_exports.record(external_exports.string().min(1), external_exports.record(external_exports.string().min(1), external_exports.string())).optional().describe("Per-repo environment variable overrides.")
36302
+ });
36303
+ var runbookAddSkill = defineSkill({
36304
+ name: "runbook-add",
36305
+ description: "Create a new runbook. Validates that all referenced repos exist in the global registry and that portMap has no duplicates.",
36306
+ inputSchema: runbookAddInputSchema,
36307
+ handler: ({ name, repos }) => JSON.stringify({ name, repos, note: "runbook creation requires config store + registry access; use MCP surface" }, null, 2)
36308
+ });
36309
+
36310
+ // ../skill-runtime/dist/skills/runbook-remove.js
36311
+ init_v3();
36312
+ var runbookRemoveInputSchema = external_exports.object({
36313
+ name: external_exports.string().min(1).describe("Runbook name to remove.")
36314
+ });
36315
+ var runbookRemoveSkill = defineSkill({
36316
+ name: "runbook-remove",
36317
+ description: "Remove a runbook from the global config. Does not affect any running worlds.",
36318
+ inputSchema: runbookRemoveInputSchema,
36319
+ handler: ({ name }) => JSON.stringify({ name, note: "runbook removal requires config store access; use MCP surface" }, null, 2)
36320
+ });
36321
+
36322
+ // ../skill-runtime/dist/skills/runbook-apply.js
36323
+ init_v3();
36324
+ var runbookApplyInputSchema = external_exports.object({
36325
+ name: external_exports.string().min(1).describe("Runbook name."),
36326
+ worldName: external_exports.string().optional().describe("Override the world name."),
36327
+ task: external_exports.string().optional().describe("Initial task to dispatch into the world."),
36328
+ branchName: external_exports.string().optional().describe("Override the default branch name.")
36329
+ });
36330
+ var runbookApplySkill = defineSkill({
36331
+ name: "runbook-apply",
36332
+ description: "Validate ports then create a world from a runbook. Errors (isError: true) on port conflicts. Returns world metadata on success.",
36333
+ inputSchema: runbookApplyInputSchema,
36334
+ handler: ({ name, worldName, task }) => JSON.stringify({ name, worldName: worldName ?? name, task: task ?? `Apply runbook "${name}"`, note: "apply requires OlamContext + port validation; use MCP surface" }, null, 2)
36335
+ });
36336
+
36337
+ // ../mcp-server/src/tools/runbook.ts
35532
36338
  function asMessage6(err) {
35533
36339
  return err instanceof Error ? err.message : String(err);
35534
36340
  }
@@ -35540,10 +36346,11 @@ function formatConflicts(conflicts) {
35540
36346
  return lines.join("\n");
35541
36347
  }
35542
36348
  function register24(server, ctx, _initError) {
36349
+ const listTool = runbookListSkill.asMcpTool();
35543
36350
  server.tool(
35544
36351
  "olam_runbook_list",
35545
- "List all runbooks in ~/.olam/config.json. Returns { runbooks: Runbook[] }.",
35546
- {},
36352
+ listTool.description,
36353
+ listTool.inputSchema,
35547
36354
  async () => {
35548
36355
  const runbooks = listRunbooks();
35549
36356
  return {
@@ -35551,12 +36358,11 @@ function register24(server, ctx, _initError) {
35551
36358
  };
35552
36359
  }
35553
36360
  );
36361
+ const showTool = runbookShowSkill.asMcpTool();
35554
36362
  server.tool(
35555
36363
  "olam_runbook_show",
35556
- "Show a single runbook by name. Returns { runbook: Runbook }.",
35557
- {
35558
- name: external_exports.string().min(1).describe("Runbook name.")
35559
- },
36364
+ showTool.description,
36365
+ showTool.inputSchema,
35560
36366
  async ({ name }) => {
35561
36367
  try {
35562
36368
  const runbook = getRunbook(name);
@@ -35568,23 +36374,11 @@ function register24(server, ctx, _initError) {
35568
36374
  }
35569
36375
  }
35570
36376
  );
36377
+ const addTool = runbookAddSkill.asMcpTool();
35571
36378
  server.tool(
35572
36379
  "olam_runbook_add",
35573
- "Create a new runbook. Validates that all referenced repos exist in the global registry and that portMap has no duplicates.",
35574
- {
35575
- name: external_exports.string().min(1).describe("Runbook name (lowercase, digits, dash; 1\u201364 chars)."),
35576
- repos: external_exports.array(external_exports.string().min(1)).min(1).describe("Repo names (must exist in registry)."),
35577
- description: external_exports.string().optional().describe("Optional human-readable description."),
35578
- portMap: external_exports.record(external_exports.string().min(1), external_exports.record(external_exports.string().min(1), external_exports.number().int())).optional().describe("Port mappings: { repoName: { serviceName: hostPort } }."),
35579
- seeds: external_exports.array(
35580
- external_exports.union([
35581
- external_exports.object({ type: external_exports.literal("sql-file"), repo: external_exports.string(), service: external_exports.string(), path: external_exports.string() }),
35582
- external_exports.object({ type: external_exports.literal("command"), repo: external_exports.string(), run: external_exports.string() }),
35583
- external_exports.object({ type: external_exports.literal("fixture-copy"), repo: external_exports.string(), src: external_exports.string(), dest: external_exports.string() })
35584
- ])
35585
- ).optional().describe("Optional seed operations to run when applying the runbook."),
35586
- env: external_exports.record(external_exports.string().min(1), external_exports.record(external_exports.string().min(1), external_exports.string())).optional().describe("Per-repo environment variable overrides.")
35587
- },
36380
+ addTool.description,
36381
+ addTool.inputSchema,
35588
36382
  async ({ name, repos, description, portMap, seeds, env }) => {
35589
36383
  try {
35590
36384
  const runbook = addRunbook({ name, repos, description, portMap, seeds, env });
@@ -35599,12 +36393,11 @@ function register24(server, ctx, _initError) {
35599
36393
  }
35600
36394
  }
35601
36395
  );
36396
+ const removeTool = runbookRemoveSkill.asMcpTool();
35602
36397
  server.tool(
35603
36398
  "olam_runbook_remove",
35604
- "Remove a runbook from the global config. Does not affect any running worlds.",
35605
- {
35606
- name: external_exports.string().min(1).describe("Runbook name to remove.")
35607
- },
36399
+ removeTool.description,
36400
+ removeTool.inputSchema,
35608
36401
  async ({ name }) => {
35609
36402
  try {
35610
36403
  removeRunbook(name);
@@ -35619,15 +36412,11 @@ function register24(server, ctx, _initError) {
35619
36412
  }
35620
36413
  }
35621
36414
  );
36415
+ const applyTool = runbookApplySkill.asMcpTool();
35622
36416
  server.tool(
35623
36417
  "olam_runbook_apply",
35624
- "Validate ports then create a world from a runbook. Errors (isError: true) on port conflicts. Returns world metadata on success.",
35625
- {
35626
- name: external_exports.string().min(1).describe("Runbook name."),
35627
- worldName: external_exports.string().optional().describe("Override the world name."),
35628
- task: external_exports.string().optional().describe("Initial task to dispatch into the world."),
35629
- branchName: external_exports.string().optional().describe("Override the default branch name.")
35630
- },
36418
+ applyTool.description,
36419
+ applyTool.inputSchema,
35631
36420
  async ({ name, worldName, task, branchName }) => {
35632
36421
  if (!ctx) {
35633
36422
  return {
@@ -35686,8 +36475,70 @@ var skill_source_exports = {};
35686
36475
  __export(skill_source_exports, {
35687
36476
  register: () => register25
35688
36477
  });
35689
- init_v3();
35690
36478
  init_skill_sources();
36479
+
36480
+ // ../skill-runtime/dist/skills/skills-source-list.js
36481
+ init_v3();
36482
+ var skillsSourceListInputSchema = external_exports.object({});
36483
+ var skillsSourceListSkill = defineSkill({
36484
+ name: "skills-source-list",
36485
+ description: "List every registered skill source. Returns { skillSources: SkillSource[] }.",
36486
+ inputSchema: skillsSourceListInputSchema,
36487
+ handler: (_input) => JSON.stringify({ note: "skillSource listing requires global config access; use MCP surface" }, null, 2)
36488
+ });
36489
+
36490
+ // ../skill-runtime/dist/skills/skills-source-add.js
36491
+ init_v3();
36492
+ var skillsSourceAddInputSchema = external_exports.object({
36493
+ name: external_exports.string().min(1).describe("Display name (lowercase, digits, dash; 1\u201364 chars)."),
36494
+ gitUrl: external_exports.string().min(1).describe("Git URL (https://, git@, ssh://, file://, or absolute path)."),
36495
+ branch: external_exports.string().min(1).optional().describe('Branch to track. Defaults to "main".'),
36496
+ trust: external_exports.boolean().optional().describe("Explicit trust (T6 capability-class). MCP context is non-interactive \u2014 must be true to register; otherwise the call is refused with an audit-log entry.")
36497
+ });
36498
+ var skillsSourceAddSkill = defineSkill({
36499
+ name: "skills-source-add",
36500
+ description: "Register and clone a skill source. Clones to ~/.olam/state/skill-sources/<id>/. Atomic: rolls back the state-store entry if the clone fails.",
36501
+ inputSchema: skillsSourceAddInputSchema,
36502
+ handler: ({ name, gitUrl, branch, trust }) => JSON.stringify({ name, gitUrl, branch: branch ?? "main", trust: trust ?? false, note: "add/clone requires fs + config store; use MCP surface" }, null, 2)
36503
+ });
36504
+
36505
+ // ../skill-runtime/dist/skills/skills-source-remove.js
36506
+ init_v3();
36507
+ var skillsSourceRemoveInputSchema = external_exports.object({
36508
+ id: external_exports.string().length(12).describe("Skill source id (from olam_skills_source_list).")
36509
+ });
36510
+ var skillsSourceRemoveSkill = defineSkill({
36511
+ name: "skills-source-remove",
36512
+ description: "Remove a registered skill source. Deletes the clone directory and the state entry.",
36513
+ inputSchema: skillsSourceRemoveInputSchema,
36514
+ handler: ({ id }) => JSON.stringify({ id, note: "remove requires config store + fs access; use MCP surface" }, null, 2)
36515
+ });
36516
+
36517
+ // ../skill-runtime/dist/skills/skills-source-pull.js
36518
+ init_v3();
36519
+ var skillsSourcePullInputSchema = external_exports.object({
36520
+ id: external_exports.string().length(12).describe("Skill source id.")
36521
+ });
36522
+ var skillsSourcePullSkill = defineSkill({
36523
+ name: "skills-source-pull",
36524
+ description: "Fetch + reset the clone to upstream HEAD. Updates lastPulledSha.",
36525
+ inputSchema: skillsSourcePullInputSchema,
36526
+ handler: ({ id }) => JSON.stringify({ id, note: "pull requires git + config store access; use MCP surface" }, null, 2)
36527
+ });
36528
+
36529
+ // ../skill-runtime/dist/skills/skills-source-show.js
36530
+ init_v3();
36531
+ var skillsSourceShowInputSchema = external_exports.object({
36532
+ id: external_exports.string().length(12).describe("Skill source id.")
36533
+ });
36534
+ var skillsSourceShowSkill = defineSkill({
36535
+ name: "skills-source-show",
36536
+ description: "Show details for a single skill source (state-store entry + resolved clone path).",
36537
+ inputSchema: skillsSourceShowInputSchema,
36538
+ handler: ({ id }) => JSON.stringify({ id, note: "show requires config store access; use MCP surface" }, null, 2)
36539
+ });
36540
+
36541
+ // ../mcp-server/src/tools/skill-source.ts
35691
36542
  function asMessage7(err) {
35692
36543
  return err instanceof Error ? err.message : String(err);
35693
36544
  }
@@ -35703,23 +36554,20 @@ function fail(err) {
35703
36554
  };
35704
36555
  }
35705
36556
  function register25(server, _ctx, _initError) {
36557
+ const listTool = skillsSourceListSkill.asMcpTool();
35706
36558
  server.tool(
35707
36559
  "olam_skills_source_list",
35708
- "List every registered skill source. Returns { skillSources: SkillSource[] }.",
35709
- {},
36560
+ listTool.description,
36561
+ listTool.inputSchema,
35710
36562
  async () => {
35711
36563
  return ok({ skillSources: listSkillSources() });
35712
36564
  }
35713
36565
  );
36566
+ const addTool = skillsSourceAddSkill.asMcpTool();
35714
36567
  server.tool(
35715
36568
  "olam_skills_source_add",
35716
- "Register and clone a skill source. Clones to ~/.olam/state/skill-sources/<id>/. Atomic: rolls back the state-store entry if the clone fails.",
35717
- {
35718
- name: external_exports.string().min(1).describe("Display name (lowercase, digits, dash; 1\u201364 chars)."),
35719
- gitUrl: external_exports.string().min(1).describe("Git URL (https://, git@, ssh://, file://, or absolute path)."),
35720
- branch: external_exports.string().min(1).optional().describe('Branch to track. Defaults to "main".'),
35721
- trust: external_exports.boolean().optional().describe("Explicit trust (T6 capability-class). MCP context is non-interactive \u2014 must be true to register; otherwise the call is refused with an audit-log entry.")
35722
- },
36569
+ addTool.description,
36570
+ addTool.inputSchema,
35723
36571
  async ({ name, gitUrl, branch, trust }) => {
35724
36572
  if (trust !== true) {
35725
36573
  try {
@@ -35763,12 +36611,11 @@ function register25(server, _ctx, _initError) {
35763
36611
  }
35764
36612
  }
35765
36613
  );
36614
+ const removeTool = skillsSourceRemoveSkill.asMcpTool();
35766
36615
  server.tool(
35767
36616
  "olam_skills_source_remove",
35768
- "Remove a registered skill source. Deletes the clone directory and the state entry.",
35769
- {
35770
- id: external_exports.string().length(12).describe("Skill source id (from olam_skills_source_list).")
35771
- },
36617
+ removeTool.description,
36618
+ removeTool.inputSchema,
35772
36619
  async ({ id }) => {
35773
36620
  try {
35774
36621
  removeSkillSourceClone(id);
@@ -35779,12 +36626,11 @@ function register25(server, _ctx, _initError) {
35779
36626
  }
35780
36627
  }
35781
36628
  );
36629
+ const pullTool = skillsSourcePullSkill.asMcpTool();
35782
36630
  server.tool(
35783
36631
  "olam_skills_source_pull",
35784
- "Fetch + reset the clone to upstream HEAD. Updates lastPulledSha.",
35785
- {
35786
- id: external_exports.string().length(12).describe("Skill source id.")
35787
- },
36632
+ pullTool.description,
36633
+ pullTool.inputSchema,
35788
36634
  async ({ id }) => {
35789
36635
  const entry = getSkillSource(id);
35790
36636
  if (!entry) {
@@ -35806,12 +36652,11 @@ function register25(server, _ctx, _initError) {
35806
36652
  }
35807
36653
  }
35808
36654
  );
36655
+ const showTool = skillsSourceShowSkill.asMcpTool();
35809
36656
  server.tool(
35810
36657
  "olam_skills_source_show",
35811
- "Show details for a single skill source (state-store entry + resolved clone path).",
35812
- {
35813
- id: external_exports.string().length(12).describe("Skill source id.")
35814
- },
36658
+ showTool.description,
36659
+ showTool.inputSchema,
35815
36660
  async ({ id }) => {
35816
36661
  const entry = getSkillSource(id);
35817
36662
  if (!entry) {
@@ -35830,10 +36675,65 @@ var skills_exports = {};
35830
36675
  __export(skills_exports, {
35831
36676
  register: () => register26
35832
36677
  });
35833
- init_v3();
35834
36678
  init_skill_sources();
35835
36679
  import * as fs36 from "node:fs";
35836
36680
  import * as path35 from "node:path";
36681
+
36682
+ // ../skill-runtime/dist/skills/skills-sync.js
36683
+ init_v3();
36684
+ var skillsSyncInputSchema = external_exports.object({
36685
+ dryRun: external_exports.boolean().optional().describe("Resolve artifacts but do not deploy or merge. Returns the summary that sync would produce."),
36686
+ atlasUser: external_exports.string().optional().describe("Override atlas-user identifier (defaults to ~/.claude/.atlas-user).")
36687
+ });
36688
+ var skillsSyncSkill = defineSkill({
36689
+ name: "skills-sync",
36690
+ description: "Sync all registered skill sources to ~/.claude/. Returns SyncSummary with per-source artifact breakdown, deploy result, and merge result.",
36691
+ inputSchema: skillsSyncInputSchema,
36692
+ handler: (input) => {
36693
+ const mode = input.dryRun ? "dry-run" : "live";
36694
+ const userNote = input.atlasUser ? ` (atlas-user: ${input.atlasUser})` : "";
36695
+ return `Skills sync (${mode}${userNote}) requires the skill-sources engine. Use the MCP tool surface or HTTP endpoint with a configured host.`;
36696
+ }
36697
+ });
36698
+
36699
+ // ../skill-runtime/dist/skills/skills-diff.js
36700
+ init_v3();
36701
+ var skillsDiffInputSchema = external_exports.object({
36702
+ atlasUser: external_exports.string().optional().describe("Override atlas-user identifier.")
36703
+ });
36704
+ var skillsDiffSkill = defineSkill({
36705
+ name: "skills-diff",
36706
+ description: "Show what `olam skills sync` would deploy without making on-disk changes. Equivalent to sync with dryRun=true.",
36707
+ inputSchema: skillsDiffInputSchema,
36708
+ handler: (input) => {
36709
+ const userNote = input.atlasUser ? ` (atlas-user: ${input.atlasUser})` : "";
36710
+ return `Skills diff${userNote} requires the skill-sources engine. Use the MCP tool surface or HTTP endpoint with a configured host.`;
36711
+ }
36712
+ });
36713
+
36714
+ // ../skill-runtime/dist/skills/skills-list.js
36715
+ init_v3();
36716
+ var skillsListInputSchema = external_exports.object({});
36717
+ var skillsListSkill = defineSkill({
36718
+ name: "skills-list",
36719
+ description: "List artifacts currently deployed under ~/.claude/{skills,agents,scripts,rules,commands}/ along with their source-id (if from a registered olam source).",
36720
+ inputSchema: skillsListInputSchema,
36721
+ handler: (_input) => "Skills listing requires filesystem access to ~/.claude/. Use the MCP tool surface or HTTP endpoint with a configured host."
36722
+ });
36723
+
36724
+ // ../skill-runtime/dist/skills/skills-show.js
36725
+ init_v3();
36726
+ var skillsShowInputSchema = external_exports.object({
36727
+ name: external_exports.string().min(1).describe("Artifact name (basename under any of the managed buckets).")
36728
+ });
36729
+ var skillsShowSkill = defineSkill({
36730
+ name: "skills-show",
36731
+ description: 'Show details for a deployed artifact by name (e.g. "plan-hard" or "codex-second-opinion.md"). Returns one or more matches across buckets.',
36732
+ inputSchema: skillsShowInputSchema,
36733
+ handler: (input) => `Skills show for "${input.name}" requires filesystem access to ~/.claude/. Use the MCP tool surface or HTTP endpoint with a configured host.`
36734
+ });
36735
+
36736
+ // ../mcp-server/src/tools/skills.ts
35837
36737
  function asMessage8(err) {
35838
36738
  return err instanceof Error ? err.message : String(err);
35839
36739
  }
@@ -35879,13 +36779,11 @@ function listDeployed() {
35879
36779
  return entries;
35880
36780
  }
35881
36781
  function register26(server, _ctx, _initError) {
36782
+ const skillsSync = skillsSyncSkill.asMcpTool();
35882
36783
  server.tool(
35883
36784
  "olam_skills_sync",
35884
- "Sync all registered skill sources to ~/.claude/. Returns SyncSummary with per-source artifact breakdown, deploy result, and merge result.",
35885
- {
35886
- dryRun: external_exports.boolean().optional().describe("Resolve artifacts but do not deploy or merge. Returns the summary that sync would produce."),
35887
- atlasUser: external_exports.string().optional().describe("Override atlas-user identifier (defaults to ~/.claude/.atlas-user).")
35888
- },
36785
+ skillsSync.description,
36786
+ skillsSync.inputSchema,
35889
36787
  async ({ dryRun, atlasUser }) => {
35890
36788
  try {
35891
36789
  const summary = await syncSkills({ dryRun, atlasUser });
@@ -35895,12 +36793,11 @@ function register26(server, _ctx, _initError) {
35895
36793
  }
35896
36794
  }
35897
36795
  );
36796
+ const skillsDiff = skillsDiffSkill.asMcpTool();
35898
36797
  server.tool(
35899
36798
  "olam_skills_diff",
35900
- "Show what `olam skills sync` would deploy without making on-disk changes. Equivalent to sync with dryRun=true.",
35901
- {
35902
- atlasUser: external_exports.string().optional().describe("Override atlas-user identifier.")
35903
- },
36799
+ skillsDiff.description,
36800
+ skillsDiff.inputSchema,
35904
36801
  async ({ atlasUser }) => {
35905
36802
  try {
35906
36803
  const summary = await syncSkills({ dryRun: true, atlasUser });
@@ -35910,10 +36807,11 @@ function register26(server, _ctx, _initError) {
35910
36807
  }
35911
36808
  }
35912
36809
  );
36810
+ const skillsList = skillsListSkill.asMcpTool();
35913
36811
  server.tool(
35914
36812
  "olam_skills_list",
35915
- "List artifacts currently deployed under ~/.claude/{skills,agents,scripts,rules,commands}/ along with their source-id (if from a registered olam source).",
35916
- {},
36813
+ skillsList.description,
36814
+ skillsList.inputSchema,
35917
36815
  async () => {
35918
36816
  try {
35919
36817
  const entries = listDeployed();
@@ -35923,12 +36821,11 @@ function register26(server, _ctx, _initError) {
35923
36821
  }
35924
36822
  }
35925
36823
  );
36824
+ const skillsShow = skillsShowSkill.asMcpTool();
35926
36825
  server.tool(
35927
36826
  "olam_skills_show",
35928
- 'Show details for a deployed artifact by name (e.g. "plan-hard" or "codex-second-opinion.md"). Returns one or more matches across buckets.',
35929
- {
35930
- name: external_exports.string().min(1).describe("Artifact name (basename under any of the managed buckets).")
35931
- },
36827
+ skillsShow.description,
36828
+ skillsShow.inputSchema,
35932
36829
  async ({ name }) => {
35933
36830
  try {
35934
36831
  const entries = listDeployed().filter((e) => e.name === name);
@@ -35948,19 +36845,18 @@ var skills_search_exports = {};
35948
36845
  __export(skills_search_exports, {
35949
36846
  register: () => register27
35950
36847
  });
35951
- init_v3();
35952
36848
  import * as path37 from "node:path";
35953
36849
  import * as os22 from "node:os";
35954
36850
 
35955
36851
  // ../mcp-server/src/lib/skills-index.mjs
35956
- import { createRequire as createRequire4 } from "node:module";
36852
+ import { createRequire as createRequire5 } from "node:module";
35957
36853
  import * as fs37 from "node:fs";
35958
36854
  import * as path36 from "node:path";
35959
36855
  import * as os21 from "node:os";
35960
36856
  var VECTOR_DIM = 256;
35961
36857
  var SCHEMA_VERSION3 = "1";
35962
36858
  var SCHEMA_KEY = "skills_index_schema_version";
35963
- var _require4 = createRequire4(import.meta.url);
36859
+ var _require4 = createRequire5(import.meta.url);
35964
36860
  var _Database3 = null;
35965
36861
  function getDatabase3() {
35966
36862
  if (_Database3 === null) {
@@ -36090,6 +36986,19 @@ function searchIndex(dbPath, query, k = 5) {
36090
36986
  var DEFAULT_DB_PATH = path36.join(os21.homedir(), ".olam", "skills.vec.db");
36091
36987
  var DEFAULT_SOURCE_DIR = path36.join(os21.homedir(), ".claude", "skills");
36092
36988
 
36989
+ // ../skill-runtime/dist/skills/skills-search.js
36990
+ init_v3();
36991
+ var skillsSearchInputSchema = external_exports.object({
36992
+ query: external_exports.string().min(1).describe('Natural-language description of the kind of skill you want (e.g. "stage and commit my changes" or "roll out a new release").'),
36993
+ k: external_exports.number().int().min(1).max(50).optional().describe("Number of hits to return (default 5, max 50).")
36994
+ });
36995
+ var skillsSearchSkill = defineSkill({
36996
+ name: "skills-search",
36997
+ description: "Semantic search over the local Claude skills index. Returns top-k skills ranked by cosine similarity against a deterministic bag-of-words embedding. Requires `scripts/skills-index-build.mjs` to have been run; set `SKILLS_INDEX_PATH` to override the index location (defaults to ~/.olam/skills.vec.db).",
36998
+ inputSchema: skillsSearchInputSchema,
36999
+ handler: ({ query, k }) => JSON.stringify({ query, k: k ?? 5, note: "searchIndex call requires on-disk DB; use MCP surface" }, null, 2)
37000
+ });
37001
+
36093
37002
  // ../mcp-server/src/tools/skills-search.ts
36094
37003
  function defaultDbPath() {
36095
37004
  const override = process.env["SKILLS_INDEX_PATH"];
@@ -36111,13 +37020,11 @@ function fail3(err) {
36111
37020
  };
36112
37021
  }
36113
37022
  function register27(server, _ctx, _initError) {
37023
+ const tool = skillsSearchSkill.asMcpTool();
36114
37024
  server.tool(
36115
37025
  "olam_skills_search",
36116
- "Semantic search over the local Claude skills index. Returns top-k skills ranked by cosine similarity against a deterministic bag-of-words embedding. Requires `scripts/skills-index-build.mjs` to have been run; set `SKILLS_INDEX_PATH` to override the index location (defaults to ~/.olam/skills.vec.db).",
36117
- {
36118
- query: external_exports.string().min(1).describe('Natural-language description of the kind of skill you want (e.g. "stage and commit my changes" or "roll out a new release").'),
36119
- k: external_exports.number().int().min(1).max(50).optional().describe("Number of hits to return (default 5, max 50).")
36120
- },
37026
+ tool.description,
37027
+ tool.inputSchema,
36121
37028
  async ({ query, k }) => {
36122
37029
  try {
36123
37030
  const hits = searchIndex(defaultDbPath(), query, k ?? 5);
@@ -36134,7 +37041,6 @@ var kg_classify_exports = {};
36134
37041
  __export(kg_classify_exports, {
36135
37042
  register: () => register28
36136
37043
  });
36137
- init_v3();
36138
37044
 
36139
37045
  // ../core/dist/kg/kg-service-client.js
36140
37046
  var KG_SERVICE_PORT_DEFAULT = 9997;
@@ -36206,15 +37112,34 @@ async function status(opts = {}) {
36206
37112
  return getJson(kgServiceStatusUrl(), opts.timeoutMs);
36207
37113
  }
36208
37114
 
37115
+ // ../skill-runtime/dist/skills/kg-classify.js
37116
+ init_v3();
37117
+ var kgClassifyInputSchema = external_exports.object({
37118
+ question: external_exports.string().describe("The natural-language question to route"),
37119
+ workspace: external_exports.string().optional().describe("Optional workspace name to scope the L2 probe gate (defaults to no probe)")
37120
+ });
37121
+ var kgClassifySkill = defineSkill({
37122
+ name: "kg-classify",
37123
+ description: "Route a question to kg | grep | both via the 4-layer classifier (kg-service container). Returns route + layer + reason + matched exemplar. Requires olam-kg-service running (see `olam services up`).",
37124
+ inputSchema: kgClassifyInputSchema,
37125
+ handler: (input) => {
37126
+ return JSON.stringify({
37127
+ route: "grep",
37128
+ layer: 0,
37129
+ reason: "skill-runtime stub \u2014 kg-service not available outside MCP context",
37130
+ question: input.question,
37131
+ workspace: input.workspace ?? null
37132
+ }, null, 2);
37133
+ }
37134
+ });
37135
+
36209
37136
  // ../mcp-server/src/tools/kg-classify.ts
36210
37137
  function register28(server, _ctx, _initError) {
37138
+ const { description, inputSchema } = kgClassifySkill.asMcpTool();
36211
37139
  server.tool(
36212
37140
  "olam_kg_classify",
36213
- "Route a question to kg | grep | both via the 4-layer classifier (kg-service container). Returns route + layer + reason + matched exemplar. Requires olam-kg-service running (see `olam services up`).",
36214
- {
36215
- question: external_exports.string().describe("The natural-language question to route"),
36216
- workspace: external_exports.string().optional().describe("Optional workspace name to scope the L2 probe gate (defaults to no probe)")
36217
- },
37141
+ description,
37142
+ inputSchema,
36218
37143
  async (params) => {
36219
37144
  try {
36220
37145
  const result = await classify({ q: params.question, workspace: params.workspace });
@@ -36257,6 +37182,29 @@ var kg_doctor_exports = {};
36257
37182
  __export(kg_doctor_exports, {
36258
37183
  register: () => register29
36259
37184
  });
37185
+
37186
+ // ../skill-runtime/dist/skills/kg-doctor.js
37187
+ init_v3();
37188
+ var kgDoctorInputSchema = external_exports.object({});
37189
+ var kgDoctorSkill = defineSkill({
37190
+ name: "kg-doctor",
37191
+ description: "Diagnostic report for olam-kg-service over HTTP: /health readiness, workspace inventory, and /classify round-trip. Returns a JSON `probes` array. If the container isn't running, all probes fail with a hint to run `olam services up`.",
37192
+ inputSchema: kgDoctorInputSchema,
37193
+ handler: (_input) => {
37194
+ return JSON.stringify({
37195
+ probes: [
37196
+ {
37197
+ name: "health",
37198
+ status: "fail",
37199
+ detail: "skill-runtime stub \u2014 kg-service not available outside MCP context",
37200
+ remedy: "`olam services up`"
37201
+ }
37202
+ ]
37203
+ }, null, 2);
37204
+ }
37205
+ });
37206
+
37207
+ // ../mcp-server/src/tools/kg-doctor.ts
36260
37208
  async function runProbes() {
36261
37209
  const results = [];
36262
37210
  try {
@@ -36306,10 +37254,11 @@ async function runProbes() {
36306
37254
  return results;
36307
37255
  }
36308
37256
  function register29(server, _ctx, _initError) {
37257
+ const { description, inputSchema } = kgDoctorSkill.asMcpTool();
36309
37258
  server.tool(
36310
37259
  "olam_kg_doctor",
36311
- "Diagnostic report for olam-kg-service over HTTP: /health readiness, workspace inventory, and /classify round-trip. Returns a JSON `probes` array. If the container isn't running, all probes fail with a hint to run `olam services up`.",
36312
- {},
37260
+ description,
37261
+ inputSchema,
36313
37262
  async () => {
36314
37263
  try {
36315
37264
  const probes = await runProbes();
@@ -36343,7 +37292,6 @@ var kg_install_hook_exports = {};
36343
37292
  __export(kg_install_hook_exports, {
36344
37293
  register: () => register30
36345
37294
  });
36346
- init_v3();
36347
37295
  init_merge_settings();
36348
37296
  import * as fs38 from "node:fs";
36349
37297
  import * as path38 from "node:path";
@@ -36394,6 +37342,29 @@ function buildHookMatcherEntry(opts) {
36394
37342
  };
36395
37343
  }
36396
37344
 
37345
+ // ../skill-runtime/dist/skills/kg-install-hook.js
37346
+ init_v3();
37347
+ var kgInstallHookInputSchema = external_exports.object({
37348
+ scope: external_exports.enum(["project", "user"]).optional().describe("project (default) or user"),
37349
+ projectPath: external_exports.string().optional().describe("Override the project root. Defaults to cwd. Ignored when scope=user.")
37350
+ });
37351
+ var kgInstallHookSkill = defineSkill({
37352
+ name: "kg-install-hook",
37353
+ description: "Install the kg-service PreToolUse hook into .claude/settings.json. Idempotent (sentinel-detected). Default scope=project (writes <cwd>/.claude/settings.json); scope=user writes ~/.claude/settings.json.",
37354
+ inputSchema: kgInstallHookInputSchema,
37355
+ handler: (input) => {
37356
+ const scope = input.scope === "user" ? "user" : "project";
37357
+ return JSON.stringify({
37358
+ ok: false,
37359
+ status: "skill-runtime-stub",
37360
+ scope,
37361
+ filePath: null,
37362
+ backup: null,
37363
+ message: "skill-runtime stub \u2014 filesystem access not available outside MCP context"
37364
+ }, null, 2);
37365
+ }
37366
+ });
37367
+
36397
37368
  // ../mcp-server/src/tools/kg-install-hook.ts
36398
37369
  function settingsPathFor2(scope, projectPath) {
36399
37370
  if (scope === "user") {
@@ -36403,13 +37374,11 @@ function settingsPathFor2(scope, projectPath) {
36403
37374
  return path38.join(root, ".claude", "settings.json");
36404
37375
  }
36405
37376
  function register30(server, _ctx, _initError) {
37377
+ const { description, inputSchema } = kgInstallHookSkill.asMcpTool();
36406
37378
  server.tool(
36407
37379
  "olam_kg_install_hook",
36408
- "Install the kg-service PreToolUse hook into .claude/settings.json. Idempotent (sentinel-detected). Default scope=project (writes <cwd>/.claude/settings.json); scope=user writes ~/.claude/settings.json.",
36409
- {
36410
- scope: external_exports.enum(["project", "user"]).optional().describe("project (default) or user"),
36411
- projectPath: external_exports.string().optional().describe("Override the project root. Defaults to cwd. Ignored when scope=user.")
36412
- },
37380
+ description,
37381
+ inputSchema,
36413
37382
  async (params) => {
36414
37383
  const scope = params.scope === "user" ? "user" : "project";
36415
37384
  const filePath = settingsPathFor2(scope, params.projectPath);
@@ -36473,10 +37442,28 @@ var kg_uninstall_hook_exports = {};
36473
37442
  __export(kg_uninstall_hook_exports, {
36474
37443
  register: () => register31
36475
37444
  });
36476
- init_v3();
36477
37445
  import * as fs39 from "node:fs";
36478
37446
  import * as path39 from "node:path";
36479
37447
  import * as os24 from "node:os";
37448
+
37449
+ // ../skill-runtime/dist/skills/kg-uninstall-hook.js
37450
+ init_v3();
37451
+ var kgUninstallHookInputSchema = external_exports.object({
37452
+ scope: external_exports.enum(["project", "user"]).optional().describe("project (default) or user"),
37453
+ projectPath: external_exports.string().optional().describe("Override the project root. Defaults to cwd.")
37454
+ });
37455
+ var kgUninstallHookSkill = defineSkill({
37456
+ name: "kg-uninstall-hook",
37457
+ description: "Remove the kg-service PreToolUse hook from .claude/settings.json. Sentinel-matched: only the olam entry is removed; other PreToolUse hooks are preserved.",
37458
+ inputSchema: kgUninstallHookInputSchema,
37459
+ handler: (input) => {
37460
+ const scope = input.scope ?? "project";
37461
+ const where = input.projectPath ? ` at ${input.projectPath}` : "";
37462
+ return `Would remove kg-service hook from ${scope} settings.json${where}. Use the MCP tool for actual execution.`;
37463
+ }
37464
+ });
37465
+
37466
+ // ../mcp-server/src/tools/kg-uninstall-hook.ts
36480
37467
  function settingsPathFor3(scope, projectPath) {
36481
37468
  if (scope === "user") {
36482
37469
  return path39.join(os24.homedir(), ".claude", "settings.json");
@@ -36505,13 +37492,11 @@ function dropSentinel(matchers) {
36505
37492
  return { matchers: out, changed };
36506
37493
  }
36507
37494
  function register31(server, _ctx, _initError) {
37495
+ const { description, inputSchema } = kgUninstallHookSkill.asMcpTool();
36508
37496
  server.tool(
36509
37497
  "olam_kg_uninstall_hook",
36510
- "Remove the kg-service PreToolUse hook from .claude/settings.json. Sentinel-matched: only the olam entry is removed; other PreToolUse hooks are preserved.",
36511
- {
36512
- scope: external_exports.enum(["project", "user"]).optional().describe("project (default) or user"),
36513
- projectPath: external_exports.string().optional().describe("Override the project root. Defaults to cwd.")
36514
- },
37498
+ description,
37499
+ inputSchema,
36515
37500
  async (params) => {
36516
37501
  const scope = params.scope === "user" ? "user" : "project";
36517
37502
  const filePath = settingsPathFor3(scope, params.projectPath);
@@ -36883,8 +37868,8 @@ function createServer4(ctx, initError) {
36883
37868
  }
36884
37869
 
36885
37870
  // ../mcp-server/src/utils/native-probe.ts
36886
- import { createRequire as createRequire5 } from "node:module";
36887
- var PROBE_REQUIRE = createRequire5(import.meta.url);
37871
+ import { createRequire as createRequire6 } from "node:module";
37872
+ var PROBE_REQUIRE = createRequire6(import.meta.url);
36888
37873
  function runtimeModuleVersion() {
36889
37874
  return Number.parseInt(process.versions.modules, 10);
36890
37875
  }
@@ -37000,7 +37985,7 @@ init_loader();
37000
37985
 
37001
37986
  // ../core/dist/world/manager.js
37002
37987
  import * as crypto8 from "node:crypto";
37003
- import { execSync as execSync5, spawnSync as spawnSync4 } from "node:child_process";
37988
+ import { execSync as execSync6, spawnSync as spawnSync4 } from "node:child_process";
37004
37989
  import * as fs49 from "node:fs";
37005
37990
  import * as os29 from "node:os";
37006
37991
  import * as path50 from "node:path";
@@ -37542,7 +38527,7 @@ function extractStderr(err) {
37542
38527
  function carryUncommittedEdits(repos, workspacePath, deps = {}) {
37543
38528
  const exec = deps.exec ?? ((cmd, args, opts) => execFileSync6(cmd, args, opts));
37544
38529
  const homedir30 = deps.homedir ?? (() => os26.homedir());
37545
- const existsSync48 = deps.existsSync ?? ((p) => fs43.existsSync(p));
38530
+ const existsSync49 = deps.existsSync ?? ((p) => fs43.existsSync(p));
37546
38531
  const copyFileSync9 = deps.copyFileSync ?? ((src, dest) => fs43.copyFileSync(src, dest));
37547
38532
  const mkdirSync30 = deps.mkdirSync ?? ((dirPath, opts) => {
37548
38533
  fs43.mkdirSync(dirPath, opts);
@@ -37553,9 +38538,9 @@ function carryUncommittedEdits(repos, workspacePath, deps = {}) {
37553
38538
  continue;
37554
38539
  const repoPath = expandHome2(repo.path, homedir30);
37555
38540
  const worktreePath = path43.join(workspacePath, repo.name);
37556
- if (!existsSync48(repoPath))
38541
+ if (!existsSync49(repoPath))
37557
38542
  continue;
37558
- if (!existsSync48(worktreePath)) {
38543
+ if (!existsSync49(worktreePath)) {
37559
38544
  console.warn(`[carry] ${repo.name}: world worktree ${worktreePath} missing; skipping carry for this repo`);
37560
38545
  continue;
37561
38546
  }
@@ -37615,7 +38600,7 @@ function carryUncommittedEdits(repos, workspacePath, deps = {}) {
37615
38600
  for (const rel of plan.diff.untracked) {
37616
38601
  const src = path43.join(plan.repoPath, rel);
37617
38602
  const dest = path43.join(plan.worktreePath, rel);
37618
- if (!existsSync48(src))
38603
+ if (!existsSync49(src))
37619
38604
  continue;
37620
38605
  try {
37621
38606
  mkdirSync30(path43.dirname(dest), { recursive: true });
@@ -39134,6 +40119,54 @@ async function runSeedHooks(seeds, containerName, servicePortMap, exec) {
39134
40119
  }
39135
40120
  }
39136
40121
 
40122
+ // ../core/dist/world/create-dedup.js
40123
+ import { execSync as execSync5 } from "node:child_process";
40124
+ var ACTIVE_STATUSES = /* @__PURE__ */ new Set([
40125
+ "creating",
40126
+ "running",
40127
+ "paused",
40128
+ "crystallizing"
40129
+ ]);
40130
+ var defaultContainerProbe = (containerName) => {
40131
+ try {
40132
+ const out = execSync5(`docker ps --filter name=^/${containerName}$ --format '{{.Names}}'`, { stdio: ["pipe", "pipe", "pipe"], timeout: 5e3 }).toString().trim();
40133
+ return out.length > 0;
40134
+ } catch {
40135
+ return false;
40136
+ }
40137
+ };
40138
+ function resolveCreateDedup(opts) {
40139
+ const { registry: registry2, name } = opts;
40140
+ const probe = opts.probeContainer ?? defaultContainerProbe;
40141
+ const containerName = `olam-${name}-devbox`;
40142
+ const existing = registry2.findByName(name);
40143
+ const active = existing.find((w) => ACTIVE_STATUSES.has(w.status));
40144
+ if (active) {
40145
+ return { action: "reuse", world: active };
40146
+ }
40147
+ const errorRows = existing.filter((w) => w.status === "error");
40148
+ for (const er of errorRows) {
40149
+ registry2.remove(er.id);
40150
+ }
40151
+ if (probe(containerName)) {
40152
+ return {
40153
+ action: "container-orphan",
40154
+ containerName,
40155
+ remediation: `A container '${containerName}' is running on this host but no worlds.db row exists for it. Either destroy the orphan with 'docker rm -f ${containerName}' and re-run, or wait for the reconciler (issue #963) to insert a row.`
40156
+ };
40157
+ }
40158
+ return { action: "fresh", cleanedErrorRows: errorRows.length };
40159
+ }
40160
+ var ContainerOrphanError = class extends Error {
40161
+ containerName;
40162
+ kind = "container-orphan";
40163
+ constructor(message, containerName) {
40164
+ super(message);
40165
+ this.containerName = containerName;
40166
+ this.name = "ContainerOrphanError";
40167
+ }
40168
+ };
40169
+
39137
40170
  // ../core/dist/world/tmux-supervisor.js
39138
40171
  function injectBindAll(start) {
39139
40172
  let result = start;
@@ -39267,7 +40300,7 @@ var BotIdentityError = class extends Error {
39267
40300
  this.name = "BotIdentityError";
39268
40301
  }
39269
40302
  };
39270
- function getTokenScopes(ghToken, _exec = execSync5) {
40303
+ function getTokenScopes(ghToken, _exec = execSync6) {
39271
40304
  try {
39272
40305
  const out = _exec("gh auth status 2>&1", {
39273
40306
  encoding: "utf-8",
@@ -39284,13 +40317,13 @@ function getTokenScopes(ghToken, _exec = execSync5) {
39284
40317
  }
39285
40318
  }
39286
40319
  async function setupContainerGit(containerName, repos, branch) {
39287
- const dockerExec = (cmd) => execSync5(`docker exec ${containerName} sh -c '${cmd.replace(/'/g, "'\\''")}'`, {
40320
+ const dockerExec = (cmd) => execSync6(`docker exec ${containerName} sh -c '${cmd.replace(/'/g, "'\\''")}'`, {
39288
40321
  stdio: "pipe",
39289
40322
  timeout: 6e4
39290
40323
  }).toString();
39291
40324
  let ghToken = "";
39292
40325
  try {
39293
- ghToken = execSync5("gh auth token 2>/dev/null", { encoding: "utf-8", timeout: 5e3 }).trim();
40326
+ ghToken = execSync6("gh auth token 2>/dev/null", { encoding: "utf-8", timeout: 5e3 }).trim();
39294
40327
  } catch {
39295
40328
  }
39296
40329
  const actorName = process.env.OLAM_BOT_NAME ?? "Claude Code (olam)";
@@ -39310,7 +40343,7 @@ async function setupContainerGit(containerName, repos, branch) {
39310
40343
  continue;
39311
40344
  const ownerRepo = ghMatch[1];
39312
40345
  try {
39313
- execSync5(`gh api repos/${ownerRepo} --silent`, {
40346
+ execSync6(`gh api repos/${ownerRepo} --silent`, {
39314
40347
  stdio: "pipe",
39315
40348
  timeout: 5e3,
39316
40349
  env: { ...process.env, GH_TOKEN: ghToken }
@@ -39358,7 +40391,7 @@ ${stderr.split("\n").slice(0, 3).join(" ")}`);
39358
40391
  if (!olamUserPresent) {
39359
40392
  const imageName = (() => {
39360
40393
  try {
39361
- return execSync5(`docker inspect ${containerName} --format '{{.Config.Image}}'`, {
40394
+ return execSync6(`docker inspect ${containerName} --format '{{.Config.Image}}'`, {
39362
40395
  encoding: "utf8",
39363
40396
  timeout: 5e3
39364
40397
  }).trim() || "(unknown)";
@@ -39401,7 +40434,7 @@ ${stderr.split("\n").slice(0, 3).join(" ")}`);
39401
40434
  function makeHostExecFn() {
39402
40435
  return async (cmd) => {
39403
40436
  try {
39404
- const stdout = execSync5(cmd, { encoding: "utf-8", timeout: 5e3 });
40437
+ const stdout = execSync6(cmd, { encoding: "utf-8", timeout: 5e3 });
39405
40438
  return { stdout, stderr: "", exitCode: 0 };
39406
40439
  } catch {
39407
40440
  return { stdout: "", stderr: "", exitCode: 1 };
@@ -39411,7 +40444,7 @@ function makeHostExecFn() {
39411
40444
  function makeContainerExecFn(containerName) {
39412
40445
  return async (cmd) => {
39413
40446
  try {
39414
- const result = execSync5(`docker exec ${containerName} sh -c '${cmd.replace(/'/g, "'\\''")}'`, { stdio: "pipe", timeout: 6e5 });
40447
+ const result = execSync6(`docker exec ${containerName} sh -c '${cmd.replace(/'/g, "'\\''")}'`, { stdio: "pipe", timeout: 6e5 });
39415
40448
  return { stdout: result.toString(), stderr: "", exitCode: 0 };
39416
40449
  } catch (err) {
39417
40450
  const execErr = err;
@@ -39425,7 +40458,7 @@ function makeContainerExecFn(containerName) {
39425
40458
  }
39426
40459
  function defaultDockerExec() {
39427
40460
  return (containerName, cmd) => {
39428
- const result = execSync5(
40461
+ const result = execSync6(
39429
40462
  `docker exec ${containerName} sh -c '${cmd.replace(/'/g, "'\\''")}'`,
39430
40463
  // Phase E E5 raise: 10min was too tight on cold-boot for atlas-core's
39431
40464
  // `rails db:create` chain (Rails 7 boot + initializer load + first
@@ -39625,7 +40658,7 @@ function buildManifestRuntime(worldId, repos) {
39625
40658
  }
39626
40659
  return { worldId, repos: runtimeRepos };
39627
40660
  }
39628
- function exposeWorldOverTailscale(appPortUrls, worldId, registry2, _exec = execSync5) {
40661
+ function exposeWorldOverTailscale(appPortUrls, worldId, registry2, _exec = execSync6) {
39629
40662
  if (process.env["OLAM_TAILSCALE_SERVE"] !== "true")
39630
40663
  return;
39631
40664
  if (appPortUrls.length === 0)
@@ -39653,7 +40686,7 @@ function exposeWorldOverTailscale(appPortUrls, worldId, registry2, _exec = execS
39653
40686
  registry2.storeTailscalePaths(worldId, registeredPaths);
39654
40687
  }
39655
40688
  }
39656
- function cleanupWorldTailscale(worldId, registry2, _exec = execSync5) {
40689
+ function cleanupWorldTailscale(worldId, registry2, _exec = execSync6) {
39657
40690
  const paths = registry2.loadTailscalePaths(worldId);
39658
40691
  if (paths.length === 0)
39659
40692
  return;
@@ -39672,7 +40705,7 @@ function cleanupWorldTailscale(worldId, registry2, _exec = execSync5) {
39672
40705
  }
39673
40706
  }
39674
40707
  }
39675
- function resolveTailscaleBin(_exec = execSync5) {
40708
+ function resolveTailscaleBin(_exec = execSync6) {
39676
40709
  const candidates = [
39677
40710
  process.env["TAILSCALE_BIN"],
39678
40711
  "/Applications/Tailscale.app/Contents/MacOS/Tailscale",
@@ -39697,19 +40730,38 @@ var WorldManager = class {
39697
40730
  dashboardManager;
39698
40731
  pleriClient;
39699
40732
  dockerExec;
40733
+ containerProbe;
39700
40734
  manifestRuntimes = /* @__PURE__ */ new Map();
39701
- constructor(config2, provider, registry2, dashboardManager, pleriClient, dockerExec) {
40735
+ constructor(config2, provider, registry2, dashboardManager, pleriClient, dockerExec, containerProbe) {
39702
40736
  this.config = config2;
39703
40737
  this.provider = provider;
39704
40738
  this.registry = registry2;
39705
40739
  this.dashboardManager = dashboardManager;
39706
40740
  this.pleriClient = pleriClient;
39707
40741
  this.dockerExec = dockerExec ?? defaultDockerExec();
40742
+ this.containerProbe = containerProbe;
39708
40743
  }
39709
40744
  // -----------------------------------------------------------------------
39710
40745
  // createWorld
39711
40746
  // -----------------------------------------------------------------------
39712
40747
  async createWorld(opts) {
40748
+ {
40749
+ const dedupOpts = {
40750
+ registry: this.registry,
40751
+ name: opts.name,
40752
+ ...this.containerProbe ? { probeContainer: this.containerProbe } : {}
40753
+ };
40754
+ const dedup = resolveCreateDedup(dedupOpts);
40755
+ if (dedup.action === "reuse") {
40756
+ return dedup.world;
40757
+ }
40758
+ if (dedup.action === "container-orphan") {
40759
+ throw new ContainerOrphanError(dedup.remediation, dedup.containerName);
40760
+ }
40761
+ if (dedup.cleanedErrorRows > 0) {
40762
+ console.log(`[manager] removed ${dedup.cleanedErrorRows} stale error row(s) for world name "${opts.name}" before fresh create (issue #962)`);
40763
+ }
40764
+ }
39713
40765
  if (!opts.noAuth) {
39714
40766
  const preflight = await runAuthPreflight({ autoStart: true });
39715
40767
  if (preflight.verdict !== "ok") {
@@ -40393,7 +41445,7 @@ ${detail}`);
40393
41445
  const escapedDir = `/home/olam/workspace/${repo.name.replace(/["$`\\]/g, "\\$&")}`;
40394
41446
  for (const cmd of repo.setup_commands) {
40395
41447
  try {
40396
- execSync5(`docker exec ${containerName} sh -c 'cd "${escapedDir}" && ${cmd.replace(/'/g, "'\\''")}'`, { stdio: "pipe", timeout: 6e5 });
41448
+ execSync6(`docker exec ${containerName} sh -c 'cd "${escapedDir}" && ${cmd.replace(/'/g, "'\\''")}'`, { stdio: "pipe", timeout: 6e5 });
40397
41449
  } catch (err) {
40398
41450
  const msg = err instanceof Error ? err.message : String(err);
40399
41451
  console.warn(`[WorldManager] setup command failed for ${repo.name}: ${msg}`);
@@ -40414,11 +41466,11 @@ ${detail}`);
40414
41466
  });
40415
41467
  if (allPolicies.length > 0) {
40416
41468
  try {
40417
- execSync5(`docker exec ${containerName} mkdir -p /home/olam/.olam/policies`, { stdio: "pipe", timeout: 1e4 });
41469
+ execSync6(`docker exec ${containerName} mkdir -p /home/olam/.olam/policies`, { stdio: "pipe", timeout: 1e4 });
40418
41470
  for (const repo of repos) {
40419
41471
  const policiesDir = path50.join(workspacePath, repo.name, ".olam", "policies");
40420
41472
  if (fs49.existsSync(policiesDir)) {
40421
- execSync5(`docker cp "${policiesDir}/." "${containerName}:/home/olam/.olam/policies/"`, { stdio: "pipe", timeout: 15e3 });
41473
+ execSync6(`docker cp "${policiesDir}/." "${containerName}:/home/olam/.olam/policies/"`, { stdio: "pipe", timeout: 15e3 });
40422
41474
  }
40423
41475
  }
40424
41476
  } catch (err) {
@@ -41924,11 +42976,11 @@ function isDashboardRunning() {
41924
42976
  }
41925
42977
 
41926
42978
  // ../core/dist/dashboard/tunnel.js
41927
- import { spawn as spawn4, execSync as execSync6 } from "node:child_process";
42979
+ import { spawn as spawn4, execSync as execSync7 } from "node:child_process";
41928
42980
  var tunnelProcess = null;
41929
42981
  function isCloudflaredAvailable() {
41930
42982
  try {
41931
- execSync6("which cloudflared", { stdio: "ignore" });
42983
+ execSync7("which cloudflared", { stdio: "ignore" });
41932
42984
  return true;
41933
42985
  } catch {
41934
42986
  return false;
@@ -42186,7 +43238,7 @@ var PleriClient = class {
42186
43238
  };
42187
43239
 
42188
43240
  // ../mcp-server/src/env-loader.ts
42189
- import { readFileSync as readFileSync39, existsSync as existsSync47, statSync as statSync14 } from "node:fs";
43241
+ import { readFileSync as readFileSync39, existsSync as existsSync48, statSync as statSync14 } from "node:fs";
42190
43242
  import { join as join52, dirname as dirname29, resolve as resolve14 } from "node:path";
42191
43243
  var PROJECT_MARKERS = [
42192
43244
  ".olam/config.yaml",
@@ -42199,10 +43251,10 @@ function findProjectRoot2(startDir) {
42199
43251
  const root = resolve14("/");
42200
43252
  while (true) {
42201
43253
  for (const marker of PROJECT_MARKERS) {
42202
- if (existsSync47(join52(dir, marker))) return dir;
43254
+ if (existsSync48(join52(dir, marker))) return dir;
42203
43255
  }
42204
43256
  const pkg = join52(dir, "package.json");
42205
- if (existsSync47(pkg)) {
43257
+ if (existsSync48(pkg)) {
42206
43258
  try {
42207
43259
  const json = JSON.parse(readFileSync39(pkg, "utf8"));
42208
43260
  const isOlamWorkspace = typeof json.name === "string" && json.name.startsWith("@olam/");
@@ -42242,7 +43294,7 @@ function loadProjectEnv(startDir = process.cwd()) {
42242
43294
  const merged = {};
42243
43295
  for (const name of [".env", ".env.local"]) {
42244
43296
  const p = join52(root, name);
42245
- if (existsSync47(p) && statSync14(p).isFile()) {
43297
+ if (existsSync48(p) && statSync14(p).isFile()) {
42246
43298
  Object.assign(merged, parseEnvFile(p));
42247
43299
  filesRead.push(p);
42248
43300
  }