@wei840222/qmd 2026.8.23

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 (94) hide show
  1. package/CHANGELOG.md +1373 -0
  2. package/LICENSE +45 -0
  3. package/README.md +1439 -0
  4. package/THIRD_PARTY_NOTICES.md +31 -0
  5. package/bin/qmd +192 -0
  6. package/dist/ast.d.ts +65 -0
  7. package/dist/ast.js +334 -0
  8. package/dist/bench/bench.d.ts +35 -0
  9. package/dist/bench/bench.js +338 -0
  10. package/dist/bench/cjk-baseline.d.ts +36 -0
  11. package/dist/bench/cjk-baseline.js +111 -0
  12. package/dist/bench/fixture.d.ts +2 -0
  13. package/dist/bench/fixture.js +84 -0
  14. package/dist/bench/score.d.ts +38 -0
  15. package/dist/bench/score.js +107 -0
  16. package/dist/bench/types.d.ts +110 -0
  17. package/dist/bench/types.js +8 -0
  18. package/dist/cli/build-info.json +4 -0
  19. package/dist/cli/embed-lock.d.ts +24 -0
  20. package/dist/cli/embed-lock.js +94 -0
  21. package/dist/cli/embedding-owner.d.ts +10 -0
  22. package/dist/cli/embedding-owner.js +20 -0
  23. package/dist/cli/formatter.d.ts +120 -0
  24. package/dist/cli/formatter.js +355 -0
  25. package/dist/cli/mcp-pid.d.ts +25 -0
  26. package/dist/cli/mcp-pid.js +86 -0
  27. package/dist/cli/qmd.d.ts +72 -0
  28. package/dist/cli/qmd.js +4806 -0
  29. package/dist/cli/version.d.ts +42 -0
  30. package/dist/cli/version.js +80 -0
  31. package/dist/collections.d.ts +200 -0
  32. package/dist/collections.js +433 -0
  33. package/dist/db.d.ts +65 -0
  34. package/dist/db.js +143 -0
  35. package/dist/diagnostics.d.ts +62 -0
  36. package/dist/diagnostics.js +260 -0
  37. package/dist/embedding/config.d.ts +52 -0
  38. package/dist/embedding/config.js +229 -0
  39. package/dist/embedding/identity.d.ts +58 -0
  40. package/dist/embedding/identity.js +321 -0
  41. package/dist/embedding/local-identity.d.ts +1 -0
  42. package/dist/embedding/local-identity.js +15 -0
  43. package/dist/embedding/local.d.ts +34 -0
  44. package/dist/embedding/local.js +290 -0
  45. package/dist/embedding/openai.d.ts +79 -0
  46. package/dist/embedding/openai.js +477 -0
  47. package/dist/embedding/owner.d.ts +13 -0
  48. package/dist/embedding/owner.js +36 -0
  49. package/dist/embedding/provider.d.ts +68 -0
  50. package/dist/embedding/provider.js +16 -0
  51. package/dist/embedding/remote-chunking.d.ts +22 -0
  52. package/dist/embedding/remote-chunking.js +83 -0
  53. package/dist/embedding/remote-embedding.d.ts +15 -0
  54. package/dist/embedding/remote-embedding.js +77 -0
  55. package/dist/hybrid-llm.d.ts +18 -0
  56. package/dist/hybrid-llm.js +53 -0
  57. package/dist/index.d.ts +244 -0
  58. package/dist/index.js +418 -0
  59. package/dist/llm.d.ts +566 -0
  60. package/dist/llm.js +1847 -0
  61. package/dist/maintenance.d.ts +33 -0
  62. package/dist/maintenance.js +52 -0
  63. package/dist/mcp/origin-guard.d.ts +67 -0
  64. package/dist/mcp/origin-guard.js +137 -0
  65. package/dist/mcp/server.d.ts +116 -0
  66. package/dist/mcp/server.js +919 -0
  67. package/dist/paths.d.ts +1 -0
  68. package/dist/paths.js +4 -0
  69. package/dist/remote-llm.d.ts +52 -0
  70. package/dist/remote-llm.js +464 -0
  71. package/dist/search/cjk-analyzer.d.ts +33 -0
  72. package/dist/search/cjk-analyzer.js +158 -0
  73. package/dist/search/cjk-index.d.ts +104 -0
  74. package/dist/search/cjk-index.js +1031 -0
  75. package/dist/search/jieba-loader.d.ts +23 -0
  76. package/dist/search/jieba-loader.js +79 -0
  77. package/dist/search/query-expansion.d.ts +23 -0
  78. package/dist/search/query-expansion.js +43 -0
  79. package/dist/search/zh-dict.txt +624013 -0
  80. package/dist/store.d.ts +1218 -0
  81. package/dist/store.js +6076 -0
  82. package/dist/trust.d.ts +152 -0
  83. package/dist/trust.js +249 -0
  84. package/package.json +139 -0
  85. package/scripts/build.mjs +83 -0
  86. package/scripts/check-package-grammars.mjs +29 -0
  87. package/scripts/package-smoke.mjs +205 -0
  88. package/scripts/sync-zh-dict.mjs +187 -0
  89. package/scripts/test-all.mjs +45 -0
  90. package/skills/qmd/SKILL.md +324 -0
  91. package/skills/qmd/references/mcp-setup.md +119 -0
  92. package/skills/release/SKILL.md +141 -0
  93. package/skills/release/scripts/install-hooks.sh +38 -0
  94. package/skills/release/scripts/release-context.sh +129 -0
