superbee 0.4.4-pre.14 → 0.4.4-pre.16

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.
package/README.md CHANGED
@@ -78,7 +78,7 @@ first-party support promise for native Windows. An older installed version is no
78
78
  removed by this source change, and existing bundle files are not migrated by it. Review the affected release's notes before changing an existing
79
79
  Windows installation. macOS/Linux users can continue using the normal installation and setup flow.
80
80
 
81
- Run `superbee version --check` to compare your install with the current stable release.
81
+ Run `superbee version` to see which build you have. `superbee version --check` is not available on published builds yet: it exits 1 with `policy_disabled` and makes no network request. Check the npm page or `npm view superbee version` for the current release.
82
82
 
83
83
  ## First run: let your agent finish setup
84
84
 
@@ -127,6 +127,51 @@ In a hosted checkout, `superbee view read <id>` prints a View page's version on
127
127
  `superbee view update <id> --expected-version <version>` replaces the page there (sync never sends
128
128
  `views/`). Uploading a page never approves its execution.
129
129
 
130
+ ## Copying documents between bundles
131
+
132
+ `superbee doc import` copies chosen documents from another bundle with new ids and rewritten links.
133
+ Declare the source first, then review the plan before applying it:
134
+
135
+ ```sh
136
+ superbee deps add wave --bundle-id <source-bundle-id> --path ../wave
137
+ superbee doc import --from wave --with wave=../wave --select type=Theme --prefix wave
138
+ superbee doc import --from wave --with wave=../wave --select type=Theme --prefix wave --apply <plan-token>
139
+ superbee doc import --from wave --with wave=../wave --refresh <record-id>
140
+ ```
141
+
142
+ The first import command only prints the plan and its token. Applying writes new documents and one
143
+ Promotion record, and never overwrites a document. `--resume <record-id>` finishes an interrupted
144
+ run. `--refresh <record-id>` updates the copies from their sources with the same plan and apply
145
+ steps; a copy edited since the import is a conflict until you name it with `--overwrite <id>` or
146
+ `--skip <id>`. The target must be a local bundle or Git board. `superbee doc import --help` has the
147
+ full reference.
148
+
149
+ ## When a document cannot be read
150
+
151
+ Superbee bounds how much frontmatter it parses: 262,144 characters of YAML, 32 levels of nesting, and limits on
152
+ YAML alias use. Ordinary documents are far below these limits. A write over a limit is refused with
153
+ `USAGE` and nothing is stored. A document already in the bundle that is over a limit, or whose YAML
154
+ is invalid, cannot be read:
155
+
156
+ - `doc read` on it fails with `RUNTIME`. `details.frontmatter.code` is `FRONTMATTER_TOO_LARGE`,
157
+ `FRONTMATTER_TOO_DEEP`, `FRONTMATTER_ALIAS_LIMIT`, `FRONTMATTER_CYCLE` or
158
+ `FRONTMATTER_MALFORMED`, and `details.frontmatter.document` names the file.
159
+ - To find every affected document, run `superbee status` (the `malformed` count and rows) or
160
+ `superbee list` (the `skipped` rows). `search`, `query`, `index generate`, `link list`,
161
+ `link show`, `view list` and `kinds --coverage` also skip it, say so, and keep working.
162
+ - To fix it, edit the lines between the `---` markers in a text editor, or remove the document with
163
+ `superbee doc delete <id>`. `doc update` and `doc field` cannot change a document they cannot read.
164
+
165
+ On a Git board, `sync` holds a changed file it cannot read: it exits 5, names the file, and commits
166
+ and pushes nothing until you fix it. If a conflict kept a version that cannot be read, the receipt
167
+ tells you to copy your saved export over the document.
168
+
169
+ In a hosted checkout, a host document this client cannot store comes back as a `held` row named with
170
+ its `FRONTMATTER_*` code, exit 5, and everything else proceeds. The next sync places it once it is
171
+ repaired on the host. If the checkout's own local store holds a copy it cannot read, sync stops with
172
+ `store_damaged` and sends and places nothing. Reads continue and `status` reports it. Copy out any
173
+ unsent edits and make a fresh checkout, as the error's help describes.
174
+
130
175
  ## Upgrading from aslite
131
176
 
132
177
  If you installed the earlier `@holaxis/aslite` package or its marketplace plugin: install
