@hraness/wordcell 0.21.3 → 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.
Files changed (81) hide show
  1. package/README.md +170 -64
  2. package/dist/{authority-4sp9d552.js → authority-1pgftmwq.js} +55 -10
  3. package/dist/benchmark.js +3 -3
  4. package/dist/capture.js +6 -5
  5. package/dist/cli.js +37 -31
  6. package/dist/clip/acquire.js +3 -2
  7. package/dist/clip/cli.js +6 -5
  8. package/dist/clip/doctor.js +4 -3
  9. package/dist/clip/jobs.js +3 -3
  10. package/dist/clip/refresh.js +1 -1
  11. package/dist/evaluation-builder.js +18 -17
  12. package/dist/evaluation-kb.js +14 -13
  13. package/dist/graph-authority.js +3 -3
  14. package/dist/graph-percolation.js +5 -5
  15. package/dist/index-0dqfnrz9.js +1985 -0
  16. package/dist/{index-0k2x4nn9.js → index-1tm7bgx7.js} +16 -4
  17. package/dist/{index-vdw5yjfa.js → index-2hf27mws.js} +108 -18
  18. package/dist/index-3agn8scn.js +306 -0
  19. package/dist/{index-0rfcqab6.js → index-4ag64rc1.js} +555 -59
  20. package/dist/index-4knsp9qj.js +34 -0
  21. package/dist/index-66pshdtx.js +341 -0
  22. package/dist/index-9rf81m0p.js +368 -0
  23. package/dist/{index-59ssmqqm.js → index-a6kbv718.js} +1 -1
  24. package/dist/{index-51bap0mq.js → index-avbce90h.js} +9 -9
  25. package/dist/{index-9zcpdbjg.js → index-de2w8crk.js} +13 -13
  26. package/dist/{index-pbr6rv47.js → index-e6p9e926.js} +6 -6
  27. package/dist/{index-bcfn9xah.js → index-famy7fhs.js} +5 -5
  28. package/dist/{index-van48n3g.js → index-fc4dr114.js} +4 -2
  29. package/dist/{index-jzcws0zn.js → index-g5vsqmdy.js} +27 -57
  30. package/dist/{index-115b07ap.js → index-hcw140eb.js} +5 -5
  31. package/dist/index-j70m75wd.js +217 -0
  32. package/dist/{index-3ehtt59c.js → index-qwgsmtsz.js} +1 -1
  33. package/dist/{index-bgfzwt4h.js → index-t2bs9xdr.js} +4 -4
  34. package/dist/index.js +285 -93
  35. package/dist/portfolio.js +14 -13
  36. package/dist/publish-model.js +36 -0
  37. package/dist/publish-reader/reader.css +1042 -0
  38. package/dist/publish-reader/reader.js +1534 -0
  39. package/dist/publish-reader/theme.js +92 -0
  40. package/dist/publish-search.js +52 -0
  41. package/dist/publish.js +36 -0
  42. package/dist/rerank-typesafe.js +13 -0
  43. package/dist/rerank.js +14 -0
  44. package/dist/sdk.js +14 -11
  45. package/dist/search.js +2 -2
  46. package/dist/semantic.js +4 -4
  47. package/dist/{support-vm4mkw0k.js → support-mywv1em0.js} +38 -32
  48. package/dist/workflows/decision-context.js +13 -12
  49. package/dist/workflows/index.js +13 -12
  50. package/package.json +63 -5
  51. package/skills/wordcell/AGENTS.md +1 -0
  52. package/skills/wordcell/SKILL.md +2 -1
  53. package/skills/wordcell/agents/openai.yaml +1 -1
  54. package/skills/wordcell/references/publish.md +93 -0
  55. package/skills/wordcell/references/query.md +22 -0
  56. package/src/cli-program.ts +347 -9
  57. package/src/index.ts +4 -0
  58. package/src/oh/authority.ts +14 -5
  59. package/src/oh/canonical-rust.ts +127 -0
  60. package/src/oh/projection-rust.ts +63 -0
  61. package/src/oh-adoption.ts +7 -1
  62. package/src/publish-graph.ts +0 -0
  63. package/src/publish-index.ts +195 -0
  64. package/src/publish-markdown.ts +617 -0
  65. package/src/publish-model.ts +599 -0
  66. package/src/publish-nav.ts +204 -0
  67. package/src/publish-pages.ts +382 -0
  68. package/src/publish-reader/reader.css +1042 -0
  69. package/src/publish-reader/reader.ts +1017 -0
  70. package/src/publish-reader/theme.ts +97 -0
  71. package/src/publish-search.ts +418 -0
  72. package/src/publish-select.ts +419 -0
  73. package/src/publish-theme.ts +106 -0
  74. package/src/publish.ts +904 -0
  75. package/src/rerank-credentials.ts +56 -0
  76. package/src/rerank-typesafe.ts +472 -0
  77. package/src/rerank.ts +389 -0
  78. package/src/sdk.ts +157 -6
  79. package/src/serve.ts +180 -0
  80. package/src/vault.ts +23 -9
  81. package/dist/{index-why54q5p.js → index-gm9t95d9.js} +3 -3
@@ -3,22 +3,28 @@ import {
3
3
  backfillSavedUrlMetadata,
4
4
  createRustMetadataSearchProvider
5
5
  } from "./index-bcknqxrq.js";
