superbee 0.4.0-pre.3 → 0.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.
- package/README.md +1 -5
- package/dist/superbee.mjs +150 -74
- package/package.json +2 -2
- package/references/hosted-checkout.md +5 -4
package/README.md
CHANGED
|
@@ -53,11 +53,7 @@ Requires Node.js 20 or newer on macOS and Linux. Native Windows is not supported
|
|
|
53
53
|
npm install -g superbee
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
Stable releases publish on npm's `latest` tag and prereleases on `next`.
|
|
57
|
-
|
|
58
|
-
```sh
|
|
59
|
-
npm install -g superbee@next
|
|
60
|
-
```
|
|
56
|
+
Stable releases publish on npm's `latest` tag and prereleases on `next`.
|
|
61
57
|
|
|
62
58
|
Windows adapters and the `superbee-windows` executable live in a separate repository and are
|
|
63
59
|
not included in `superbee`. Most Windows users should run Superbee in WSL2, where npm sees a
|
package/dist/superbee.mjs
CHANGED
|
@@ -44,7 +44,7 @@ var __toESM = (mod, isNodeMode, target2) => (target2 = mod != null ? __create(__
|
|
|
44
44
|
var define_SUPERBEE_BUILD_IDENTITY_default;
|
|
45
45
|
var init_define_SUPERBEE_BUILD_IDENTITY = __esm({
|
|
46
46
|
"<define:__SUPERBEE_BUILD_IDENTITY__>"() {
|
|
47
|
-
define_SUPERBEE_BUILD_IDENTITY_default = { schema: "superbee.build-identity.v1", package: { name: "superbee", version: "0.4.0
|
|
47
|
+
define_SUPERBEE_BUILD_IDENTITY_default = { schema: "superbee.build-identity.v1", package: { name: "superbee", version: "0.4.0" }, source: { commit: "b2911c7f26c60d6648ad2d53a1962259cb8b3a08", dirty: false }, artifact: { channel: "npm-package" }, compatibility_contracts: { skill: 1, hook: 1, mcp: 1 } };
|
|
48
48
|
}
|
|
49
49
|
});
|
|
50
50
|
|
|
@@ -11645,41 +11645,6 @@ var init_src = __esm({
|
|
|
11645
11645
|
}
|
|
11646
11646
|
});
|
|
11647
11647
|
|
|
11648
|
-
// ../cli/src/filesystem-runtime.ts
|
|
11649
|
-
function cliFilesystemRuntime() {
|
|
11650
|
-
const host = currentFilesystemHost();
|
|
11651
|
-
if (!host) return defaultRuntime ??= createFilesystemRuntime();
|
|
11652
|
-
let runtime = runtimes.get(host);
|
|
11653
|
-
if (!runtime) {
|
|
11654
|
-
runtime = createFilesystemRuntime(host);
|
|
11655
|
-
runtimes.set(host, runtime);
|
|
11656
|
-
}
|
|
11657
|
-
return runtime;
|
|
11658
|
-
}
|
|
11659
|
-
function configuredBundle(root) {
|
|
11660
|
-
return { root, backend: cliFilesystemRuntime().backend(root) };
|
|
11661
|
-
}
|
|
11662
|
-
function configuredInitBundle(root, options) {
|
|
11663
|
-
return cliFilesystemRuntime().initBundle(root, options);
|
|
11664
|
-
}
|
|
11665
|
-
function withCliFilesystemMutationLock(target2, body, options) {
|
|
11666
|
-
return cliFilesystemRuntime().withMutationLock(target2, body, options);
|
|
11667
|
-
}
|
|
11668
|
-
function isFilesystemBundle(bundle) {
|
|
11669
|
-
return !bundle.backend || bundle.backend instanceof FilesystemBackend;
|
|
11670
|
-
}
|
|
11671
|
-
var runtimes, defaultRuntime;
|
|
11672
|
-
var init_filesystem_runtime = __esm({
|
|
11673
|
-
"../cli/src/filesystem-runtime.ts"() {
|
|
11674
|
-
"use strict";
|
|
11675
|
-
init_define_SUPERBEE_BUILD_IDENTITY();
|
|
11676
|
-
init_define_SUPERBEE_UPDATE_POLICY();
|
|
11677
|
-
init_src();
|
|
11678
|
-
init_runtime_context();
|
|
11679
|
-
runtimes = /* @__PURE__ */ new WeakMap();
|
|
11680
|
-
}
|
|
11681
|
-
});
|
|
11682
|
-
|
|
11683
11648
|
// ../board-git/src/errors.ts
|
|
11684
11649
|
function isBoardGitError(v2) {
|
|
11685
11650
|
if (typeof v2 !== "object" || v2 === null) return false;
|
|
@@ -14113,6 +14078,41 @@ var init_src2 = __esm({
|
|
|
14113
14078
|
}
|
|
14114
14079
|
});
|
|
14115
14080
|
|
|
14081
|
+
// ../cli/src/filesystem-runtime.ts
|
|
14082
|
+
function cliFilesystemRuntime() {
|
|
14083
|
+
const host = currentFilesystemHost();
|
|
14084
|
+
if (!host) return defaultRuntime ??= createFilesystemRuntime();
|
|
14085
|
+
let runtime = runtimes.get(host);
|
|
14086
|
+
if (!runtime) {
|
|
14087
|
+
runtime = createFilesystemRuntime(host);
|
|
14088
|
+
runtimes.set(host, runtime);
|
|
14089
|
+
}
|
|
14090
|
+
return runtime;
|
|
14091
|
+
}
|
|
14092
|
+
function configuredBundle(root) {
|
|
14093
|
+
return { root, backend: cliFilesystemRuntime().backend(root) };
|
|
14094
|
+
}
|
|
14095
|
+
function configuredInitBundle(root, options) {
|
|
14096
|
+
return cliFilesystemRuntime().initBundle(root, options);
|
|
14097
|
+
}
|
|
14098
|
+
function withCliFilesystemMutationLock(target2, body, options) {
|
|
14099
|
+
return cliFilesystemRuntime().withMutationLock(target2, body, options);
|
|
14100
|
+
}
|
|
14101
|
+
function isFilesystemBundle(bundle) {
|
|
14102
|
+
return !bundle.backend || bundle.backend instanceof FilesystemBackend;
|
|
14103
|
+
}
|
|
14104
|
+
var runtimes, defaultRuntime;
|
|
14105
|
+
var init_filesystem_runtime = __esm({
|
|
14106
|
+
"../cli/src/filesystem-runtime.ts"() {
|
|
14107
|
+
"use strict";
|
|
14108
|
+
init_define_SUPERBEE_BUILD_IDENTITY();
|
|
14109
|
+
init_define_SUPERBEE_UPDATE_POLICY();
|
|
14110
|
+
init_src();
|
|
14111
|
+
init_runtime_context();
|
|
14112
|
+
runtimes = /* @__PURE__ */ new WeakMap();
|
|
14113
|
+
}
|
|
14114
|
+
});
|
|
14115
|
+
|
|
14116
14116
|
// ../cli/src/actor-guidance.ts
|
|
14117
14117
|
function legacyEnvCleanup(env) {
|
|
14118
14118
|
return env[LEGACY_ACTOR_VARIABLE] === void 0 ? "" : `; unset ${LEGACY_ACTOR_VARIABLE}`;
|
|
@@ -15317,7 +15317,7 @@ var init_env_policy = __esm({
|
|
|
15317
15317
|
});
|
|
15318
15318
|
|
|
15319
15319
|
// ../cli/src/bundle.ts
|
|
15320
|
-
import { constants as constants3, promises as fs3 } from "node:fs";
|
|
15320
|
+
import { closeSync as closeSync3, constants as constants3, fstatSync as fstatSync3, openSync as openSync3, promises as fs3, readSync } from "node:fs";
|
|
15321
15321
|
import path14 from "node:path";
|
|
15322
15322
|
async function exists(p2) {
|
|
15323
15323
|
try {
|
|
@@ -15327,6 +15327,21 @@ async function exists(p2) {
|
|
|
15327
15327
|
return false;
|
|
15328
15328
|
}
|
|
15329
15329
|
}
|
|
15330
|
+
function looksLikeBundle(dir) {
|
|
15331
|
+
let fd;
|
|
15332
|
+
try {
|
|
15333
|
+
fd = openSync3(path14.join(dir, "index.md"), constants3.O_RDONLY | (constants3.O_NOFOLLOW ?? 0) | (constants3.O_NONBLOCK ?? 0));
|
|
15334
|
+
if (!fstatSync3(fd).isFile()) return false;
|
|
15335
|
+
const buffer = Buffer.alloc(16 * 1024);
|
|
15336
|
+
const text4 = buffer.subarray(0, readSync(fd, buffer, 0, buffer.length, 0)).toString("utf8");
|
|
15337
|
+
const block = /^---\r?\n([\s\S]*?)\r?\n---\r?(?:\n|$)/.exec(text4);
|
|
15338
|
+
return block !== null && /^okf_version\s*:/m.test(block[1]);
|
|
15339
|
+
} catch {
|
|
15340
|
+
return false;
|
|
15341
|
+
} finally {
|
|
15342
|
+
if (fd !== void 0) closeSync3(fd);
|
|
15343
|
+
}
|
|
15344
|
+
}
|
|
15330
15345
|
function resolveTargetDir(dirFlag) {
|
|
15331
15346
|
return path14.resolve(dirFlag ?? process.cwd());
|
|
15332
15347
|
}
|
|
@@ -16551,7 +16566,7 @@ async function openBundle(dirFlag, remoteFlag) {
|
|
|
16551
16566
|
await assertResolvedLocalRouteIdentity(route);
|
|
16552
16567
|
return route.bundle;
|
|
16553
16568
|
}
|
|
16554
|
-
var CONVENTIONAL_BUNDLE_DIR_NAME, LEGACY_CONVENTIONAL_BUNDLE_DIR_NAME, SUPERBEE_PROJECT_BINDING_FILE_NAME, PROJECT_BINDING_FILE_NAME, PROJECT_BINDING_FILE_NAMES, HOSTED_PROBE_STATUSES, HOSTED_PROBE_PATH, HOSTED_PROBE_TIMEOUT_MS, API_KEY_ENV_VAR, SUPERBEE_API_KEY_ENV_VAR, REMOTE_ENV_VAR, createOnlyFs;
|
|
16569
|
+
var TOP_LEVEL_BUNDLE_MOVE, CONVENTIONAL_BUNDLE_DIR_NAME, LEGACY_CONVENTIONAL_BUNDLE_DIR_NAME, SUPERBEE_PROJECT_BINDING_FILE_NAME, PROJECT_BINDING_FILE_NAME, PROJECT_BINDING_FILE_NAMES, HOSTED_PROBE_STATUSES, HOSTED_PROBE_PATH, HOSTED_PROBE_TIMEOUT_MS, API_KEY_ENV_VAR, SUPERBEE_API_KEY_ENV_VAR, REMOTE_ENV_VAR, createOnlyFs;
|
|
16555
16570
|
var init_bundle2 = __esm({
|
|
16556
16571
|
"../cli/src/bundle.ts"() {
|
|
16557
16572
|
"use strict";
|
|
@@ -16571,6 +16586,7 @@ var init_bundle2 = __esm({
|
|
|
16571
16586
|
init_bound_board_owner();
|
|
16572
16587
|
init_env_policy();
|
|
16573
16588
|
init_command_text();
|
|
16589
|
+
TOP_LEVEL_BUNDLE_MOVE = `move the bundle at the work tree's top into ${BUNDLE_DIR}/ (index.md, conventions/ and its document folders; \`git mv\` if they are committed, \`mv\` if not)`;
|
|
16574
16590
|
CONVENTIONAL_BUNDLE_DIR_NAME = BUNDLE_DIR;
|
|
16575
16591
|
LEGACY_CONVENTIONAL_BUNDLE_DIR_NAME = LEGACY_BUNDLE_DIR;
|
|
16576
16592
|
SUPERBEE_PROJECT_BINDING_FILE_NAME = ".superbee.json";
|
|
@@ -18702,15 +18718,30 @@ var init_recipe_source = __esm({
|
|
|
18702
18718
|
import { parseArgs } from "node:util";
|
|
18703
18719
|
import { existsSync as existsSync5 } from "node:fs";
|
|
18704
18720
|
import path17 from "node:path";
|
|
18705
|
-
function
|
|
18721
|
+
function gitWorkTreeTop(dir) {
|
|
18706
18722
|
let cur = path17.resolve(dir);
|
|
18723
|
+
let found = null;
|
|
18707
18724
|
for (; ; ) {
|
|
18708
|
-
if (existsSync5(path17.join(cur, ".git")))
|
|
18725
|
+
if (existsSync5(path17.join(cur, ".git"))) {
|
|
18726
|
+
if (!BUNDLE_DIRS.includes(path17.basename(cur))) return cur;
|
|
18727
|
+
found ??= cur;
|
|
18728
|
+
}
|
|
18709
18729
|
const parent = path17.dirname(cur);
|
|
18710
|
-
if (parent === cur) return
|
|
18730
|
+
if (parent === cur) return found;
|
|
18711
18731
|
cur = parent;
|
|
18712
18732
|
}
|
|
18713
18733
|
}
|
|
18734
|
+
function plainInitDir(cwd) {
|
|
18735
|
+
const top = gitWorkTreeTop(cwd);
|
|
18736
|
+
if (top === null) return void 0;
|
|
18737
|
+
for (let dir = path17.resolve(cwd); ; dir = path17.dirname(dir)) {
|
|
18738
|
+
if (existsSync5(path17.join(dir, "index.md"))) return dir;
|
|
18739
|
+
const conventional = BUNDLE_DIRS.find((name) => existsSync5(path17.join(dir, name, "index.md")));
|
|
18740
|
+
if (conventional) return path17.join(dir, conventional);
|
|
18741
|
+
if (dir === top || path17.dirname(dir) === dir) break;
|
|
18742
|
+
}
|
|
18743
|
+
return path17.join(top, BUNDLE_DIR);
|
|
18744
|
+
}
|
|
18714
18745
|
async function init(argv2, deps = {}) {
|
|
18715
18746
|
const stdout = deps.stdout ?? ((s) => void process.stdout.write(s));
|
|
18716
18747
|
const { values } = parseLeafOrUsage(
|
|
@@ -18770,7 +18801,7 @@ async function init(argv2, deps = {}) {
|
|
|
18770
18801
|
);
|
|
18771
18802
|
let root;
|
|
18772
18803
|
let bundle;
|
|
18773
|
-
const initDir = boundRoute?.kind === "bound-local" ? boundRoute.bundle.root : values.dir;
|
|
18804
|
+
const initDir = boundRoute?.kind === "bound-local" ? boundRoute.bundle.root : values.dir ?? (boundRoute ? void 0 : plainInitDir(process.cwd()));
|
|
18774
18805
|
if (createOnly) {
|
|
18775
18806
|
if (boundRoute) await assertResolvedLocalRouteIdentity(boundRoute);
|
|
18776
18807
|
const result3 = await withCreateOnlyTarget(
|
|
@@ -18803,8 +18834,11 @@ async function init(argv2, deps = {}) {
|
|
|
18803
18834
|
}
|
|
18804
18835
|
const receipt2 = { init: "ok", root: bundle.root, recipe: recipeApplied };
|
|
18805
18836
|
if (warnings.length > 0) receipt2.warnings = warnings;
|
|
18806
|
-
|
|
18807
|
-
|
|
18837
|
+
const top = gitWorkTreeTop(root);
|
|
18838
|
+
if (top !== null) {
|
|
18839
|
+
const joinFirst = `this bundle is local until shared \u2014 if the project already shares a board, \`${cliInvocation()} sync\` joins it (never init there, that mints a divergent second bundle); `;
|
|
18840
|
+
const conventional = path17.dirname(root) === top && BUNDLE_DIRS.includes(path17.basename(root));
|
|
18841
|
+
receipt2.hint = !conventional ? path17.resolve(root) === path17.resolve(top) ? `${joinFirst}${TOP_LEVEL_BUNDLE_MOVE}, then \`${cliInvocation()} sync --establish\`` : `${joinFirst}only the work tree's ${BUNDLE_DIR}/ folder can be shared, and this bundle is not it (\`${cliInvocation()} init --dir ${commandQuoted(path17.join(top, BUNDLE_DIR))}\` makes that one)` : existsSync5(path17.join(root, ".git")) ? `this is the project's shared board; \`${cliInvocation()} sync\` keeps it current` : `${joinFirst}to start sharing this one, \`${cliInvocation()} sync --establish\``;
|
|
18808
18842
|
}
|
|
18809
18843
|
const target2 = values.dir === void 0 ? commandLiteral("") : commandFragment` --dir ${commandQuoted(root)}`;
|
|
18810
18844
|
const help2 = [];
|
|
@@ -18825,6 +18859,7 @@ var init_init = __esm({
|
|
|
18825
18859
|
init_define_SUPERBEE_UPDATE_POLICY();
|
|
18826
18860
|
init_output2();
|
|
18827
18861
|
init_src();
|
|
18862
|
+
init_src2();
|
|
18828
18863
|
init_filesystem_runtime();
|
|
18829
18864
|
init_bundle2();
|
|
18830
18865
|
init_errors4();
|
|
@@ -18841,7 +18876,9 @@ Usage:
|
|
|
18841
18876
|
superbee init [--dir <path>] [--okf-version <v>] [--recipe <name-or-path>] [--create-only]
|
|
18842
18877
|
|
|
18843
18878
|
Options:
|
|
18844
|
-
--dir <path> Directory to init the bundle in (default: the current directory
|
|
18879
|
+
--dir <path> Directory to init the bundle in (default: the current directory; inside
|
|
18880
|
+
a Git work tree, its top-level .superbee/ folder, which
|
|
18881
|
+
'sync --establish' shares, unless the current directory is a bundle)
|
|
18845
18882
|
--okf-version <v> Compatibility override for legacy integrations (normally omit; use 0.1
|
|
18846
18883
|
only when a legacy consumer requires it). Other versions remain readable
|
|
18847
18884
|
but are not authorable
|
|
@@ -18863,10 +18900,10 @@ Options:
|
|
|
18863
18900
|
});
|
|
18864
18901
|
|
|
18865
18902
|
// ../cli/src/commands/doc/common.ts
|
|
18866
|
-
import { fstatSync as
|
|
18903
|
+
import { fstatSync as fstatSync4 } from "node:fs";
|
|
18867
18904
|
function hasRealStdinInput() {
|
|
18868
18905
|
try {
|
|
18869
|
-
const stats =
|
|
18906
|
+
const stats = fstatSync4(0);
|
|
18870
18907
|
if (stats.isFIFO() || stats.isFile() || stats.isSocket()) return true;
|
|
18871
18908
|
return currentHost().hasAdditionalStdinInput(stats);
|
|
18872
18909
|
} catch {
|
|
@@ -19136,9 +19173,11 @@ Options:
|
|
|
19136
19173
|
--offset <n> Page the record's body: 'body' is the page starting at character n (a
|
|
19137
19174
|
JavaScript string index, 0 for the first page) and 'range' says where it sits:
|
|
19138
19175
|
offset, end, total_chars, total_bytes, complete, and next_offset while more
|
|
19139
|
-
follows.
|
|
19140
|
-
|
|
19141
|
-
|
|
19176
|
+
follows. The page without next_offset is the last; complete is true only
|
|
19177
|
+
when the whole body fit in one page, so page on next_offset. A page ends
|
|
19178
|
+
within --max-bytes of UTF-8, never splits a character, and ends just after a
|
|
19179
|
+
line where one is near its end. A page is NOT the document: never write it
|
|
19180
|
+
back as the body.
|
|
19142
19181
|
--max-bytes <n> The page's body bound in UTF-8 bytes, 1024 to 983040 (default 32768). Implies
|
|
19143
19182
|
--offset 0 when --offset is absent.
|
|
19144
19183
|
--expected-version <v>
|
|
@@ -20862,7 +20901,7 @@ var init_binding = __esm({
|
|
|
20862
20901
|
});
|
|
20863
20902
|
|
|
20864
20903
|
// ../cli/src/nofollow-read.ts
|
|
20865
|
-
import { closeSync as
|
|
20904
|
+
import { closeSync as closeSync4, constants as constants5, fstatSync as fstatSync5, lstatSync as lstatSync4, openSync as openSync4, readFileSync as readFileSync8 } from "node:fs";
|
|
20866
20905
|
function leafIdentity(filePath) {
|
|
20867
20906
|
try {
|
|
20868
20907
|
const leaf = lstatSync4(filePath);
|
|
@@ -20885,19 +20924,19 @@ function readLeafSync(filePath, flags, hasNoFollow) {
|
|
|
20885
20924
|
}
|
|
20886
20925
|
let descriptor;
|
|
20887
20926
|
try {
|
|
20888
|
-
descriptor =
|
|
20927
|
+
descriptor = openSync4(filePath, flags);
|
|
20889
20928
|
} catch (error51) {
|
|
20890
20929
|
return error51.code === "ENOENT" ? { state: "missing" } : { state: "unsafe" };
|
|
20891
20930
|
}
|
|
20892
20931
|
try {
|
|
20893
|
-
const opened =
|
|
20932
|
+
const opened = fstatSync5(descriptor);
|
|
20894
20933
|
if (!opened.isFile()) return { state: "unsafe" };
|
|
20895
20934
|
if (expected && (opened.dev !== expected.dev || opened.ino !== expected.ino)) return { state: "unsafe" };
|
|
20896
20935
|
return { state: "present", bytes: readFileSync8(descriptor) };
|
|
20897
20936
|
} catch {
|
|
20898
20937
|
return { state: "unsafe" };
|
|
20899
20938
|
} finally {
|
|
20900
|
-
|
|
20939
|
+
closeSync4(descriptor);
|
|
20901
20940
|
}
|
|
20902
20941
|
}
|
|
20903
20942
|
function readRegularFileTextNoFollowSync(filePath) {
|
|
@@ -22118,7 +22157,7 @@ async function hostedWriteHost(flag, home2, retry) {
|
|
|
22118
22157
|
if (candidates.size > 1) {
|
|
22119
22158
|
const hosts = [...candidates].sort();
|
|
22120
22159
|
const signedOut = hosts.filter((host) => !stored.includes(host));
|
|
22121
|
-
throw new CliError("USAGE", `more than one hosted Superbee host could be meant (${hosts.join(", ")}); name the one this
|
|
22160
|
+
throw new CliError("USAGE", `more than one hosted Superbee host could be meant (${hosts.join(", ")}); name the one this command is for with --host`, {
|
|
22122
22161
|
details: {
|
|
22123
22162
|
reason: "ambiguous_host",
|
|
22124
22163
|
hosts,
|
|
@@ -25875,6 +25914,9 @@ function bundleOf(target2) {
|
|
|
25875
25914
|
function baseKey(id) {
|
|
25876
25915
|
return `base:${id}`;
|
|
25877
25916
|
}
|
|
25917
|
+
function holdsVersion(base, version3) {
|
|
25918
|
+
return base !== void 0 && base.version === version3 && base.refetch !== true;
|
|
25919
|
+
}
|
|
25878
25920
|
function baseRow(id, base) {
|
|
25879
25921
|
return { key: baseKey(id), value: base };
|
|
25880
25922
|
}
|
|
@@ -26584,7 +26626,7 @@ async function settleIntent(local, requestId2, outcome, attempts, options = {})
|
|
|
26584
26626
|
requestId2,
|
|
26585
26627
|
"in_flight",
|
|
26586
26628
|
{ state: "acknowledged", attempts, acknowledgedVersion: outcome.version, ...finding ? { finding } : {} },
|
|
26587
|
-
{ meta: [baseRow(current.target, { version: outcome.version, content: current.content }), acknowledged] }
|
|
26629
|
+
{ meta: [baseRow(current.target, { version: outcome.version, content: current.content, ...finding ? { refetch: true } : {} }), acknowledged] }
|
|
26588
26630
|
);
|
|
26589
26631
|
}
|
|
26590
26632
|
case "conflict": {
|
|
@@ -26895,7 +26937,7 @@ async function pull(local, remote, options = {}) {
|
|
|
26895
26937
|
return;
|
|
26896
26938
|
}
|
|
26897
26939
|
const base = fenced ? fenced.base : await backend.readMeta(baseKey(id));
|
|
26898
|
-
if (base
|
|
26940
|
+
if (holdsVersion(base, head.version)) {
|
|
26899
26941
|
report.unchanged.push(id);
|
|
26900
26942
|
return;
|
|
26901
26943
|
}
|
|
@@ -26972,7 +27014,7 @@ async function pull(local, remote, options = {}) {
|
|
|
26972
27014
|
continue;
|
|
26973
27015
|
}
|
|
26974
27016
|
const base = await backend.readMeta(baseKey(head.id));
|
|
26975
|
-
if (base
|
|
27017
|
+
if (holdsVersion(base, head.version)) report.unchanged.push(head.id);
|
|
26976
27018
|
else {
|
|
26977
27019
|
candidates.push(head.id);
|
|
26978
27020
|
versions.set(head.id, head.version);
|
|
@@ -33855,6 +33897,13 @@ function establishNextSteps(inv) {
|
|
|
33855
33897
|
function assertPlainBundleShape(bundlePath, inv) {
|
|
33856
33898
|
const bundleDir = path31.basename(bundlePath);
|
|
33857
33899
|
const runInitHelp = `${inv} init --create-only --dir ${commandToken(BUNDLE_DIR)}`;
|
|
33900
|
+
if (bundleDir === BUNDLE_DIR && !existsSync9(path31.join(bundlePath, "index.md")) && looksLikeBundle(path31.dirname(bundlePath))) {
|
|
33901
|
+
throw new CliError(
|
|
33902
|
+
"RUNTIME",
|
|
33903
|
+
`this repository's top folder is itself a bundle, and establish shares only a '${bundleDir}/' folder \u2014 ${TOP_LEVEL_BUNDLE_MOVE}, then re-run establish`,
|
|
33904
|
+
{ help: `mkdir -p ${bundleDir} && git mv index.md ${bundleDir}/ (plain mv if never committed), then the same for conventions/ if present and each document folder, then ${inv} sync --establish` }
|
|
33905
|
+
);
|
|
33906
|
+
}
|
|
33858
33907
|
if (!existsSync9(bundlePath)) {
|
|
33859
33908
|
throw new CliError(
|
|
33860
33909
|
"RUNTIME",
|
|
@@ -35774,6 +35823,25 @@ function intentFrontmatter(row4, okfVersion) {
|
|
|
35774
35823
|
return null;
|
|
35775
35824
|
}
|
|
35776
35825
|
}
|
|
35826
|
+
async function hostRestamped(store, acknowledged, folder, projection) {
|
|
35827
|
+
const ids = /* @__PURE__ */ new Set();
|
|
35828
|
+
for (const id of acknowledged.keys()) {
|
|
35829
|
+
const base = await store.readMeta(baseKey(id));
|
|
35830
|
+
if (base?.refetch !== true) continue;
|
|
35831
|
+
const bytes = await fs13.readFile(path34.join(folder, `${id}.md`)).catch(() => null);
|
|
35832
|
+
const entry = projection.files[id];
|
|
35833
|
+
if (bytes !== null && entry !== void 0 && !entry.deleted && digestOf(bytes) === entry.digest) ids.add(id);
|
|
35834
|
+
else {
|
|
35835
|
+
const { refetch: _refetch, ...kept } = base;
|
|
35836
|
+
try {
|
|
35837
|
+
await store.writeMeta(baseKey(id), kept, { expected: { present: true, value: base } });
|
|
35838
|
+
} catch (error51) {
|
|
35839
|
+
if (!(error51 instanceof JournalGuardConflict)) throw error51;
|
|
35840
|
+
}
|
|
35841
|
+
}
|
|
35842
|
+
}
|
|
35843
|
+
return ids;
|
|
35844
|
+
}
|
|
35777
35845
|
function recordLanded(landed, row4, okfVersion) {
|
|
35778
35846
|
const frontmatter = intentFrontmatter(row4, okfVersion);
|
|
35779
35847
|
if (isConventionId(row4.target)) {
|
|
@@ -36037,7 +36105,8 @@ async function runSync(binding, values, deps, mode) {
|
|
|
36037
36105
|
throw await readFailure(error51, session, resumeCommand, await unsent());
|
|
36038
36106
|
}
|
|
36039
36107
|
const hold = await recordHostHold(store, heldSink.value, scan3.hold);
|
|
36040
|
-
const
|
|
36108
|
+
const restamped = await hostRestamped(store, outcome.acknowledged, binding.path, projection);
|
|
36109
|
+
const second = first.report.held.some((id) => outcome.acknowledged.has(id)) || restamped.size > 0 ? await pullAndExport() : null;
|
|
36041
36110
|
const pulled = second?.report ?? first.report;
|
|
36042
36111
|
let root;
|
|
36043
36112
|
try {
|
|
@@ -36046,7 +36115,14 @@ async function runSync(binding, values, deps, mode) {
|
|
|
36046
36115
|
throw await readFailure(error51, session, resumeCommand, await unsent());
|
|
36047
36116
|
}
|
|
36048
36117
|
const exported = {
|
|
36049
|
-
|
|
36118
|
+
// A document this run sent and only took back with the host's stamps is not one received
|
|
36119
|
+
// (accepted: a change by someone else in the same instant is not counted either, and after
|
|
36120
|
+
// a crash between the acknowledgement and this pull, the next run counts it once).
|
|
36121
|
+
placed: [
|
|
36122
|
+
...first.placed.placed,
|
|
36123
|
+
...(second?.placed.placed ?? []).filter((id) => !restamped.has(id)),
|
|
36124
|
+
...root.refreshed ? [ROOT_INDEX] : []
|
|
36125
|
+
],
|
|
36050
36126
|
removed: [...first.placed.removed, ...second?.placed.removed ?? []],
|
|
36051
36127
|
kept: second?.placed.kept ?? first.placed.kept,
|
|
36052
36128
|
held: second?.placed.held ?? first.placed.held
|
|
@@ -125428,15 +125504,15 @@ __export(update_orientation_exports, {
|
|
|
125428
125504
|
});
|
|
125429
125505
|
import { randomBytes as randomBytes7 } from "node:crypto";
|
|
125430
125506
|
import {
|
|
125431
|
-
closeSync as
|
|
125507
|
+
closeSync as closeSync5,
|
|
125432
125508
|
constants as constants7,
|
|
125433
125509
|
fchmodSync as fchmodSync2,
|
|
125434
|
-
fstatSync as
|
|
125510
|
+
fstatSync as fstatSync6,
|
|
125435
125511
|
fsyncSync as fsyncSync2,
|
|
125436
125512
|
linkSync as linkSync3,
|
|
125437
125513
|
lstatSync as lstatSync9,
|
|
125438
|
-
openSync as
|
|
125439
|
-
readSync,
|
|
125514
|
+
openSync as openSync5,
|
|
125515
|
+
readSync as readSync2,
|
|
125440
125516
|
renameSync as renameSync7,
|
|
125441
125517
|
unlinkSync as unlinkSync3,
|
|
125442
125518
|
writeFileSync as writeFileSync5
|
|
@@ -125607,17 +125683,17 @@ function inspectStateDirectory(home2) {
|
|
|
125607
125683
|
const flags = constants7.O_RDONLY | constants7.O_DIRECTORY | (constants7.O_NOFOLLOW ?? 0);
|
|
125608
125684
|
let descriptor;
|
|
125609
125685
|
try {
|
|
125610
|
-
descriptor =
|
|
125686
|
+
descriptor = openSync5(directory, flags);
|
|
125611
125687
|
} catch (error51) {
|
|
125612
125688
|
return errno2(error51) === "ENOENT" ? "missing" : "unsafe";
|
|
125613
125689
|
}
|
|
125614
125690
|
try {
|
|
125615
|
-
const stats =
|
|
125691
|
+
const stats = fstatSync6(descriptor);
|
|
125616
125692
|
return stats.isDirectory() && privateOwnerAndMode(home2, stats, 448) ? "safe" : "unsafe";
|
|
125617
125693
|
} catch {
|
|
125618
125694
|
return "unsafe";
|
|
125619
125695
|
} finally {
|
|
125620
|
-
|
|
125696
|
+
closeSync5(descriptor);
|
|
125621
125697
|
}
|
|
125622
125698
|
}
|
|
125623
125699
|
function readPrivateFile(home2, filePath, maxBytes) {
|
|
@@ -125631,19 +125707,19 @@ function readPrivateFile(home2, filePath, maxBytes) {
|
|
|
125631
125707
|
const flags = constants7.O_RDONLY | (constants7.O_NOFOLLOW ?? 0) | (constants7.O_NONBLOCK ?? 0);
|
|
125632
125708
|
let descriptor;
|
|
125633
125709
|
try {
|
|
125634
|
-
descriptor =
|
|
125710
|
+
descriptor = openSync5(filePath, flags);
|
|
125635
125711
|
} catch (error51) {
|
|
125636
125712
|
return errno2(error51) === "ENOENT" ? { state: "missing" } : { state: "unsafe" };
|
|
125637
125713
|
}
|
|
125638
125714
|
try {
|
|
125639
|
-
const stats =
|
|
125715
|
+
const stats = fstatSync6(descriptor);
|
|
125640
125716
|
if (!stats.isFile() || stats.dev !== before.dev || stats.ino !== before.ino || !privateOwnerAndMode(home2, stats, 384) || stats.size > maxBytes) {
|
|
125641
125717
|
return { state: "unsafe" };
|
|
125642
125718
|
}
|
|
125643
125719
|
const bytes = Buffer.allocUnsafe(maxBytes + 1);
|
|
125644
125720
|
let offset = 0;
|
|
125645
125721
|
while (offset < bytes.byteLength) {
|
|
125646
|
-
const count5 =
|
|
125722
|
+
const count5 = readSync2(descriptor, bytes, offset, bytes.byteLength - offset, null);
|
|
125647
125723
|
if (count5 === 0) break;
|
|
125648
125724
|
offset += count5;
|
|
125649
125725
|
}
|
|
@@ -125663,7 +125739,7 @@ function readPrivateFile(home2, filePath, maxBytes) {
|
|
|
125663
125739
|
} catch {
|
|
125664
125740
|
return { state: "unsafe" };
|
|
125665
125741
|
} finally {
|
|
125666
|
-
|
|
125742
|
+
closeSync5(descriptor);
|
|
125667
125743
|
}
|
|
125668
125744
|
}
|
|
125669
125745
|
function inspectUpdateCache(input) {
|
|
@@ -125745,7 +125821,7 @@ function writeCompleteTemp(home2, directory, baseName, content3, maxBytes) {
|
|
|
125745
125821
|
const temporary = join17(directory, `.${baseName}.${randomBytes7(16).toString("hex")}.tmp`);
|
|
125746
125822
|
let descriptor;
|
|
125747
125823
|
try {
|
|
125748
|
-
descriptor =
|
|
125824
|
+
descriptor = openSync5(
|
|
125749
125825
|
temporary,
|
|
125750
125826
|
constants7.O_WRONLY | constants7.O_CREAT | constants7.O_EXCL | (constants7.O_NOFOLLOW ?? 0),
|
|
125751
125827
|
384
|
|
@@ -125753,13 +125829,13 @@ function writeCompleteTemp(home2, directory, baseName, content3, maxBytes) {
|
|
|
125753
125829
|
writeFileSync5(descriptor, content3, "utf8");
|
|
125754
125830
|
if (currentPrivateStateHost().enforcePrivateMode) fchmodSync2(descriptor, 384);
|
|
125755
125831
|
fsyncSync2(descriptor);
|
|
125756
|
-
|
|
125832
|
+
closeSync5(descriptor);
|
|
125757
125833
|
descriptor = void 0;
|
|
125758
125834
|
return temporary;
|
|
125759
125835
|
} catch (error51) {
|
|
125760
125836
|
if (descriptor !== void 0) {
|
|
125761
125837
|
try {
|
|
125762
|
-
|
|
125838
|
+
closeSync5(descriptor);
|
|
125763
125839
|
} catch {
|
|
125764
125840
|
}
|
|
125765
125841
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "superbee",
|
|
3
|
-
"version": "0.4.0
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Agent-facing Superbee CLI for reading and writing local OKF knowledge bundles: context notes, docs, cross-links, and live bundle Views.",
|
|
6
6
|
"keywords": [
|
|
@@ -86,6 +86,6 @@
|
|
|
86
86
|
"darwin",
|
|
87
87
|
"linux"
|
|
88
88
|
],
|
|
89
|
-
"readme": "# superbee\n\nShared, versioned, conflict-safe knowledge for AI coding agents, stored as plain markdown in\nyour repo.\n\nSuperbee is pre-1.0. Commands and formats may change between releases.\n\n## What is Superbee?\n\nAgents forget everything between sessions, overwrite each other's work, and keep what they know\ninvisible to the humans they work for. Superbee fixes all three with a **knowledge bundle**: a\nfolder of markdown documents, by convention `.superbee/` at your project root, that agents read\nand write through a small command-line tool.\n\n- **Context that persists.** Agents write context notes, decisions, plans, and research into the\n bundle. The next session, or a different agent, picks up where the last one left off. An\n optional `SessionStart` hook for Claude Code, Codex, and OpenCode orients every new session\n automatically.\n- **Safe for many writers.** Each write can carry the actor that made it. A writer can name the\n version it last read; if anyone changed the document since, the write fails with a typed\n conflict error instead of silently overwriting their work.\n- **Visible to humans.** The bundle is plain markdown. Open it in any editor, render it on\n GitHub, diff it in git. `superbee ui` serves it locally as cross-linked pages with backlinks and\n a live activity feed. No bundle content leaves your machine until you run `superbee sync`, which\n shares the bundle with teammates through the board: a copy of the bundle kept on its own git\n branch, separate from your code.\n- **Views on demand.** Ask your agent for a dashboard, a timeline, a filtered task queue, or a\n reading view of one dense document. It builds a self-contained HTML page, stores it in the bundle\n as a View, and `superbee ui` hosts it in a sandboxed frame. A View reads the bundle live and can\n change it only through a write you confirm. Views are bundle content, so they travel with `sync`.\n- **Built for agents.** Output is structured and token-lean, and errors carry a small, stable set\n of exit codes. Agents act on responses without parsing prose or flooding their context window.\n- **Yours, and portable.** Bundles follow the Open Knowledge Format (OKF), a convention of\n markdown with frontmatter, so they outlive the tool: hand the folder to someone else, or read it\n with anything that speaks markdown. New bundles are written as OKF v0.2, and existing v0.1\n bundles keep working as they are. Reading and writing the bundle works offline; only sharing\n needs a network. The document schemas, called kinds, live inside the bundle, so it describes\n its own structure.\n\nBees build comb one cell at a time. The comb holds what the colony gathers, and its shape shows\nthe next bee where to build and what belongs where. Superbee works the same way: people and\nagents record what they learn in a structure that fits their domain, and that structure guides\nwhoever works next. Each session builds on the last instead of starting over.\n\nThe npm package is one self-contained executable with zero runtime dependencies, plus an Agent\nSkill, an instruction file your agent loads, that teaches it how to use the tool.\n\n## Install\n\nRequires Node.js 20 or newer on macOS and Linux. Native Windows is not supported by this package.\n\n```sh\nnpm install -g superbee\n```\n\nStable releases publish on npm's `latest` tag and prereleases on `next
|
|
89
|
+
"readme": "# superbee\n\nShared, versioned, conflict-safe knowledge for AI coding agents, stored as plain markdown in\nyour repo.\n\nSuperbee is pre-1.0. Commands and formats may change between releases.\n\n## What is Superbee?\n\nAgents forget everything between sessions, overwrite each other's work, and keep what they know\ninvisible to the humans they work for. Superbee fixes all three with a **knowledge bundle**: a\nfolder of markdown documents, by convention `.superbee/` at your project root, that agents read\nand write through a small command-line tool.\n\n- **Context that persists.** Agents write context notes, decisions, plans, and research into the\n bundle. The next session, or a different agent, picks up where the last one left off. An\n optional `SessionStart` hook for Claude Code, Codex, and OpenCode orients every new session\n automatically.\n- **Safe for many writers.** Each write can carry the actor that made it. A writer can name the\n version it last read; if anyone changed the document since, the write fails with a typed\n conflict error instead of silently overwriting their work.\n- **Visible to humans.** The bundle is plain markdown. Open it in any editor, render it on\n GitHub, diff it in git. `superbee ui` serves it locally as cross-linked pages with backlinks and\n a live activity feed. No bundle content leaves your machine until you run `superbee sync`, which\n shares the bundle with teammates through the board: a copy of the bundle kept on its own git\n branch, separate from your code.\n- **Views on demand.** Ask your agent for a dashboard, a timeline, a filtered task queue, or a\n reading view of one dense document. It builds a self-contained HTML page, stores it in the bundle\n as a View, and `superbee ui` hosts it in a sandboxed frame. A View reads the bundle live and can\n change it only through a write you confirm. Views are bundle content, so they travel with `sync`.\n- **Built for agents.** Output is structured and token-lean, and errors carry a small, stable set\n of exit codes. Agents act on responses without parsing prose or flooding their context window.\n- **Yours, and portable.** Bundles follow the Open Knowledge Format (OKF), a convention of\n markdown with frontmatter, so they outlive the tool: hand the folder to someone else, or read it\n with anything that speaks markdown. New bundles are written as OKF v0.2, and existing v0.1\n bundles keep working as they are. Reading and writing the bundle works offline; only sharing\n needs a network. The document schemas, called kinds, live inside the bundle, so it describes\n its own structure.\n\nBees build comb one cell at a time. The comb holds what the colony gathers, and its shape shows\nthe next bee where to build and what belongs where. Superbee works the same way: people and\nagents record what they learn in a structure that fits their domain, and that structure guides\nwhoever works next. Each session builds on the last instead of starting over.\n\nThe npm package is one self-contained executable with zero runtime dependencies, plus an Agent\nSkill, an instruction file your agent loads, that teaches it how to use the tool.\n\n## Install\n\nRequires Node.js 20 or newer on macOS and Linux. Native Windows is not supported by this package.\n\n```sh\nnpm install -g superbee\n```\n\nStable releases publish on npm's `latest` tag and prereleases on `next`.\n\nWindows adapters and the `superbee-windows` executable live in a separate repository and are\nnot included in `superbee`. Most Windows users should run Superbee in WSL2, where npm sees a\nLinux platform and the normal installation applies. An experimental, unsupported native Windows\nbuild is available as open source, with build-from-source instructions: https://github.com/Holaxis-ai/superbee-windows-cli\n\n### Upgrading an existing Windows installation\n\nThe first release containing the Windows extraction removes native Windows support from this\npackage. Its npm `os` metadata permits only `darwin` and `linux`, so a Windows upgrade to an affected\nversion is rejected with `EBADPLATFORM`. This applies to existing prerelease users too. Forcing the\ninstallation does not restore support: the executable refuses commands on unsupported hosts\nbefore running them (the bare `--version` flag can still identify the installed build).\n\nThere is no supported Windows replacement on npm. The alternatives are WSL2 or the experimental\nbuild from source at https://github.com/Holaxis-ai/superbee-windows-cli; neither carries a\nfirst-party support promise for native Windows. An older installed version is not converted or\nremoved by this source change, and existing bundle files are not migrated by it. Review the affected release's notes before changing an existing\nWindows installation. macOS/Linux users can continue using the normal installation and setup flow.\n\nRun `superbee version --check` to compare your install with the current stable release.\n\n## First run: let your agent finish setup\n\n`npm install` gives you the CLI. The integrations (the Agent Skill, the `SessionStart` hook, and\nMCP server registration, where MCP is the Model Context Protocol) are installed by your agent,\nnot by hand. Ask it:\n\n> Run `superbee setup` and follow its instructions.\n\nSetup itself changes nothing. It inspects your configuration and returns one safe next command at\na time, and the agent runs each with your approval. Setup knows Claude Code, Codex, and OpenCode,\nplus Claude Desktop for the MCP registration only.\n\n## Everyday use\n\nYou rarely type Superbee commands yourself. You ask your agent, and the Agent Skill translates the\nrequest into CLI calls:\n\n- \"Set up a Superbee bundle for this project and track our tasks in it.\"\n- \"Write up what we decided about the auth design as a doc, and link it to the task.\"\n- \"What did the last session leave off on? Check the context notes.\"\n- \"Sync the board so my teammate's agent sees this.\"\n- \"Give me a view of the open tasks grouped by owner.\"\n\nBehind those requests the agent uses a small set of commands: `init --dir .superbee` creates the\nbundle, `new` creates a document of a declared kind, `doc write` writes a free-form one,\n`doc update` changes a document, `link add` connects two, `list` and `doc read` query them, and\n`sync` shares the board. `superbee --help` lists the commands, and `superbee <command> --help`\ngives each one's full reference.\n\nThe two commands meant for you are the ones that show you the knowledge:\n\n```sh\nsuperbee ui --open # the whole bundle, rendered in your browser\nsuperbee doc open <id> # one document, by an id from `superbee list`\n```\n\n## Upgrading from aslite\n\nIf you installed the earlier `@holaxis/aslite` package or its marketplace plugin: install\n`superbee` alongside it, have your agent run `superbee setup` to migrate the integrations, then\nrun `npm uninstall -g @holaxis/aslite`. Existing `.agentstate-lite/` bundles and\n`.agentstate.json` bindings keep working with no migration.\n\n## Learn more\n\nThe [repository](https://github.com/Holaxis-ai/superbee) holds the source, the\n[CLI contract](https://github.com/Holaxis-ai/superbee/blob/main/packages/superbee/AXI-CONTRACT.md),\nand the [wire protocol](https://github.com/Holaxis-ai/superbee/blob/main/docs/WIRE-PROTOCOL.md).\n\n## License\n\nApache-2.0 © 2026 Holaxis\n",
|
|
90
90
|
"readmeFilename": "README.md"
|
|
91
91
|
}
|
|
@@ -150,10 +150,11 @@ the host accepts it. Never shorten someone else's document just to make it sync.
|
|
|
150
150
|
|
|
151
151
|
Read a large document a page at a time instead of pulling it whole into context: `superbee doc read
|
|
152
152
|
<id> --offset 0 --json` answers `body` as one page of about 32 KiB with `range` (`complete`,
|
|
153
|
-
`next_offset`); continue with `--offset <next_offset> --expected-version <head_version>`
|
|
154
|
-
`
|
|
155
|
-
|
|
156
|
-
<
|
|
153
|
+
`next_offset`); continue with `--offset <next_offset> --expected-version <head_version>` while
|
|
154
|
+
`next_offset` is present. The page without `next_offset` is the last one; `complete` is true only
|
|
155
|
+
when the whole body fit in one page, so never loop on it. A page is not the document: never write
|
|
156
|
+
it back as the body. To edit, use `doc read <id> --body-out <path-outside-bundle>`, edit that file,
|
|
157
|
+
then `doc update <id> --body-file <path> --expected-version <version>`.
|
|
157
158
|
|
|
158
159
|
## Host reads with no verb yet: `op list` and `op run`
|
|
159
160
|
|