ocx 1.0.10 → 1.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.js +315 -483
  2. package/dist/index.js.map +16 -13
  3. package/package.json +63 -62
package/dist/index.js CHANGED
@@ -8486,76 +8486,7 @@ var coerce = {
8486
8486
  date: (arg) => ZodDate.create({ ...arg, coerce: true })
8487
8487
  };
8488
8488
  var NEVER = INVALID;
8489
- // src/utils/errors.ts
8490
- var EXIT_CODES = {
8491
- SUCCESS: 0,
8492
- GENERAL: 1,
8493
- NOT_FOUND: 66,
8494
- NETWORK: 69,
8495
- CONFIG: 78,
8496
- INTEGRITY: 1
8497
- };
8498
-
8499
- class OCXError extends Error {
8500
- code;
8501
- exitCode;
8502
- constructor(message, code, exitCode = EXIT_CODES.GENERAL) {
8503
- super(message);
8504
- this.code = code;
8505
- this.exitCode = exitCode;
8506
- this.name = "OCXError";
8507
- }
8508
- }
8509
-
8510
- class NotFoundError extends OCXError {
8511
- constructor(message) {
8512
- super(message, "NOT_FOUND", EXIT_CODES.NOT_FOUND);
8513
- this.name = "NotFoundError";
8514
- }
8515
- }
8516
-
8517
- class NetworkError extends OCXError {
8518
- constructor(message) {
8519
- super(message, "NETWORK_ERROR", EXIT_CODES.NETWORK);
8520
- this.name = "NetworkError";
8521
- }
8522
- }
8523
-
8524
- class ConfigError extends OCXError {
8525
- constructor(message) {
8526
- super(message, "CONFIG_ERROR", EXIT_CODES.CONFIG);
8527
- this.name = "ConfigError";
8528
- }
8529
- }
8530
-
8531
- class ValidationError extends OCXError {
8532
- constructor(message) {
8533
- super(message, "VALIDATION_ERROR", EXIT_CODES.GENERAL);
8534
- this.name = "ValidationError";
8535
- }
8536
- }
8537
-
8538
- class ConflictError extends OCXError {
8539
- constructor(message) {
8540
- super(message, "CONFLICT", EXIT_CODES.GENERAL);
8541
- this.name = "ConflictError";
8542
- }
8543
- }
8544
-
8545
- class IntegrityError extends OCXError {
8546
- constructor(component, expected, found) {
8547
- const message = `Integrity verification failed for "${component}"
8548
- ` + ` Expected: ${expected}
8549
- ` + ` Found: ${found}
8550
-
8551
- ` + `The registry content has changed since this component was locked.
8552
- ` + `This could indicate tampering or an unauthorized update.`;
8553
- super(message, "INTEGRITY_ERROR", EXIT_CODES.INTEGRITY);
8554
- this.name = "IntegrityError";
8555
- }
8556
- }
8557
-
8558
- // src/schemas/registry.ts
8489
+ // ../schemas/dist/registry.js
8559
8490
  var openCodeNameSchema = exports_external.string().min(1, "Name cannot be empty").max(64, "Name cannot exceed 64 characters").regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, {
8560
8491
  message: "Must be lowercase alphanumeric with single hyphen separators (e.g., 'my-component', 'my-plugin'). Cannot start/end with hyphen or have consecutive hyphens."
8561
8492
  });
@@ -8565,11 +8496,11 @@ var qualifiedComponentSchema = exports_external.string().regex(/^[a-z0-9]+(-[a-z
8565
8496
  });
8566
8497
  function parseQualifiedComponent(ref) {
8567
8498
  if (!ref.includes("/")) {
8568
- throw new ValidationError(`Invalid component reference: "${ref}". Use format: namespace/component`);
8499
+ throw new Error(`Invalid component reference: "${ref}". Use format: namespace/component`);
8569
8500
  }
8570
8501
  const [namespace, component] = ref.split("/");
8571
8502
  if (!namespace || !component) {
8572
- throw new ValidationError(`Invalid component reference: "${ref}". Both namespace and component are required.`);
8503
+ throw new Error(`Invalid component reference: "${ref}". Both namespace and component are required.`);
8573
8504
  }
8574
8505
  return { namespace, component };
8575
8506
  }
@@ -8602,9 +8533,6 @@ var targetPathSchema = exports_external.string().refine((path) => path.startsWit
8602
8533
  }, {
8603
8534
  message: 'Target must be in a valid directory: ".opencode/{agent|skill|plugin|command|tool|philosophy}/..."'
8604
8535
  });