6
+ import {
7
+ main as main2
8
+ } from "./index-054mb7d3.js";
6
9
  import {
7
10
  MAX_AUTHORIZED_VAULTS,
8
11
  auditKnowledgePortfolio,
9
12
  loadPortfolioRegistry,
10
13
  openKnowledgePortfolio,
11
14
  snapshotPortfolioRegistry
12
- } from "./index-51bap0mq.js";
13
- import {
14
- main as main2
15
- } from "./index-054mb7d3.js";
15
+ } from "./index-avbce90h.js";
16
16
  import {
17
17
  diffCaptureBundle
18
18
  } from "./index-j4zgmzjr.js";
19
+ import {
20
+ main
21
+ } from "./index-de2w8crk.js";
19
22
  import {
20
23
  verifyCaptureBundle
21
24
  } from "./index-npg9z1a4.js";
25
+ import {
26
+ redactSensitiveText
27
+ } from "./index-mxxxytys.js";
22
28
  import {
23
29
  initVault
24
30
  } from "./index-23z4zxgg.js";
@@ -27,18 +33,22 @@ import {
27
33
  MAX_SOURCE_INBOX_RESULTS,
28
34
  sourceInbox
29
35
  } from "./index-pj501bh1.js";
36
+ import {
37
+ createTypeSafeReranker
38
+ } from "./index-9rf81m0p.js";
30
39
  import {
31
40
  knowledgeBaseEvaluationRetrieverIds,
32
41
  openKnowledgeBaseEvaluation
33
- } from "./index-pbr6rv47.js";
42
+ } from "./index-e6p9e926.js";
34
43
  import {
35
44
  DEFAULT_SEARCH_RESULTS,
36
45
  MAX_SEARCH_CANDIDATES,
37
46
  MAX_SEARCH_NOTE_REFERENCE_BYTES,
38
47
  MAX_SEARCH_RELATED_SEEDS,
39
48
  MAX_SEARCH_RESULTS,
40
- openKnowledgeBase
41
- } from "./index-vdw5yjfa.js";
49
+ openKnowledgeBase,
50
+ searchEvidenceRank
51
+ } from "./index-2hf27mws.js";
42
52
  import {
43
53
  MAX_SEARCH_RULE_CONFIG_BYTES,
44
54
  parseSearchRules
@@ -49,22 +59,20 @@ import {
49
59
  recommendedEmbeddingModel,
50
60
  recommendedEmbeddingModelSha256,
51
61
  sha256EmbeddingModelFile
52
- } from "./index-115b07ap.js";
53
- import {
54
- percolateWithGraph
55
- } from "./index-bgfzwt4h.js";
62
+ } from "./index-hcw140eb.js";
56
63
  import {
57
- queryGraph,
58
- rebuildGraph,
59
- verifyGraph
60
- } from "./index-bcfn9xah.js";
64
+ MAX_RERANK_CANDIDATES
65
+ } from "./index-j70m75wd.js";
61
66
  import {
62
- validateGraphQueryRequest
63
- } from "./index-11621h23.js";
67
+ MAX_EVALUATION_RESULTS_PER_QUERY,
68
+ MAX_EVALUATION_TIMEOUT_MS,
69
+ buildRetrievalEvaluationReport,
70
+ parseRetrievalEvaluationCorpus,
71
+ runRetrievalEvaluation
72
+ } from "./index-b88v3vtm.js";
64
73
  import {
65
- refreshVault,
66
- scanVault
67
- } from "./index-0k2x4nn9.js";
74
+ percolateWithGraph
75
+ } from "./index-t2bs9xdr.js";
68
76
  import {
69
77
  MAX_PERCOLATION_MENTIONS,
70
78
  MAX_PERCOLATION_MENTION_PAIRS,
@@ -73,26 +81,33 @@ import {
73
81
  percolateVault
74
82
  } from "./index-nd6nynv2.js";
75
83
  import {
76
- MAX_EVALUATION_RESULTS_PER_QUERY,
77
- MAX_EVALUATION_TIMEOUT_MS,
78
- buildRetrievalEvaluationReport,
79
- parseRetrievalEvaluationCorpus,
80
- runRetrievalEvaluation
81
- } from "./index-b88v3vtm.js";
84
+ queryGraph,
85
+ rebuildGraph,
86
+ verifyGraph
87
+ } from "./index-famy7fhs.js";
82
88
  import {
83
- auditAgentGuideRepository
84
- } from "./index-hya40gb2.js";
89
+ validateGraphQueryRequest
90
+ } from "./index-11621h23.js";
85
91
  import {
86
- validateMarkdownAttachments
87
- } from "./index-x3fthpsc.js";
92
+ validateSearchQuery
93
+ } from "./index-gm9t95d9.js";
88
94
  import {
89
- addNoteRelation,
90
- createNote,
91
- removeNoteRelation
92
- } from "./index-6sw24nvv.js";
95
+ sanitizeTerminalLine,
96
+ sanitizeTerminalText
97
+ } from "./index-1xxnjn0d.js";
93
98
  import {
94
- validateSearchQuery
95
- } from "./index-why54q5p.js";
99
+ DEFAULT_PUBLISH_LIST_LIMIT,
100
+ MAX_PUBLISH_LIST_LIMIT,
101
+ publishVault,
102
+ renderPublishReportText
103
+ } from "./index-0dqfnrz9.js";
104
+ import {
105
+ refreshVault,
106
+ scanVault
107
+ } from "./index-1tm7bgx7.js";
108
+ import {
109
+ navigateLinks
110
+ } from "./index-d13v9ckt.js";
96
111
  import {
97
112
  MAX_QUERY_FILTERS,
98
113
  MAX_QUERY_TAGS,
@@ -105,8 +120,11 @@ import {
105
120
  repositoryMemoryGroupKeys
106
121
  } from "./index-06c9ctr6.js";
107
122
  import {
108
- navigateLinks
109
- } from "./index-d13v9ckt.js";
123
+ findKbPackageRoot
124
+ } from "./index-4knsp9qj.js";
125
+ import {
126
+ auditAgentGuideRepository
127
+ } from "./index-hya40gb2.js";
110
128
  import {
111
129
  agentContextGuidePath,
112
130
  agentContextMarkerForScope,
@@ -116,24 +134,19 @@ import {
116
134
  inspectAgentContextRepository,
117
135
  normalizeRepositoryScope
118
136
  } from "./index-5vwpzb5a.js";
137
+ import {
138
+ validateMarkdownAttachments
139
+ } from "./index-x3fthpsc.js";
140
+ import {
141
+ addNoteRelation,
142
+ createNote,
143
+ removeNoteRelation
144
+ } from "./index-6sw24nvv.js";
119
145
  import {
120
146
  lookupNote,
121
147
  parseVaultKey,
122
148
  renderCatalog
123
149
  } from "./index-ekpwvbra.js";
124
- import {
125
- main
126
- } from "./index-9zcpdbjg.js";
127
- import {
128
- findKbPackageRoot
129
- } from "./index-jzcws0zn.js";
130
- import {
131
- redactSensitiveText
132
- } from "./index-mxxxytys.js";
133
- import {
134
- sanitizeTerminalLine,
135
- sanitizeTerminalText
136
- } from "./index-1xxnjn0d.js";
137
150
 
138
151
  // src/clip/url-metadata-cli.ts
139
152
  import { resolve as resolve2 } from "path";
@@ -800,7 +813,7 @@ function terminalIntro(terminal) {
800
813
  }
801
814
 
802
815
  // src/cli-program.ts
803
- import { open } from "fs/promises";
816
+ import { open as open2 } from "fs/promises";
804
817
  import { cpus, release, totalmem } from "os";
805
818
  import { relative, resolve as resolve3 } from "path";
806
819
  import { format } from "util";
@@ -905,13 +918,197 @@ Snapshot: ${value.revision}
905
918
  };
