@xberg-io/opencode-xberg 1.3.7 → 1.3.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/.opencode/ai-rulez-bundle.json +52 -0
  2. package/.opencode/ai-rulez-content.js +167 -0
  3. package/.opencode/commands/xberg-extract.md +14 -0
  4. package/.opencode/commands/xberg-ocr.md +14 -0
  5. package/.opencode/plugins/xberg.js +75 -62
  6. package/.opencode/skills/xberg/SKILL.md +422 -0
  7. package/.opencode/skills/xberg/index.md +10 -0
  8. package/.opencode/skills/xberg/references/advanced-features.md +628 -0
  9. package/.opencode/skills/xberg/references/cli-reference.md +462 -0
  10. package/.opencode/skills/xberg/references/configuration.md +587 -0
  11. package/.opencode/skills/xberg/references/nodejs-api.md +485 -0
  12. package/.opencode/skills/xberg/references/other-bindings.md +345 -0
  13. package/.opencode/skills/xberg/references/python-api.md +657 -0
  14. package/.opencode/skills/xberg/references/rust-api.md +412 -0
  15. package/.opencode/skills/xberg/references/supported-formats.md +252 -0
  16. package/.opencode/skills/xberg-batch-extraction/SKILL.md +170 -0
  17. package/.opencode/skills/xberg-batch-extraction/index.md +10 -0
  18. package/.opencode/skills/xberg-batch-extraction/references/advanced-features.md +628 -0
  19. package/.opencode/skills/xberg-batch-extraction/references/cli-reference.md +462 -0
  20. package/.opencode/skills/xberg-batch-extraction/references/configuration.md +587 -0
  21. package/.opencode/skills/xberg-batch-extraction/references/nodejs-api.md +485 -0
  22. package/.opencode/skills/xberg-batch-extraction/references/other-bindings.md +345 -0
  23. package/.opencode/skills/xberg-batch-extraction/references/python-api.md +657 -0
  24. package/.opencode/skills/xberg-batch-extraction/references/rust-api.md +412 -0
  25. package/.opencode/skills/xberg-batch-extraction/references/supported-formats.md +252 -0
  26. package/.opencode/skills/xberg-chunking/SKILL.md +163 -0
  27. package/.opencode/skills/xberg-chunking/index.md +10 -0
  28. package/.opencode/skills/xberg-chunking/references/advanced-features.md +628 -0
  29. package/.opencode/skills/xberg-chunking/references/cli-reference.md +462 -0
  30. package/.opencode/skills/xberg-chunking/references/configuration.md +587 -0
  31. package/.opencode/skills/xberg-chunking/references/nodejs-api.md +485 -0
  32. package/.opencode/skills/xberg-chunking/references/other-bindings.md +345 -0
  33. package/.opencode/skills/xberg-chunking/references/python-api.md +657 -0
  34. package/.opencode/skills/xberg-chunking/references/rust-api.md +412 -0
  35. package/.opencode/skills/xberg-chunking/references/supported-formats.md +252 -0
  36. package/.opencode/skills/xberg-extracting-keywords/SKILL.md +170 -0
  37. package/.opencode/skills/xberg-extracting-keywords/index.md +10 -0
  38. package/.opencode/skills/xberg-extracting-keywords/references/advanced-features.md +628 -0
  39. package/.opencode/skills/xberg-extracting-keywords/references/cli-reference.md +462 -0
  40. package/.opencode/skills/xberg-extracting-keywords/references/configuration.md +587 -0
  41. package/.opencode/skills/xberg-extracting-keywords/references/nodejs-api.md +485 -0
  42. package/.opencode/skills/xberg-extracting-keywords/references/other-bindings.md +345 -0
  43. package/.opencode/skills/xberg-extracting-keywords/references/python-api.md +657 -0
  44. package/.opencode/skills/xberg-extracting-keywords/references/rust-api.md +412 -0
  45. package/.opencode/skills/xberg-extracting-keywords/references/supported-formats.md +252 -0
  46. package/.opencode/skills/xberg-extracting-tables/SKILL.md +155 -0
  47. package/.opencode/skills/xberg-extracting-tables/index.md +10 -0
  48. package/.opencode/skills/xberg-extracting-tables/references/advanced-features.md +628 -0
  49. package/.opencode/skills/xberg-extracting-tables/references/cli-reference.md +462 -0
  50. package/.opencode/skills/xberg-extracting-tables/references/configuration.md +587 -0
  51. package/.opencode/skills/xberg-extracting-tables/references/nodejs-api.md +485 -0
  52. package/.opencode/skills/xberg-extracting-tables/references/other-bindings.md +345 -0
  53. package/.opencode/skills/xberg-extracting-tables/references/python-api.md +657 -0
  54. package/.opencode/skills/xberg-extracting-tables/references/rust-api.md +412 -0
  55. package/.opencode/skills/xberg-extracting-tables/references/supported-formats.md +252 -0
  56. package/.opencode/skills/xberg-extracting-with-ocr/SKILL.md +129 -0
  57. package/.opencode/skills/xberg-extracting-with-ocr/index.md +10 -0
  58. package/.opencode/skills/xberg-extracting-with-ocr/references/advanced-features.md +628 -0
  59. package/.opencode/skills/xberg-extracting-with-ocr/references/cli-reference.md +462 -0
  60. package/.opencode/skills/xberg-extracting-with-ocr/references/configuration.md +587 -0
  61. package/.opencode/skills/xberg-extracting-with-ocr/references/nodejs-api.md +485 -0
  62. package/.opencode/skills/xberg-extracting-with-ocr/references/other-bindings.md +345 -0
  63. package/.opencode/skills/xberg-extracting-with-ocr/references/python-api.md +657 -0
  64. package/.opencode/skills/xberg-extracting-with-ocr/references/rust-api.md +412 -0
  65. package/.opencode/skills/xberg-extracting-with-ocr/references/supported-formats.md +252 -0
  66. package/.opencode/skills/xberg-picking-a-format/SKILL.md +104 -0
  67. package/.opencode/skills/xberg-picking-a-format/index.md +10 -0
  68. package/.opencode/skills/xberg-picking-a-format/references/advanced-features.md +628 -0
  69. package/.opencode/skills/xberg-picking-a-format/references/cli-reference.md +462 -0
  70. package/.opencode/skills/xberg-picking-a-format/references/configuration.md +587 -0
  71. package/.opencode/skills/xberg-picking-a-format/references/nodejs-api.md +485 -0
  72. package/.opencode/skills/xberg-picking-a-format/references/other-bindings.md +345 -0
  73. package/.opencode/skills/xberg-picking-a-format/references/python-api.md +657 -0
  74. package/.opencode/skills/xberg-picking-a-format/references/rust-api.md +412 -0
  75. package/.opencode/skills/xberg-picking-a-format/references/supported-formats.md +252 -0
  76. package/.pi/prompts/xberg-extract.md +14 -0
  77. package/.pi/prompts/xberg-ocr.md +14 -0
  78. package/.pi/skills/xberg/SKILL.md +422 -0
  79. package/.pi/skills/xberg/index.md +10 -0
  80. package/.pi/skills/xberg/references/advanced-features.md +628 -0
  81. package/.pi/skills/xberg/references/cli-reference.md +462 -0
  82. package/.pi/skills/xberg/references/configuration.md +587 -0
  83. package/.pi/skills/xberg/references/nodejs-api.md +485 -0
  84. package/.pi/skills/xberg/references/other-bindings.md +345 -0
  85. package/.pi/skills/xberg/references/python-api.md +657 -0
  86. package/.pi/skills/xberg/references/rust-api.md +412 -0
  87. package/.pi/skills/xberg/references/supported-formats.md +252 -0
  88. package/.pi/skills/xberg-batch-extraction/SKILL.md +170 -0
  89. package/.pi/skills/xberg-batch-extraction/index.md +10 -0
  90. package/.pi/skills/xberg-batch-extraction/references/advanced-features.md +628 -0
  91. package/.pi/skills/xberg-batch-extraction/references/cli-reference.md +462 -0
  92. package/.pi/skills/xberg-batch-extraction/references/configuration.md +587 -0
  93. package/.pi/skills/xberg-batch-extraction/references/nodejs-api.md +485 -0
  94. package/.pi/skills/xberg-batch-extraction/references/other-bindings.md +345 -0
  95. package/.pi/skills/xberg-batch-extraction/references/python-api.md +657 -0
  96. package/.pi/skills/xberg-batch-extraction/references/rust-api.md +412 -0
  97. package/.pi/skills/xberg-batch-extraction/references/supported-formats.md +252 -0
  98. package/.pi/skills/xberg-chunking/SKILL.md +163 -0
  99. package/.pi/skills/xberg-chunking/index.md +10 -0
  100. package/.pi/skills/xberg-chunking/references/advanced-features.md +628 -0
  101. package/.pi/skills/xberg-chunking/references/cli-reference.md +462 -0
  102. package/.pi/skills/xberg-chunking/references/configuration.md +587 -0
  103. package/.pi/skills/xberg-chunking/references/nodejs-api.md +485 -0
  104. package/.pi/skills/xberg-chunking/references/other-bindings.md +345 -0
  105. package/.pi/skills/xberg-chunking/references/python-api.md +657 -0
  106. package/.pi/skills/xberg-chunking/references/rust-api.md +412 -0
  107. package/.pi/skills/xberg-chunking/references/supported-formats.md +252 -0
  108. package/.pi/skills/xberg-extracting-keywords/SKILL.md +170 -0
  109. package/.pi/skills/xberg-extracting-keywords/index.md +10 -0
  110. package/.pi/skills/xberg-extracting-keywords/references/advanced-features.md +628 -0
  111. package/.pi/skills/xberg-extracting-keywords/references/cli-reference.md +462 -0
  112. package/.pi/skills/xberg-extracting-keywords/references/configuration.md +587 -0
  113. package/.pi/skills/xberg-extracting-keywords/references/nodejs-api.md +485 -0
  114. package/.pi/skills/xberg-extracting-keywords/references/other-bindings.md +345 -0
  115. package/.pi/skills/xberg-extracting-keywords/references/python-api.md +657 -0
  116. package/.pi/skills/xberg-extracting-keywords/references/rust-api.md +412 -0
  117. package/.pi/skills/xberg-extracting-keywords/references/supported-formats.md +252 -0
  118. package/.pi/skills/xberg-extracting-tables/SKILL.md +155 -0
  119. package/.pi/skills/xberg-extracting-tables/index.md +10 -0
  120. package/.pi/skills/xberg-extracting-tables/references/advanced-features.md +628 -0
  121. package/.pi/skills/xberg-extracting-tables/references/cli-reference.md +462 -0
  122. package/.pi/skills/xberg-extracting-tables/references/configuration.md +587 -0
  123. package/.pi/skills/xberg-extracting-tables/references/nodejs-api.md +485 -0
  124. package/.pi/skills/xberg-extracting-tables/references/other-bindings.md +345 -0
  125. package/.pi/skills/xberg-extracting-tables/references/python-api.md +657 -0
  126. package/.pi/skills/xberg-extracting-tables/references/rust-api.md +412 -0
  127. package/.pi/skills/xberg-extracting-tables/references/supported-formats.md +252 -0
  128. package/.pi/skills/xberg-extracting-with-ocr/SKILL.md +129 -0
  129. package/.pi/skills/xberg-extracting-with-ocr/index.md +10 -0
  130. package/.pi/skills/xberg-extracting-with-ocr/references/advanced-features.md +628 -0
  131. package/.pi/skills/xberg-extracting-with-ocr/references/cli-reference.md +462 -0
  132. package/.pi/skills/xberg-extracting-with-ocr/references/configuration.md +587 -0
  133. package/.pi/skills/xberg-extracting-with-ocr/references/nodejs-api.md +485 -0
  134. package/.pi/skills/xberg-extracting-with-ocr/references/other-bindings.md +345 -0
  135. package/.pi/skills/xberg-extracting-with-ocr/references/python-api.md +657 -0
  136. package/.pi/skills/xberg-extracting-with-ocr/references/rust-api.md +412 -0
  137. package/.pi/skills/xberg-extracting-with-ocr/references/supported-formats.md +252 -0
  138. package/.pi/skills/xberg-picking-a-format/SKILL.md +104 -0
  139. package/.pi/skills/xberg-picking-a-format/index.md +10 -0
  140. package/.pi/skills/xberg-picking-a-format/references/advanced-features.md +628 -0
  141. package/.pi/skills/xberg-picking-a-format/references/cli-reference.md +462 -0
  142. package/.pi/skills/xberg-picking-a-format/references/configuration.md +587 -0
  143. package/.pi/skills/xberg-picking-a-format/references/nodejs-api.md +485 -0
  144. package/.pi/skills/xberg-picking-a-format/references/other-bindings.md +345 -0
  145. package/.pi/skills/xberg-picking-a-format/references/python-api.md +657 -0
  146. package/.pi/skills/xberg-picking-a-format/references/rust-api.md +412 -0
  147. package/.pi/skills/xberg-picking-a-format/references/supported-formats.md +252 -0
  148. package/README.md +112 -6
  149. package/package.json +184 -16
  150. package/scripts/mcp-launch.sh +189 -0