@@ -139,6 +184,8 @@ run `npm uninstall -g @holaxis/aslite`. Existing `.agentstate-lite/` bundles and
139
184
  The [repository](https://github.com/Holaxis-ai/superbee) holds the source, the
140
185
  [CLI contract](https://github.com/Holaxis-ai/superbee/blob/main/packages/superbee/AXI-CONTRACT.md),
141
186
  and the [wire protocol](https://github.com/Holaxis-ai/superbee/blob/main/docs/WIRE-PROTOCOL.md).
187
+ Site builders can read a publication's Site declaration through `superbee/publication/site-manifest`;
188
+ see [Site declarations](https://github.com/Holaxis-ai/superbee/blob/main/packages/publication/SITE-MANIFEST.md).
142
189
 
143
190
  ## License
144
191
 
package/SKILL.md CHANGED
@@ -74,7 +74,7 @@ files, and stop. Use `superbee <command> --help` for exact current syntax rather
74
74
  copied command manual. Use `--body-file` for multiline Markdown. `list` / `query` filter metadata, not body text; `doc update` / `doc field` patch metadata while preserving omitted fields and body. For body edits, read the complete body with `doc read <id> --body-out <temp-file>` outside the bundle, edit that temporary file, then `doc update <id> --body-file <temp-file> --expected-version <head_version> --actor <actor>` using the version from the read. Re-read and reconcile on a version conflict. Never feed a truncated preview or a full-document `--out` export to `--body-file`: body input is Markdown only; tooling owns YAML frontmatter. With MCP, use available tools and version preconditions; check tool schemas rather than inventing search or patch verbs.
75
75
 
76
76
  Focused shipped material is available under `$REFS/recipes/` for portable examples,
77
- `$REFS/views/` for View authoring and examples, and `$REFS/sample-bundle/` for OKF interop. Integration authoring and explicit local source bind/run use `$REFS/author-an-integration.md`; recorded `$REFS/integrations/` examples need no credentials.
77
+ `$REFS/views/` for View authoring and examples, and `$REFS/sample-bundle/` for OKF interop. Integration authoring and explicit local source bind/run use `$REFS/author-an-integration.md`; recorded `$REFS/integrations/` examples need no credentials. `RUNTIME` with `details.frontmatter.code` means a document cannot be read: see "When a document cannot be read" in `$REFS/modeling-and-delivery.md`, which also covers `superbee doc import`.
78
78
  Read only what the accepted work requires.
79
79
 
80
80
  ## Make the value visible
@@ -93,7 +93,7 @@ When the tone fits, a single 🐝 may mark a successful Superbee outcome.
93
93
 
94
94
  ## Sync conflicts
95
95
 
96
- - A Git board and a hosted checkout share one playbook. When `superbee sync` exits 5 with conflict rows, run `superbee sync --inspect --doc <id>` to see your version and theirs, then `superbee sync --resolve keep|take|revise --doc <id>`: keep writes yours over theirs, take keeps theirs, revise keeps the document as you edited it to the merged result. `--resolve` never sends: run `superbee sync` after keep or revise. A Git board has already kept the teammate's version and saved yours aside; a hosted checkout keeps your file, treats any concurrent change to one document (even different frontmatter keys) as a conflict, and is explained in `$REFS/hosted-checkout.md`.
96
+ - A Git board and a hosted checkout share one playbook. When `superbee sync` exits 5 with conflict rows, run `superbee sync --inspect --doc <id>` to see your version and theirs, then `superbee sync --resolve keep|take|revise --doc <id>`: keep writes yours over theirs, take keeps theirs, revise keeps the document as you edited it to the merged result. `--resolve` never sends: run `superbee sync` after keep or revise. A Git board has already kept the teammate's version and saved yours aside, and only the new conflict exits 5: `status` and later `sync` runs keep naming the saved copy, with the command that discards it, until it is settled; keep is refused when your copy also differs in frontmatter, so edit the document to the result and use revise. A hosted checkout keeps your file, treats any concurrent change to one document (even different frontmatter keys) as a conflict, and is explained in `$REFS/hosted-checkout.md`.
97
97
 
98
98
  ## Host setup
99
99
 
@@ -3175,8 +3175,18 @@ function parseFrontmatter(source, context, options = {}) {
3175
3175
  }
3176
3176
  return normalizeFrontmatter(parsed, options);
3177
3177
  }
3178
+ function isStackExhaustion(error) {
3179
+ if (error instanceof RangeError) return /call stack/i.test(error.message);
3180
+ return error?.name === "InternalError" && /recursion/i.test(error.message);
3181
+ }
3178
3182
  function stringifyFrontmatter(data) {
3179
- const dumped = import_js_yaml2.default.safeDump(data).trim();
3183
+ let dumped;
3184
+ try {
3185
+ dumped = import_js_yaml2.default.safeDump(data).trim();
3186
+ } catch (error) {
3187
+ if (isStackExhaustion(error)) throw new FrontmatterRefusedError("FRONTMATTER_TOO_DEEP", `YAML nesting is deeper than ${FRONTMATTER_LIMITS.depth} levels`);
3188
+ throw error;
3189
+ }
3180
3190
  try {
3181
3191
  yamlEngine.parse(`${dumped}
3182
3192
  `);
@@ -3268,6 +3278,17 @@ function isReservedFile(relPath) {
3268
3278
  const base = toPosix(relPath).split("/").pop() ?? "";
3269
3279
  return RESERVED_FILENAMES.includes(base);
3270
3280
  }
3281
+ function isHiddenWalkEntry(name) {
3282
+ return name.startsWith(".");
3283
+ }
3284
+ function hasMarkdownExtension(name) {
3285
+ return name.toLowerCase().endsWith(".md");
3286
+ }
3287
+ function classifyWalkPath(relPath) {
3288
+ const segments = toPosix(relPath).split("/").filter((segment) => segment !== "");
3289
+ if (segments.some(isHiddenWalkEntry)) return "hidden";
3290
+ return hasMarkdownExtension(segments.at(-1) ?? "") ? "markdown" : "other";
3291
+ }
3271
3292
  function conceptIdFromPath(relPath) {
3272
3293
  const norm = toPosix(relPath).replace(/^\.?\//, "").split("/").filter((segment) => segment !== ".").join("/");
3273
3294
  return norm.endsWith(".md") ? norm.slice(0, -3) : norm;
@@ -3998,24 +4019,7 @@ function hasSourceIdentity(sources, identity) {
3998
4019
  return Array.isArray(sources) && sources.some((entry) => matchesSourceIdentity(entry, identity));
3999
4020
  }
4000
4021
 
4001
- // ../core/src/engine.ts
4002
- async function readBundleOkfVersion(backend) {
4003
- const index = await backend.readReserved("", "index.md");
4004
- if (!index) return void 0;
4005
- const value = parseLeadingFrontmatter(index.content, "index.md").okf_version;
4006
- return typeof value === "string" && value.trim() !== "" ? value : void 0;
4007
- }
4008
- function assertReadableConceptId(id) {
4009
- assertSafeConceptId(id);
4010
- const rel = pathFromConceptId(id);
4011
- if (isReservedFile(rel)) {
4012
- throw new InvalidInputError(`'${id}' is a reserved file (index.md / log.md), not a concept document.`);
4013
- }
4014
- }
4015
- async function readDocVersioned(backend, id) {
4016
- assertReadableConceptId(id);
4017
- return backend.read(id);
4018
- }
4022
+ // ../core/src/document-scan.ts
4019
4023
  function isEnoent(err) {
4020
4024
  return typeof err === "object" && err !== null && err.code === "ENOENT";
4021
4025
  }
@@ -4046,6 +4050,25 @@ async function readManyExisting(backend, ids, onMalformed, onAliased) {
4046
4050
  return out;
4047
4051
  }
4048
4052
  }
4053
+
4054
+ // ../core/src/engine.ts
4055
+ async function readBundleOkfVersion(backend) {
4056
+ const index = await backend.readReserved("", "index.md");
4057
+ if (!index) return void 0;
4058
+ const value = parseLeadingFrontmatter(index.content, "index.md").okf_version;
4059
+ return typeof value === "string" && value.trim() !== "" ? value : void 0;
4060
+ }
4061
+ function assertReadableConceptId(id) {
4062
+ assertSafeConceptId(id);
4063
+ const rel = pathFromConceptId(id);
4064
+ if (isReservedFile(rel)) {
4065
+ throw new InvalidInputError(`'${id}' is a reserved file (index.md / log.md), not a concept document.`);
4066
+ }
4067
+ }
4068
+ async function readDocVersioned(backend, id) {
4069
+ assertReadableConceptId(id);
4070
+ return backend.read(id);
4071
+ }
4049
4072
  async function scanMatching(backend, filter, onSkip) {
4050
4073
  const ids = await backend.list(filter.prefix);
4051
4074
  const results = [];
@@ -5321,7 +5344,7 @@ async function walkFiles(port, root, keep, sub = "") {
5321
5344
  const entries = await port.entries(path3.join(root, sub)) ?? [];
5322
5345
  const out = [];
5323
5346
  for (const entry of entries) {
5324
- if (entry.name.startsWith(".")) continue;
5347
+ if (isHiddenWalkEntry(entry.name)) continue;
5325
5348
  const rel = sub === "" ? entry.name : `${sub}/${entry.name}`;
5326
5349
  if (entry.kind === "directory") {
5327
5350
  out.push(...await walkFiles(port, root, keep, rel));
@@ -5580,12 +5603,15 @@ var FilesystemBackend = class _FilesystemBackend {
5580
5603
  return observed.state === "exact" && observed.value.kind === "file";
5581
5604
  }
5582
5605
  async listBlobs(prefix) {
5583
- const files = await walkFiles(this.#port, this.#root, (name) => !name.toLowerCase().endsWith(".md"));
5606
+ const files = await walkFiles(this.#port, this.#root, (name) => !hasMarkdownExtension(name));
5584
5607
  const keys2 = files.map(toPosix);
5585
5608
  const filtered = prefix ? keys2.filter((k) => k.startsWith(prefix)) : keys2;
5586
5609
  filtered.sort(compareStorageKeys);
5587
5610
  return filtered;
5588
5611
  }
5612
+ classifyChangedPath(relPath) {
5613
+ return classifyWalkPath(relPath);
5614
+ }
5589
5615
  capabilities() {
5590
5616
  return {
5591
5617
  history: false,
@@ -31129,8 +31129,18 @@ function parseFrontmatter(source, context, options = {}) {
31129
31129
  }
31130
31130
  return normalizeFrontmatter(parsed, options);
31131
31131
  }
31132
+ function isStackExhaustion(error) {
31133
+ if (error instanceof RangeError) return /call stack/i.test(error.message);
31134
+ return error?.name === "InternalError" && /recursion/i.test(error.message);
31135
+ }
31132
31136
  function stringifyFrontmatter(data) {
31133
- const dumped = import_js_yaml2.default.safeDump(data).trim();
31137
+ let dumped;
31138
+ try {
31139
+ dumped = import_js_yaml2.default.safeDump(data).trim();
31140
+ } catch (error) {
31141
+ if (isStackExhaustion(error)) throw new FrontmatterRefusedError("FRONTMATTER_TOO_DEEP", `YAML nesting is deeper than ${FRONTMATTER_LIMITS.depth} levels`);
31142
+ throw error;
31143
+ }
31134
31144
  try {
31135
31145
  yamlEngine.parse(`${dumped}
31136
31146
  `);
@@ -31222,6 +31232,17 @@ function isReservedFile(relPath) {
31222
31232
  const base = toPosix(relPath).split("/").pop() ?? "";
31223
31233
  return RESERVED_FILENAMES.includes(base);
31224
31234
  }
31235
+ function isHiddenWalkEntry(name) {
31236
+ return name.startsWith(".");
31237
+ }
31238
+ function hasMarkdownExtension(name) {
31239
+ return name.toLowerCase().endsWith(".md");
31240
+ }
31241
+ function classifyWalkPath(relPath) {
31242
+ const segments = toPosix(relPath).split("/").filter((segment) => segment !== "");
31243
+ if (segments.some(isHiddenWalkEntry)) return "hidden";
31244
+ return hasMarkdownExtension(segments.at(-1) ?? "") ? "markdown" : "other";
31245
+ }
31225
31246
  function conceptIdFromPath(relPath) {
31226
31247
  const norm = toPosix(relPath).replace(/^\.?\//, "").split("/").filter((segment) => segment !== ".").join("/");
31227
31248
  return norm.endsWith(".md") ? norm.slice(0, -3) : norm;
@@ -31959,24 +31980,7 @@ function hasSourceIdentity(sources, identity) {
31959
31980
  return Array.isArray(sources) && sources.some((entry) => matchesSourceIdentity(entry, identity));
31960
31981
  }
31961
31982
 
31962
- // ../core/src/engine.ts
31963
- async function readBundleOkfVersion(backend) {
31964
- const index2 = await backend.readReserved("", "index.md");
31965
- if (!index2) return void 0;
31966
- const value = parseLeadingFrontmatter(index2.content, "index.md").okf_version;
31967
- return typeof value === "string" && value.trim() !== "" ? value : void 0;
31968
- }
31969
- function assertReadableConceptId(id) {
31970
- assertSafeConceptId(id);
31971
- const rel = pathFromConceptId(id);
31972
- if (isReservedFile(rel)) {
31973
- throw new InvalidInputError(`'${id}' is a reserved file (index.md / log.md), not a concept document.`);
31974
- }
31975
- }
31976
- async function readDocVersioned(backend, id) {
31977
- assertReadableConceptId(id);
31978
- return backend.read(id);
31979
- }
31983
+ // ../core/src/document-scan.ts
31980
31984
  function isEnoent(err) {
31981
31985
  return typeof err === "object" && err !== null && err.code === "ENOENT";
31982
31986
  }
@@ -32007,6 +32011,25 @@ async function readManyExisting(backend, ids, onMalformed, onAliased) {
32007
32011
  return out;
32008
32012
  }
32009
32013
  }
32014
+
32015
+ // ../core/src/engine.ts
32016
+ async function readBundleOkfVersion(backend) {
32017
+ const index2 = await backend.readReserved("", "index.md");
32018
+ if (!index2) return void 0;
32019
+ const value = parseLeadingFrontmatter(index2.content, "index.md").okf_version;
32020
+ return typeof value === "string" && value.trim() !== "" ? value : void 0;
32021
+ }
32022
+ function assertReadableConceptId(id) {
32023
+ assertSafeConceptId(id);
32024
+ const rel = pathFromConceptId(id);
32025
+ if (isReservedFile(rel)) {
32026
+ throw new InvalidInputError(`'${id}' is a reserved file (index.md / log.md), not a concept document.`);
32027
+ }
32028
+ }
32029
+ async function readDocVersioned(backend, id) {
32030
+ assertReadableConceptId(id);
32031
+ return backend.read(id);
32032
+ }
32010
32033
  async function scanMatching(backend, filter, onSkip) {
32011
32034
  const ids = await backend.list(filter.prefix);
32012
32035
  const results = [];
@@ -33282,7 +33305,7 @@ async function walkFiles(port, root, keep, sub = "") {
33282
33305
  const entries = await port.entries(path3.join(root, sub)) ?? [];
33283
33306
  const out = [];
33284
33307
  for (const entry of entries) {
33285
- if (entry.name.startsWith(".")) continue;
33308
+ if (isHiddenWalkEntry(entry.name)) continue;
33286
33309
  const rel = sub === "" ? entry.name : `${sub}/${entry.name}`;
33287
33310
  if (entry.kind === "directory") {
33288
33311
  out.push(...await walkFiles(port, root, keep, rel));
@@ -33541,12 +33564,15 @@ var FilesystemBackend = class _FilesystemBackend {
33541
33564
  return observed.state === "exact" && observed.value.kind === "file";
33542
33565
  }
33543
33566
  async listBlobs(prefix) {
33544
- const files = await walkFiles(this.#port, this.#root, (name) => !name.toLowerCase().endsWith(".md"));
33567
+ const files = await walkFiles(this.#port, this.#root, (name) => !hasMarkdownExtension(name));
33545
33568
  const keys2 = files.map(toPosix);
33546
33569
  const filtered = prefix ? keys2.filter((k) => k.startsWith(prefix)) : keys2;
33547
33570
  filtered.sort(compareStorageKeys);
33548
33571
  return filtered;
33549
33572
  }
33573
+ classifyChangedPath(relPath) {
33574
+ return classifyWalkPath(relPath);
33575
+ }
33550
33576
  capabilities() {
33551
33577
  return {
33552
33578
  history: false,