oh-my-opencode-cohub 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -28,6 +28,11 @@ export declare class ContextEngine {
28
28
  * 返回替换后的完整文本,或 null(无标记或未找到上下文)。
29
29
  */
30
30
  consumeMarkedContext(messageText: string): string | null;
31
+ /**
32
+ * 格式化上下文详情(文件、决策、错误、依赖),不含主标题。
33
+ * 用于在 tool.execute.before 中直接追加到子代理 prompt。
34
+ */
35
+ formatContextDetails(contextId: string): string;
31
36
  /**
32
37
  * Phase C: 捕获子代理结果。
33
38
  * 读取子 session 的最终输出 → 提取关键信息 → 存入 dependencyCache。
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/context/engine.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAe,aAAa,EAAoB,MAAM,SAAS,CAAC;AAI7F,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAE7D,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAEzD,qBAAa,aAAa;IACxB,8BAA8B;IAC9B,OAAO,CAAC,QAAQ,CAAkC;IAClD,sBAAsB;IACtB,OAAO,CAAC,eAAe,CAAuC;IAC9D,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,MAAM,CAAgB;gBAElB,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC;IAU9D;;;OAGG;IACH,eAAe,CAAC,IAAI,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM;IAYtD;;OAEG;IACG,gBAAgB,CACpB,SAAS,EAAE,MAAM,EACjB,eAAe,EAAE,MAAM,EACvB,IAAI,EAAE;QAAE,QAAQ,EAAE,eAAe,CAAA;KAAE,GAClC,OAAO,CAAC,IAAI,CAAC;IAgChB;;;;OAIG;IACH,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAiBxD;;;OAGG;IACG,aAAa,CACjB,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC;IAuChB;;OAEG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAIvC;;OAEG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe;IAI/C;;OAEG;IACH,wBAAwB,CAAC,QAAQ,GAAE,MAAuB,GAAG,IAAI;CAQlE"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/context/engine.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAe,aAAa,EAAoB,MAAM,SAAS,CAAC;AAI7F,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAE7D,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAEzD,qBAAa,aAAa;IACxB,8BAA8B;IAC9B,OAAO,CAAC,QAAQ,CAAkC;IAClD,sBAAsB;IACtB,OAAO,CAAC,eAAe,CAAuC;IAC9D,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,MAAM,CAAgB;gBAElB,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC;IAU9D;;;OAGG;IACH,eAAe,CAAC,IAAI,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM;IAYtD;;OAEG;IACG,gBAAgB,CACpB,SAAS,EAAE,MAAM,EACjB,eAAe,EAAE,MAAM,EACvB,IAAI,EAAE;QAAE,QAAQ,EAAE,eAAe,CAAA;KAAE,GAClC,OAAO,CAAC,IAAI,CAAC;IAgChB;;;;OAIG;IACH,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAiBxD;;;OAGG;IACH,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAqC/C;;;OAGG;IACG,aAAa,CACjB,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC;IAuChB;;OAEG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAIvC;;OAEG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe;IAI/C;;OAEG;IACH,wBAAwB,CAAC,QAAQ,GAAE,MAAuB,GAAG,IAAI;CAQlE"}
@@ -1 +1 @@
1
- {"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../../src/context/extractor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,sBAAsB;AACtB,UAAU,UAAU;IAClB,IAAI,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzB,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACvG;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,UAAU,EAAE,EACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,YAAY,EAAE,CAwChB;AAUD;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,UAAU,EAAE,EACtB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GACjB,MAAM,EAAE,CAqBV;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,UAAU,EAAE,EACtB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,MAAM,EAAE,CAsBV"}
1
+ {"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../../src/context/extractor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAuB5C,sBAAsB;AACtB,UAAU,UAAU;IAClB,IAAI,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzB,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACvG;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,UAAU,EAAE,EACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,YAAY,EAAE,CAuEhB;AAUD;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,UAAU,EAAE,EACtB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GACjB,MAAM,EAAE,CAqBV;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,UAAU,EAAE,EACtB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,MAAM,EAAE,CAsBV"}
package/dist/index.d.ts CHANGED
@@ -1,10 +1,4 @@
1
1
  import type { Plugin } from '@opencode-ai/plugin';
2
- /** 中文提示词映射表 —— 供外部使用 */
3
- export declare const CHINESE_PROMPTS: Record<string, string>;
4
- /**
5
- * 中文语言指令 —— 可注入到 AGENTS.md 或 instructions
6
- */
7
- export declare const CHINESE_INSTRUCTION = "# \u4E2D\u6587\u8BED\u8A00\u8981\u6C42\n\n\u4F60\u5FC5\u987B\u59CB\u7EC8\u4F7F\u7528\u4E2D\u6587\u8FDB\u884C\u601D\u8003\u3001\u63A8\u7406\u548C\u56DE\u590D\u3002\n\n- \u6240\u6709\u81EA\u7136\u8BED\u8A00\u90E8\u5206\uFF08\u5206\u6790\u3001\u89C4\u5212\u3001\u89E3\u91CA\u3001\u8BA8\u8BBA\uFF09\u5FC5\u987B\u4F7F\u7528\u4E2D\u6587\n- \u4EE3\u7801\u3001\u6280\u672F\u672F\u8BED\u3001\u6587\u4EF6\u540D\u3001\u547D\u4EE4\u53EF\u4EE5\u4FDD\u7559\u539F\u6837\n- \u6B64\u89C4\u5219\u5BF9\u6240\u6709\u4EE3\u7406\uFF08Orchestrator \u548C\u6240\u6709\u5B50\u4EE3\u7406\uFF09\u751F\u6548\n- \u8DE8\u6240\u6709\u9879\u76EE\u751F\u6548\uFF0C\u4F18\u5148\u7EA7\u9AD8\u4E8E\u9879\u76EE\u7EA7\u89C4\u5219";
8
2
  declare const CoHubPlugin: Plugin;
9
3
  export declare const server: Plugin;
10
4
  export default CoHubPlugin;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAyClD,wBAAwB;AACxB,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAalD,CAAC;AAwEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,wrBAA+B,CAAC;AAEhE,QAAA,MAAM,WAAW,EAAE,MAuYlB,CAAC;AAEF,eAAO,MAAM,MAAM,QAAc,CAAC;AAClC,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAoIlD,QAAA,MAAM,WAAW,EAAE,MAoXlB,CAAC;AAEF,eAAO,MAAM,MAAM,QAAc,CAAC;AAClC,eAAe,WAAW,CAAC"}
package/dist/index.js CHANGED
@@ -657,6 +657,48 @@ var DEFAULT_CONTEXT_CONFIG = {
657
657
  };
658
658
 
659
659
  // src/context/extractor.ts
660
+ var KNOWN_EXTENSIONS = new Set([
661
+ "ts",
662
+ "tsx",
663
+ "js",
664
+ "jsx",
665
+ "mjs",
666
+ "cjs",
667
+ "json",
668
+ "md",
669
+ "mdx",
670
+ "css",
671
+ "scss",
672
+ "less",
673
+ "html",
674
+ "htm",
675
+ "py",
676
+ "rs",
677
+ "go",
678
+ "java",
679
+ "cpp",
680
+ "c",
681
+ "h",
682
+ "hpp",
683
+ "rb",
684
+ "php",
685
+ "swift",
686
+ "kt",
687
+ "scala",
688
+ "yml",
689
+ "yaml",
690
+ "toml",
691
+ "xml",
692
+ "sql",
693
+ "env",
694
+ "sh",
695
+ "bash",
696
+ "ps1",
697
+ "dockerfile",
698
+ "gitignore",
699
+ "editorconfig"
700
+ ]);
701
+ var FILE_PATH_RE = /`?((?:[A-Za-z]:[\\\/]|\.{1,2}[\\\/]|~\/|\/|[\w-]+[\\\/])[\w\-\.\\\/]+\.\w{1,10})`?/g;
660
702
  function extractRelevantFiles(messages, maxFiles, windowSize) {
661
703
  const recent = messages.slice(-windowSize);
662
704
  const fileMap = new Map;
@@ -668,6 +710,22 @@ function extractRelevantFiles(messages, maxFiles, windowSize) {
668
710
  if (path && !fileMap.has(path)) {
669
711
  fileMap.set(path, { path, summary: "" });
670
712
  }
713
+ for (const value of Object.values(args)) {
714
+ if (typeof value !== "string")
715
+ continue;
716
+ let match;
717
+ FILE_PATH_RE.lastIndex = 0;
718
+ while ((match = FILE_PATH_RE.exec(value)) !== null) {
719
+ const rawPath = match[1];
720
+ const ext = rawPath.split(".").pop()?.toLowerCase();
721
+ if (!ext || !KNOWN_EXTENSIONS.has(ext))
722
+ continue;
723
+ const cleanPath = rawPath.replace(/^`|`$/g, "");
724
+ if (!fileMap.has(cleanPath)) {
725
+ fileMap.set(cleanPath, { path: cleanPath, summary: "" });
726
+ }
727
+ }
728
+ }
671
729
  }
672
730
  if (part.type === "tool_result" && part.tool_result) {
673
731
  const tr = part.tool_result;
@@ -689,6 +747,20 @@ function extractRelevantFiles(messages, maxFiles, windowSize) {
689
747
  }
690
748
  }
691
749
  }
750
+ if (typeof part.text === "string") {
751
+ let match;
752
+ FILE_PATH_RE.lastIndex = 0;
753
+ while ((match = FILE_PATH_RE.exec(part.text)) !== null) {
754
+ const rawPath = match[1];
755
+ const ext = rawPath.split(".").pop()?.toLowerCase();
756
+ if (!ext || !KNOWN_EXTENSIONS.has(ext))
757
+ continue;
758
+ const cleanPath = rawPath.replace(/^`|`$/g, "");
759
+ if (!fileMap.has(cleanPath)) {
760
+ fileMap.set(cleanPath, { path: cleanPath, summary: "" });
761
+ }
762
+ }
763
+ }
692
764
  }