@@ -0,0 +1,52 @@
1
+ {
2
+ "plugin": "xberg",
3
+ "mcp": {
4
+ "xberg": {
5
+ "type": "local",
6
+ "command": [
7
+ "${PLUGIN_ROOT}/scripts/mcp-launch.sh",
8
+ "mcp",
9
+ "--transport",
10
+ "stdio"
11
+ ]
12
+ }
13
+ },
14
+ "skills": {
15
+ "xberg": {
16
+ "name": "xberg",
17
+ "description": "Extract text, tables, metadata, and images from 107 document formats (PDF, Office, images, HTML, email, archives, academic) using Xberg. Use when writing code that calls Xberg APIs in Python, Node.js/TypeScript, Rust, or CLI. Covers installation, extraction (sync/async), configuration (OCR, chunking, output format), batch processing, error handling, and plugins."
18
+ },
19
+ "xberg-batch-extraction": {
20
+ "name": "xberg-batch-extraction",
21
+ "description": "Use when extracting from many files at once with shared config, bounded parallelism, per-file overrides, and error recovery. Covers the `batch` command, `--file-configs`, `--max-concurrent`, and output layout."
22
+ },
23
+ "xberg-chunking": {
24
+ "name": "xberg-chunking",
25
+ "description": "Use when splitting extracted text into chunks for LLM context windows or RAG ingestion. Covers chunk size, overlap, markdown/yaml/semantic chunkers, tokenizer-based sizing, and the standalone `chunk` command."
26
+ },
27
+ "xberg-extracting-keywords": {
28
+ "name": "xberg-extracting-keywords",
29
+ "description": "Use when extracting keywords (YAKE/RAKE) from documents — and, secondarily, when detecting document language or generating embeddings for RAG and search. Covers the keyword config (and its feature gating), `--detect-language`, and the standalone `embed` command with real flags."
30
+ },
31
+ "xberg-extracting-tables": {
32
+ "name": "xberg-extracting-tables",
33
+ "description": "Use when extracting tabular data from PDFs, spreadsheets, or images. Covers layout-aware table detection, table model selection, output formats (markdown / JSON cells), and known limits."
34
+ },
35
+ "xberg-extracting-with-ocr": {
36
+ "name": "xberg-extracting-with-ocr",
37
+ "description": "Use when extracting text from scanned PDFs, photographed pages, or images that have no embedded text layer. Covers OCR backends, language packs, force-OCR, and performance tuning."
38
+ },
39
+ "xberg-picking-a-format": {
40
+ "name": "xberg-picking-a-format",
41
+ "description": "Use when choosing an output format for extracted documents — plain text, markdown, djot, HTML, JSON, or DocTags. Maps consumer (LLM, parser, archive) to the right `--format` / `--content-format` pair."
42
+ }
43
+ },
44
+ "commands": {
45
+ "xberg-extract": {
46
+ "description": "Extract a document locally with Xberg"
47
+ },
48
+ "xberg-ocr": {
49
+ "description": "Extract scanned documents with OCR"
50
+ }
51
+ }
52
+ }
@@ -0,0 +1,167 @@
1
+ // AI-RULEZ :: GENERATED FILE — DO NOT EDIT
2
+ // Content-Hash: blake3:011ec20caa671eb4bd4e886b09e15f720a786de4bf747419efd0690889fc5549
3
+ // Source-Hash: blake3:662aa704cf97ad20f02258c9dcd1c73822213a426e27ec7ebc292d3f599fb7c2
4
+ // Schema-Version: v1
5
+
6
+ // Generated by ai-rulez. Registers the skills, commands, agents, and MCP servers bundled with
7
+ // this plugin in OpenCode v2. OpenCode only scans its own config directories, so a plugin installed from
8
+ // npm has to register its bundled content itself.
9
+ import { readdir, readFile } from "node:fs/promises";
10
+ import path from "node:path";
11
+
12
+ const root = import.meta.dirname;
13
+
14
+ // Returns the text after a leading YAML frontmatter block. The frontmatter fields the
15
+ // helper needs (name, description) are resolved when the bundle is generated and read
16
+ // from ai-rulez-bundle.json, so no YAML is parsed here.
17
+ function body(text) {
18
+ const match = /^---\r?\n[\s\S]*?\r?\n---\r?\n?([\s\S]*)$/u.exec(text);
19
+ return match ? match[1] : text;
20
+ }
21
+
22
+ function skip(kind, name, error) {
23
+ console.warn(`ai-rulez: skipping bundled ${kind} ${name}: ${error?.message ?? error}`);
24
+ }
25
+
26
+ async function entries(dir) {
27
+ try {
28
+ return (await readdir(path.join(root, dir), { withFileTypes: true })).toSorted((a, b) =>
29
+ a.name.localeCompare(b.name),
30
+ );
31
+ } catch {
32
+ return [];
33
+ }
34
+ }
35
+
36
+ async function readBundle() {
37
+ try {
38
+ return JSON.parse(await readFile(path.join(root, "ai-rulez-bundle.json"), "utf8"));
39
+ } catch {
40
+ return {};
41
+ }
42
+ }
43
+
44
+ // Plugin install directory: the parent of the .opencode directory this file lives in.
45
+ const pluginRoot = path.dirname(root);
46
+
47
+ // Resolves ${PLUGIN_ROOT} to the install directory and ${NAME} to process.env at runtime,
48
+ // so no install path or secret is baked into the package.
49
+ function expand(value) {
50
+ return value.replaceAll(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/gu, (_, name) =>
51
+ name === "PLUGIN_ROOT" ? pluginRoot : (process.env[name] ?? ""),
52
+ );
53
+ }
54
+
55
+ function mcpServer(server) {
56
+ if (server.type === "remote") {
57
+ const entry = { type: "remote", url: expand(server.url) };
58
+ if (server.disabled) entry.disabled = true;
59
+ return entry;
60
+ }
61
+ const entry = { type: "local", command: server.command.map(expand) };
62
+ if (server.disabled) entry.disabled = true;
63
+ if (server.environment) {
64
+ entry.environment = Object.fromEntries(
65
+ Object.entries(server.environment).map(([key, value]) => [key, expand(value)]),
66
+ );
67
+ }
68
+ return entry;
69
+ }
70
+
71
+ async function load() {
72
+ const bundle = await readBundle();
73
+ const skills = [];
74
+ for (const entry of await entries("skills")) {
75
+ if (!entry.isDirectory()) continue;
76
+ const file = path.join(root, "skills", entry.name, "SKILL.md");
77
+ try {
78
+ const info = bundle.skills?.[entry.name] ?? {};
79
+ skills.push({
80
+ id: bundle.plugin ? `${bundle.plugin}/${entry.name}` : entry.name,
81
+ name: info.name || entry.name,
82
+ description: info.description,
83
+ path: file,
84
+ content: body(await readFile(file, "utf8")),
85
+ });
86
+ } catch (error) {
87
+ skip("skill", entry.name, error);
88
+ }
89
+ }
90
+ const commands = [];
91
+ for (const entry of await entries("commands")) {
92
+ if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
93
+ const name = entry.name.slice(0, -3);
94
+ try {
95
+ const template = body(await readFile(path.join(root, "commands", entry.name), "utf8"));
96
+ commands.push({ name, description: bundle.commands?.[name]?.description, template });
97
+ } catch (error) {
98
+ skip("command", name, error);
99
+ }
100
+ }
101
+ const agents = [];
102
+ for (const entry of await entries("agents")) {
103
+ if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
104
+ const id = entry.name.slice(0, -3);
105
+ try {
106
+ // The bundle holds settings resolved at generation time (the model in particular).
107
+ const system = body(await readFile(path.join(root, "agents", entry.name), "utf8"));
108
+ agents.push({ id, system: system.trim(), settings: bundle.agents?.[id] ?? {} });
109
+ } catch (error) {
110
+ skip("agent", id, error);
111
+ }
112
+ }
113
+ return { skills, commands, agents, mcp: bundle.mcp ?? {} };
114
+ }
115
+
116
+ export async function registerBundledContent(ctx) {
117
+ const { skills, commands, agents, mcp } = await load();
118
+ if (skills.length > 0) {
119
+ await ctx.skill.transform((editor) => {
120
+ for (const skill of skills) if (!editor.get(skill.id)) editor.add(skill);
121
+ });
122
+ }
123
+ if (commands.length > 0) {
124
+ await ctx.command.transform((editor) => {
125
+ for (const command of commands) {
126
+ editor.add({
127
+ name: command.name,
128
+ description: command.description,
129
+ execute: async ({ sessionID, prompt, delivery }) => {
130
+ await ctx.session.prompt({
131
+ ...prompt,
132
+ sessionID,
133
+ text: command.template.replaceAll("$ARGUMENTS", () => String(prompt.text ?? "")),
134
+ delivery,
135
+ });
136
+ },
137
+ });
138
+ }
139
+ });
140
+ }
141
+ if (agents.length > 0) {
142
+ await ctx.agent.transform((editor) => {
143
+ for (const agent of agents) {
144
+ editor.update(agent.id, (item) => {
145
+ const settings = agent.settings;
146
+ if (settings.description) item.description = settings.description;
147
+ if (["subagent", "primary", "all"].includes(settings.mode)) item.mode = settings.mode;
148
+ if (typeof settings.hidden === "boolean") item.hidden = settings.hidden;
149
+ if (typeof settings.model === "string" && settings.model.includes("/")) {
150
+ const split = settings.model.indexOf("/");
151
+ item.model = { providerID: settings.model.slice(0, split), id: settings.model.slice(split + 1) };
152
+ if (settings.variant) item.model.variant = settings.variant;
153
+ }
154
+ for (const key of ["temperature", "top_p"]) {
155
+ if (typeof settings[key] === "number") item.request.body[key] = settings[key];
156
+ }
157
+ if (agent.system) item.system = agent.system;
158
+ });
159
+ }
160
+ });
161
+ }
162
+ if (Object.keys(mcp).length > 0) {
163
+ await ctx.mcp.transform((editor) => {
164
+ for (const [name, server] of Object.entries(mcp)) editor.set(name, mcpServer(server));
165
+ });
166
+ }
167
+ }
@@ -0,0 +1,14 @@
1
+ ---
2
+ description: Extract a document locally with Xberg
3
+ ---
4
+
5
+ <!--
6
+ AI-RULEZ :: GENERATED FILE — DO NOT EDIT
7
+ Content-Hash: blake3:3d695d0525269a1935328e6568418a562cac9ba7a64c6e8121d5136dc93b36e3
8
+ Source-Hash: blake3:662aa704cf97ad20f02258c9dcd1c73822213a426e27ec7ebc292d3f599fb7c2
9
+ Schema-Version: v1
10
+ -->
11
+
12
+ Extract this document: $ARGUMENTS
13
+
14
+ Read the xberg and picking-a-format skills. Use `xberg extract <file> --format json` and choose --content-format for the consumer. Preserve input paths as separate arguments. Report extraction errors and content omissions.
@@ -0,0 +1,14 @@
1
+ ---
2
+ description: Extract scanned documents with OCR
3
+ ---
4
+
5
+ <!--
6
+ AI-RULEZ :: GENERATED FILE — DO NOT EDIT
7
+ Content-Hash: blake3:1715ebd3d000212eb2e4dbfa814d6d5990bcc0c636bae67734a503f2c3ae8be4
8
+ Source-Hash: blake3:662aa704cf97ad20f02258c9dcd1c73822213a426e27ec7ebc292d3f599fb7c2
9
+ Schema-Version: v1
10
+ -->
11
+
12
+ OCR this document: $ARGUMENTS
13
+
14
+ Read the extracting-with-ocr skill. Confirm the document needs OCR and language packs are installed. Use `xberg extract <file> --force-ocr=true` when forced OCR is appropriate. Preserve paths as separate arguments and report OCR failures.
@@ -1,19 +1,10 @@
1
1
  // AI-RULEZ :: GENERATED FILE — DO NOT EDIT