906
919
  }
907
920
 
921
+ // src/serve.ts
922
+ import { realpath, stat } from "fs/promises";
923
+ import { join as join2, sep } from "path";
924
+ var MAX_PATH_BYTES = 8 * 1024;
925
+ var CONTENT_TYPES = Object.freeze({
926
+ ".avif": "image/avif",
927
+ ".css": "text/css; charset=utf-8",
928
+ ".gif": "image/gif",
929
+ ".html": "text/html; charset=utf-8",
930
+ ".ico": "image/x-icon",
931
+ ".jpeg": "image/jpeg",
932
+ ".jpg": "image/jpeg",
933
+ ".js": "text/javascript; charset=utf-8",
934
+ ".json": "application/json; charset=utf-8",
935
+ ".map": "application/json; charset=utf-8",
936
+ ".mp3": "audio/mpeg",
937
+ ".mp4": "video/mp4",
938
+ ".pdf": "application/pdf",
939
+ ".png": "image/png",
940
+ ".svg": "image/svg+xml",
941
+ ".txt": "text/plain; charset=utf-8",
942
+ ".wasm": "application/wasm",
943
+ ".wav": "audio/wav",
944
+ ".webm": "video/webm",
945
+ ".webp": "image/webp",
946
+ ".woff": "font/woff",
947
+ ".woff2": "font/woff2",
948
+ ".xml": "application/xml; charset=utf-8"
949
+ });
950
+ function contentType(path) {
951
+ const extension = path.slice(path.lastIndexOf("."));
952
+ return CONTENT_TYPES[extension] ?? "application/octet-stream";
953
+ }
954
+ async function regularFileUnder(rootReal, candidate) {
955
+ let resolved;
956
+ try {
957
+ resolved = await realpath(candidate);
958
+ } catch {
959
+ return null;
960
+ }
961
+ if (resolved !== rootReal && !resolved.startsWith(`${rootReal}${sep}`))
962
+ return null;
963
+ try {
964
+ return (await stat(resolved)).isFile() ? resolved : null;
965
+ } catch {
966
+ return null;
967
+ }
968
+ }
969
+ async function resolveRequest(rootReal, pathname) {
970
+ let decoded;
971
+ try {
972
+ decoded = decodeURIComponent(pathname);
973
+ } catch {
974
+ return null;
975
+ }
976
+ if (decoded.includes("\x00") || decoded.includes("\\"))
977
+ return null;
978
+ const segments = decoded.split("/").filter((segment) => segment !== "" && segment !== ".");
979
+ if (segments.some((segment) => segment === ".."))
980
+ return null;
981
+ const relative = segments.join("/");
982
+ const candidates = [
983
+ join2(rootReal, relative),
984
+ join2(rootReal, relative, "index.html")
985
+ ];
986
+ for (const candidate of candidates) {
987
+ const file = await regularFileUnder(rootReal, candidate);
988
+ if (file !== null)
989
+ return file;
990
+ }
991
+ return null;
992
+ }
993
+ async function createServeHandler(root) {
994
+ const rootReal = await realpath(root);
995
+ return async (request) => {
996
+ const method = request.method.toUpperCase();
997
+ if (method !== "GET" && method !== "HEAD") {
998
+ return new Response("Method not allowed", { status: 405 });
999
+ }
1000
+ let pathname;
1001
+ try {
1002
+ pathname = new URL(request.url).pathname;
1003
+ } catch {
1004
+ return new Response("Bad request", { status: 400 });
1005
+ }
1006
+ if (pathname.length > MAX_PATH_BYTES) {
1007
+ return new Response("URI too long", { status: 414 });
1008
+ }
1009
+ const head = method === "HEAD";
1010
+ const file = await resolveRequest(rootReal, pathname);
1011
+ if (file !== null) {
1012
+ const body = Bun.file(file);
1013
+ return new Response(head ? null : body, {
1014
+ headers: {
1015
+ "Cache-Control": "no-store",
1016
+ "Content-Length": String(body.size),
1017
+ "Content-Type": contentType(file)
1018
+ }
1019
+ });
1020
+ }
1021
+ const notFound = await regularFileUnder(rootReal, join2(rootReal, "404.html"));
1022
+ if (notFound !== null) {
1023
+ const body = Bun.file(notFound);
1024
+ return new Response(head ? null : body, {
1025
+ status: 404,
1026
+ headers: {
1027
+ "Cache-Control": "no-store",
1028
+ "Content-Length": String(body.size),
1029
+ "Content-Type": "text/html; charset=utf-8"
1030
+ }
1031
+ });
1032
+ }
1033
+ return new Response(head ? null : "Not found", {
1034
+ status: 404,
1035
+ headers: { "Cache-Control": "no-store" }
1036
+ });
1037
+ };
1038
+ }
1039
+ async function serveSite(options, io = {}) {
1040
+ const host = options.host ?? "127.0.0.1";
1041
+ const port = options.port ?? 8080;
1042
+ if (!Number.isSafeInteger(port) || port < 0 || port > 65535) {
1043
+ throw new Error("Port must be an integer from 0 through 65535");
1044
+ }
1045
+ const rootReal = await realpath(options.root);
1046
+ if (!(await stat(rootReal)).isDirectory()) {
1047
+ throw new Error(`Site root is not a directory: ${options.root}`);
1048
+ }
1049
+ const listen = io.listen ?? ((init) => Bun.serve(init));
1050
+ const server = listen({
1051
+ hostname: host,
1052
+ port,
1053
+ fetch: await createServeHandler(rootReal)
1054
+ });
1055
+ const boundPort = server.port ?? port;
1056
+ return Object.freeze({
1057
+ root: rootReal,
1058
+ host,
1059
+ port: boundPort,
1060
+ url: `http://${host}:${String(boundPort)}/`,
1061
+ close: () => server.stop()
1062
+ });
1063
+ }
1064
+
1065
+ // src/rerank-credentials.ts
1066
+ import { constants as constants2 } from "fs";
1067
+ import { open } from "fs/promises";
1068
+ import { homedir } from "os";
1069
+ import { isAbsolute as isAbsolute2, join as join3 } from "path";
1070
+ function unavailable(message) {
1071
+ return { id: "typesafe", rerank: async () => ({ status: "unavailable", message }) };
1072
+ }
1073
+ async function createCliTypeSafeReranker(environment = process.env, homeDirectory = homedir()) {
1074
+ if (Object.hasOwn(environment, "TYPESAFE_API_KEY")) {
1075
+ return createTypeSafeReranker({ environment: { TYPESAFE_API_KEY: environment["TYPESAFE_API_KEY"] } });
1076
+ }
1077
+ const explicitFile = environment["TYPESAFE_API_KEY_FILE"];
1078
+ const configDirectory = environment["XDG_CONFIG_HOME"];
1079
+ if (explicitFile !== undefined && !isAbsolute2(explicitFile) || configDirectory !== undefined && configDirectory !== "" && !isAbsolute2(configDirectory)) {
1080
+ return unavailable("TypeSafe credential file configuration requires absolute paths.");
1081
+ }
1082
+ const path = explicitFile ?? join3(configDirectory || join3(homeDirectory, ".config"), "wordcell", "typesafe-api-key");
1083
+ let handle;
1084
+ try {
1085
+ handle = await open(path, constants2.O_RDONLY | constants2.O_NOFOLLOW | constants2.O_NONBLOCK);
1086
+ const metadata = await handle.stat();
1087
+ const uid = process.getuid?.();
1088
+ if (!metadata.isFile() || metadata.nlink !== 1 || metadata.size < 1 || metadata.size > 514 || process.platform !== "win32" && ((metadata.mode & 63) !== 0 || metadata.uid !== uid)) {
1089
+ return unavailable("TypeSafe credential file must be an owner-only regular file containing one API key.");
1090
+ }
1091
+ const bytes = Buffer.alloc(515);
1092
+ const { bytesRead } = await handle.read(bytes, 0, bytes.length, 0);
1093
+ const key = bytes.subarray(0, bytesRead).toString("utf8").replace(/\r?\n$/u, "");
1094
+ if (!/^[\x21-\x7e]{1,512}$/u.test(key)) {
1095
+ return unavailable("TypeSafe credential file must contain one valid API key.");
1096
+ }
1097
+ return createTypeSafeReranker({ environment: { TYPESAFE_API_KEY: key } });
1098
+ } catch {
1099
+ return unavailable("TypeSafe credentials are unavailable; set TYPESAFE_API_KEY or an owner-only TYPESAFE_API_KEY_FILE.");
1100
+ } finally {
1101
+ await handle?.close();
1102
+ }
1103
+ }
1104
+
908
1105
  // src/cli-program.ts