693
765
  }
694
766
  return Array.from(fileMap.values()).slice(0, maxFiles);
@@ -873,6 +945,40 @@ class ContextEngine {
873
945
  this.registry.delete(contextId);
874
946
  return result;
875
947
  }
948
+ formatContextDetails(contextId) {
949
+ const context = this.registry.get(contextId);
950
+ if (!context)
951
+ return "";
952
+ const parts = [];
953
+ if (context.relevantFiles.length > 0) {
954
+ parts.push("### \uD83D\uDCC1 相关文件");
955
+ for (const file of context.relevantFiles) {
956
+ parts.push(`- \`${file.path}\`` + (file.summary ? ` — ${file.summary}` : ""));
957
+ }
958
+ }
959
+ if (context.decisions.length > 0) {
960
+ parts.push("### \uD83D\uDCA1 前置决策");
961
+ for (const d of context.decisions) {
962
+ parts.push(`- ${d}`);
963
+ }
964
+ }
965
+ if (context.errors.length > 0) {
966
+ parts.push("### ⚠️ 近期错误");
967
+ for (const e of context.errors) {
968
+ parts.push(`- ${e}`);
969
+ }
970
+ }
971
+ if (context.dependencies.length > 0) {
972
+ parts.push("### \uD83D\uDCE6 依赖结果");
973
+ for (const dep of context.dependencies) {
974
+ parts.push(`- **${dep.agent}**: ${dep.keyOutput}`);
975
+ }
976
+ }
977
+ return parts.length > 0 ? `
978
+ ` + parts.join(`
979
+ `) + `
980
+ ` : "";
981
+ }
876
982
  async captureResult(childSessionId, alias, agent) {
877
983
  if (!this.config.dependencyPropagation)
878
984
  return;
@@ -920,13 +1026,6 @@ class ContextEngine {
920
1026
  }
921
1027
  }