@@ -0,0 +1,158 @@
1
+ import { createJiebaUnavailableCapability, loadJiebaCapability, loadJiebaCapabilitySync, } from "./jieba-loader.js";
2
+ const LEGACY_CJK_RUN_PATTERN = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]+/gu;
3
+ const DIRECT_CJK_PATTERN = /^(?:\p{Script=Han}|\p{Script=Hiragana}|\p{Script=Katakana}|\p{Script=Hangul})$/u;
4
+ const CJK_SCRIPT_EXTENSIONS_PATTERN = /^(?:\p{Script_Extensions=Han}|\p{Script_Extensions=Hiragana}|\p{Script_Extensions=Katakana}|\p{Script_Extensions=Hangul})$/u;
5
+ const DIRECT_HAN_PATTERN = /^\p{Script=Han}$/u;
6
+ const HAN_SCRIPT_EXTENSIONS_PATTERN = /^\p{Script_Extensions=Han}$/u;
7
+ const DIRECT_KANA_OR_HANGUL_PATTERN = /^(?:\p{Script=Hiragana}|\p{Script=Katakana}|\p{Script=Hangul})$/u;
8
+ const KANA_OR_HANGUL_SCRIPT_EXTENSIONS_PATTERN = /^(?:\p{Script_Extensions=Hiragana}|\p{Script_Extensions=Katakana}|\p{Script_Extensions=Hangul})$/u;
9
+ const LETTER_OR_MARK_PATTERN = /^[\p{L}\p{M}]$/u;
10
+ const LINE_BREAK_PATTERN = /[\u000b\u000c\r\n\u0085\u2028\u2029]/u;
11
+ const SENTENCE_TERMINAL_PATTERN = /\p{Sentence_Terminal}/u;
12
+ const IDENTIFIER_DOT_PATTERN = /^[.\u2024\ufe52\uff0e]$/u;
13
+ const IDENTIFIER_CHARACTER_PATTERN = /[\p{L}\p{M}\p{N}_-]/u;
14
+ const SEARCHABLE_TOKEN_PATTERN = /[\p{L}\p{N}]/u;
15
+ export const CJK_ANALYZER_POLICY_VERSIONS = Object.freeze({
16
+ analyzer: "cjk-analyzer-v3",
17
+ char: "legacy-script-runs-v1",
18
+ wordBoundary: "unicode-sentence-terminal-v3",
19
+ wordEligibility: "direct-script-and-shared-letter-mark-v2",
20
+ bigram: "direct-script-and-shared-letter-mark-v2",
21
+ });
22
+ function serializeCharSignal(text) {
23
+ return text.replace(LEGACY_CJK_RUN_PATTERN, run => ` ${Array.from(run).join(" ")} `);
24
+ }
25
+ function belongsToScript(codePoint, directPattern, scriptExtensionsPattern) {
26
+ return directPattern.test(codePoint)
27
+ || (LETTER_OR_MARK_PATTERN.test(codePoint) && scriptExtensionsPattern.test(codePoint));
28
+ }
29
+ function isCjkCodePoint(codePoint) {
30
+ return belongsToScript(codePoint, DIRECT_CJK_PATTERN, CJK_SCRIPT_EXTENSIONS_PATTERN);
31
+ }
32
+ export function containsCjk(text) {
33
+ return Array.from(text).some(isCjkCodePoint);
34
+ }
35
+ function hasScriptCodePoint(text, directPattern, scriptExtensionsPattern) {
36
+ return Array.from(text).some(codePoint => belongsToScript(codePoint, directPattern, scriptExtensionsPattern));
37
+ }
38
+ function serializeBigramSignal(text) {
39
+ const tokens = [];
40
+ let previous;
41
+ for (const codePoint of text) {
42
+ if (!isCjkCodePoint(codePoint)) {
43
+ previous = undefined;
44
+ continue;
45
+ }
46
+ if (previous !== undefined)
47
+ tokens.push(previous + codePoint);
48
+ previous = codePoint;
49
+ }
50
+ return tokens.join(" ");
51
+ }
52
+ function isIdentifierDot(codePoints, index) {
53
+ const codePoint = codePoints[index];
54
+ if (codePoint === undefined || !IDENTIFIER_DOT_PATTERN.test(codePoint))
55
+ return false;
56
+ const previous = codePoints[index - 1];
57
+ const next = codePoints[index + 1];
58
+ return previous !== undefined
59
+ && next !== undefined
60
+ && IDENTIFIER_CHARACTER_PATTERN.test(previous)
61
+ && IDENTIFIER_CHARACTER_PATTERN.test(next);
62
+ }
63
+ function isSegmentBoundary(codePoints, index) {
64
+ const codePoint = codePoints[index];
65
+ if (LINE_BREAK_PATTERN.test(codePoint))
66
+ return true;
67
+ return SENTENCE_TERMINAL_PATTERN.test(codePoint) && !isIdentifierDot(codePoints, index);
68
+ }
69
+ function getJiebaSegments(text) {
70
+ const segments = [];
71
+ const codePoints = Array.from(text);
72
+ let current = "";
73
+ const flush = () => {
74
+ const segment = current.trim();
75
+ const hasHan = hasScriptCodePoint(segment, DIRECT_HAN_PATTERN, HAN_SCRIPT_EXTENSIONS_PATTERN);
76
+ const hasKanaOrHangul = hasScriptCodePoint(segment, DIRECT_KANA_OR_HANGUL_PATTERN, KANA_OR_HANGUL_SCRIPT_EXTENSIONS_PATTERN);
77
+ if (hasHan && !hasKanaOrHangul) {
78
+ segments.push(segment);
79
+ }
80
+ current = "";
81
+ };
82
+ for (let index = 0; index < codePoints.length; index++) {
83
+ if (isSegmentBoundary(codePoints, index))
84
+ flush();
85
+ else
86
+ current += codePoints[index];
87
+ }
88
+ flush();
89
+ return segments;
90
+ }
91
+ function serializeWordSignal(segments, capability) {
92
+ const tokens = [];
93
+ for (const segment of segments) {
94
+ const segmentTokens = capability.cut(segment, false);
95
+ if (!Array.isArray(segmentTokens))
96
+ throw new TypeError("Invalid jieba token stream");
97
+ for (const token of segmentTokens) {
98
+ if (typeof token !== "string")
99
+ throw new TypeError("Invalid jieba token");
100
+ const trimmed = token.trim();
101
+ if (trimmed && SEARCHABLE_TOKEN_PATTERN.test(trimmed))
102
+ tokens.push(trimmed);
103
+ }
104
+ }
105
+ return tokens.join(" ");
106
+ }
107
+ function unavailableWordCapability() {
108
+ const { diagnostic } = createJiebaUnavailableCapability();
109
+ return Object.freeze({ available: false, diagnostic });
110
+ }
111
+ function failedWordCapability() {
112
+ const diagnostic = Object.freeze({
113
+ code: "CJK_ANALYZER_FAILED",
114
+ message: "Chinese word segmentation failed while analyzing indexed content.",
115
+ runtime: `${process.platform}-${process.arch}`,
116
+ remediation: "Retry the operation; if it continues to fail, verify the dictionary and @node-rs/jieba runtime compatibility.",
117
+ });
118
+ return Object.freeze({ available: false, diagnostic });
119
+ }
120
+ export function analyzeCjkWithCapability(text, capability) {
121
+ const char = serializeCharSignal(text);
122
+ const bigram = serializeBigramSignal(text);
123
+ if (!capability.available) {
124
+ return { char, word: "", bigram, wordCapability: unavailableWordCapability() };
125
+ }
126
+ try {
127
+ const word = serializeWordSignal(getJiebaSegments(text), capability);
128
+ return {
129
+ char,
130
+ word,
131
+ bigram,
132
+ wordCapability: Object.freeze({ available: true }),
133
+ };
134
+ }
135
+ catch {
136
+ return { char, word: "", bigram, wordCapability: failedWordCapability() };
137
+ }
138
+ }
139
+ export async function analyzeCjk(text, loadCapability = loadJiebaCapability) {
140
+ try {
141
+ return analyzeCjkWithCapability(text, await loadCapability());
142
+ }
143
+ catch {
144
+ const char = serializeCharSignal(text);
145
+ const bigram = serializeBigramSignal(text);
146
+ return { char, word: "", bigram, wordCapability: unavailableWordCapability() };
147
+ }
148
+ }
149
+ export function analyzeCjkSync(text, loadCapability = loadJiebaCapabilitySync) {
150
+ try {
151
+ return analyzeCjkWithCapability(text, loadCapability());
152
+ }
153
+ catch {
154
+ const char = serializeCharSignal(text);
155
+ const bigram = serializeBigramSignal(text);
156
+ return { char, word: "", bigram, wordCapability: unavailableWordCapability() };
157
+ }
158
+ }
@@ -0,0 +1,104 @@
1
+ import { type CjkWordDiagnostic, type JiebaCapabilityLoader } from "./cjk-analyzer.js";
2
+ import { type Database } from "../db.js";
3
+ export interface CjkAnalyzerFingerprintSource {
4
+ analyzerVersion: string;
5
+ charPolicy: string;
6
+ wordBoundaryPolicy: string;
7
+ wordEligibilityPolicy: string;
8
+ bigramPolicy: string;
9
+ jiebaPackage: string;
10
+ jiebaVersion: string;
11
+ jiebaHmm: string;
12
+ dictionaryVersion: string;
13
+ dictionarySha256: string;
14
+ userDictionarySha256?: string;
15
+ }
16
+ export declare const CJK_ANALYZER_FINGERPRINT_SOURCE: Readonly<CjkAnalyzerFingerprintSource>;
17
+ export declare function computeCjkAnalyzerFingerprint(source: CjkAnalyzerFingerprintSource): string;
18
+ export declare function getCjkAnalyzerFingerprint(userDictContent?: Uint8Array | string): string;
19
+ export type CjkLexicalIndexStatus = "empty" | "building" | "ready" | "unavailable" | "dirty";
20
+ export interface CjkLexicalIndexState {
21
+ status: CjkLexicalIndexStatus;
22
+ generation: number;
23
+ analyzerFingerprint: string | null;
24
+ wordCapability: "unknown" | "available" | "unavailable";
25
+ diagnosticCode: string | null;
26
+ activeBuildId: string | null;
27
+ publishedBuildId: string | null;
28
+ dirtySinceMutationSeq: number | null;
29
+ updatedAt: number;
30
+ }
31
+ export type CjkIndexBuildPhase = {
32
+ phase: "snapshot-complete";
33
+ buildId: string;
34
+ baseMutationSeq: number;
35
+ } | {
36
+ phase: "catchup-page-complete";
37
+ buildId: string;
38
+ targetMutationSeq: number;
39
+ appliedMutationSeq: number;
40
+ documentCount: number;
41
+ } | {
42
+ phase: "catchup-complete";
43
+ buildId: string;
44
+ targetMutationSeq: number;
45
+ documentCount: number;
46
+ } | {
47
+ phase: "published";
48
+ buildId: string;
49
+ generation: number;
50
+ };
51
+ export type CjkIndexBuildResult = {
52
+ status: "ready";
53
+ buildId: string;
54
+ generation: number;
55
+ fingerprint: string;
56
+ reused: boolean;
57
+ } | {
58
+ status: "busy";
59
+ buildId: string;
60
+ } | {
61
+ status: "unavailable";
62
+ diagnostic: CjkWordDiagnostic;
63
+ publishedBuildId: string | null;
64
+ };
65
+ export interface CjkIndexBuildOptions {
66
+ force?: boolean;
67
+ loadCapability?: JiebaCapabilityLoader;
68
+ leaseDurationMs?: number;
69
+ onPhase?: (event: CjkIndexBuildPhase) => void | Promise<void>;
70
+ }
71
+ /**
72
+ * Return the kernel process start-time token used to distinguish a live build
73
+ * owner from an unrelated process that later reused the same PID. Linux exposes
74
+ * this as field 22 in /proc/<pid>/stat. Other platforms conservatively return
75
+ * null, preserving legacy PID-only cleanup rather than risking a live owner.
76
+ */
77
+ export declare function getProcessStartToken(pid?: number): string | null;
78
+ /**
79
+ * Reclaim a bounded number of atomically registered retired FTS table pairs.
80
+ * Each pair is dropped and its tombstone cleared in one transaction, so a
81
+ * crash leaves either a fully discoverable pair or a fully reclaimed pair.
82
+ */
83
+ export declare function cleanupRetiredCjkIndexTables(db: Database, options?: {
84
+ limit?: number;
85
+ }): string[];
86
+ export declare function cleanupExpiredCjkIndexBuilds(db: Database, options?: {
87
+ nowMs?: number;
88
+ }): string[];
89
+ export declare function initializeCjkLexicalIndexSchema(db: Database): void;
90
+ export declare function getCjkLexicalIndexState(db: Database): CjkLexicalIndexState;
91
+ /**
92
+ * Repair the char-only fallback rows affected by raw document writes.
93
+ * Word/bigram rows and the dirty state intentionally remain gated until a
94
+ * complete analyzer rebuild publishes the journal head.
95
+ */
96
+ export declare function repairDirtyCjkCharFallback(db: Database): number;
97
+ /**
98
+ * Commit a document/collection mutation and all published CJK lexical signals
99
+ * at one SQLite transaction boundary. If the published index is not safe to
100
+ * update (missing, rebuilding, stale fingerprint), raw triggers remove stale
101
+ * word/bigram rows and leave an explicit dirty marker for maintenance rebuild.
102
+ */
103
+ export declare function runCjkSynchronizedMutation<T>(db: Database, mutation: () => T): T;
104
+ export declare function rebuildCjkLexicalIndex(dbPath: string, options?: CjkIndexBuildOptions): Promise<CjkIndexBuildResult>;