@hraness/wordcell 0.21.2 → 0.22.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 +196 -66
- package/dist/{authority-4sp9d552.js → authority-1pgftmwq.js} +55 -10
- package/dist/benchmark.js +3 -3
- package/dist/capture.js +6 -5
- package/dist/cli.js +61 -3800
- package/dist/clip/acquire.js +3 -2
- package/dist/clip/cli.js +6 -5
- package/dist/clip/doctor.js +4 -3
- package/dist/clip/jobs.js +3 -3
- package/dist/clip/refresh.js +1 -1
- package/dist/evaluation-builder.js +18 -17
- package/dist/evaluation-kb.js +14 -13
- package/dist/graph-authority.js +3 -3
- package/dist/graph-percolation.js +5 -5
- package/dist/index-0dqfnrz9.js +1985 -0
- package/dist/{index-0k2x4nn9.js → index-1tm7bgx7.js} +16 -4
- package/dist/{index-vdw5yjfa.js → index-2hf27mws.js} +108 -18
- package/dist/index-3agn8scn.js +306 -0
- package/dist/index-4ag64rc1.js +4299 -0
- package/dist/index-4knsp9qj.js +34 -0
- package/dist/index-66pshdtx.js +341 -0
- package/dist/index-9rf81m0p.js +368 -0
- package/dist/{index-59ssmqqm.js → index-a6kbv718.js} +1 -1
- package/dist/{index-51bap0mq.js → index-avbce90h.js} +9 -9
- package/dist/{index-180zbkkt.js → index-de2w8crk.js} +14 -13
- package/dist/{index-pbr6rv47.js → index-e6p9e926.js} +6 -6
- package/dist/{index-bcfn9xah.js → index-famy7fhs.js} +5 -5
- package/dist/{index-by0pbant.js → index-fc4dr114.js} +5 -2
- package/dist/{index-x6m57nfc.js → index-g5vsqmdy.js} +28 -57
- package/dist/{index-115b07ap.js → index-hcw140eb.js} +5 -5
- package/dist/index-j70m75wd.js +217 -0
- package/dist/{index-3ehtt59c.js → index-qwgsmtsz.js} +1 -1
- package/dist/{index-bgfzwt4h.js → index-t2bs9xdr.js} +4 -4
- package/dist/index.js +285 -93
- package/dist/portfolio.js +14 -13
- package/dist/publish-model.js +36 -0
- package/dist/publish-reader/reader.css +1042 -0
- package/dist/publish-reader/reader.js +1534 -0
- package/dist/publish-reader/theme.js +92 -0
- package/dist/publish-search.js +52 -0
- package/dist/publish.js +36 -0
- package/dist/rerank-typesafe.js +13 -0
- package/dist/rerank.js +14 -0
- package/dist/sdk.js +14 -11
- package/dist/search.js +2 -2
- package/dist/semantic.js +4 -4
- package/dist/support-mywv1em0.js +140 -0
- package/dist/workflows/decision-context.js +13 -12
- package/dist/workflows/index.js +13 -12
- package/package.json +68 -7
- package/skills/wordcell/AGENTS.md +1 -0
- package/skills/wordcell/SKILL.md +21 -1
- package/skills/wordcell/agents/openai.yaml +1 -1
- package/skills/wordcell/references/publish.md +93 -0
- package/skills/wordcell/references/query.md +22 -0
- package/src/cli-program.ts +353 -9
- package/src/cli.ts +12 -1
- package/src/clip/acquire.ts +2 -0
- package/src/clip/doctor.ts +2 -0
- package/src/clip/media.ts +2 -0
- package/src/index.ts +4 -0
- package/src/oh/authority.ts +14 -5
- package/src/oh/canonical-rust.ts +127 -0
- package/src/oh/projection-rust.ts +63 -0
- package/src/oh-adoption.ts +7 -1
- package/src/publish-graph.ts +0 -0
- package/src/publish-index.ts +195 -0
- package/src/publish-markdown.ts +617 -0
- package/src/publish-model.ts +599 -0
- package/src/publish-nav.ts +204 -0
- package/src/publish-pages.ts +382 -0
- package/src/publish-reader/reader.css +1042 -0
- package/src/publish-reader/reader.ts +1017 -0
- package/src/publish-reader/theme.ts +97 -0
- package/src/publish-search.ts +418 -0
- package/src/publish-select.ts +419 -0
- package/src/publish-theme.ts +106 -0
- package/src/publish.ts +904 -0
- package/src/rerank-credentials.ts +56 -0
- package/src/rerank-typesafe.ts +472 -0
- package/src/rerank.ts +389 -0
- package/src/sdk.ts +157 -6
- package/src/serve.ts +180 -0
- package/src/support-profile.ts +7 -0
- package/src/support.ts +73 -0
- package/src/vault.ts +23 -9
- package/dist/{index-why54q5p.js → index-gm9t95d9.js} +3 -3
package/src/cli-program.ts
CHANGED
|
@@ -96,6 +96,14 @@ import {
|
|
|
96
96
|
openKnowledgePortfolio,
|
|
97
97
|
type PortfolioSearchResult,
|
|
98
98
|
} from "./portfolio.js";
|
|
99
|
+
import {
|
|
100
|
+
DEFAULT_PUBLISH_LIST_LIMIT,
|
|
101
|
+
MAX_PUBLISH_LIST_LIMIT,
|
|
102
|
+
publishVault,
|
|
103
|
+
renderPublishReportText,
|
|
104
|
+
type PublishSelectionInput,
|
|
105
|
+
} from "./publish.js";
|
|
106
|
+
import { serveSite } from "./serve.js";
|
|
99
107
|
import {
|
|
100
108
|
MAX_AUTHORIZED_VAULTS,
|
|
101
109
|
loadPortfolioRegistry,
|
|
@@ -140,11 +148,14 @@ import {
|
|
|
140
148
|
MAX_SEARCH_RELATED_SEEDS,
|
|
141
149
|
MAX_SEARCH_RESULTS,
|
|
142
150
|
openKnowledgeBase,
|
|
151
|
+
searchEvidenceRank,
|
|
143
152
|
type KnowledgeBaseGraphOptions,
|
|
144
153
|
type KnowledgeBaseSearchMode,
|
|
145
154
|
type KnowledgeBaseSearchOrdering,
|
|
146
155
|
type KnowledgeBaseSearchResult,
|
|
147
156
|
} from "./sdk.js";
|
|
157
|
+
import { MAX_RERANK_CANDIDATES, type SearchReranker } from "./rerank.js";
|
|
158
|
+
import { createCliTypeSafeReranker } from "./rerank-credentials.js";
|
|
148
159
|
import {
|
|
149
160
|
refreshVault,
|
|
150
161
|
scanVault,
|
|
@@ -215,7 +226,16 @@ async function loadSearchRulesFile(path: string): Promise<SearchRulesV1> {
|
|
|
215
226
|
return parseSearchRules(input);
|
|
216
227
|
}
|
|
217
228
|
|
|
218
|
-
export const usage = `wordcell —
|
|
229
|
+
export const usage = `wordcell — a local knowledge base for coding agents
|
|
230
|
+
|
|
231
|
+
Start here (no account or model needed):
|
|
232
|
+
wordcell init kb
|
|
233
|
+
wordcell note create notes/decision --title "A decision" --body "Keep retries bounded." --root kb
|
|
234
|
+
wordcell search "retries" --root kb --mode exact
|
|
235
|
+
|
|
236
|
+
Already have Markdown? Search it with --root <your-notes-directory>.
|
|
237
|
+
Use --mode exact for model-free search; optional hybrid search needs a local index.
|
|
238
|
+
Quick start and examples: https://wordcell.io/docs
|
|
219
239
|
|
|
220
240
|
Usage:
|
|
221
241
|
wordcell init [directory] [--json]
|
|
@@ -243,12 +263,14 @@ Usage:
|
|
|
243
263
|
wordcell percolate [note] [--proofs] [--root <directory>] [--min-support <count>] [--limit <count>] [--json]
|
|
244
264
|
wordcell list [--root <directory>] [--where <path=value>] [--has <path>] [--tag <tag>] [--scope <repository-path>] [--sort <field>] [--order <asc|desc>] [--limit <count>] [--json]
|
|
245
265
|
wordcell index [--root <directory>] [--database <path>] [--force] [--json]
|
|
246
|
-
wordcell search <query> [--root <directory>] [--repo <repository>] [--database <path>] [--mode <hybrid|exact|keyword|semantic>] [--rules <file>] [--priority] [--where <path=value>] [--has <path>] [--tag <tag>] [--scope <repository-path>] [--related <note>] [--graph-depth <1|2>] [--no-graph] [--history | --no-history | --require-history] [--limit <count>] [--candidate-limit <count>] [--min-score <score>] [--json]
|
|
266
|
+
wordcell search <query> [--root <directory>] [--repo <repository>] [--database <path>] [--mode <hybrid|exact|keyword|semantic>] [--rules <file>] [--priority] [--where <path=value>] [--has <path>] [--tag <tag>] [--scope <repository-path>] [--related <note>] [--graph-depth <1|2>] [--no-graph] [--history | --no-history | --require-history] [--limit <count>] [--candidate-limit <count>] [--min-score <score>] [--rerank <typesafe>] [--rerank-limit <2..25>] [--json]
|
|
247
267
|
wordcell history <note> [--root <directory>] [--repo <repository>] [--limit <count>] [--cochanged-limit <count>] [--json]
|
|
248
268
|
wordcell history search <query-or-path> [--root <directory>] [--repo <repository>] [--limit <count>] [--commit-limit <count>] [--cochanged-limit <count>] [--json]
|
|
249
269
|
wordcell evaluate <manifest.json> [--root <directory>] [--repo <repository>] [--database <path>] [--retriever <id>] [--split <development|test|all>] [--limit <count>] [--cutoff <count>] [--timeout <milliseconds>] [--baseline <id>] [--model-file <path>] [--cache-state <cold|mixed|warm>] [--json]
|
|
250
270
|
wordcell portfolio search <query> --registry <file> --workspace <directory> (--shared | --vault <owner/id>...) [--mode <hybrid|exact|keyword|semantic>] [--rules <file>] [--priority] [--limit <count>] [--require-all] [--json]
|
|
251
271
|
wordcell portfolio audit --registry <file> --workspace <directory> (--all | --shared | --vault <owner/id>...) [--strict] [--json]
|
|
272
|
+
wordcell publish --out <directory> [--root <directory>] [--index <path>] [--include <path>]... [--exclude <path>]... [--include-glob <pattern>]... [--exclude-glob <pattern>]... [--where <path=value>]... [--has <path>]... [--tag <tag>]... [--scope <repository-path>]... [--from <note> [--depth <count>] [--direction <in|out|both>]] [--title <title>] [--description <text>] [--base-path <path>] [--base-url <url>] [--noindex] [--no-index-content] [--deterministic] [--dry-run] [--list-limit <0-1000>] [--force] [--json]
|
|
273
|
+
wordcell serve --root <directory> [--host <host>] [--port <port>] [--json]
|
|
252
274
|
wordcell inbox [--root <directory>] [--source-prefix <directory>] [--limit <count>] [--json]
|
|
253
275
|
wordcell context <repository-path> [--root <vault>] [--repo <repository>] [--kind <auto|file|directory>] [--json]
|
|
254
276
|
wordcell agents identity <repository-scope> [--json]
|
|
@@ -256,6 +278,12 @@ Usage:
|
|
|
256
278
|
wordcell agents audit [--root <vault>] [--repo <repository>] [--json]
|
|
257
279
|
wordcell doctor [--json]
|
|
258
280
|
wordcell adapters [--json]
|
|
281
|
+
wordcell support [--json | protocol --json | offer --json | shown <id> | release <id> | dismiss | snooze | enable | status --json]
|
|
282
|
+
|
|
283
|
+
Optional support uses separate local preferences. HRANESS_SUPPORT=off or
|
|
284
|
+
HRANESS_SUPPORT_AUDIENCE=off keeps invitations quiet; explicit support remains available.
|
|
285
|
+
Agents receive discovery on stderr after useful standalone work. Human mode requires
|
|
286
|
+
HRANESS_SUPPORT_AUDIENCE=human and an interactive stderr. No command signs up or pays.
|
|
259
287
|
|
|
260
288
|
Run \`wordcell clip --help\` for web capture options or \`wordcell pdf --help\` for PDF conversion options.
|
|
261
289
|
`;
|
|
@@ -353,6 +381,8 @@ type ParsedCommand =
|
|
|
353
381
|
readonly limit?: number;
|
|
354
382
|
readonly candidateLimit?: number;
|
|
355
383
|
readonly minScore?: number;
|
|
384
|
+
readonly rerank?: "typesafe";
|
|
385
|
+
readonly rerankLimit?: number;
|
|
356
386
|
readonly query: string;
|
|
357
387
|
readonly json: boolean;
|
|
358
388
|
}
|
|
@@ -440,6 +470,31 @@ type ParsedCommand =
|
|
|
440
470
|
readonly minSupport: number;
|
|
441
471
|
readonly limit: number;
|
|
442
472
|
readonly json: boolean;
|
|
473
|
+
}
|
|
474
|
+
| {
|
|
475
|
+
readonly kind: "publish";
|
|
476
|
+
readonly root: string;
|
|
477
|
+
readonly out: string;
|
|
478
|
+
readonly index?: string;
|
|
479
|
+
readonly title?: string;
|
|
480
|
+
readonly description?: string;
|
|
481
|
+
readonly basePath?: string;
|
|
482
|
+
readonly baseUrl?: string;
|
|
483
|
+
readonly noindex: boolean;
|
|
484
|
+
readonly indexContent: boolean;
|
|
485
|
+
readonly deterministic: boolean;
|
|
486
|
+
readonly dryRun: boolean;
|
|
487
|
+
readonly listLimit: number;
|
|
488
|
+
readonly force: boolean;
|
|
489
|
+
readonly selection: PublishSelectionInput;
|
|
490
|
+
readonly json: boolean;
|
|
491
|
+
}
|
|
492
|
+
| {
|
|
493
|
+
readonly kind: "serve";
|
|
494
|
+
readonly root: string;
|
|
495
|
+
readonly host: string;
|
|
496
|
+
readonly port: number;
|
|
497
|
+
readonly json: boolean;
|
|
443
498
|
};
|
|
444
499
|
|
|
445
500
|
type ParseResult =
|
|
@@ -462,6 +517,8 @@ type CliDependencies = GraphCliDependencies & {
|
|
|
462
517
|
readonly indexSemanticVault?: typeof indexSemanticVault;
|
|
463
518
|
readonly openKnowledgeBase?: typeof openKnowledgeBase;
|
|
464
519
|
readonly openKnowledgeBaseEvaluation?: typeof openKnowledgeBaseEvaluation;
|
|
520
|
+
/** Test seam; production wiring builds the TypeSafe reranker lazily per flag. */
|
|
521
|
+
readonly rerankers?: readonly SearchReranker[];
|
|
465
522
|
readonly digestEvaluationModel?: (path: string) => Promise<string>;
|
|
466
523
|
readonly evaluationNow?: () => Date;
|
|
467
524
|
readonly createNote?: typeof createNote;
|
|
@@ -473,6 +530,8 @@ type CliDependencies = GraphCliDependencies & {
|
|
|
473
530
|
readonly buildRepositoryMemoryContext?: typeof buildRepositoryMemoryContext;
|
|
474
531
|
readonly auditAgentGuideRepository?: typeof auditAgentGuideRepository;
|
|
475
532
|
readonly validateMarkdownAttachments?: typeof validateMarkdownAttachments;
|
|
533
|
+
readonly publishVault?: typeof publishVault;
|
|
534
|
+
readonly serveSite?: typeof serveSite;
|
|
476
535
|
};
|
|
477
536
|
|
|
478
537
|
function safe(value: string): string {
|
|
@@ -860,6 +919,195 @@ function parseListCommand(arguments_: readonly string[]): ParseResult {
|
|
|
860
919
|
};
|
|
861
920
|
}
|
|
862
921
|
|
|
922
|
+
function parsePublishCommand(arguments_: readonly string[]): ParseResult {
|
|
923
|
+
let root = ".";
|
|
924
|
+
let out: string | undefined;
|
|
925
|
+
let index: string | undefined;
|
|
926
|
+
let title: string | undefined;
|
|
927
|
+
let description: string | undefined;
|
|
928
|
+
let basePath: string | undefined;
|
|
929
|
+
let baseUrl: string | undefined;
|
|
930
|
+
let noindex = false;
|
|
931
|
+
let indexContent = true;
|
|
932
|
+
let deterministic = false;
|
|
933
|
+
let dryRun = false;
|
|
934
|
+
let listLimit = DEFAULT_PUBLISH_LIST_LIMIT;
|
|
935
|
+
let force = false;
|
|
936
|
+
let json = false;
|
|
937
|
+
let from: string | undefined;
|
|
938
|
+
let depth = 1;
|
|
939
|
+
let direction: LinkDirection = "both";
|
|
940
|
+
const includes: string[] = [];
|
|
941
|
+
const excludes: string[] = [];
|
|
942
|
+
const includeGlobs: string[] = [];
|
|
943
|
+
const excludeGlobs: string[] = [];
|
|
944
|
+
const filters: MetadataFilter[] = [];
|
|
945
|
+
const tags: string[] = [];
|
|
946
|
+
const repositoryScopes: string[] = [];
|
|
947
|
+
|
|
948
|
+
for (let cursor = 0; cursor < arguments_.length; cursor += 1) {
|
|
949
|
+
const argument = arguments_[cursor];
|
|
950
|
+
if (argument === undefined) continue;
|
|
951
|
+
if (argument === "--json") { json = true; continue; }
|
|
952
|
+
if (argument === "--noindex") { noindex = true; continue; }
|
|
953
|
+
if (argument === "--no-index-content") { indexContent = false; continue; }
|
|
954
|
+
if (argument === "--deterministic") { deterministic = true; continue; }
|
|
955
|
+
if (argument === "--dry-run") { dryRun = true; continue; }
|
|
956
|
+
if (argument === "--force") { force = true; continue; }
|
|
957
|
+
if (
|
|
958
|
+
argument === "--root" || argument === "--out" || argument === "--index"
|
|
959
|
+
|| argument === "--title" || argument === "--description"
|
|
960
|
+
|| argument === "--base-path" || argument === "--base-url"
|
|
961
|
+
|| argument === "--include" || argument === "--exclude"
|
|
962
|
+
|| argument === "--include-glob" || argument === "--exclude-glob"
|
|
963
|
+
|| argument === "--list-limit"
|
|
964
|
+
|| argument === "--where" || argument === "--has" || argument === "--tag"
|
|
965
|
+
|| argument === "--scope" || argument === "--repository-scope"
|
|
966
|
+
|| argument === "--from" || argument === "--depth" || argument === "--direction"
|
|
967
|
+
) {
|
|
968
|
+
const value = readValue(arguments_, cursor);
|
|
969
|
+
if (value === null) return { ok: false, message: `${argument} requires a value` };
|
|
970
|
+
if (argument === "--root") root = value;
|
|
971
|
+
else if (argument === "--out") out = value;
|
|
972
|
+
else if (argument === "--index") index = value;
|
|
973
|
+
else if (argument === "--title") title = value;
|
|
974
|
+
else if (argument === "--description") description = value;
|
|
975
|
+
else if (argument === "--base-path") basePath = value;
|
|
976
|
+
else if (argument === "--base-url") baseUrl = value;
|
|
977
|
+
else if (argument === "--include") includes.push(value);
|
|
978
|
+
else if (argument === "--exclude") excludes.push(value);
|
|
979
|
+
else if (argument === "--include-glob") includeGlobs.push(value);
|
|
980
|
+
else if (argument === "--exclude-glob") excludeGlobs.push(value);
|
|
981
|
+
else if (argument === "--list-limit") {
|
|
982
|
+
if (!/^(0|[1-9][0-9]*)$/u.test(value) || Number(value) > MAX_PUBLISH_LIST_LIMIT) {
|
|
983
|
+
return { ok: false, message: `--list-limit must be an integer from 0 through ${MAX_PUBLISH_LIST_LIMIT}` };
|
|
984
|
+
}
|
|
985
|
+
listLimit = Number(value);
|
|
986
|
+
}
|
|
987
|
+
else if (argument === "--from") from = value;
|
|
988
|
+
else if (argument === "--depth") {
|
|
989
|
+
const parsed = Number(value);
|
|
990
|
+
if (!Number.isSafeInteger(parsed) || parsed < 1 || parsed > 10) {
|
|
991
|
+
return { ok: false, message: "--depth must be an integer from 1 through 10" };
|
|
992
|
+
}
|
|
993
|
+
depth = parsed;
|
|
994
|
+
} else if (argument === "--direction") {
|
|
995
|
+
if (value !== "in" && value !== "out" && value !== "both") {
|
|
996
|
+
return { ok: false, message: "--direction must be in, out, or both" };
|
|
997
|
+
}
|
|
998
|
+
direction = value;
|
|
999
|
+
} else if (argument === "--tag") {
|
|
1000
|
+
if (tags.length >= MAX_QUERY_TAGS) {
|
|
1001
|
+
return { ok: false, message: `Query tags may contain at most ${MAX_QUERY_TAGS} entries.` };
|
|
1002
|
+
}
|
|
1003
|
+
tags.push(value);
|
|
1004
|
+
} else if (argument === "--scope" || argument === "--repository-scope") {
|
|
1005
|
+
if (repositoryScopes.length >= MAX_REPOSITORY_SCOPES) {
|
|
1006
|
+
return { ok: false, message: `Repository scope filters may contain at most ${MAX_REPOSITORY_SCOPES} entries.` };
|
|
1007
|
+
}
|
|
1008
|
+
repositoryScopes.push(value);
|
|
1009
|
+
} else if (argument === "--has") {
|
|
1010
|
+
if (value.trim() === "") return { ok: false, message: "--has requires a metadata path" };
|
|
1011
|
+
if (filters.length >= MAX_QUERY_FILTERS) {
|
|
1012
|
+
return { ok: false, message: `Query filters may contain at most ${MAX_QUERY_FILTERS} entries.` };
|
|
1013
|
+
}
|
|
1014
|
+
filters.push({ kind: "exists", path: value });
|
|
1015
|
+
} else {
|
|
1016
|
+
const equals = value.indexOf("=");
|
|
1017
|
+
const path = equals === -1 ? "" : value.slice(0, equals).trim();
|
|
1018
|
+
if (path === "") return { ok: false, message: "--where requires path=value" };
|
|
1019
|
+
const scalar = metadataScalar(value.slice(equals + 1));
|
|
1020
|
+
if (!scalar.ok) return scalar;
|
|
1021
|
+
if (filters.length >= MAX_QUERY_FILTERS) {
|
|
1022
|
+
return { ok: false, message: `Query filters may contain at most ${MAX_QUERY_FILTERS} entries.` };
|
|
1023
|
+
}
|
|
1024
|
+
filters.push({ kind: "equals", path, value: scalar.value });
|
|
1025
|
+
}
|
|
1026
|
+
cursor += 1;
|
|
1027
|
+
continue;
|
|
1028
|
+
}
|
|
1029
|
+
return {
|
|
1030
|
+
ok: false,
|
|
1031
|
+
message: argument.startsWith("--")
|
|
1032
|
+
? "unknown publish option"
|
|
1033
|
+
: "publish does not accept positional arguments",
|
|
1034
|
+
};
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
if (out === undefined) return { ok: false, message: "publish requires --out <directory>" };
|
|
1038
|
+
try {
|
|
1039
|
+
validateQueryOptions({ filters, tags, repositoryScopes });
|
|
1040
|
+
} catch (error: unknown) {
|
|
1041
|
+
return { ok: false, message: error instanceof Error ? error.message : String(error) };
|
|
1042
|
+
}
|
|
1043
|
+
const selection: PublishSelectionInput = {
|
|
1044
|
+
includes,
|
|
1045
|
+
excludes,
|
|
1046
|
+
includeGlobs,
|
|
1047
|
+
excludeGlobs,
|
|
1048
|
+
filters,
|
|
1049
|
+
tags,
|
|
1050
|
+
repositoryScopes,
|
|
1051
|
+
...(from === undefined ? {} : { from: { note: from, depth, direction } }),
|
|
1052
|
+
};
|
|
1053
|
+
return {
|
|
1054
|
+
ok: true,
|
|
1055
|
+
value: {
|
|
1056
|
+
kind: "publish",
|
|
1057
|
+
root,
|
|
1058
|
+
out,
|
|
1059
|
+
noindex,
|
|
1060
|
+
indexContent,
|
|
1061
|
+
deterministic,
|
|
1062
|
+
dryRun,
|
|
1063
|
+
listLimit,
|
|
1064
|
+
force,
|
|
1065
|
+
selection,
|
|
1066
|
+
json,
|
|
1067
|
+
...(index === undefined ? {} : { index }),
|
|
1068
|
+
...(title === undefined ? {} : { title }),
|
|
1069
|
+
...(description === undefined ? {} : { description }),
|
|
1070
|
+
...(basePath === undefined ? {} : { basePath }),
|
|
1071
|
+
...(baseUrl === undefined ? {} : { baseUrl }),
|
|
1072
|
+
},
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
function parseServeCommand(arguments_: readonly string[]): ParseResult {
|
|
1077
|
+
let root: string | undefined;
|
|
1078
|
+
let host = "127.0.0.1";
|
|
1079
|
+
let port = 8080;
|
|
1080
|
+
let json = false;
|
|
1081
|
+
for (let cursor = 0; cursor < arguments_.length; cursor += 1) {
|
|
1082
|
+
const argument = arguments_[cursor];
|
|
1083
|
+
if (argument === undefined) continue;
|
|
1084
|
+
if (argument === "--json") { json = true; continue; }
|
|
1085
|
+
if (argument === "--root" || argument === "--host" || argument === "--port") {
|
|
1086
|
+
const value = readValue(arguments_, cursor);
|
|
1087
|
+
if (value === null) return { ok: false, message: `${argument} requires a value` };
|
|
1088
|
+
if (argument === "--root") root = value;
|
|
1089
|
+
else if (argument === "--host") host = value;
|
|
1090
|
+
else {
|
|
1091
|
+
const parsed = Number(value);
|
|
1092
|
+
if (!Number.isSafeInteger(parsed) || parsed < 0 || parsed > 65_535) {
|
|
1093
|
+
return { ok: false, message: "--port must be an integer from 0 through 65535" };
|
|
1094
|
+
}
|
|
1095
|
+
port = parsed;
|
|
1096
|
+
}
|
|
1097
|
+
cursor += 1;
|
|
1098
|
+
continue;
|
|
1099
|
+
}
|
|
1100
|
+
return {
|
|
1101
|
+
ok: false,
|
|
1102
|
+
message: argument.startsWith("--")
|
|
1103
|
+
? "unknown serve option"
|
|
1104
|
+
: "serve does not accept positional arguments",
|
|
1105
|
+
};
|
|
1106
|
+
}
|
|
1107
|
+
if (root === undefined) return { ok: false, message: "serve requires --root <directory>" };
|
|
1108
|
+
return { ok: true, value: { kind: "serve", root, host, port, json } };
|
|
1109
|
+
}
|
|
1110
|
+
|
|
863
1111
|
function parseInboxCommand(arguments_: readonly string[]): ParseResult {
|
|
864
1112
|
let root = ".";
|
|
865
1113
|
let index: string | undefined;
|
|
@@ -936,6 +1184,8 @@ function parseSemanticCommand(command: "index" | "search", arguments_: readonly
|
|
|
936
1184
|
let limit: number | undefined;
|
|
937
1185
|
let candidateLimit: number | undefined;
|
|
938
1186
|
let minScore: number | undefined;
|
|
1187
|
+
let rerank: "typesafe" | undefined;
|
|
1188
|
+
let rerankLimit: number | undefined;
|
|
939
1189
|
let graphDepth: number | undefined;
|
|
940
1190
|
let noGraph = false;
|
|
941
1191
|
let history = false;
|
|
@@ -997,6 +1247,8 @@ function parseSemanticCommand(command: "index" | "search", arguments_: readonly
|
|
|
997
1247
|
|| argument === "--limit"
|
|
998
1248
|
|| argument === "--candidate-limit"
|
|
999
1249
|
|| argument === "--min-score"
|
|
1250
|
+
|| argument === "--rerank"
|
|
1251
|
+
|| argument === "--rerank-limit"
|
|
1000
1252
|
|| argument === "--where"
|
|
1001
1253
|
|| argument === "--has"
|
|
1002
1254
|
|| argument === "--tag"
|
|
@@ -1012,6 +1264,17 @@ function parseSemanticCommand(command: "index" | "search", arguments_: readonly
|
|
|
1012
1264
|
return { ok: false, message: "--mode must be hybrid, exact, keyword, or semantic" };
|
|
1013
1265
|
}
|
|
1014
1266
|
mode = value;
|
|
1267
|
+
} else if (argument === "--rerank") {
|
|
1268
|
+
if (value !== "typesafe") {
|
|
1269
|
+
return { ok: false, message: "--rerank must be typesafe" };
|
|
1270
|
+
}
|
|
1271
|
+
rerank = value;
|
|
1272
|
+
} else if (argument === "--rerank-limit") {
|
|
1273
|
+
const parsed = Number(value);
|
|
1274
|
+
if (!Number.isSafeInteger(parsed) || parsed < 2 || parsed > MAX_RERANK_CANDIDATES) {
|
|
1275
|
+
return { ok: false, message: `--rerank-limit must be an integer from 2 through ${MAX_RERANK_CANDIDATES}` };
|
|
1276
|
+
}
|
|
1277
|
+
rerankLimit = parsed;
|
|
1015
1278
|
} else if (argument === "--where") {
|
|
1016
1279
|
const equals = value.indexOf("=");
|
|
1017
1280
|
const path = equals === -1 ? "" : value.slice(0, equals).trim();
|
|
@@ -1141,6 +1404,9 @@ function parseSemanticCommand(command: "index" | "search", arguments_: readonly
|
|
|
1141
1404
|
message: "Search minimum score applies only to hybrid, keyword, or semantic mode.",
|
|
1142
1405
|
};
|
|
1143
1406
|
}
|
|
1407
|
+
if (rerankLimit !== undefined && rerank === undefined) {
|
|
1408
|
+
return { ok: false, message: "--rerank-limit requires --rerank typesafe" };
|
|
1409
|
+
}
|
|
1144
1410
|
if (priority && rulesPath === undefined) {
|
|
1145
1411
|
return { ok: false, message: "--priority requires --rules" };
|
|
1146
1412
|
}
|
|
@@ -1179,6 +1445,8 @@ function parseSemanticCommand(command: "index" | "search", arguments_: readonly
|
|
|
1179
1445
|
...(limit === undefined ? {} : { limit }),
|
|
1180
1446
|
...(candidateLimit === undefined ? {} : { candidateLimit }),
|
|
1181
1447
|
...(minScore === undefined ? {} : { minScore }),
|
|
1448
|
+
...(rerank === undefined ? {} : { rerank }),
|
|
1449
|
+
...(rerankLimit === undefined ? {} : { rerankLimit }),
|
|
1182
1450
|
query,
|
|
1183
1451
|
json,
|
|
1184
1452
|
},
|
|
@@ -1932,6 +2200,8 @@ export function parseArguments(arguments_: readonly string[]): ParseResult {
|
|
|
1932
2200
|
if (command === "note") return parseNoteCommand(arguments_.slice(1));
|
|
1933
2201
|
if (command === "relation") return parseRelationCommand(arguments_.slice(1));
|
|
1934
2202
|
if (command === "percolate") return parsePercolateCommand(arguments_.slice(1));
|
|
2203
|
+
if (command === "publish") return parsePublishCommand(arguments_.slice(1));
|
|
2204
|
+
if (command === "serve") return parseServeCommand(arguments_.slice(1));
|
|
1935
2205
|
return { ok: false, message: "unknown command" };
|
|
1936
2206
|
}
|
|
1937
2207
|
|
|
@@ -1954,10 +2224,28 @@ function renderKnowledgeBaseSearch(result: KnowledgeBaseSearchResult): string {
|
|
|
1954
2224
|
const lines = [
|
|
1955
2225
|
`${result.mode[0]?.toLocaleUpperCase("en-US") ?? ""}${result.mode.slice(1)} results for “${safe(result.query)}” (${result.results.length})${result.partial ? " [partial]" : ""}`,
|
|
1956
2226
|
];
|
|
2227
|
+
const rerankLane = result.diagnostics.lanes.find(({ lane }) => lane === "rerank");
|
|
2228
|
+
if (rerankLane !== undefined) {
|
|
2229
|
+
lines.push(
|
|
2230
|
+
` Rerank: typesafe over ${rerankLane.results} candidates (${rerankLane.status})`
|
|
2231
|
+
+ (rerankLane.message === undefined ? "" : ` — ${safe(rerankLane.message)}`),
|
|
2232
|
+
);
|
|
2233
|
+
const receipt = rerankLane.rerank;
|
|
2234
|
+
if (receipt?.accounting !== undefined) {
|
|
2235
|
+
const { attempted, candidates, completed, elapsedMs, usageComplete } = receipt.accounting;
|
|
2236
|
+
lines.push(` Rerank requests: ${attempted}/${candidates} attempted, ${completed} settled; ${elapsedMs.toFixed(0)} ms`
|
|
2237
|
+
+ (usageComplete ? "; usage complete" : "; usage incomplete, additional charges may be unknown"));
|
|
2238
|
+
if (rerankLane.status !== "ready" && receipt.usage !== undefined) {
|
|
2239
|
+
lines.push(` Known rerank usage: ${receipt.usage.inputTokens ?? 0} input tokens, ${receipt.usage.outputTokens ?? 0} output tokens`);
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
1957
2243
|
if (result.results.length === 0) lines.push(" None.");
|
|
1958
2244
|
for (const hit of result.results) {
|
|
1959
2245
|
const location = `${safe(hit.path)}${hit.line === undefined ? "" : `:${hit.line}`}`;
|
|
1960
|
-
const evidence = hit.evidence
|
|
2246
|
+
const evidence = hit.evidence
|
|
2247
|
+
.map((item) => `${item.kind}#${searchEvidenceRank(item)}`)
|
|
2248
|
+
.join(", ");
|
|
1961
2249
|
lines.push(` ${hit.rank}. ${hit.score.toFixed(3)} ${location} — ${safe(hit.title)} [${safe(evidence)}]`);
|
|
1962
2250
|
if (hit.snippet !== "") lines.push(` ${safe(hit.snippet)}`);
|
|
1963
2251
|
}
|
|
@@ -1993,12 +2281,17 @@ async function runSemantic(
|
|
|
1993
2281
|
const searchRules = command.rulesPath === undefined
|
|
1994
2282
|
? undefined
|
|
1995
2283
|
: await loadSearchRulesFile(command.rulesPath);
|
|
1996
|
-
const kb = await (dependencies.openKnowledgeBase ?? openKnowledgeBase)(
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2284
|
+
const kb = await (dependencies.openKnowledgeBase ?? openKnowledgeBase)(
|
|
2285
|
+
{
|
|
2286
|
+
root: command.root,
|
|
2287
|
+
repository: command.repository,
|
|
2288
|
+
...(command.database === undefined ? {} : { database: command.database }),
|
|
2289
|
+
...(searchRules === undefined ? {} : { searchRules }),
|
|
2290
|
+
},
|
|
2291
|
+
...(command.rerank === undefined
|
|
2292
|
+
? []
|
|
2293
|
+
: [{ rerankers: dependencies.rerankers ?? [await createCliTypeSafeReranker()] }]),
|
|
2294
|
+
);
|
|
2002
2295
|
try {
|
|
2003
2296
|
const result = await kb.search({
|
|
2004
2297
|
query: command.query,
|
|
@@ -2014,6 +2307,12 @@ async function runSemantic(
|
|
|
2014
2307
|
? {}
|
|
2015
2308
|
: { candidateLimit: command.candidateLimit }),
|
|
2016
2309
|
...(command.minScore === undefined ? {} : { minScore: command.minScore }),
|
|
2310
|
+
...(command.rerank === undefined ? {} : {
|
|
2311
|
+
rerank: {
|
|
2312
|
+
engine: command.rerank,
|
|
2313
|
+
...(command.rerankLimit === undefined ? {} : { limit: command.rerankLimit }),
|
|
2314
|
+
},
|
|
2315
|
+
}),
|
|
2017
2316
|
});
|
|
2018
2317
|
output.stdout(command.json
|
|
2019
2318
|
? terminalSafeJson(result)
|
|
@@ -2828,6 +3127,49 @@ async function runList(
|
|
|
2828
3127
|
return 0;
|
|
2829
3128
|
}
|
|
2830
3129
|
|
|
3130
|
+
async function runPublish(
|
|
3131
|
+
command: Extract<ParsedCommand, { readonly kind: "publish" }>,
|
|
3132
|
+
output: Output,
|
|
3133
|
+
dependencies: CliDependencies,
|
|
3134
|
+
): Promise<number> {
|
|
3135
|
+
const result = await (dependencies.publishVault ?? publishVault)({
|
|
3136
|
+
root: command.root,
|
|
3137
|
+
out: command.out,
|
|
3138
|
+
noindex: command.noindex,
|
|
3139
|
+
indexContent: command.indexContent,
|
|
3140
|
+
deterministic: command.deterministic,
|
|
3141
|
+
dryRun: command.dryRun,
|
|
3142
|
+
listLimit: command.listLimit,
|
|
3143
|
+
force: command.force,
|
|
3144
|
+
selection: command.selection,
|
|
3145
|
+
...(command.index === undefined ? {} : { index: command.index }),
|
|
3146
|
+
...(command.title === undefined ? {} : { title: command.title }),
|
|
3147
|
+
...(command.description === undefined ? {} : { description: command.description }),
|
|
3148
|
+
...(command.basePath === undefined ? {} : { basePath: command.basePath }),
|
|
3149
|
+
...(command.baseUrl === undefined ? {} : { baseUrl: command.baseUrl }),
|
|
3150
|
+
});
|
|
3151
|
+
output.stdout(command.json
|
|
3152
|
+
? terminalSafeJson(result.report)
|
|
3153
|
+
: sanitizeTerminalText(renderPublishReportText(result.report, command.dryRun)));
|
|
3154
|
+
return 0;
|
|
3155
|
+
}
|
|
3156
|
+
|
|
3157
|
+
async function runServe(
|
|
3158
|
+
command: Extract<ParsedCommand, { readonly kind: "serve" }>,
|
|
3159
|
+
output: Output,
|
|
3160
|
+
dependencies: CliDependencies,
|
|
3161
|
+
): Promise<number> {
|
|
3162
|
+
const site = await (dependencies.serveSite ?? serveSite)({
|
|
3163
|
+
root: command.root,
|
|
3164
|
+
host: command.host,
|
|
3165
|
+
port: command.port,
|
|
3166
|
+
});
|
|
3167
|
+
output.stdout(command.json
|
|
3168
|
+
? terminalSafeJson({ root: site.root, host: site.host, port: site.port, url: site.url })
|
|
3169
|
+
: `Serving ${safe(site.root)} at ${safe(site.url)}\n`);
|
|
3170
|
+
return 0;
|
|
3171
|
+
}
|
|
3172
|
+
|
|
2831
3173
|
async function runCatalog(
|
|
2832
3174
|
command: Extract<ParsedCommand, { readonly kind: "catalog" }>,
|
|
2833
3175
|
output: Output,
|
|
@@ -3574,6 +3916,8 @@ export async function main(
|
|
|
3574
3916
|
}
|
|
3575
3917
|
if (command.kind === "percolate") return await runPercolate(command, output, dependencies);
|
|
3576
3918
|
if (command.kind === "list") return await runList(command, output, dependencies);
|
|
3919
|
+
if (command.kind === "publish") return await runPublish(command, output, dependencies);
|
|
3920
|
+
if (command.kind === "serve") return await runServe(command, output, dependencies);
|
|
3577
3921
|
if (command.kind === "inbox") return await runInbox(command, output, dependencies);
|
|
3578
3922
|
if (command.kind === "catalog") return await runCatalog(command, output, dependencies);
|
|
3579
3923
|
return await runVault(command, output, dependencies);
|
package/src/cli.ts
CHANGED
|
@@ -3,4 +3,15 @@ import { runExecutable } from "./cli-program.js";
|
|
|
3
3
|
|
|
4
4
|
export * from "./cli-program.js";
|
|
5
5
|
|
|
6
|
-
if (import.meta.main)
|
|
6
|
+
if (import.meta.main) {
|
|
7
|
+
const { standaloneSupportEnvironment, isUsefulSupportResult, runProductSupportCommand, showProductSupportInvitation } = await import("./support.js");
|
|
8
|
+
const env = standaloneSupportEnvironment();
|
|
9
|
+
const args = process.argv.slice(2);
|
|
10
|
+
if (args[0] === "support") {
|
|
11
|
+
process.exitCode = await runProductSupportCommand(args.slice(1), { env });
|
|
12
|
+
} else {
|
|
13
|
+
const exitCode = await runExecutable(args);
|
|
14
|
+
process.exitCode = exitCode;
|
|
15
|
+
if (exitCode === 0 && isUsefulSupportResult(args, env)) await showProductSupportInvitation({ env });
|
|
16
|
+
}
|
|
17
|
+
}
|
package/src/clip/acquire.ts
CHANGED
|
@@ -146,6 +146,8 @@ async function runCommand(
|
|
|
146
146
|
stdin?: string,
|
|
147
147
|
): Promise<CommandResult> {
|
|
148
148
|
const child = Bun.spawn([...command], {
|
|
149
|
+
// Bun otherwise inherits its launch-time environment instead of current CLI preferences.
|
|
150
|
+
env: process.env,
|
|
149
151
|
stdin: stdin === undefined ? "ignore" : new Blob([stdin]),
|
|
150
152
|
stdout: "pipe",
|
|
151
153
|
stderr: "pipe",
|
package/src/clip/doctor.ts
CHANGED
|
@@ -172,6 +172,8 @@ async function readBoundedStream(stream: ReadableStream<Uint8Array>, maxBytes: n
|
|
|
172
172
|
|
|
173
173
|
export const runDiagnosticCommand: DiagnosticCommandRunner = async (specification) => {
|
|
174
174
|
const child = Bun.spawn([...specification.command], {
|
|
175
|
+
// Bun otherwise inherits its launch-time environment instead of current CLI preferences.
|
|
176
|
+
env: process.env,
|
|
175
177
|
stdin: "ignore",
|
|
176
178
|
stdout: "pipe",
|
|
177
179
|
stderr: "pipe",
|
package/src/clip/media.ts
CHANGED
|
@@ -274,6 +274,8 @@ export const runMediaCommand: MediaCommandRunner = async (specification) => {
|
|
|
274
274
|
if (executable === undefined) throw new Error("media command is empty");
|
|
275
275
|
const useProcessGroup = process.platform !== "win32";
|
|
276
276
|
const child = spawn(executable, specification.command.slice(1), {
|
|
277
|
+
// Bun otherwise inherits its launch-time environment instead of current CLI preferences.
|
|
278
|
+
env: process.env,
|
|
277
279
|
...(specification.cwd === undefined ? {} : { cwd: specification.cwd }),
|
|
278
280
|
detached: useProcessGroup,
|
|
279
281
|
stdio: ["pipe", "pipe", "pipe"],
|
package/src/index.ts
CHANGED
|
@@ -11,7 +11,11 @@ export * from "./init.js";
|
|
|
11
11
|
export * from "./navigation.js";
|
|
12
12
|
export * from "./oh-adoption.js";
|
|
13
13
|
export * from "./percolate.js";
|
|
14
|
+
export * from "./publish.js";
|
|
15
|
+
export * from "./publish-model.js";
|
|
14
16
|
export * from "./query.js";
|
|
17
|
+
export * from "./rerank.js";
|
|
18
|
+
export * from "./rerank-typesafe.js";
|
|
15
19
|
export * from "./repository-memory.js";
|
|
16
20
|
export * from "./search.js";
|
|
17
21
|
export * from "./semantic.js";
|
package/src/oh/authority.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { isProxy } from "node:util/types";
|
|
|
3
3
|
import { canonicalJson, canonicalSha256, type KnowledgeGraphChangeV1, type KnowledgeGraphRecordV1 } from "@hraness/oh";
|
|
4
4
|
import {
|
|
5
5
|
createOhProjectionDatasetV1, createOhProjectionFactV1, createOhProjectionSnapshotV1,
|
|
6
|
-
|
|
6
|
+
parseOhProjectionResultV1, type OhProjectionProofV1,
|
|
7
7
|
} from "@hraness/oh/projection";
|
|
8
8
|
import { createOhSqliteStoreAuthorityV1 } from "@hraness/oh/sqlite";
|
|
9
9
|
import { createOhStoreProfileV1, OH_WORKING_STORE_PROFILE_V1, type OhStoreV1 } from "@hraness/oh/store";
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
GRAPH_LIMITS, GraphAuthorityError, type GraphAuthority, type GraphProof,
|
|
12
12
|
type GraphQueryLimits, type GraphQueryRequest, type GraphQueryResult, type GraphSnapshot,
|
|
13
13
|
} from "../graph-authority-model";
|
|
14
|
+
import { evaluateOhProjectionWithFallbackV1, loadProjectionRustEngineV1 } from "./projection-rust";
|
|
14
15
|
import { compileGraphProgram, normalizeGraphQueryRequest } from "./programs";
|
|
15
16
|
import { GRAPH_EXTRACTOR_SHA256, GRAPH_MANIFEST_KEY, manifestGraphRecord, parseGraphSnapshot, sourceGraphRecord } from "./snapshot";
|
|
16
17
|
import { deepFreeze, detachedData, exactKeys } from "./validation";
|
|
@@ -95,6 +96,7 @@ export async function openOhGraphAdapter(input: GraphSnapshot, options: OhGraphA
|
|
|
95
96
|
const materialized = await selectedStore.snapshot({ maximumRecords: GRAPH_LIMITS.notes + 1 });
|
|
96
97
|
if (canonicalJson(materialized.records) !== canonicalJson(expected)) throw new GraphAuthorityError("corrupt-cache", "Graph reconciliation did not retain exact source records.");
|
|
97
98
|
const projectionSnapshot = createOhProjectionSnapshotV1({ ...materialized, spaceId });
|
|
99
|
+
const rustEngine = await loadProjectionRustEngineV1();
|
|
98
100
|
const recordMap = new Map(records.map((record, index) => [record.key, { record, source: snapshot.records[index]! }]));
|
|
99
101
|
const assertOpen = () => { if (closed) throw new GraphAuthorityError("closed", "The graph authority is closed."); };
|
|
100
102
|
const assertCurrent = async () => {
|
|
@@ -141,10 +143,17 @@ export async function openOhGraphAdapter(input: GraphSnapshot, options: OhGraphA
|
|
|
141
143
|
});
|
|
142
144
|
try {
|
|
143
145
|
const dataset = createOhProjectionDatasetV1({ extractorSha256: GRAPH_EXTRACTOR_SHA256, factPackId: "wordcell.graph-facts", factPackRevision: 1, facts, snapshot: projectionSnapshot });
|
|
144
|
-
const result =
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
146
|
+
const result = evaluateOhProjectionWithFallbackV1({
|
|
147
|
+
dataset,
|
|
148
|
+
engine: rustEngine,
|
|
149
|
+
rulePack: program.rulePack,
|
|
150
|
+
query: program.query,
|
|
151
|
+
snapshot: projectionSnapshot,
|
|
152
|
+
options: {
|
|
153
|
+
maximumDerivedTuples: limits.derivedTuples, maximumRounds: limits.rounds, maximumWorkUnits: limits.workUnits,
|
|
154
|
+
maximumProofDepth: limits.proofDepth, maximumProofNodes: limits.proofNodes, maximumTotalProofNodes: limits.totalProofNodes, maximumResultBytes: limits.resultBytes,
|
|
155
|
+
},
|
|
156
|
+
});
|
|
148
157
|
if (parseOhProjectionResultV1(result, result.identity.projectionSha256) === null) throw new GraphAuthorityError("corrupt-cache", "Oh returned an invalid bounded projection result.");
|
|
149
158
|
await assertCurrent();
|
|
150
159
|
const proofBudget = { remaining: limits.resultBytes };
|