922
1028
 
923
- // src/context/strategy.ts
924
- function resolveStrategy(agentType, defaults, override) {
925
- if (override)
926
- return override;
927
- return defaults[agentType] ?? "none";
928
- }
929
-
930
1029
  // src/config/loader.ts
931
1030
  import * as fs from "node:fs";
932
1031
  import * as path from "node:path";
@@ -1288,20 +1387,6 @@ function createCouncilTool(ctx, councilManager) {
1288
1387
  import * as fs2 from "node:fs";
1289
1388
  import * as path2 from "node:path";
1290
1389
  import * as os2 from "node:os";
1291
- var CHINESE_PROMPTS = {
1292
- "co-orchestrator": ORCHESTRATOR_PROMPT,
1293
- "co-oracle": ORACLE_PROMPT,
1294
- "co-librarian": LIBRARIAN_PROMPT,
1295
- "co-explorer": EXPLORER_PROMPT,
1296
- "co-designer": DESIGNER_PROMPT,
1297
- "co-fixer": FIXER_PROMPT,
1298
- "co-observer": OBSERVER_PROMPT,
1299
- "co-council": COUNCIL_PROMPT,
1300
- "co-rule-user": RULE_USER_PROMPT,
1301
- "co-rule-project": RULE_PROJECT_PROMPT,
1302
- "co-rule-app": RULE_APP_PROMPT,
1303
- "co-planner": PLANNER_PROMPT
1304
- };
1305
1390
  function loadFileOverrides(projectDir) {
1306
1391
  const overrides = {};
1307
1392
  const agentNames = [
@@ -1354,7 +1439,6 @@ ${append}`;
1354
1439
  }
1355
1440
  return base;
1356
1441
  }
1357
- var CHINESE_INSTRUCTION = CHINESE_LANGUAGE_INSTRUCTION;
1358
1442
  var CoHubPlugin = async (input, options) => {
1359
1443
  const projectDir = input.directory || process.cwd();
1360
1444
  const fileOverrides = loadFileOverrides(projectDir);
@@ -1587,24 +1671,30 @@ var CoHubPlugin = async (input, options) => {
1587
1671
  });
1588
1672
  syncTrackerState(input2.sessionID ?? "");
1589
1673
  if (subagentType) {
1590
- const strategy = resolveStrategy(subagentType, contextEngine.getStrategy(subagentType) !== undefined ? { [subagentType]: contextEngine.getStrategy(subagentType) } : contextConfig.strategy ?? {}, typeof args.context_override === "string" ? args.context_override : undefined);
1674
+ const strategy = typeof args.context_override === "string" ? args.context_override : contextEngine.getStrategy(subagentType);
1591
1675
  if (strategy !== "none") {
1592
- console.error("[CONTEXT-DEBUG] before hook: strategy=", strategy, "agent=", subagentType);
1593
- console.error("[CONTEXT-DEBUG] before hook: output.args type=", typeof output.args, "isNull=", output.args === null);
1594
- const contextId = contextEngine.registerContext({
1595
- description
1596
- });
1676
+ const contextId = contextEngine.registerContext({ description });
1597
1677
  output.args ??= {};
1598
- output.args.description = description + contextEngine.formatMarker(contextId);
1599
- console.error("[CONTEXT-DEBUG] before hook: marker appended, contextId=", contextId);
1600
- console.error("[CONTEXT-DEBUG] before hook: description preview=", output.args.description.slice(-80));
1601
- contextEngine.fillContextAsync(contextId, input2.sessionID, {
1678
+ const contextBlock = `
1679
+
1680
+ ### \uD83D\uDCCB 任务上下文 (CoHub 自动注入)
1681
+ ` + "**当前任务**: " + description + `
1682
+ `;
1683
+ const targetField = typeof output.args.prompt === "string" ? "prompt" : "description";
1684
+ output.args[targetField] = (targetField === "prompt" ? output.args.prompt : description) + contextBlock;
1685
+ await contextEngine.fillContextAsync(contextId, input2.sessionID, {
1602
1686
  strategy
1603
- }).catch(() => {});
1687
+ });
1688
+ const details = contextEngine.formatContextDetails(contextId);
1689
+ if (details) {
1690
+ output.args[targetField] += details;
1691
+ }
1604
1692
  }
1605
1693
  }
1606
1694
  }
1607
- } catch {}
1695
+ } catch (err) {
1696
+ console.warn("[oh-my-opencode-cohub] tool.execute.before hook 失败:", err);
1697
+ }
1608
1698
  },
1609
1699
  "tool.execute.after": async (input2, output) => {
1610
1700
  try {
@@ -1643,26 +1733,6 @@ var CoHubPlugin = async (input, options) => {
1643
1733
  },
1644
1734
  "experimental.chat.messages.transform": async (_input, output) => {
1645
1735
  try {
1646
- if (output.messages && Array.isArray(output.messages)) {
1647
- const userMsgs = output.messages.filter((m) => m.info?.role === "user");
1648
- console.error("[CONTEXT-DEBUG] transform: total messages=", output.messages.length, "user messages=", userMsgs.length);
1649
- for (const msg of output.messages) {
1650
- if (msg.info.role !== "user")
1651
- continue;
1652
- for (const part of msg.parts ?? []) {
1653
- if (part.type !== "text" || !part.text)
1654
- continue;
1655
- const hasMarker = /CONTEXT:ID=/.test(part.text);
1656
- if (hasMarker) {
1657
- console.error("[CONTEXT-DEBUG] transform: FOUND marker in user msg, text preview=", part.text.slice(0, 200));
1658
- }
1659
- const replaced = contextEngine.consumeMarkedContext(part.text);
1660
- if (replaced !== null) {
1661
- console.error("[CONTEXT-DEBUG] transform: marker REPLACED, new text preview=", part.text.slice(0, 200));
1662
- }
1663
- }
1664
- }
1665
- }
1666
1736
  const board = tracker.getBoardText();
1667
1737
  if (board && output.messages && Array.isArray(output.messages)) {
1668
1738
  for (let i = output.messages.length - 1;i >= 0; i--) {
@@ -1681,7 +1751,9 @@ var CoHubPlugin = async (input, options) => {
1681
1751
  }
1682
1752
  }
1683
1753
  }
1684
- } catch {}
1754
+ } catch (err) {
1755
+ console.warn("[oh-my-opencode-cohub] messages.transform hook 失败:", err);
1756
+ }
1685
1757
  },
1686
1758
  "experimental.chat.system.transform": async (_input, output) => {
1687
1759
  output.system.push(CHINESE_LANGUAGE_INSTRUCTION);
@@ -1696,7 +1768,5 @@ var server = CoHubPlugin;
1696
1768
  var src_default = CoHubPlugin;
1697
1769
  export {
1698
1770
  server,
1699
- src_default as default,
1700
- CHINESE_PROMPTS,
1701
- CHINESE_INSTRUCTION
1771
+ src_default as default
1702
1772
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-opencode-cohub",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/Mr-cjf/oh-my-opencode-cohub.git"