8605
- var skillTargetSchema = exports_external.string().regex(/^\.opencode\/skill\/[a-z0-9]+(-[a-z0-9]+)*\/SKILL\.md$/, {
8606
- message: 'Skill target must match pattern ".opencode/skill/<name>/SKILL.md" where name follows OpenCode naming rules'
8607
- });
8608
8536
  var mcpServerObjectSchema = exports_external.object({
8609
8537
  type: exports_external.enum(["remote", "local"]),
8610
8538
  url: exports_external.string().url().optional(),
@@ -8640,11 +8568,18 @@ var agentConfigSchema = exports_external.object({
8640
8568
  prompt: exports_external.string().optional(),
8641
8569
  permission: exports_external.record(exports_external.enum(["allow", "deny"])).optional()
8642
8570
  });
8571
+ var permissionConfigSchema = exports_external.object({
8572
+ bash: exports_external.union([exports_external.enum(["ask", "allow", "deny"]), exports_external.record(exports_external.enum(["ask", "allow", "deny"]))]).optional(),
8573
+ edit: exports_external.union([exports_external.enum(["ask", "allow", "deny"]), exports_external.record(exports_external.enum(["ask", "allow", "deny"]))]).optional(),
8574
+ mcp: exports_external.record(exports_external.enum(["ask", "allow", "deny"])).optional()
8575
+ });
8643
8576
  var opencodeConfigSchema = exports_external.object({
8644
- plugins: exports_external.array(exports_external.string()).optional(),
8577
+ mcp: exports_external.record(mcpServerRefSchema).optional(),
8578
+ plugin: exports_external.array(exports_external.string()).optional(),
8645
8579
  tools: exports_external.record(exports_external.boolean()).optional(),
8646
8580
  agent: exports_external.record(agentConfigSchema).optional(),
8647
- instructions: exports_external.array(exports_external.string()).optional()
8581
+ instructions: exports_external.array(exports_external.string()).optional(),
8582
+ permission: permissionConfigSchema.optional()
8648
8583
  });
8649
8584
  var componentManifestSchema = exports_external.object({
8650
8585
  name: openCodeNameSchema,
@@ -8654,10 +8589,7 @@ var componentManifestSchema = exports_external.object({
8654
8589
  dependencies: exports_external.array(dependencyRefSchema).default([]),
8655
8590
  npmDependencies: exports_external.array(exports_external.string()).optional(),
8656
8591
  npmDevDependencies: exports_external.array(exports_external.string()).optional(),
8657
- mcpServers: exports_external.record(mcpServerRefSchema).optional(),
8658
- mcpScope: exports_external.enum(["agent", "global"]).default("agent"),
8659
- opencode: opencodeConfigSchema.optional(),
8660
- disabledTools: exports_external.array(exports_external.string()).optional()
8592
+ opencode: opencodeConfigSchema.optional()
8661
8593
  });
8662
8594
  function inferTargetPath(sourcePath) {
8663
8595
  return `.opencode/${sourcePath}`;
@@ -8682,13 +8614,20 @@ function normalizeMcpServer(server) {
8682
8614
  return server;
8683
8615
  }
8684
8616
  function normalizeComponentManifest(manifest) {
8617
+ let normalizedOpencode;
8618
+ if (manifest.opencode) {
8619
+ const { mcp, ...rest } = manifest.opencode;
8620
+ normalizedOpencode = {
8621
+ ...rest,
8622
+ ...mcp && {
8623
+ mcp: Object.fromEntries(Object.entries(mcp).map(([name, server]) => [name, normalizeMcpServer(server)]))
8624
+ }
8625
+ };
8626
+ }
8685
8627
  return {
8686
8628
  ...manifest,
8687
8629
  files: manifest.files.map(normalizeFile),
8688
- mcpServers: manifest.mcpServers ? Object.fromEntries(Object.entries(manifest.mcpServers).map(([name, server]) => [
8689
- name,
8690
- normalizeMcpServer(server)
8691
- ])) : undefined
8630
+ opencode: normalizedOpencode
8692
8631
  };
8693
8632
  }
8694
8633
  var semverRegex = /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/;
@@ -8732,6 +8671,97 @@ var registryIndexSchema = exports_external.object({
8732
8671
  }))
8733
8672
  });
8734
8673
 
8674
+ // ../schemas/dist/config.js
8675
+ var registryConfigSchema = exports_external.object({
8676
+ url: exports_external.string().url("Registry URL must be a valid URL"),
8677
+ version: exports_external.string().optional(),
8678
+ headers: exports_external.record(exports_external.string()).optional()
8679
+ });
8680
+ var ocxConfigSchema = exports_external.object({
8681
+ $schema: exports_external.string().optional(),
8682
+ registries: exports_external.record(registryConfigSchema).default({}),
8683
+ lockRegistries: exports_external.boolean().default(false)
8684
+ });
8685
+ var installedComponentSchema = exports_external.object({
8686
+ registry: exports_external.string(),
8687
+ version: exports_external.string(),
8688
+ hash: exports_external.string(),
8689
+ files: exports_external.array(exports_external.string()),
8690
+ installedAt: exports_external.string()
8691
+ });
8692
+ var ocxLockSchema = exports_external.object({
8693
+ lockVersion: exports_external.literal(1),
8694
+ installed: exports_external.record(qualifiedComponentSchema, installedComponentSchema).default({})
8695
+ });
8696
+ // src/utils/errors.ts
8697
+ var EXIT_CODES = {
8698
+ SUCCESS: 0,
8699
+ GENERAL: 1,
8700
+ NOT_FOUND: 66,
8701
+ NETWORK: 69,
8702
+ CONFIG: 78,
8703
+ INTEGRITY: 1
8704
+ };
8705
+
8706
+ class OCXError extends Error {
8707
+ code;
8708
+ exitCode;
8709
+ constructor(message, code, exitCode = EXIT_CODES.GENERAL) {
8710
+ super(message);
8711
+ this.code = code;
8712
+ this.exitCode = exitCode;
8713
+ this.name = "OCXError";
8714
+ }
8715
+ }
8716
+
8717
+ class NotFoundError extends OCXError {
8718
+ constructor(message) {
8719
+ super(message, "NOT_FOUND", EXIT_CODES.NOT_FOUND);
8720
+ this.name = "NotFoundError";
8721
+ }
8722
+ }
8723
+
8724
+ class NetworkError extends OCXError {
8725
+ constructor(message) {
8726
+ super(message, "NETWORK_ERROR", EXIT_CODES.NETWORK);
8727
+ this.name = "NetworkError";
8728
+ }
8729
+ }
8730
+
8731
+ class ConfigError extends OCXError {
8732
+ constructor(message) {
8733
+ super(message, "CONFIG_ERROR", EXIT_CODES.CONFIG);
8734
+ this.name = "ConfigError";
8735
+ }
8736
+ }
8737
+
8738
+ class ValidationError extends OCXError {
8739
+ constructor(message) {
8740
+ super(message, "VALIDATION_ERROR", EXIT_CODES.GENERAL);
8741
+ this.name = "ValidationError";
8742
+ }
8743
+ }
8744
+
8745
+ class ConflictError extends OCXError {
8746
+ constructor(message) {
8747
+ super(message, "CONFLICT", EXIT_CODES.GENERAL);
8748
+ this.name = "ConflictError";
8749
+ }
8750
+ }
8751
+
8752
+ class IntegrityError extends OCXError {
8753
+ constructor(component, expected, found) {
8754
+ const message = `Integrity verification failed for "${component}"
8755
+ ` + ` Expected: ${expected}
8756
+ ` + ` Found: ${found}
8757
+
8758
+ ` + `The registry content has changed since this component was locked.
8759
+ ` + `This could indicate tampering or an unauthorized update.`;
8760
+ super(message, "INTEGRITY_ERROR", EXIT_CODES.INTEGRITY);
8761
+ this.name = "IntegrityError";
8762
+ }
8763
+ }
8764
+
8735
8765
  // src/registry/fetcher.ts
8736
8766
  var cache = new Map;
8737
8767
  async function fetchWithCache(url, parse) {
@@ -8835,6 +8865,18 @@ function r(e3, n2) {
8835
8865
  return i;
8836
8866
  }
8837
8867
 
8868
+ // src/registry/merge.ts
8869
+ function mergeOpencodeConfig(target, source) {
8870
+ const merged = n(target, source);
8871
+ if (target.plugin && source.plugin) {
8872
+ merged.plugin = Array.from(new Set([...target.plugin, ...source.plugin]));
8873
+ }
8874
+ if (target.instructions && source.instructions) {
8875
+ merged.instructions = Array.from(new Set([...target.instructions, ...source.instructions]));
8876
+ }
8877
+ return merged;
8878
+ }
8879
+
8838
8880
  // src/registry/resolver.ts
8839
8881
  function parseComponentRef(ref, defaultNamespace) {
8840
8882
  if (ref.includes("/")) {
@@ -8848,14 +8890,9 @@ function parseComponentRef(ref, defaultNamespace) {
8848
8890
  async function resolveDependencies(registries, componentNames) {
8849
8891
  const resolved = new Map;
8850
8892
  const visiting = new Set;
8851
- const mcpServers = {};
8852
- const agentMcpBindings = [];
8853
8893
  const npmDeps = new Set;
8854
8894
  const npmDevDeps = new Set;
8855
- const disabledTools = new Set;
8856
- const plugins = new Set;
8857
- const agentConfigs = {};
8858
- const instructionsSet = new Set;
8895
+ let opencode = {};
8859
8896
  async function resolve(componentNamespace, componentName, path = []) {
8860
8897
  const qualifiedName = createQualifiedComponent(componentNamespace, componentName);
8861
8898
  if (resolved.has(qualifiedName)) {
@@ -8889,20 +8926,6 @@ async function resolveDependencies(registries, componentNames) {
8889
8926
  qualifiedName
8890
8927
  });
8891
8928
  visiting.delete(qualifiedName);
8892
- if (normalizedComponent.mcpServers) {
8893
- const serverNames = [];
8894
- for (const [serverName, config] of Object.entries(normalizedComponent.mcpServers)) {
8895
- mcpServers[serverName] = config;
8896
- serverNames.push(serverName);
8897
- }
8898
- const scope = component.mcpScope ?? "agent";
8899
- if (component.type === "ocx:agent" && scope === "agent" && serverNames.length > 0) {
8900
- agentMcpBindings.push({
8901
- agentName: component.name,
8902
- serverNames
8903
- });
8904
- }
8905
- }
8906
8929
  if (component.npmDependencies) {
8907
8930
  for (const dep of component.npmDependencies) {
8908
8931
  npmDeps.add(dep);
@@ -8913,34 +8936,8 @@ async function resolveDependencies(registries, componentNames) {
8913
8936
  npmDevDeps.add(dep);
8914
8937
  }
8915
8938
  }
8916
- if (component.disabledTools) {
8917
- for (const tool of component.disabledTools) {
8918
- disabledTools.add(tool);
8919
- }
8920
- }
8921
- if (component.opencode) {
8922
- if (component.opencode.plugins) {
8923
- for (const plugin of component.opencode.plugins) {
8924
- plugins.add(plugin);
8925
- }
8926
- }
8927
- if (component.opencode.agent) {
8928
- for (const [agentName, config] of Object.entries(component.opencode.agent)) {
8929
- agentConfigs[agentName] = n(agentConfigs[agentName] ?? {}, config);
8930
- }
8931
- }
8932
- if (component.opencode.instructions) {
8933
- for (const instruction of component.opencode.instructions) {
8934
- instructionsSet.add(instruction);
8935
- }
8936
- }
8937
- if (component.opencode.tools) {
8938
- for (const [tool, enabled] of Object.entries(component.opencode.tools)) {
8939
- if (enabled === false) {
8940
- disabledTools.add(tool);
8941
- }
8942
- }
8943
- }
8939
+ if (normalizedComponent.opencode) {
8940
+ opencode = mergeOpencodeConfig(opencode, normalizedComponent.opencode);
8944
8941
  }
8945
8942
  }
8946
8943
  for (const name of componentNames) {
@@ -8952,14 +8949,9 @@ async function resolveDependencies(registries, componentNames) {
8952
8949
  return {
8953
8950
  components,
8954
8951
  installOrder,
8955
- mcpServers,
8956
- agentMcpBindings,
8957
8952
  npmDependencies: Array.from(npmDeps),
8958
8953
  npmDevDependencies: Array.from(npmDevDeps),
8959
- disabledTools: Array.from(disabledTools),
8960
- plugins: Array.from(plugins),
8961
- agentConfigs,
8962
- instructions: Array.from(instructionsSet)
8954
+ opencode
8963
8955
  };
8964
8956
  }
8965
8957
 
@@ -10287,44 +10279,6 @@ function applyEdits(text, edits) {
10287
10279
  }
10288
10280
 
10289
10281
  // src/schemas/config.ts
10290
- var registryConfigSchema = exports_external.object({
10291
- url: exports_external.string().url("Registry URL must be a valid URL"),
10292
- version: exports_external.string().optional(),
10293
- headers: exports_external.record(exports_external.string()).optional()
10294
- });
10295
- var ocxConfigSchema = exports_external.object({
10296
- $schema: exports_external.string().optional(),
10297
- registries: exports_external.record(registryConfigSchema).default({}),
10298
- lockRegistries: exports_external.boolean().default(false)
10299
- });
10300
- var installedComponentSchema = exports_external.object({
10301
- registry: exports_external.string(),
10302
- version: exports_external.string(),
10303
- hash: exports_external.string(),
10304
- files: exports_external.array(exports_external.string()),
10305
- installedAt: exports_external.string(),
10306
- alias: exports_external.string().optional()
10307
- });
10308
- var ocxLockSchema = exports_external.object({
10309
- lockVersion: exports_external.literal(1),
10310
- installed: exports_external.record(qualifiedComponentSchema, installedComponentSchema).default({})
10311
- });
10312
- var opencodeMcpSchema = exports_external.record(mcpServerRefSchema);
10313
- var opencodeAgentSchema = exports_external.object({
10314
- disable: exports_external.boolean().optional(),
10315
- tools: exports_external.record(exports_external.boolean()).optional(),
10316
- temperature: exports_external.number().min(0).max(2).optional(),
10317
- prompt: exports_external.string().optional(),
10318
- permission: exports_external.record(exports_external.enum(["allow", "deny"])).optional()
10319
- });
10320
- var opencodeConfigPatchSchema = exports_external.object({
10321
- default_agent: exports_external.string().optional(),
10322
- mcp: opencodeMcpSchema.optional(),
10323
- tools: exports_external.record(exports_external.boolean()).optional(),
10324
- agent: exports_external.record(opencodeAgentSchema).optional(),
10325
- plugin: exports_external.array(exports_external.string()).optional(),
10326
- instructions: exports_external.array(exports_external.string()).optional()
10327
- });
10328
10282
  var CONFIG_FILE = "ocx.jsonc";
10329
10283
  var LOCK_FILE = "ocx.lock";
10330
10284
  async function readOcxConfig(cwd) {
@@ -10342,9 +10296,9 @@ async function readOcxConfig(cwd) {
10342
10296
  throw error;
10343
10297
  }
10344
10298
  }
10345
- async function writeOcxConfig(cwd, config) {
10299
+ async function writeOcxConfig(cwd, config2) {
10346
10300
  const configPath = `${cwd}/${CONFIG_FILE}`;
10347
- const content = JSON.stringify(config, null, 2);
10301
+ const content = JSON.stringify(config2, null, 2);
10348
10302
  await Bun.write(configPath, content);
10349
10303
  }
10350
10304
  async function readOcxLock(cwd) {
@@ -10367,7 +10321,7 @@ var JSONC_OPTIONS = {
10367
10321
  `
10368
10322
  }
10369
10323
  };
10370
- async function readOpencodeConfig(cwd) {
10324
+ async function readOpencodeJsonConfig(cwd) {
10371
10325
  const jsonPath = `${cwd}/opencode.json`;
10372
10326
  const jsoncPath = `${cwd}/opencode.jsonc`;
10373
10327
  for (const configPath of [jsoncPath, jsonPath]) {
@@ -10383,224 +10337,62 @@ async function readOpencodeConfig(cwd) {
10383
10337
  }
10384
10338
  return null;
10385
10339
  }
10386
- async function writeOpencodeConfig(path, content) {
10340
+ async function writeOpencodeJsonConfig(path, content) {
10387
10341
  await Bun.write(path, content);
10388
10342
  }
10389
- function applyMcpServers(content, config, mcpServers) {
10390
- const added = [];
10391
- const skipped = [];
10392
- let updatedContent = content;
10393
- const existingMcp = config.mcp ?? {};
10394
- for (const [name, server] of Object.entries(mcpServers)) {
10395
- if (existingMcp[name]) {
10396
- skipped.push(name);
10397
- continue;
10398
- }
10399
- const serverConfig = {
10400
- type: server.type
10401
- };
10402
- if (server.type === "remote" && server.url) {
10403
- serverConfig.url = server.url;
10404
- }
10405
- if (server.type === "local" && server.command) {
10406
- serverConfig.command = server.command;
10407
- }
10408
- if (server.args) {
10409
- serverConfig.args = server.args;
10410
- }
10411
- if (server.headers) {
10412
- serverConfig.headers = server.headers;
10413
- }
10414
- if (server.oauth !== undefined) {
10415
- serverConfig.oauth = server.oauth;
10416
- }
10417
- if (server.enabled !== undefined) {
10418
- serverConfig.enabled = server.enabled;
10419
- }
10420
- if (server.environment) {
10421
- serverConfig.environment = server.environment;
10422
- }
10423
- const edits = modify(updatedContent, ["mcp", name], serverConfig, JSONC_OPTIONS);
10424
- updatedContent = applyEdits(updatedContent, edits);
10425
- added.push(name);
10426
- }
10427
- return { content: updatedContent, added, skipped };
10428
- }
10429
- function applyGlobalToolDisables(content, serverNames) {
10430
- let updatedContent = content;
10431
- for (const name of serverNames) {
10432
- const toolPattern = `${name}_*`;
10433
- const edits = modify(updatedContent, ["tools", toolPattern], false, JSONC_OPTIONS);
10434
- updatedContent = applyEdits(updatedContent, edits);
10435
- }
10436
- return updatedContent;
10437
- }
10438
- function applyDisabledTools(content, toolNames) {
10439
- let updatedContent = content;
10440
- for (const name of toolNames) {
10441
- const edits = modify(updatedContent, ["tools", name], false, JSONC_OPTIONS);
10442
- updatedContent = applyEdits(updatedContent, edits);
10443
- }
10444
- return updatedContent;
10445
- }
10446
- function applyAgentToolEnables(content, bindings) {
10447
- let updatedContent = content;
10448
- const agentsConfigured = [];
10449
- for (const binding of bindings) {
10450
- for (const serverName of binding.serverNames) {
10451
- const toolPattern = `${serverName}_*`;
10452
- const edits = modify(updatedContent, ["agent", binding.agentName, "tools", toolPattern], true, JSONC_OPTIONS);
10453
- updatedContent = applyEdits(updatedContent, edits);
10454
- }
10455
- if (binding.serverNames.length > 0) {
10456
- agentsConfigured.push(binding.agentName);
10457
- }
10458
- }
10459
- return { content: updatedContent, agentsConfigured: [...new Set(agentsConfigured)] };
10460
- }
10461
- function applyDefaultAgent(content, config, defaultAgent) {
10462
- if (config.default_agent) {
10343
+ function applyValueAtPath(content, path, value) {
10344
+ if (value === null || value === undefined) {
10463
10345
  return content;
10464
10346
  }
10465
- const edits = modify(content, ["default_agent"], defaultAgent, JSONC_OPTIONS);
10466
- return applyEdits(content, edits);
10467
- }
10468
- function applyPlugins(content, config, plugins) {
10469
- const added = [];
10470
- let updatedContent = content;
10471
- const existingPlugins = config.plugin ?? [];
10472
- for (const plugin of plugins) {
10473
- if (existingPlugins.includes(plugin)) {
10474
- continue;
10347
+ if (typeof value === "object" && !Array.isArray(value)) {
10348
+ let updatedContent = content;
10349
+ for (const [key, val] of Object.entries(value)) {
10350
+ updatedContent = applyValueAtPath(updatedContent, [...path, key], val);
10475
10351
  }
10476
- const newIndex = existingPlugins.length + added.length;
10477
- const edits = modify(updatedContent, ["plugin", newIndex], plugin, JSONC_OPTIONS);
10478
- updatedContent = applyEdits(updatedContent, edits);
10479
- added.push(plugin);
10352
+ return updatedContent;
10480
10353
  }
10481
- return { content: updatedContent, added };
10482
- }
10483
- function applyAgentConfigs(content, existingConfig, agentConfigs) {
10484
- let updatedContent = content;
10485
- const agentsConfigured = [];
10486
- for (const [agentName, componentAgentConfig] of Object.entries(agentConfigs)) {
10487
- const existingAgentConfig = existingConfig.agent?.[agentName] ?? {};
10488
- const merged = n(componentAgentConfig, existingAgentConfig);
10489
- if (merged.tools) {
10490
- for (const [toolPattern, enabled] of Object.entries(merged.tools)) {
10491
- const edits = modify(updatedContent, ["agent", agentName, "tools", toolPattern], enabled, JSONC_OPTIONS);
10492
- updatedContent = applyEdits(updatedContent, edits);
10493
- }
10494
- }
10495
- if (merged.temperature !== undefined) {
10496
- const edits = modify(updatedContent, ["agent", agentName, "temperature"], merged.temperature, JSONC_OPTIONS);
10497
- updatedContent = applyEdits(updatedContent, edits);
10498
- }
10499
- if (merged.prompt) {
10500
- const edits = modify(updatedContent, ["agent", agentName, "prompt"], merged.prompt, JSONC_OPTIONS);
10501
- updatedContent = applyEdits(updatedContent, edits);
10502
- }
10503
- if (merged.permission) {
10504
- for (const [pattern, permission] of Object.entries(merged.permission)) {
10505
- const edits = modify(updatedContent, ["agent", agentName, "permission", pattern], permission, JSONC_OPTIONS);
10506
- updatedContent = applyEdits(updatedContent, edits);
10507
- }
10508
- }
10509
- agentsConfigured.push(agentName);
10510
- }
10511
- return { content: updatedContent, agentsConfigured };
10512
- }
10513
- function applyInstructions(content, config, instructions) {
10514
- const added = [];
10515
- let updatedContent = content;
10516
- const existingInstructions = config.instructions ?? [];
10517
- for (const instruction of instructions) {
10518
- if (existingInstructions.includes(instruction)) {
10519
- continue;
10520
- }
10521
- const newIndex = existingInstructions.length + added.length;
10522
- const edits = modify(updatedContent, ["instructions", newIndex], instruction, JSONC_OPTIONS);
10523
- updatedContent = applyEdits(updatedContent, edits);
10524
- added.push(instruction);
10354
+ if (Array.isArray(value)) {
10355
+ const edits2 = modify(content, path, value, JSONC_OPTIONS);
10356
+ return applyEdits(content, edits2);
10525
10357
  }
10526
- return { content: updatedContent, added };
10358
+ const edits = modify(content, path, value, JSONC_OPTIONS);
10359
+ return applyEdits(content, edits);
10527
10360
  }
10528
- async function updateOpencodeConfig(cwd, options) {
10529
- const existing = await readOpencodeConfig(cwd);
10361
+ async function updateOpencodeJsonConfig(cwd, opencode) {
10362
+ const existing = await readOpencodeJsonConfig(cwd);
10530
10363
  let content;
10531
- let config;
10532
10364
  let configPath;
10533
10365
  let created = false;
10534
10366
  if (existing) {
10535
10367
  content = existing.content;
10536
- config = existing.config;
10537
10368
  configPath = existing.path;
10538
10369
  } else {
10539
- config = { $schema: "https://opencode.ai/config.json" };
10540
- content = JSON.stringify(config, null, "\t");
10541
- configPath = `${cwd}/opencode.json`;
10370
+ const config2 = { $schema: "https://opencode.ai/config.json" };
10371
+ content = JSON.stringify(config2, null, "\t");
10372
+ configPath = `${cwd}/opencode.jsonc`;
10542
10373
  created = true;
10543
10374
  }
10544
- let mcpAdded = [];
10545
- let mcpSkipped = [];
10546
- let agentsConfigured = [];
10547
- let toolsDisabled = [];
10548
- let pluginsAdded = [];
10549
- let instructionsAdded = [];
10550
- if (options.mcpServers && Object.keys(options.mcpServers).length > 0) {
10551
- const result = applyMcpServers(content, config, options.mcpServers);
10552
- content = result.content;
10553
- mcpAdded = result.added;
10554
- mcpSkipped = result.skipped;
10555
- }
10556
- if (options.agentMcpBindings && options.agentMcpBindings.length > 0) {
10557
- const allScopedServers = [...new Set(options.agentMcpBindings.flatMap((b) => b.serverNames))];
10558
- if (allScopedServers.length > 0) {
10559
- content = applyGlobalToolDisables(content, allScopedServers);
10560
- const agentResult = applyAgentToolEnables(content, options.agentMcpBindings);
10561
- content = agentResult.content;
10562
- agentsConfigured = agentResult.agentsConfigured;
10563
- }
10564
- }
10565
- if (options.defaultAgent) {
10566
- const updatedConfig = parse2(content, [], { allowTrailingComma: true });
10567
- content = applyDefaultAgent(content, updatedConfig, options.defaultAgent);
10568
- }
10569
- if (options.disabledTools && options.disabledTools.length > 0) {
10570
- content = applyDisabledTools(content, options.disabledTools);
10571
- toolsDisabled = options.disabledTools;
10572
- }
10573
- if (options.plugins && options.plugins.length > 0) {
10574
- const updatedConfig = parse2(content, [], { allowTrailingComma: true });
10575
- const result = applyPlugins(content, updatedConfig, options.plugins);
10576
- content = result.content;
10577
- pluginsAdded = result.added;
10578
- }
10579
- if (options.agentConfigs && Object.keys(options.agentConfigs).length > 0) {
10580
- const updatedConfig = parse2(content, [], { allowTrailingComma: true });
10581
- const result = applyAgentConfigs(content, updatedConfig, options.agentConfigs);
10582
- content = result.content;
10583
- agentsConfigured = [...new Set([...agentsConfigured, ...result.agentsConfigured])];
10584
- }
10585
- if (options.instructions && options.instructions.length > 0) {
10586
- const updatedConfig = parse2(content, [], { allowTrailingComma: true });
10587
- const result = applyInstructions(content, updatedConfig, options.instructions);
10588
- content = result.content;
10589
- instructionsAdded = result.added;
10590
- }
10591
- await writeOpencodeConfig(configPath, content);
10375
+ const originalContent = content;
10376
+ content = applyValueAtPath(content, [], opencode);
10377
+ const changed = content !== originalContent;
10378
+ if (changed) {
10379
+ await writeOpencodeJsonConfig(configPath, content);
10380
+ }
10592
10381
  return {
10593
10382
  path: configPath,
10594
10383
  created,
10595
- mcpAdded,
10596
- mcpSkipped,
10597
- agentsConfigured,
10598
- toolsDisabled,
10599
- pluginsAdded,
10600
- instructionsAdded
10384
+ changed
10601
10385
  };
10602
10386
  }
10603
10387
 
10388
+ // src/utils/content.ts
10389
+ function normalizeContent(content) {
10390
+ return content.replace(/\r\n/g, `
10391
+ `).trim();
10392
+ }
10393
+ function isContentIdentical(existing, incoming) {
10394
+ return normalizeContent(existing) === normalizeContent(incoming);
10395
+ }
10604
10396
  // src/utils/env.ts
10605
10397
  var isCI = Boolean(process.env.CI || process.env.GITHUB_ACTIONS || process.env.GITLAB_CI || process.env.CIRCLECI || process.env.JENKINS_URL || process.env.BUILDKITE);
10606
10398
  var isTTY = Boolean(process.stdout.isTTY && !isCI);
@@ -12140,8 +11932,8 @@ function registerAddCommand(program2) {
12140
11932
  async function runAdd(componentNames, options2) {
12141
11933
  const cwd = options2.cwd ?? process.cwd();
12142
11934
  const lockPath = join(cwd, "ocx.lock");
12143
- const config = await readOcxConfig(cwd);
12144
- if (!config) {
11935
+ const config2 = await readOcxConfig(cwd);
11936
+ if (!config2) {
12145
11937
  throw new ConfigError("No ocx.jsonc found. Run 'ocx init' first.");
12146
11938
  }
12147
11939
  let lock = { lockVersion: 1, installed: {} };
@@ -12152,7 +11944,7 @@ async function runAdd(componentNames, options2) {
12152
11944
  const spin = options2.quiet ? null : createSpinner({ text: "Resolving dependencies..." });
12153
11945
  spin?.start();
12154
11946
  try {
12155
- const resolved = await resolveDependencies(config.registries, componentNames);
11947
+ const resolved = await resolveDependencies(config2.registries, componentNames);
12156
11948
  spin?.succeed(`Resolved ${resolved.components.length} components`);
12157
11949
  if (options2.verbose) {
12158
11950
  logger.info("Install order:");
@@ -12166,17 +11958,19 @@ async function runAdd(componentNames, options2) {
12166
11958
  logResolved(resolved);
12167
11959
  return;
12168
11960
  }
12169
- const installSpin = options2.quiet ? null : createSpinner({ text: "Installing components..." });
12170
- installSpin?.start();
11961
+ const fetchSpin = options2.quiet ? null : createSpinner({ text: "Fetching components..." });
11962
+ fetchSpin?.start();
11963
+ const componentBundles = [];
12171
11964
  for (const component of resolved.components) {
12172
11965
  const files = [];
12173
11966
  for (const file of component.files) {
12174
- const content = await fetchFileContent(component.baseUrl, component.name, file.path);
12175
- files.push({ path: file.path, content: Buffer.from(content) });
11967
+ const content2 = await fetchFileContent(component.baseUrl, component.name, file.path);
11968
+ files.push({ path: file.path, content: Buffer.from(content2) });
12176
11969
  }
12177
11970
  const computedHash = await hashBundle(files);
12178
11971
  const existingEntry = lock.installed[component.qualifiedName];
12179
11972
  if (existingEntry && existingEntry.hash !== computedHash) {
11973
+ fetchSpin?.fail("Integrity check failed");
12180
11974
  throw new IntegrityError(component.qualifiedName, existingEntry.hash, computedHash);
12181
11975
  }
12182
11976
  for (const file of component.files) {
@@ -12184,6 +11978,7 @@ async function runAdd(componentNames, options2) {
12184
11978
  if (existsSync(targetPath)) {
12185
11979
  const conflictingComponent = findComponentByFile(lock, file.target);
12186
11980
  if (conflictingComponent && conflictingComponent !== component.qualifiedName) {
11981
+ fetchSpin?.fail("File conflict detected");
12187
11982
  throw new ConflictError(`File conflict: ${file.target} already exists (installed by '${conflictingComponent}').
12188
11983
 
12189
11984
  ` + `To resolve:
@@ -12193,7 +11988,54 @@ async function runAdd(componentNames, options2) {
12193
11988
  }
12194
11989
  }
12195
11990
  }
12196
- await installComponent(component, files, cwd);
11991
+ componentBundles.push({ component, files, computedHash });
11992
+ }
11993
+ fetchSpin?.succeed(`Fetched ${resolved.components.length} components`);
11994
+ const allConflicts = [];
11995
+ for (const { component, files } of componentBundles) {
11996
+ for (const file of files) {
11997
+ const componentFile = component.files.find((f) => f.path === file.path);
11998
+ if (!componentFile)
11999
+ continue;
12000
+ const targetPath = join(cwd, componentFile.target);
12001
+ if (existsSync(targetPath)) {
12002
+ const existingContent = await Bun.file(targetPath).text();
12003
+ const incomingContent = file.content.toString("utf-8");
12004
+ if (!isContentIdentical(existingContent, incomingContent) && !options2.yes) {
12005
+ allConflicts.push(componentFile.target);
12006
+ }
12007
+ }
12008
+ }
12009
+ }
12010
+ if (allConflicts.length > 0) {
12011
+ logger.error("");
12012
+ logger.error("File conflicts detected:");
12013
+ for (const conflict of allConflicts) {
12014
+ logger.error(` \u2717 ${conflict}`);
12015
+ }
12016
+ logger.error("");
12017
+ logger.error("These files have been modified since installation.");
12018
+ logger.error("Use --yes to overwrite, or review the changes first.");
12019
+ throw new ConflictError(`${allConflicts.length} file(s) have conflicts. Use --yes to overwrite.`);
12020
+ }
12021
+ const installSpin = options2.quiet ? null : createSpinner({ text: "Installing components..." });
12022
+ installSpin?.start();
12023
+ for (const { component, files, computedHash } of componentBundles) {
12024
+ const installResult = await installComponent(component, files, cwd, {
12025
+ yes: options2.yes,
12026
+ verbose: options2.verbose
12027
+ });
12028
+ if (options2.verbose) {
12029
+ for (const f of installResult.skipped) {
12030
+ logger.info(` \u25CB Skipped ${f} (unchanged)`);
12031
+ }
12032
+ for (const f of installResult.overwritten) {
12033
+ logger.info(` \u2713 Overwrote ${f}`);
12034
+ }
12035
+ for (const f of installResult.written) {
12036
+ logger.info(` \u2713 Wrote ${f}`);
12037
+ }
12038
+ }
12197
12039
  const index = await fetchRegistryIndex(component.baseUrl);
12198
12040
  lock.installed[component.qualifiedName] = {
12199
12041
  registry: component.registryName,
@@ -12204,43 +12046,15 @@ async function runAdd(componentNames, options2) {
12204
12046
  };
12205
12047
  }
12206
12048
  installSpin?.succeed(`Installed ${resolved.components.length} components`);
12207
- const hasMcpChanges = Object.keys(resolved.mcpServers).length > 0 || resolved.agentMcpBindings.length > 0;
12208
- const hasDisabledTools = resolved.disabledTools.length > 0;
12209
- const hasPlugins = resolved.plugins.length > 0;
12210
- const hasAgentConfigs = Object.keys(resolved.agentConfigs).length > 0;
12211
- const hasInstructions = resolved.instructions.length > 0;
12212
- if (hasMcpChanges || hasDisabledTools || hasPlugins || hasAgentConfigs || hasInstructions) {
12213
- const result = await updateOpencodeConfig(cwd, {
12214
- mcpServers: resolved.mcpServers,
12215
- agentMcpBindings: resolved.agentMcpBindings,
12216
- disabledTools: resolved.disabledTools,
12217
- plugins: resolved.plugins,
12218
- agentConfigs: resolved.agentConfigs,
12219
- instructions: resolved.instructions
12220
- });
12221
- if (result.mcpSkipped.length > 0 && !options2.quiet) {
12222
- for (const name of result.mcpSkipped) {
12223
- logger.warn(`MCP server "${name}" already configured, skipped`);
12224
- }
12225
- }
12226
- if (!options2.quiet && result.mcpAdded.length > 0) {
12227
- logger.info(`Configured ${result.mcpAdded.length} MCP servers`);
12228
- for (const binding of resolved.agentMcpBindings) {
12229
- logger.info(` Scoped to agent "${binding.agentName}": ${binding.serverNames.join(", ")}`);
12049
+ if (resolved.opencode && Object.keys(resolved.opencode).length > 0) {
12050
+ const result = await updateOpencodeJsonConfig(cwd, resolved.opencode);
12051
+ if (!options2.quiet && result.changed) {
12052
+ if (result.created) {
12053
+ logger.info("Created opencode.jsonc");
12054
+ } else {
12055
+ logger.info("Updated opencode.jsonc");
12230
12056
  }
12231
12057
  }
12232
- if (!options2.quiet && result.toolsDisabled.length > 0) {
12233
- logger.info(`Disabled ${result.toolsDisabled.length} tools: ${result.toolsDisabled.join(", ")}`);
12234
- }
12235
- if (!options2.quiet && result.pluginsAdded.length > 0) {
12236
- logger.info(`Added ${result.pluginsAdded.length} plugins: ${result.pluginsAdded.join(", ")}`);
12237
- }
12238
- if (!options2.quiet && result.agentsConfigured.length > 0) {
12239
- logger.info(`Configured ${result.agentsConfigured.length} agents: ${result.agentsConfigured.join(", ")}`);
12240
- }
12241
- if (!options2.quiet && result.instructionsAdded.length > 0) {
12242
- logger.info(`Added ${result.instructionsAdded.length} instructions`);
12243
- }
12244
12058
  }
12245
12059
  const hasNpmDeps = resolved.npmDependencies.length > 0;
12246
12060
  const hasNpmDevDeps = resolved.npmDevDependencies.length > 0;
@@ -12261,32 +12075,51 @@ async function runAdd(componentNames, options2) {
12261
12075
  console.log(JSON.stringify({
12262
12076
  success: true,
12263
12077
  installed: resolved.installOrder,
12264
- mcpServers: Object.keys(resolved.mcpServers)
12078
+ opencode: !!resolved.opencode
12265
12079
  }, null, 2));
12266
12080
  } else if (!options2.quiet) {
12267
12081
  logger.info("");
12268
12082
  logger.success(`Done! Installed ${resolved.components.length} components.`);
12269
12083
  }
12270
12084
  } catch (error) {
12271
- spin?.fail("Failed to resolve dependencies");
12085
+ if (spin && !spin.isSpinning) {} else {
12086
+ spin?.fail("Failed to add components");
12087
+ }
12272
12088
  throw error;
12273
12089
  }
12274
12090
  }
12275
- async function installComponent(component, files, cwd) {
12091
+ async function installComponent(component, files, cwd, _options) {
12092
+ const result = {
12093
+ written: [],
12094
+ skipped: [],
12095
+ overwritten: []
12096
+ };
12276
12097
  for (const file of files) {
12277
12098
  const componentFile = component.files.find((f) => f.path === file.path);
12278
12099
  if (!componentFile)
12279
12100
  continue;
12280
12101
  const targetPath = join(cwd, componentFile.target);
12281
12102
  const targetDir = dirname(targetPath);
12103
+ if (existsSync(targetPath)) {
12104
+ const existingContent = await Bun.file(targetPath).text();
12105
+ const incomingContent = file.content.toString("utf-8");
12106
+ if (isContentIdentical(existingContent, incomingContent)) {
12107
+ result.skipped.push(componentFile.target);
12108
+ continue;
12109
+ }
12110
+ result.overwritten.push(componentFile.target);
12111
+ } else {
12112
+ result.written.push(componentFile.target);
12113
+ }
12282
12114
  if (!existsSync(targetDir)) {
12283
12115
  await mkdir(targetDir, { recursive: true });
12284
12116
  }
12285
12117
  await writeFile(targetPath, file.content);
12286
12118
  }
12119
+ return result;
12287
12120
  }
12288
- async function hashContent(content) {
12289
- return createHash("sha256").update(content).digest("hex");
12121
+ async function hashContent(content2) {
12122
+ return createHash("sha256").update(content2).digest("hex");
12290
12123
  }
12291
12124
  async function hashBundle(files) {
12292
12125
  const sorted = [...files].sort((a, b) => a.path.localeCompare(b.path));
@@ -12304,11 +12137,11 @@ function logResolved(resolved) {
12304
12137
  for (const component of resolved.components) {
12305
12138
  logger.info(` ${component.name} (${component.type}) from ${component.registryName}`);
12306
12139
  }
12307
- if (Object.keys(resolved.mcpServers).length > 0) {
12140
+ if (resolved.opencode && Object.keys(resolved.opencode).length > 0) {
12308
12141
  logger.info("");
12309
- logger.info("Would configure MCP servers:");
12310
- for (const name of Object.keys(resolved.mcpServers)) {
12311
- logger.info(` ${name}`);
12142
+ logger.info("Would update opencode.json with:");
12143
+ for (const key of Object.keys(resolved.opencode)) {
12144
+ logger.info(` ${key}`);
12312
12145
  }
12313
12146
  }
12314
12147
  if (resolved.npmDependencies.length > 0) {
@@ -12360,8 +12193,8 @@ async function readOpencodePackageJson(opencodeDir) {
12360
12193
  return { ...DEFAULT_PACKAGE_JSON };
12361
12194
  }
12362
12195
  try {
12363
- const content = await Bun.file(pkgPath).text();
12364
- return JSON.parse(content);
12196
+ const content2 = await Bun.file(pkgPath).text();
12197
+ return JSON.parse(content2);
12365
12198
  } catch (e3) {
12366
12199
  const message = e3 instanceof Error ? e3.message : String(e3);
12367
12200
  throw new ConfigError(`Invalid .opencode/package.json: ${message}`);
@@ -12373,8 +12206,8 @@ async function ensureManifestFilesAreTracked(opencodeDir) {
12373
12206
  const requiredIgnores = ["node_modules"];
12374
12207
  let lines = [];
12375
12208
  if (existsSync(gitignorePath)) {
12376
- const content = await Bun.file(gitignorePath).text();
12377
- lines = content.split(`
12209
+ const content2 = await Bun.file(gitignorePath).text();
12210
+ lines = content2.split(`
12378
12211
  `).map((l) => l.trim()).filter(Boolean);
12379
12212
  }
12380
12213
  lines = lines.filter((line) => !filesToTrack.has(line));
@@ -12441,18 +12274,18 @@ function registerBuildCommand(program2) {
12441
12274
  }
12442
12275
  process.exit(1);
12443
12276
  }
12444
- const registry = parseResult.data;
12277
+ const registry2 = parseResult.data;
12445
12278
  const validationErrors = [];
12446
12279
  const componentsDir = join2(outPath, "components");
12447
12280
  await mkdir2(componentsDir, { recursive: true });
12448
- for (const component of registry.components) {
12281
+ for (const component of registry2.components) {
12449
12282
  const packument = {
12450
12283
  name: component.name,
12451
12284
  versions: {
12452
- [registry.version]: component
12285
+ [registry2.version]: component
12453
12286
  },
12454
12287
  "dist-tags": {
12455
- latest: registry.version
12288
+ latest: registry2.version
12456
12289
  }
12457
12290
  };
12458
12291
  const packumentPath = join2(componentsDir, `${component.name}.json`);
@@ -12481,31 +12314,35 @@ function registerBuildCommand(program2) {
12481
12314
  process.exit(1);
12482
12315
  }
12483
12316
  const index = {
12484
- name: registry.name,
12485
- namespace: registry.namespace,
12486
- version: registry.version,
12487
- author: registry.author,
12488
- components: registry.components.map((c) => ({
12317
+ name: registry2.name,
12318
+ namespace: registry2.namespace,
12319
+ version: registry2.version,
12320
+ author: registry2.author,
12321
+ components: registry2.components.map((c) => ({
12489
12322
  name: c.name,
12490
12323
  type: c.type,
12491
12324
  description: c.description
12492
12325
  }))
12493
12326
  };
12494
12327
  await Bun.write(join2(outPath, "index.json"), JSON.stringify(index, null, 2));
12328
+ const wellKnownDir = join2(outPath, ".well-known");
12329
+ await mkdir2(wellKnownDir, { recursive: true });
12330
+ const discovery = { registry: "/index.json" };
12331
+ await Bun.write(join2(wellKnownDir, "ocx.json"), JSON.stringify(discovery, null, 2));
12495
12332
  if (!options2.json) {
12496
- const msg = `Built ${registry.components.length} components to ${relative(options2.cwd, outPath)}`;
12333
+ const msg = `Built ${registry2.components.length} components to ${relative(options2.cwd, outPath)}`;
12497
12334
  spinner2.succeed(msg);
12498
12335
  if (!process.stdout.isTTY) {
12499
- logger.success(`Built ${registry.components.length} components`);
12336
+ logger.success(`Built ${registry2.components.length} components`);
12500
12337
  }
12501
12338
  }
12502
12339
  if (options2.json) {
12503
12340
  outputJson({
12504
12341
  success: true,
12505
12342
  data: {
12506
- name: registry.name,
12507
- version: registry.version,
12508
- components: registry.components.length,
12343
+ name: registry2.name,
12344
+ version: registry2.version,
12345
+ components: registry2.components.length,
12509
12346
  output: outPath
12510
12347
  }
12511
12348
  });
@@ -13320,8 +13157,8 @@ function registerDiffCommand(program2) {
13320
13157
  }
13321
13158
  return;
13322
13159
  }
13323
- const config = await readOcxConfig(options2.cwd);
13324
- if (!config) {
13160
+ const config2 = await readOcxConfig(options2.cwd);
13161
+ if (!config2) {
13325
13162
  if (options2.json) {
13326
13163
  outputJson({
13327
13164
  success: false,
@@ -13361,7 +13198,7 @@ function registerDiffCommand(program2) {
13361
13198
  continue;
13362
13199
  }
13363
13200
  const localContent = await localFile.text();
13364
- const registryConfig = config.registries[installed.registry];
13201
+ const registryConfig = config2.registries[installed.registry];
13365
13202
  if (!registryConfig) {
13366
13203
  logger.warn(`Registry '${installed.registry}' not configured for component '${name}'.`);
13367
13204
  continue;
@@ -13413,12 +13250,7 @@ import { join as join3 } from "path";
13413
13250
 
13414
13251
  // src/constants.ts
13415
13252
  var OCX_DOMAIN = "ocx.kdco.dev";
13416
- var GITHUB_REPO = "kdcokenny/ocx";
13417
13253
  var OCX_SCHEMA_URL = `https://${OCX_DOMAIN}/schema.json`;
13418
- var OCX_LOCK_SCHEMA_URL = `https://${OCX_DOMAIN}/lock.schema.json`;
13419
- var OCX_INSTALL_URL = `https://${OCX_DOMAIN}/install.sh`;
13420
- var GITHUB_RELEASES_URL = `https://github.com/${GITHUB_REPO}/releases`;
13421
- var GITHUB_RAW_URL = `https://raw.githubusercontent.com/${GITHUB_REPO}/main`;
13422
13254
 
13423
13255
  // src/commands/init.ts
13424
13256
  function registerInitCommand(program2) {
@@ -13448,9 +13280,9 @@ async function runInit(options2) {
13448
13280
  $schema: OCX_SCHEMA_URL,
13449
13281
  registries: {}
13450
13282
  };
13451
- const config = ocxConfigSchema.parse(rawConfig);
13452
- const content = JSON.stringify(config, null, 2);
13453
- await writeFile2(configPath, content, "utf-8");
13283
+ const config2 = ocxConfigSchema.parse(rawConfig);
13284
+ const content2 = JSON.stringify(config2, null, 2);
13285
+ await writeFile2(configPath, content2, "utf-8");
13454
13286
  if (!options2.quiet && !options2.json) {
13455
13287
  logger.success("Initialized OCX configuration");
13456
13288
  }
@@ -13472,28 +13304,28 @@ async function runInit(options2) {
13472
13304
 
13473
13305
  // src/commands/registry.ts
13474
13306
  function registerRegistryCommand(program2) {
13475
- const registry = program2.command("registry").description("Manage registries");
13476
- registry.command("add").description("Add a registry").argument("<url>", "Registry URL").option("--name <name>", "Registry alias (defaults to hostname)").option("--version <version>", "Pin to specific version").option("--cwd <path>", "Working directory", process.cwd()).option("--json", "Output as JSON", false).option("-q, --quiet", "Suppress output", false).action(async (url, options2) => {
13307
+ const registry2 = program2.command("registry").description("Manage registries");
13308
+ registry2.command("add").description("Add a registry").argument("<url>", "Registry URL").option("--name <name>", "Registry alias (defaults to hostname)").option("--version <version>", "Pin to specific version").option("--cwd <path>", "Working directory", process.cwd()).option("--json", "Output as JSON", false).option("-q, --quiet", "Suppress output", false).action(async (url, options2) => {
13477
13309
  try {
13478
- const config = await readOcxConfig(options2.cwd);
13479
- if (!config) {
13310
+ const config2 = await readOcxConfig(options2.cwd);
13311
+ if (!config2) {
13480
13312
  logger.error("No ocx.jsonc found. Run 'ocx init' first.");
13481
13313
  process.exit(1);
13482
13314
  }
13483
- if (config.lockRegistries) {
13315
+ if (config2.lockRegistries) {
13484
13316
  logger.error("Registries are locked. Cannot add.");
13485
13317
  process.exit(1);
13486
13318
  }
13487
13319
  const name = options2.name || new URL(url).hostname.replace(/\./g, "-");
13488
- if (config.registries[name]) {
13320
+ if (config2.registries[name]) {
13489
13321
  logger.warn(`Registry '${name}' already exists. Use a different name.`);
13490
13322
  return;
13491
13323
  }
13492
- config.registries[name] = {
13324
+ config2.registries[name] = {
13493
13325
  url,
13494
13326
  version: options2.version
13495
13327
  };
13496
- await writeOcxConfig(options2.cwd, config);
13328
+ await writeOcxConfig(options2.cwd, config2);
13497
13329
  if (options2.json) {
13498
13330
  outputJson({ success: true, data: { name, url } });
13499
13331
  } else {
@@ -13503,23 +13335,23 @@ function registerRegistryCommand(program2) {
13503
13335
  handleError(error);
13504
13336
  }
13505
13337
  });
13506
- registry.command("remove").description("Remove a registry").argument("<name>", "Registry name").option("--cwd <path>", "Working directory", process.cwd()).option("--json", "Output as JSON", false).option("-q, --quiet", "Suppress output", false).action(async (name, options2) => {
13338
+ registry2.command("remove").description("Remove a registry").argument("<name>", "Registry name").option("--cwd <path>", "Working directory", process.cwd()).option("--json", "Output as JSON", false).option("-q, --quiet", "Suppress output", false).action(async (name, options2) => {
13507
13339
  try {
13508
- const config = await readOcxConfig(options2.cwd);
13509
- if (!config) {
13340
+ const config2 = await readOcxConfig(options2.cwd);
13341
+ if (!config2) {
13510
13342
  logger.error("No ocx.jsonc found. Run 'ocx init' first.");
13511
13343
  process.exit(1);
13512
13344
  }
13513
- if (config.lockRegistries) {
13345
+ if (config2.lockRegistries) {
13514
13346
  logger.error("Registries are locked. Cannot remove.");
13515
13347
  process.exit(1);
13516
13348
  }
13517
- if (!config.registries[name]) {
13349
+ if (!config2.registries[name]) {
13518
13350
  logger.warn(`Registry '${name}' not found.`);
13519
13351
  return;
13520
13352
  }
13521
- delete config.registries[name];
13522
- await writeOcxConfig(options2.cwd, config);
13353
+ delete config2.registries[name];
13354
+ await writeOcxConfig(options2.cwd, config2);
13523
13355
  if (options2.json) {
13524
13356
  outputJson({ success: true, data: { removed: name } });
13525
13357
  } else {
@@ -13529,14 +13361,14 @@ function registerRegistryCommand(program2) {
13529
13361
  handleError(error);
13530
13362
  }
13531
13363
  });
13532
- registry.command("list").description("List configured registries").option("--cwd <path>", "Working directory", process.cwd()).option("--json", "Output as JSON", false).option("-q, --quiet", "Suppress output", false).action(async (options2) => {
13364
+ registry2.command("list").description("List configured registries").option("--cwd <path>", "Working directory", process.cwd()).option("--json", "Output as JSON", false).option("-q, --quiet", "Suppress output", false).action(async (options2) => {
13533
13365
  try {
13534
- const config = await readOcxConfig(options2.cwd);
13535
- if (!config) {
13366
+ const config2 = await readOcxConfig(options2.cwd);
13367
+ if (!config2) {
13536
13368
  logger.warn("No ocx.jsonc found. Run 'ocx init' first.");
13537
13369
  return;
13538
13370
  }
13539
- const registries = Object.entries(config.registries).map(([name, cfg]) => ({
13371
+ const registries = Object.entries(config2.registries).map(([name, cfg]) => ({
13540
13372
  name,
13541
13373
  url: cfg.url,
13542
13374
  version: cfg.version || "latest"
@@ -13546,14 +13378,14 @@ function registerRegistryCommand(program2) {
13546
13378
  success: true,
13547
13379
  data: {
13548
13380
  registries,
13549
- locked: config.lockRegistries
13381
+ locked: config2.lockRegistries
13550
13382
  }
13551
13383
  });
13552
13384
  } else {
13553
13385
  if (registries.length === 0) {
13554
13386
  logger.info("No registries configured.");
13555
13387
  } else {
13556
- logger.info(`Configured registries${config.lockRegistries ? kleur_default.yellow(" (locked)") : ""}:`);
13388
+ logger.info(`Configured registries${config2.lockRegistries ? kleur_default.yellow(" (locked)") : ""}:`);
13557
13389
  for (const reg of registries) {
13558
13390
  console.log(` ${kleur_default.cyan(reg.name)}: ${reg.url} ${kleur_default.dim(`(${reg.version})`)}`);
13559
13391
  }
@@ -13597,13 +13429,13 @@ function registerSearchCommand(program2) {
13597
13429
  }
13598
13430
  return;
13599
13431
  }
13600
- const config = await readOcxConfig(options2.cwd);
13601
- if (!config) {
13432
+ const config2 = await readOcxConfig(options2.cwd);
13433
+ if (!config2) {
13602
13434
  logger.warn("No ocx.jsonc found. Run 'ocx init' first.");
13603
13435
  return;
13604
13436
  }
13605
13437
  if (options2.verbose) {
13606
- logger.info(`Searching in ${Object.keys(config.registries).length} registries...`);
13438
+ logger.info(`Searching in ${Object.keys(config2.registries).length} registries...`);
13607
13439
  }
13608
13440
  const allComponents = [];
13609
13441
  const spinner2 = createSpinner({
@@ -13613,7 +13445,7 @@ function registerSearchCommand(program2) {
13613
13445
  if (!options2.json && !options2.verbose) {
13614
13446
  spinner2.start();
13615
13447
  }
13616
- for (const [registryName, registryConfig] of Object.entries(config.registries)) {
13448
+ for (const [registryName, registryConfig] of Object.entries(config2.registries)) {
13617
13449
  try {
13618
13450
  if (options2.verbose) {
13619
13451
  logger.info(`Fetching index from ${registryName} (${registryConfig.url})...`);
@@ -13668,7 +13500,7 @@ function registerSearchCommand(program2) {
13668
13500
  }
13669
13501
 
13670
13502
  // src/index.ts
13671
- var version = "1.0.10";
13503
+ var version = "1.0.12";
13672
13504
  async function main2() {
13673
13505
  const program2 = new Command().name("ocx").description("OpenCode Extensions - Install agents, skills, plugins, and commands").version(version);
13674
13506
  registerInitCommand(program2);
@@ -13683,4 +13515,4 @@ main2().catch((err) => {
13683
13515
  handleError(err);
13684
13516
  });
13685
13517
 
13686
- //# debugId=D9C733580818864264756E2164756E21
13518
+ //# debugId=12A4C4DB6A9FC8A464756E2164756E21