909
1106
  var defaultOutput2 = {
910
1107
  stdout: (value) => process.stdout.write(value),
911
1108
  stderr: (value) => process.stderr.write(value)
912
1109
  };
913
1110
  async function readBoundedUtf8(path, maximumBytes, label) {
914
- const handle = await open(path, "r");
1111
+ const handle = await open2(path, "r");
915
1112
  try {
916
1113
  const bytes = new Uint8Array(maximumBytes + 1);
917
1114
  let offset = 0;
@@ -943,7 +1140,16 @@ async function loadSearchRulesFile(path) {
943
1140
  }
944
1141
  return parseSearchRules(input);
945
1142
  }
946
- var usage = `wordcell \u2014 auditable capture and derived links for Markdown vaults
1143
+ var usage = `wordcell \u2014 a local knowledge base for coding agents
1144
+
1145
+ Start here (no account or model needed):
1146
+ wordcell init kb
1147
+ wordcell note create notes/decision --title "A decision" --body "Keep retries bounded." --root kb
1148
+ wordcell search "retries" --root kb --mode exact
1149
+
1150
+ Already have Markdown? Search it with --root <your-notes-directory>.
1151
+ Use --mode exact for model-free search; optional hybrid search needs a local index.
1152
+ Quick start and examples: https://wordcell.io/docs
947
1153
 
948
1154
  Usage:
949
1155
  wordcell init [directory] [--json]
@@ -971,12 +1177,14 @@ Usage:
971
1177
  wordcell percolate [note] [--proofs] [--root <directory>] [--min-support <count>] [--limit <count>] [--json]
972
1178
  wordcell list [--root <directory>] [--where <path=value>] [--has <path>] [--tag <tag>] [--scope <repository-path>] [--sort <field>] [--order <asc|desc>] [--limit <count>] [--json]
973
1179
  wordcell index [--root <directory>] [--database <path>] [--force] [--json]
974
- 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]
1180
+ 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]
975
1181
  wordcell history <note> [--root <directory>] [--repo <repository>] [--limit <count>] [--cochanged-limit <count>] [--json]