2
- // Content-Hash: blake3:0c4b20b2428ca03b76e22085bdcc856fbac85091cd3f6d157655d6c25c02714f
3
- // Source-Hash: blake3:4fe0d36f1a27b5607297a6d73b66eb41eb2264ca9a2c91ab638ab27a12ebb2cd
2
+ // Content-Hash: blake3:3fe1a33d721dfa8b49134cbe88659999cf9e481c558eb8e4b5ac81bc672c88db
3
+ // Source-Hash: blake3:662aa704cf97ad20f02258c9dcd1c73822213a426e27ec7ebc292d3f599fb7c2
4
4
  // Schema-Version: v1
5
5
 
6
- import { tool } from "@opencode-ai/plugin";
7
6
  import { spawn } from "node:child_process";
8
-
9
- const schema = tool.schema;
10
-
11
- const wireFormat = schema.enum(["text", "json", "toon"]).default("json").describe("CLI output format.");
12
-
13
- const contentFormat = schema
14
- .enum(["plain", "markdown", "djot", "html", "json"])
15
- .optional()
16
- .describe("Document content rendering format.");
7
+ import { registerBundledContent } from "../ai-rulez-content.js";
17
8
 
18
9
  function hasValue(value) {
19
10
  return value !== undefined && value !== null && value !== "";
@@ -37,14 +28,12 @@ function validateJson(value, name) {
37
28
  }
38
29
  }