976
1182
  wordcell history search <query-or-path> [--root <directory>] [--repo <repository>] [--limit <count>] [--commit-limit <count>] [--cochanged-limit <count>] [--json]
977
1183
  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]
978
1184
  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]
979
1185
  wordcell portfolio audit --registry <file> --workspace <directory> (--all | --shared | --vault <owner/id>...) [--strict] [--json]
1186
+ 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]
1187
+ wordcell serve --root <directory> [--host <host>] [--port <port>] [--json]
980
1188
  wordcell inbox [--root <directory>] [--source-prefix <directory>] [--limit <count>] [--json]
981
1189
  wordcell context <repository-path> [--root <vault>] [--repo <repository>] [--kind <auto|file|directory>] [--json]
982
1190
  wordcell agents identity <repository-scope> [--json]
@@ -1367,6 +1575,220 @@ function parseListCommand(arguments_) {
1367
1575
  }
1368
1576
  };
1369
1577
  }
1578
+ function parsePublishCommand(arguments_) {
1579
+ let root = ".";
1580
+ let out;
1581
+ let index;
1582
+ let title;
1583
+ let description;
1584
+ let basePath;
1585
+ let baseUrl;
1586
+ let noindex = false;
1587
+ let indexContent = true;
1588
+ let deterministic = false;
1589
+ let dryRun = false;
1590
+ let listLimit = DEFAULT_PUBLISH_LIST_LIMIT;
1591
+ let force = false;
1592
+ let json = false;
1593
+ let from;
1594
+ let depth = 1;
1595
+ let direction = "both";
1596
+ const includes = [];
1597
+ const excludes = [];
1598
+ const includeGlobs = [];
1599
+ const excludeGlobs = [];
1600
+ const filters = [];
1601
+ const tags = [];
1602
+ const repositoryScopes = [];
1603
+ for (let cursor = 0;cursor < arguments_.length; cursor += 1) {
1604
+ const argument = arguments_[cursor];
1605
+ if (argument === undefined)
1606
+ continue;
1607
+ if (argument === "--json") {
1608
+ json = true;
1609
+ continue;
1610
+ }
1611
+ if (argument === "--noindex") {
1612
+ noindex = true;
1613
+ continue;
1614
+ }
1615
+ if (argument === "--no-index-content") {
1616
+ indexContent = false;
1617
+ continue;
1618
+ }
1619
+ if (argument === "--deterministic") {
1620
+ deterministic = true;
1621
+ continue;
1622
+ }
1623
+ if (argument === "--dry-run") {
1624
+ dryRun = true;
1625
+ continue;
1626
+ }
1627
+ if (argument === "--force") {
1628
+ force = true;
1629
+ continue;
1630
+ }
1631
+ if (argument === "--root" || argument === "--out" || argument === "--index" || argument === "--title" || argument === "--description" || argument === "--base-path" || argument === "--base-url" || argument === "--include" || argument === "--exclude" || argument === "--include-glob" || argument === "--exclude-glob" || argument === "--list-limit" || argument === "--where" || argument === "--has" || argument === "--tag" || argument === "--scope" || argument === "--repository-scope" || argument === "--from" || argument === "--depth" || argument === "--direction") {
1632
+ const value = readValue(arguments_, cursor);
1633
+ if (value === null)
1634
+ return { ok: false, message: `${argument} requires a value` };
1635
+ if (argument === "--root")
1636
+ root = value;
1637
+ else if (argument === "--out")
1638
+ out = value;
1639
+ else if (argument === "--index")
1640
+ index = value;
1641
+ else if (argument === "--title")
1642
+ title = value;
1643
+ else if (argument === "--description")
1644
+ description = value;
1645
+ else if (argument === "--base-path")
1646
+ basePath = value;
1647
+ else if (argument === "--base-url")
1648
+ baseUrl = value;
1649
+ else if (argument === "--include")
1650
+ includes.push(value);
1651
+ else if (argument === "--exclude")
1652
+ excludes.push(value);
1653
+ else if (argument === "--include-glob")
1654
+ includeGlobs.push(value);
1655
+ else if (argument === "--exclude-glob")
1656
+ excludeGlobs.push(value);
1657
+ else if (argument === "--list-limit") {
1658
+ if (!/^(0|[1-9][0-9]*)$/u.test(value) || Number(value) > MAX_PUBLISH_LIST_LIMIT) {
1659
+ return { ok: false, message: `--list-limit must be an integer from 0 through ${MAX_PUBLISH_LIST_LIMIT}` };
1660
+ }
1661
+ listLimit = Number(value);
1662
+ } else if (argument === "--from")
1663
+ from = value;
1664
+ else if (argument === "--depth") {
1665
+ const parsed = Number(value);
1666
+ if (!Number.isSafeInteger(parsed) || parsed < 1 || parsed > 10) {
1667
+ return { ok: false, message: "--depth must be an integer from 1 through 10" };
1668
+ }
1669
+ depth = parsed;
1670
+ } else if (argument === "--direction") {
1671
+ if (value !== "in" && value !== "out" && value !== "both") {
1672
+ return { ok: false, message: "--direction must be in, out, or both" };
1673
+ }
1674
+ direction = value;
1675
+ } else if (argument === "--tag") {
1676
+ if (tags.length >= MAX_QUERY_TAGS) {
1677
+ return { ok: false, message: `Query tags may contain at most ${MAX_QUERY_TAGS} entries.` };
1678
+ }
1679
+ tags.push(value);
1680
+ } else if (argument === "--scope" || argument === "--repository-scope") {
1681
+ if (repositoryScopes.length >= MAX_REPOSITORY_SCOPES) {
1682
+ return { ok: false, message: `Repository scope filters may contain at most ${MAX_REPOSITORY_SCOPES} entries.` };
1683
+ }
1684
+ repositoryScopes.push(value);
1685
+ } else if (argument === "--has") {
1686
+ if (value.trim() === "")
1687
+ return { ok: false, message: "--has requires a metadata path" };
1688
+ if (filters.length >= MAX_QUERY_FILTERS) {
1689
+ return { ok: false, message: `Query filters may contain at most ${MAX_QUERY_FILTERS} entries.` };
1690
+ }
1691
+ filters.push({ kind: "exists", path: value });
1692
+ } else {
1693
+ const equals = value.indexOf("=");
1694
+ const path = equals === -1 ? "" : value.slice(0, equals).trim();
1695
+ if (path === "")
1696
+ return { ok: false, message: "--where requires path=value" };
1697
+ const scalar = metadataScalar(value.slice(equals + 1));
1698
+ if (!scalar.ok)
1699
+ return scalar;
1700
+ if (filters.length >= MAX_QUERY_FILTERS) {
1701
+ return { ok: false, message: `Query filters may contain at most ${MAX_QUERY_FILTERS} entries.` };
1702
+ }
1703
+ filters.push({ kind: "equals", path, value: scalar.value });
1704
+ }
1705
+ cursor += 1;
1706
+ continue;
1707
+ }
1708
+ return {
1709
+ ok: false,
1710
+ message: argument.startsWith("--") ? "unknown publish option" : "publish does not accept positional arguments"
1711
+ };
1712
+ }
1713
+ if (out === undefined)
1714
+ return { ok: false, message: "publish requires --out <directory>" };
1715
+ try {
1716
+ validateQueryOptions({ filters, tags, repositoryScopes });
1717
+ } catch (error) {
1718
+ return { ok: false, message: error instanceof Error ? error.message : String(error) };
1719
+ }
1720
+ const selection = {
1721
+ includes,
1722
+ excludes,
1723
+ includeGlobs,
1724
+ excludeGlobs,
1725
+ filters,
1726
+ tags,
1727
+ repositoryScopes,
1728
+ ...from === undefined ? {} : { from: { note: from, depth, direction } }
1729
+ };
1730
+ return {
1731
+ ok: true,
1732
+ value: {
1733
+ kind: "publish",
1734
+ root,
1735
+ out,
1736
+ noindex,
1737
+ indexContent,
1738
+ deterministic,
1739
+ dryRun,
1740
+ listLimit,
1741
+ force,
1742
+ selection,
1743
+ json,
1744
+ ...index === undefined ? {} : { index },
1745
+ ...title === undefined ? {} : { title },
1746
+ ...description === undefined ? {} : { description },
1747
+ ...basePath === undefined ? {} : { basePath },
1748
+ ...baseUrl === undefined ? {} : { baseUrl }
1749
+ }
1750
+ };
1751
+ }
1752
+ function parseServeCommand(arguments_) {
1753
+ let root;
1754
+ let host = "127.0.0.1";
1755
+ let port = 8080;
1756
+ let json = false;
1757
+ for (let cursor = 0;cursor < arguments_.length; cursor += 1) {
1758
+ const argument = arguments_[cursor];
1759
+ if (argument === undefined)
1760
+ continue;
1761
+ if (argument === "--json") {
1762
+ json = true;
1763
+ continue;
1764
+ }
1765
+ if (argument === "--root" || argument === "--host" || argument === "--port") {
1766
+ const value = readValue(arguments_, cursor);
1767
+ if (value === null)
1768
+ return { ok: false, message: `${argument} requires a value` };
1769
+ if (argument === "--root")
1770
+ root = value;
1771
+ else if (argument === "--host")
1772
+ host = value;
1773
+ else {
1774
+ const parsed = Number(value);
1775
+ if (!Number.isSafeInteger(parsed) || parsed < 0 || parsed > 65535) {
1776
+ return { ok: false, message: "--port must be an integer from 0 through 65535" };
1777
+ }
1778
+ port = parsed;
1779
+ }
1780
+ cursor += 1;
1781
+ continue;
1782
+ }
1783
+ return {
1784
+ ok: false,
1785
+ message: argument.startsWith("--") ? "unknown serve option" : "serve does not accept positional arguments"
1786
+ };
1787
+ }
1788
+ if (root === undefined)
1789
+ return { ok: false, message: "serve requires --root <directory>" };
1790
+ return { ok: true, value: { kind: "serve", root, host, port, json } };
1791
+ }
1370
1792
  function parseInboxCommand(arguments_) {
1371
1793
  let root = ".";
1372
1794
  let index;
@@ -1439,6 +1861,8 @@ function parseSemanticCommand(command, arguments_) {
1439
1861
  let limit;
1440
1862
  let candidateLimit;
1441
1863
  let minScore;
1864
+ let rerank;
1865
+ let rerankLimit;
1442
1866
  let graphDepth;
1443
1867
  let noGraph = false;
1444
1868
  let history = false;
@@ -1496,7 +1920,7 @@ function parseSemanticCommand(command, arguments_) {
1496
1920
  cursor += 1;
1497
1921
  continue;
1498
1922
  }
1499
- if (command === "search" && (argument === "--mode" || argument === "--limit" || argument === "--candidate-limit" || argument === "--min-score" || argument === "--where" || argument === "--has" || argument === "--tag" || argument === "--scope" || argument === "--repository-scope" || argument === "--related" || argument === "--graph-depth")) {
1923
+ if (command === "search" && (argument === "--mode" || argument === "--limit" || argument === "--candidate-limit" || argument === "--min-score" || argument === "--rerank" || argument === "--rerank-limit" || argument === "--where" || argument === "--has" || argument === "--tag" || argument === "--scope" || argument === "--repository-scope" || argument === "--related" || argument === "--graph-depth")) {
1500
1924
  const value = readValue(arguments_, cursor);
1501
1925
  if (value === null)
1502
1926
  return { ok: false, message: `${argument} requires a value` };
@@ -1505,6 +1929,17 @@ function parseSemanticCommand(command, arguments_) {
1505
1929
  return { ok: false, message: "--mode must be hybrid, exact, keyword, or semantic" };
1506
1930
  }
1507
1931
  mode = value;
1932
+ } else if (argument === "--rerank") {
1933
+ if (value !== "typesafe") {
1934
+ return { ok: false, message: "--rerank must be typesafe" };
1935
+ }
1936
+ rerank = value;
1937
+ } else if (argument === "--rerank-limit") {
1938
+ const parsed = Number(value);
1939
+ if (!Number.isSafeInteger(parsed) || parsed < 2 || parsed > MAX_RERANK_CANDIDATES) {
1940
+ return { ok: false, message: `--rerank-limit must be an integer from 2 through ${MAX_RERANK_CANDIDATES}` };
1941
+ }
1942
+ rerankLimit = parsed;
1508
1943
  } else if (argument === "--where") {
1509
1944
  const equals = value.indexOf("=");
1510
1945
  const path = equals === -1 ? "" : value.slice(0, equals).trim();
@@ -1637,6 +2072,9 @@ function parseSemanticCommand(command, arguments_) {
1637
2072
  message: "Search minimum score applies only to hybrid, keyword, or semantic mode."
1638
2073
  };
1639
2074
  }
2075
+ if (rerankLimit !== undefined && rerank === undefined) {
2076
+ return { ok: false, message: "--rerank-limit requires --rerank typesafe" };
2077
+ }
1640
2078
  if (priority && rulesPath === undefined) {
1641
2079
  return { ok: false, message: "--priority requires --rules" };
1642
2080
  }
@@ -1674,6 +2112,8 @@ function parseSemanticCommand(command, arguments_) {
1674
2112
  ...limit === undefined ? {} : { limit },
1675
2113
  ...candidateLimit === undefined ? {} : { candidateLimit },
1676
2114
  ...minScore === undefined ? {} : { minScore },
2115
+ ...rerank === undefined ? {} : { rerank },
2116
+ ...rerankLimit === undefined ? {} : { rerankLimit },
1677
2117
  query,
1678
2118
  json
1679
2119
  }
@@ -2420,6 +2860,10 @@ function parseArguments(arguments_) {
2420
2860
  return parseRelationCommand(arguments_.slice(1));
2421
2861
  if (command === "percolate")
2422
2862
  return parsePercolateCommand(arguments_.slice(1));
2863
+ if (command === "publish")
2864
+ return parsePublishCommand(arguments_.slice(1));
2865
+ if (command === "serve")
2866
+ return parseServeCommand(arguments_.slice(1));
2423
2867
  return { ok: false, message: "unknown command" };
2424
2868
  }
2425
2869
  function embeddingCount(result) {
@@ -2440,11 +2884,23 @@ function renderKnowledgeBaseSearch(result) {
2440
2884
  const lines = [
2441
2885
  `${result.mode[0]?.toLocaleUpperCase("en-US") ?? ""}${result.mode.slice(1)} results for \u201C${safe(result.query)}\u201D (${result.results.length})${result.partial ? " [partial]" : ""}`
2442
2886
  ];
2887
+ const rerankLane = result.diagnostics.lanes.find(({ lane }) => lane === "rerank");
2888
+ if (rerankLane !== undefined) {
2889
+ lines.push(` Rerank: typesafe over ${rerankLane.results} candidates (${rerankLane.status})` + (rerankLane.message === undefined ? "" : ` \u2014 ${safe(rerankLane.message)}`));
2890
+ const receipt = rerankLane.rerank;
2891
+ if (receipt?.accounting !== undefined) {
2892
+ const { attempted, candidates, completed, elapsedMs, usageComplete } = receipt.accounting;
2893
+ lines.push(` Rerank requests: ${attempted}/${candidates} attempted, ${completed} settled; ${elapsedMs.toFixed(0)} ms` + (usageComplete ? "; usage complete" : "; usage incomplete, additional charges may be unknown"));
2894
+ if (rerankLane.status !== "ready" && receipt.usage !== undefined) {
2895
+ lines.push(` Known rerank usage: ${receipt.usage.inputTokens ?? 0} input tokens, ${receipt.usage.outputTokens ?? 0} output tokens`);
2896
+ }
2897
+ }
2898
+ }
2443
2899
  if (result.results.length === 0)
2444
2900
  lines.push(" None.");
2445
2901
  for (const hit of result.results) {
2446
2902
  const location = `${safe(hit.path)}${hit.line === undefined ? "" : `:${hit.line}`}`;
2447
- const evidence = hit.evidence.map((item) => `${item.kind}#${item.rank}`).join(", ");
2903
+ const evidence = hit.evidence.map((item) => `${item.kind}#${searchEvidenceRank(item)}`).join(", ");
2448
2904
  lines.push(` ${hit.rank}. ${hit.score.toFixed(3)} ${location} \u2014 ${safe(hit.title)} [${safe(evidence)}]`);
2449
2905
  if (hit.snippet !== "")
2450
2906
  lines.push(` ${safe(hit.snippet)}`);
@@ -2476,7 +2932,7 @@ async function runSemantic(command, output, dependencies) {
2476
2932
  repository: command.repository,
2477
2933
  ...command.database === undefined ? {} : { database: command.database },
2478
2934
  ...searchRules === undefined ? {} : { searchRules }
2479
- });
2935
+ }, ...command.rerank === undefined ? [] : [{ rerankers: dependencies.rerankers ?? [await createCliTypeSafeReranker()] }]);
2480
2936
  try {
2481
2937
  const result = await kb.search({
2482
2938
  query: command.query,
@@ -2489,7 +2945,13 @@ async function runSemantic(command, output, dependencies) {
2489
2945
  history: command.history,
2490
2946
  ...command.limit === undefined ? {} : { limit: command.limit },
2491
2947
  ...command.candidateLimit === undefined ? {} : { candidateLimit: command.candidateLimit },
2492
- ...command.minScore === undefined ? {} : { minScore: command.minScore }
2948
+ ...command.minScore === undefined ? {} : { minScore: command.minScore },
2949
+ ...command.rerank === undefined ? {} : {
2950
+ rerank: {
2951
+ engine: command.rerank,
2952
+ ...command.rerankLimit === undefined ? {} : { limit: command.rerankLimit }
2953
+ }
2954
+ }
2493
2955
  });
2494
2956
  output.stdout(command.json ? terminalSafeJson(result) : sanitizeTerminalText(renderKnowledgeBaseSearch(result)));
2495
2957
  return 0;
@@ -3104,6 +3566,36 @@ async function runList(command, output, dependencies) {
3104
3566
  output.stdout(command.json ? terminalSafeJson({ root: snapshot.root, count: rows.length, notes: rows }) : sanitizeTerminalText(renderList(rows)));
3105
3567
  return 0;
3106
3568
  }
3569
+ async function runPublish(command, output, dependencies) {
3570
+ const result = await (dependencies.publishVault ?? publishVault)({
3571
+ root: command.root,
3572
+ out: command.out,
3573
+ noindex: command.noindex,
3574
+ indexContent: command.indexContent,
3575
+ deterministic: command.deterministic,
3576
+ dryRun: command.dryRun,
3577
+ listLimit: command.listLimit,
3578
+ force: command.force,
3579
+ selection: command.selection,
3580
+ ...command.index === undefined ? {} : { index: command.index },
3581
+ ...command.title === undefined ? {} : { title: command.title },
3582
+ ...command.description === undefined ? {} : { description: command.description },
3583
+ ...command.basePath === undefined ? {} : { basePath: command.basePath },
3584
+ ...command.baseUrl === undefined ? {} : { baseUrl: command.baseUrl }
3585
+ });
3586
+ output.stdout(command.json ? terminalSafeJson(result.report) : sanitizeTerminalText(renderPublishReportText(result.report, command.dryRun)));
3587
+ return 0;
3588
+ }
3589
+ async function runServe(command, output, dependencies) {
3590
+ const site = await (dependencies.serveSite ?? serveSite)({
3591
+ root: command.root,
3592
+ host: command.host,
3593
+ port: command.port
3594
+ });
3595
+ output.stdout(command.json ? terminalSafeJson({ root: site.root, host: site.host, port: site.port, url: site.url }) : `Serving ${safe(site.root)} at ${safe(site.url)}
3596
+ `);
3597
+ return 0;
3598
+ }
3107
3599
  async function runCatalog(command, output, dependencies) {
3108
3600
  const snapshot = await (dependencies.scanVault ?? scanVault)(command.root, command.options);
3109
3601
  const relativeIndex = relative(snapshot.root, snapshot.indexPath).split("\\").join("/");
@@ -3673,6 +4165,10 @@ ${sanitizeTerminalText(usage)}`);
3673
4165
  return await runPercolate(command, output, dependencies);
3674
4166
  if (command.kind === "list")
3675
4167
  return await runList(command, output, dependencies);
4168
+ if (command.kind === "publish")
4169
+ return await runPublish(command, output, dependencies);
4170
+ if (command.kind === "serve")
4171
+ return await runServe(command, output, dependencies);
3676
4172
  if (command.kind === "inbox")
3677
4173
  return await runInbox(command, output, dependencies);
3678
4174
  if (command.kind === "catalog")