39
30
 
40
- function runCli(args, context) {
41
- const directory = context?.directory ?? context?.worktree ?? process.cwd();
42
-
31
+ function runCli(args, context, directory) {
43
32
  return new Promise((resolve, reject) => {
44
33
  const child = spawn("xberg", args, {
45
34
  cwd: directory,
46
35
  env: process.env,
47
- signal: context?.abort,
36
+ signal: context?.signal,
48
37
  stdio: ["ignore", "pipe", "pipe"],
49
38
  });
50
39
 
@@ -56,8 +45,7 @@ function runCli(args, context) {
56
45
  child.on("error", (error) => {
57
46
  if (error.code === "ENOENT") {
58
47
  resolve({
59
- title: "xberg CLI not found",
60
- output:
48
+ content:
61
49
  "Install the xberg CLI with `brew install xberg-io/tap/xberg`, or run it via `npx -y @xberg-io/xberg-cli` / `uvx --from xberg-cli xberg`.",
62
50
  metadata: { exitCode: 127, command: "xberg", subcommand: args[0] },
63
51
  });
@@ -71,8 +59,7 @@ function runCli(args, context) {
71
59
  const output = [stdoutText, stderrText && `stderr:\n${stderrText}`].filter(Boolean).join("\n\n");
72
60
 
73
61
  resolve({
74
- title: exitCode === 0 ? `xberg ${args[0]}` : `xberg ${args[0]} failed`,
75
- output: output || "(no output)",
62
+ content: output || "(no output)",
76
63
  metadata: {
77
64
  exitCode,
78
65
  signal,
@@ -84,49 +71,75 @@ function runCli(args, context) {
84
71
  });
85
72
  }
86
73
 
87
- export const XbergPlugin = () =>
88
- Promise.resolve({
89
- tool: {
90
- xberg_extract: tool({
91
- description: "Extract text, tables, metadata, and images from a local document with the xberg CLI.",
92
- args: {
93
- path: schema.string().min(1).describe("Path to the local document."),
94
- format: wireFormat,
95
- content_format: contentFormat,
96
- mime_type: schema.string().min(1).optional().describe("Optional MIME type hint."),
97
- config_json: schema.string().min(2).optional().describe("Optional ExtractionConfig JSON."),
98
- },
99
- async execute(args, context) {
100
- validateJson(args.config_json, "config_json");
74
+ const wireFormat = { type: "string", enum: ["text", "json", "toon"], default: "json" };
75
+ const contentFormat = { type: "string", enum: ["plain", "markdown", "djot", "html", "json"] };
76
+
77
+ function extractTool(directory) {
78
+ return {
79
+ name: "xberg_extract",
80
+ description: "Extract text, tables, metadata, and images from a local document with the xberg CLI.",
81
+ input: {
82
+ type: "object",
83
+ properties: {
84
+ path: { type: "string", minLength: 1 },
85
+ format: wireFormat,
86
+ content_format: contentFormat,
87
+ mime_type: { type: "string", minLength: 1 },
88
+ config_json: { type: "string", minLength: 2 },
89
+ },
90
+ required: ["path"],
91
+ additionalProperties: false,
92
+ },
93
+ execute(args, context) {
94
+ validateJson(args.config_json, "config_json");
95
+ const cliArgs = ["extract", args.path, "--format", args.format ?? "json"];
96
+ pushOption(cliArgs, "--content-format", args.content_format);
97
+ pushOption(cliArgs, "--mime-type", args.mime_type);
98
+ pushOption(cliArgs, "--config-json", args.config_json);
99
+ return runCli(cliArgs, context, directory);
100
+ },
101
+ };
102
+ }
101
103
 
102
- const cliArgs = ["extract", args.path, "--format", args.format];
103
- pushOption(cliArgs, "--content-format", args.content_format);
104
- pushOption(cliArgs, "--mime-type", args.mime_type);
105
- pushOption(cliArgs, "--config-json", args.config_json);
104
+ function detectTool(directory) {
105
+ return {
106
+ name: "xberg_detect",
107
+ description: "Detect the MIME type for a local file with the xberg CLI.",
108
+ input: {
109
+ type: "object",
110
+ properties: { path: { type: "string", minLength: 1 }, format: wireFormat },
111
+ required: ["path"],
112
+ additionalProperties: false,
113
+ },
114
+ execute(args, context) {
115
+ return runCli(["detect", args.path, "--format", args.format ?? "json"], context, directory);
116
+ },
117
+ };
118
+ }
106
119
 
107
- return await runCli(cliArgs, context);
108
- },
109
- }),
110
- xberg_detect: tool({
111
- description: "Detect the MIME type for a local file with the xberg CLI.",
112
- args: {
113
- path: schema.string().min(1).describe("Path to the local file."),
114
- format: wireFormat,
115
- },
116
- async execute(args, context) {
117
- return await runCli(["detect", args.path, "--format", args.format], context);
118
- },
119
- }),
120
- xberg_formats: tool({
121
- description: "List document formats supported by the xberg CLI.",
122
- args: {
123
- format: wireFormat,
124
- },
125
- async execute(args, context) {
126
- return await runCli(["formats", "--format", args.format], context);
127
- },
128
- }),
120
+ function formatsTool(directory) {
121
+ return {
122
+ name: "xberg_formats",
123
+ description: "List document formats supported by the xberg CLI.",
124
+ input: { type: "object", properties: { format: wireFormat }, additionalProperties: false },
125
+ execute(args, context) {
126
+ return runCli(["formats", "--format", args.format ?? "json"], context, directory);
129
127
  },
130
- });
128
+ };
129
+ }
131
130
 
132
- export default XbergPlugin;
131
+ export default {
132
+ id: "xberg",
133
+ async setup(ctx) {
134
+ await registerBundledContent(ctx);
135
+ await ctx.tool.transform((editor) => {
136
+ for (const tool of [
137
+ extractTool(ctx.location.directory),
138
+ detectTool(ctx.location.directory),
139
+ formatsTool(ctx.location.directory),
140
+ ]) {
141
+ editor.add(tool);
142
+ }
143
+ });
144
+ },
145
+ };