ko-qmd 2.8.3-ko.10
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/CHANGELOG.md +1937 -0
- package/LICENSE +21 -0
- package/README.en.md +184 -0
- package/README.md +292 -0
- package/bin/qmd +192 -0
- package/dist/ast.d.ts +65 -0
- package/dist/ast.js +51 -0
- package/dist/bench/bench.d.ts +41 -0
- package/dist/bench/bench.js +12 -0
- package/dist/bench/score.d.ts +33 -0
- package/dist/bench/score.js +1 -0
- package/dist/bench/types.d.ts +101 -0
- package/dist/bench/types.js +1 -0
- package/dist/cli/build-info.json +4 -0
- package/dist/cli/embed-lock.d.ts +24 -0
- package/dist/cli/embed-lock.js +2 -0
- package/dist/cli/formatter.d.ts +120 -0
- package/dist/cli/formatter.js +60 -0
- package/dist/cli/mcp-pid.d.ts +33 -0
- package/dist/cli/mcp-pid.js +1 -0
- package/dist/cli/qmd.d.ts +101 -0
- package/dist/cli/qmd.js +165 -0
- package/dist/cli/version.d.ts +42 -0
- package/dist/cli/version.js +1 -0
- package/dist/collections.d.ts +162 -0
- package/dist/collections.js +1 -0
- package/dist/db.d.ts +63 -0
- package/dist/db.js +2 -0
- package/dist/filters.d.ts +46 -0
- package/dist/filters.js +1 -0
- package/dist/grep.d.ts +85 -0
- package/dist/grep.js +9 -0
- package/dist/hangul.d.ts +102 -0
- package/dist/hangul.js +1 -0
- package/dist/index.d.ts +270 -0
- package/dist/index.js +1 -0
- package/dist/llm.d.ts +584 -0
- package/dist/llm.js +40 -0
- package/dist/maintenance.d.ts +33 -0
- package/dist/maintenance.js +1 -0
- package/dist/mcp/daemon-health.d.ts +17 -0
- package/dist/mcp/daemon-health.js +1 -0
- package/dist/mcp/origin-guard.d.ts +67 -0
- package/dist/mcp/origin-guard.js +1 -0
- package/dist/mcp/server.d.ts +131 -0
- package/dist/mcp/server.js +35 -0
- package/dist/memory.d.ts +74 -0
- package/dist/memory.js +13 -0
- package/dist/metadata-filter.d.ts +74 -0
- package/dist/metadata-filter.js +1 -0
- package/dist/metadata-store.d.ts +45 -0
- package/dist/metadata-store.js +68 -0
- package/dist/metadata.d.ts +61 -0
- package/dist/metadata.js +1 -0
- package/dist/paths.d.ts +1 -0
- package/dist/paths.js +1 -0
- package/dist/query-log.d.ts +156 -0
- package/dist/query-log.js +2 -0
- package/dist/refresh.d.ts +60 -0
- package/dist/refresh.js +1 -0
- package/dist/rerank-input.d.ts +46 -0
- package/dist/rerank-input.js +5 -0
- package/dist/store.d.ts +1267 -0
- package/dist/store.js +421 -0
- package/dist/trust.d.ts +152 -0
- package/dist/trust.js +2 -0
- package/package.json +124 -0
- package/scripts/build.mjs +130 -0
- package/scripts/check-package-grammars.mjs +29 -0
- package/scripts/package-smoke.mjs +65 -0
- package/scripts/test-all.mjs +42 -0
- package/skills/qmd/SKILL.md +333 -0
- package/skills/qmd/references/mcp-setup.md +122 -0
- package/skills/release/SKILL.md +141 -0
- package/skills/release/scripts/install-hooks.sh +38 -0
- package/skills/release/scripts/release-context.sh +129 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* formatter.ts - Output formatting utilities for QMD
|
|
3
|
+
*
|
|
4
|
+
* Provides methods to format search results and documents into various output formats:
|
|
5
|
+
* JSON, CSV, XML, Markdown, files list, and CLI (colored terminal output).
|
|
6
|
+
*/
|
|
7
|
+
import type { SearchResult, MultiGetResult, DocumentResult } from "../store.js";
|
|
8
|
+
export type { SearchResult, MultiGetResult, DocumentResult };
|
|
9
|
+
export type MultiGetFile = {
|
|
10
|
+
filepath: string;
|
|
11
|
+
displayPath: string;
|
|
12
|
+
title: string;
|
|
13
|
+
body: string;
|
|
14
|
+
context?: string | null;
|
|
15
|
+
skipped: false;
|
|
16
|
+
} | {
|
|
17
|
+
filepath: string;
|
|
18
|
+
displayPath: string;
|
|
19
|
+
title: string;
|
|
20
|
+
body: string;
|
|
21
|
+
context?: string | null;
|
|
22
|
+
skipped: true;
|
|
23
|
+
skipReason: string;
|
|
24
|
+
};
|
|
25
|
+
export type OutputFormat = "cli" | "csv" | "md" | "xml" | "files" | "json";
|
|
26
|
+
export type FormatOptions = {
|
|
27
|
+
full?: boolean;
|
|
28
|
+
query?: string;
|
|
29
|
+
useColor?: boolean;
|
|
30
|
+
lineNumbers?: boolean;
|
|
31
|
+
intent?: string;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Add line numbers to text content.
|
|
35
|
+
* Each line becomes: "{lineNum}: {content}"
|
|
36
|
+
* @param text The text to add line numbers to
|
|
37
|
+
* @param startLine Optional starting line number (default: 1)
|
|
38
|
+
*/
|
|
39
|
+
export declare function addLineNumbers(text: string, startLine?: number): string;
|
|
40
|
+
/**
|
|
41
|
+
* Extract short docid from a full hash (first 6 characters).
|
|
42
|
+
*/
|
|
43
|
+
export declare function getDocid(hash: string): string;
|
|
44
|
+
export declare function escapeCSV(value: string | null | number): string;
|
|
45
|
+
export declare function escapeXml(str: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Format search results as JSON
|
|
48
|
+
*/
|
|
49
|
+
export declare function searchResultsToJson(results: SearchResult[], opts?: FormatOptions): string;
|
|
50
|
+
/**
|
|
51
|
+
* Format search results as CSV
|
|
52
|
+
*/
|
|
53
|
+
export declare function searchResultsToCsv(results: SearchResult[], opts?: FormatOptions): string;
|
|
54
|
+
/**
|
|
55
|
+
* Format search results as simple files list (docid,score,filepath,context)
|
|
56
|
+
*/
|
|
57
|
+
export declare function searchResultsToFiles(results: SearchResult[]): string;
|
|
58
|
+
/**
|
|
59
|
+
* Format search results as Markdown
|
|
60
|
+
*/
|
|
61
|
+
export declare function searchResultsToMarkdown(results: SearchResult[], opts?: FormatOptions): string;
|
|
62
|
+
/**
|
|
63
|
+
* Format search results as XML
|
|
64
|
+
*/
|
|
65
|
+
export declare function searchResultsToXml(results: SearchResult[], opts?: FormatOptions): string;
|
|
66
|
+
/**
|
|
67
|
+
* Format search results for MCP (simpler CSV format with pre-extracted snippets)
|
|
68
|
+
*/
|
|
69
|
+
export declare function searchResultsToMcpCsv(results: {
|
|
70
|
+
docid: string;
|
|
71
|
+
file: string;
|
|
72
|
+
title: string;
|
|
73
|
+
score: number;
|
|
74
|
+
context: string | null;
|
|
75
|
+
snippet: string;
|
|
76
|
+
}[]): string;
|
|
77
|
+
/**
|
|
78
|
+
* Format documents as JSON
|
|
79
|
+
*/
|
|
80
|
+
export declare function documentsToJson(results: MultiGetFile[]): string;
|
|
81
|
+
/**
|
|
82
|
+
* Format documents as CSV
|
|
83
|
+
*/
|
|
84
|
+
export declare function documentsToCsv(results: MultiGetFile[]): string;
|
|
85
|
+
/**
|
|
86
|
+
* Format documents as files list
|
|
87
|
+
*/
|
|
88
|
+
export declare function documentsToFiles(results: MultiGetFile[]): string;
|
|
89
|
+
/**
|
|
90
|
+
* Format documents as Markdown
|
|
91
|
+
*/
|
|
92
|
+
export declare function documentsToMarkdown(results: MultiGetFile[]): string;
|
|
93
|
+
/**
|
|
94
|
+
* Format documents as XML
|
|
95
|
+
*/
|
|
96
|
+
export declare function documentsToXml(results: MultiGetFile[]): string;
|
|
97
|
+
/**
|
|
98
|
+
* Format a single DocumentResult as JSON
|
|
99
|
+
*/
|
|
100
|
+
export declare function documentToJson(doc: DocumentResult): string;
|
|
101
|
+
/**
|
|
102
|
+
* Format a single DocumentResult as Markdown
|
|
103
|
+
*/
|
|
104
|
+
export declare function documentToMarkdown(doc: DocumentResult): string;
|
|
105
|
+
/**
|
|
106
|
+
* Format a single DocumentResult as XML
|
|
107
|
+
*/
|
|
108
|
+
export declare function documentToXml(doc: DocumentResult): string;
|
|
109
|
+
/**
|
|
110
|
+
* Format a single document to the specified format
|
|
111
|
+
*/
|
|
112
|
+
export declare function formatDocument(doc: DocumentResult, format: OutputFormat): string;
|
|
113
|
+
/**
|
|
114
|
+
* Format search results to the specified output format
|
|
115
|
+
*/
|
|
116
|
+
export declare function formatSearchResults(results: SearchResult[], format: OutputFormat, opts?: FormatOptions): string;
|
|
117
|
+
/**
|
|
118
|
+
* Format documents to the specified output format
|
|
119
|
+
*/
|
|
120
|
+
export declare function formatDocuments(results: MultiGetFile[], format: OutputFormat): string;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
var g=Object.defineProperty;var c=(t,n)=>g(t,"name",{value:n,configurable:!0});import{extractSnippet as x}from"../store.js";function p(t,n=1){return t.split(`
|
|
2
|
+
`).map((i,o)=>`${n+o}: ${i}`).join(`
|
|
3
|
+
`)}c(p,"addLineNumbers");function F(t){return t.slice(0,6)}c(F,"getDocid");function r(t){if(t==null)return"";const n=String(t);return n.includes(",")||n.includes('"')||n.includes(`
|
|
4
|
+
`)?`"${n.replace(/"/g,'""')}"`:n}c(r,"escapeCSV");function u(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}c(u,"escapeXml");function m(t,n={}){const e=n.query||"",i=t.map(o=>{const s=o.body||"",d=s?x(s,e,300,o.chunkPos,void 0,n.intent):void 0;let l=n.full?s:void 0,a=n.full?void 0:d?.snippet;return n.lineNumbers&&(l&&(l=p(l)),a&&(a=p(a))),{docid:`#${o.docid}`,score:Math.round(o.score*100)/100,file:o.displayPath,...d&&{line:d.line},title:o.title,...o.context&&{context:o.context},...l&&{body:l},...a&&{snippet:a}}});return JSON.stringify(i,null,2)}c(m,"searchResultsToJson");function P(t,n={}){const e=n.query||"",i="docid,score,file,title,context,line,snippet",o=t.map(s=>{const d=s.body||"",{line:l,snippet:a}=x(d,e,500,s.chunkPos,void 0,n.intent);let f=n.full?d:a;return n.lineNumbers&&f&&(f=p(f)),[`#${s.docid}`,s.score.toFixed(4),r(s.displayPath),r(s.title),r(s.context||""),l,r(f)].join(",")});return[i,...o].join(`
|
|
5
|
+
`)}c(P,"searchResultsToCsv");function k(t){return t.map(n=>{const e=n.context?`,"${n.context.replace(/"/g,'""')}"`:"";return`#${n.docid},${n.score.toFixed(2)},${n.displayPath}${e}`}).join(`
|
|
6
|
+
`)}c(k,"searchResultsToFiles");function y(t,n={}){const e=n.query||"";return t.map(i=>{const o=i.title||i.displayPath,s=i.body||"";let d;n.full?d=s:d=x(s,e,500,i.chunkPos,void 0,n.intent).snippet,n.lineNumbers&&(d=p(d));const l=`**file:** \`${i.displayPath}\`
|
|
7
|
+
`,a=i.context?`**context:** ${i.context}
|
|
8
|
+
`:"";return`---
|
|
9
|
+
# ${o}
|
|
10
|
+
|
|
11
|
+
${l}**docid:** \`#${i.docid}\`
|
|
12
|
+
${a}
|
|
13
|
+
${d}
|
|
14
|
+
`}).join(`
|
|
15
|
+
`)}c(y,"searchResultsToMarkdown");function T(t,n={}){const e=n.query||"";return t.map(o=>{const s=o.title?` title="${u(o.title)}"`:"",d=o.body||"";let l=n.full?d:x(d,e,500,o.chunkPos,void 0,n.intent).snippet;n.lineNumbers&&(l=p(l));const a=o.context?` context="${u(o.context)}"`:"";return`<file docid="#${o.docid}" name="${u(o.displayPath)}"${s}${a}>
|
|
16
|
+
${u(l)}
|
|
17
|
+
</file>`}).join(`
|
|
18
|
+
|
|
19
|
+
`)}c(T,"searchResultsToXml");function C(t){const n="docid,file,title,score,context,snippet",e=t.map(i=>[`#${i.docid}`,i.file,i.title,i.score,i.context||"",i.snippet].map(r).join(","));return[n,...e].join(`
|
|
20
|
+
`)}c(C,"searchResultsToMcpCsv");function h(t){const n=t.map(e=>({file:e.displayPath,title:e.title,...e.context&&{context:e.context},...e.skipped?{skipped:!0,reason:e.skipReason}:{body:e.body}}));return JSON.stringify(n,null,2)}c(h,"documentsToJson");function j(t){const n="file,title,context,skipped,body",e=t.map(i=>[i.displayPath,i.title,i.context||"",i.skipped?"true":"false",i.skipped?i.skipReason||"":i.body].map(r).join(","));return[n,...e].join(`
|
|
21
|
+
`)}c(j,"documentsToCsv");function S(t){return t.map(n=>{const e=n.context?`,"${n.context.replace(/"/g,'""')}"`:"",i=n.skipped?",[SKIPPED]":"";return`${n.displayPath}${e}${i}`}).join(`
|
|
22
|
+
`)}c(S,"documentsToFiles");function $(t){return t.map(n=>{let e=`## ${n.displayPath}
|
|
23
|
+
|
|
24
|
+
`;return n.title&&n.title!==n.displayPath&&(e+=`**Title:** ${n.title}
|
|
25
|
+
|
|
26
|
+
`),n.context&&(e+=`**Context:** ${n.context}
|
|
27
|
+
|
|
28
|
+
`),n.skipped?e+=`> ${n.skipReason}
|
|
29
|
+
`:e+="```\n"+n.body+"\n```\n",e}).join(`
|
|
30
|
+
`)}c($,"documentsToMarkdown");function R(t){return`<?xml version="1.0" encoding="UTF-8"?>
|
|
31
|
+
<documents>
|
|
32
|
+
${t.map(e=>{let i=` <document>
|
|
33
|
+
`;return i+=` <file>${u(e.displayPath)}</file>
|
|
34
|
+
`,i+=` <title>${u(e.title)}</title>
|
|
35
|
+
`,e.context&&(i+=` <context>${u(e.context)}</context>
|
|
36
|
+
`),e.skipped?(i+=` <skipped>true</skipped>
|
|
37
|
+
`,i+=` <reason>${u(e.skipReason||"")}</reason>
|
|
38
|
+
`):i+=` <body>${u(e.body)}</body>
|
|
39
|
+
`,i+=" </document>",i}).join(`
|
|
40
|
+
`)}
|
|
41
|
+
</documents>`}c(R,"documentsToXml");function q(t){return JSON.stringify({file:t.displayPath,title:t.title,...t.context&&{context:t.context},hash:t.hash,modifiedAt:t.modifiedAt,bodyLength:t.bodyLength,...t.body!==void 0&&{body:t.body}},null,2)}c(q,"documentToJson");function b(t){let n=`# ${t.title||t.displayPath}
|
|
42
|
+
|
|
43
|
+
`;return t.context&&(n+=`**Context:** ${t.context}
|
|
44
|
+
|
|
45
|
+
`),n+=`**File:** ${t.displayPath}
|
|
46
|
+
`,n+=`**Modified:** ${t.modifiedAt}
|
|
47
|
+
|
|
48
|
+
`,t.body!==void 0&&(n+=`---
|
|
49
|
+
|
|
50
|
+
`+t.body+`
|
|
51
|
+
`),n}c(b,"documentToMarkdown");function A(t){let n=`<?xml version="1.0" encoding="UTF-8"?>
|
|
52
|
+
<document>
|
|
53
|
+
`;return n+=` <file>${u(t.displayPath)}</file>
|
|
54
|
+
`,n+=` <title>${u(t.title)}</title>
|
|
55
|
+
`,t.context&&(n+=` <context>${u(t.context)}</context>
|
|
56
|
+
`),n+=` <hash>${u(t.hash)}</hash>
|
|
57
|
+
`,n+=` <modifiedAt>${u(t.modifiedAt)}</modifiedAt>
|
|
58
|
+
`,n+=` <bodyLength>${t.bodyLength}</bodyLength>
|
|
59
|
+
`,t.body!==void 0&&(n+=` <body>${u(t.body)}</body>
|
|
60
|
+
`),n+="</document>",n}c(A,"documentToXml");function J(t,n){switch(n){case"json":return q(t);case"md":return b(t);case"xml":return A(t);default:return b(t)}}c(J,"formatDocument");function M(t,n,e={}){switch(n){case"json":return m(t,e);case"csv":return P(t,e);case"files":return k(t);case"md":return y(t,e);case"xml":return T(t,e);case"cli":return y(t,e);default:return m(t,e)}}c(M,"formatSearchResults");function v(t,n){switch(n){case"json":return h(t);case"csv":return j(t);case"files":return S(t);case"md":return $(t);case"xml":return R(t);case"cli":return $(t);default:return h(t)}}c(v,"formatDocuments");export{p as addLineNumbers,q as documentToJson,b as documentToMarkdown,A as documentToXml,j as documentsToCsv,S as documentsToFiles,h as documentsToJson,$ as documentsToMarkdown,R as documentsToXml,r as escapeCSV,u as escapeXml,J as formatDocument,v as formatDocuments,M as formatSearchResults,F as getDocid,P as searchResultsToCsv,k as searchResultsToFiles,m as searchResultsToJson,y as searchResultsToMarkdown,C as searchResultsToMcpCsv,T as searchResultsToXml};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP daemon pidfile identity helpers.
|
|
3
|
+
*
|
|
4
|
+
* Pidfiles alone are unsafe after PID reuse (e.g. post-reboot). Callers must
|
|
5
|
+
* confirm a recorded PID still belongs to a qmd process before signalling it
|
|
6
|
+
* or treating it as "already running".
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Pid/log filenames for the MCP HTTP daemon.
|
|
10
|
+
* The default index keeps `mcp.pid` / `mcp.log` for compatibility; named
|
|
11
|
+
* indexes are scoped so a named daemon can run alongside the default (#772).
|
|
12
|
+
*/
|
|
13
|
+
export declare function mcpDaemonStateFiles(indexName?: string): {
|
|
14
|
+
pidFile: string;
|
|
15
|
+
logFile: string;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Open (truncate-or-create) the daemon mcp.log with owner-only permissions.
|
|
19
|
+
* The daemon log carries query text, so it mirrors the 0600 convention of
|
|
20
|
+
* the opt-in query log (appendFile mode 0o600). The explicit open mode
|
|
21
|
+
* covers creation; fchmod covers truncation of a pre-existing file, where
|
|
22
|
+
* the open mode is ignored.
|
|
23
|
+
*/
|
|
24
|
+
export declare function openDaemonLogFile(logPath: string): number;
|
|
25
|
+
/** True if a process command line looks like a qmd CLI invocation. */
|
|
26
|
+
export declare function looksLikeQmdMcpCommand(cmdline: string): boolean;
|
|
27
|
+
/** Read process cmdline (Linux /proc preferred; ps fallback for macOS). */
|
|
28
|
+
export declare function readProcessCmdline(pid: number): string | null;
|
|
29
|
+
/**
|
|
30
|
+
* Returns true only if `pid` is alive AND its command line looks like qmd.
|
|
31
|
+
* If cmdline cannot be read or does not match, returns false (treat as stale).
|
|
32
|
+
*/
|
|
33
|
+
export declare function isQmdMcpPid(pid: number): boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var c=Object.defineProperty;var r=(t,e)=>c(t,"name",{value:e,configurable:!0});import{existsSync as s,fchmodSync as m,openSync as l,readFileSync as u}from"node:fs";import{execFileSync as i}from"node:child_process";function y(t="index"){const e=!t||t==="index"?"":`-${t}`;return{pidFile:`mcp${e}.pid`,logFile:`mcp${e}.log`}}r(y,"mcpDaemonStateFiles");function x(t){const e=l(t,"w",384);return m(e,384),e}r(x,"openDaemonLogFile");function f(t){const e=t.trim();return e?/(?:^|[\s/\\])qmd(?:\.(?:ts|js))?(?:[\s]|$)/i.test(e):!1}r(f,"looksLikeQmdMcpCommand");function p(t){if(!Number.isInteger(t)||t<=0)return null;const e=`/proc/${t}/cmdline`;if(s(e))try{const o=u(e,"utf-8").replace(/\0/g," ").trim();if(o)return o}catch{}try{let n="";try{n=i("ps",["-p",String(t),"-o","args="],{encoding:"utf-8",timeout:2e3,stdio:["ignore","pipe","ignore"]})}catch{n=i("ps",["-p",String(t),"-o","command="],{encoding:"utf-8",timeout:2e3,stdio:["ignore","pipe","ignore"]})}return n.trim()||null}catch{return null}}r(p,"readProcessCmdline");function S(t){if(!Number.isInteger(t)||t<=0)return!1;try{process.kill(t,0)}catch{return!1}const e=p(t);return e?f(e):!1}r(S,"isQmdMcpPid");export{S as isQmdMcpPid,f as looksLikeQmdMcpCommand,y as mcpDaemonStateFiles,x as openDaemonLogFile,p as readProcessCmdline};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { spawn as nodeSpawn } from "child_process";
|
|
2
|
+
import { type RRFContributionTrace, generateEmbeddings, type ChunkStrategy } from "../store.js";
|
|
3
|
+
import { type OutputFormat } from "./formatter.js";
|
|
4
|
+
export declare const DEFAULT_MCP_PORT = 8181;
|
|
5
|
+
export declare function resolveMcpPort(raw: unknown): number;
|
|
6
|
+
/**
|
|
7
|
+
* Wait until a freshly spawned `--daemon` child reports listening in its log
|
|
8
|
+
* file, or fail. The daemon branch must not print `Started` before this
|
|
9
|
+
* resolves: a bind failure (e.g. an occupied port) would otherwise exit 0
|
|
10
|
+
* while the child dies and its exit handler removes the pidfile, leaving
|
|
11
|
+
* `stop` reporting Not running. On failure no pidfile is written, so
|
|
12
|
+
* `stop`/`status` keep their missing-pidfile behavior.
|
|
13
|
+
*/
|
|
14
|
+
export declare function waitForDaemonListening(child: ReturnType<typeof nodeSpawn>, logPath: string, timeoutMs?: number): Promise<{
|
|
15
|
+
ok: true;
|
|
16
|
+
} | {
|
|
17
|
+
ok: false;
|
|
18
|
+
message: string;
|
|
19
|
+
}>;
|
|
20
|
+
type CliLifecycleWritable = {
|
|
21
|
+
write(chunk: string | Uint8Array, callback?: (error?: Error | null) => void): boolean;
|
|
22
|
+
};
|
|
23
|
+
type FinishSuccessfulCliCommandOptions = {
|
|
24
|
+
command: string;
|
|
25
|
+
format?: OutputFormat;
|
|
26
|
+
cleanup?: () => Promise<void>;
|
|
27
|
+
exit?: (code: number) => void;
|
|
28
|
+
stdout?: CliLifecycleWritable;
|
|
29
|
+
stderr?: CliLifecycleWritable;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Finish a successful CLI command after output has been flushed.
|
|
33
|
+
*
|
|
34
|
+
* We deliberately do NOT call `process.exit(0)`. `process.exit()` skips
|
|
35
|
+
* Node's `beforeExit` event, and node-llama-cpp registers a `beforeExit` hook
|
|
36
|
+
* that auto-disposes its native handles. On darwin, without that hook firing,
|
|
37
|
+
* libggml-metal's static `ggml_metal_device` destructor asserts on a
|
|
38
|
+
* non-empty residency-set collection during `__cxa_finalize_ranges` and
|
|
39
|
+
* dumps a multi-kB backtrace (upstream ggml-org/llama.cpp#22593, fix open as
|
|
40
|
+
* PR #22595). Empirically, even with explicit `disposeDefaultLlamaCpp()` the
|
|
41
|
+
* direct `process.exit(0)` path still trips the assertion — letting the
|
|
42
|
+
* event loop drain naturally is what actually clears the rsets.
|
|
43
|
+
*
|
|
44
|
+
* So: set `process.exitCode = 0` and return. The main module finishes, the
|
|
45
|
+
* event loop drains, `beforeExit` fires, native resources tear down in
|
|
46
|
+
* order, and the process exits cleanly. The `GGML_METAL_NO_RESIDENCY=1` env
|
|
47
|
+
* var that `bin/qmd` exports is a defense-in-depth safety net for paths
|
|
48
|
+
* that still call `process.exit()` after loading the native binding
|
|
49
|
+
* (signal handlers, error paths, `bun test`).
|
|
50
|
+
*
|
|
51
|
+
* If the caller passes an explicit `exit` for testability, we honor it —
|
|
52
|
+
* the lifecycle tests verify the legacy flush → cleanup → exit ordering.
|
|
53
|
+
* Production callers must not pass `exit`.
|
|
54
|
+
*/
|
|
55
|
+
export declare function finishSuccessfulCliCommand(options: FinishSuccessfulCliCommandOptions): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Render an absolute filesystem path for human display under --full-path.
|
|
58
|
+
*
|
|
59
|
+
* If the path is the current working directory or a subpath of it, return a
|
|
60
|
+
* "./"-prefixed relative path so it is unambiguously a filesystem path (not a
|
|
61
|
+
* bare collection-relative string that could be confused for a `qmd://`
|
|
62
|
+
* fragment). Otherwise return the absolute realpath so symlinks resolve
|
|
63
|
+
* consistently. Returns `null` if the path could not be normalized (deleted
|
|
64
|
+
* between the caller's existsSync check and now) — callers fall back to
|
|
65
|
+
* whatever they had before plus the stale-index stderr warning.
|
|
66
|
+
*/
|
|
67
|
+
export declare function renderFullPath(absolutePath: string, cwd?: string): string | null;
|
|
68
|
+
export declare function parseEmbedBatchMbOption(value: unknown): number | undefined;
|
|
69
|
+
export declare function resolveEmbedModelForCli(): string;
|
|
70
|
+
export declare function resolveGenerateModelForCli(): string;
|
|
71
|
+
export declare function resolveRerankModelForCli(): string;
|
|
72
|
+
export declare function vectorIndex(model?: string, force?: boolean, batchOptions?: {
|
|
73
|
+
maxDocsPerBatch?: number;
|
|
74
|
+
maxBatchBytes?: number;
|
|
75
|
+
chunkStrategy?: ChunkStrategy;
|
|
76
|
+
collection?: string;
|
|
77
|
+
maxDurationMs?: number;
|
|
78
|
+
}, deps?: {
|
|
79
|
+
runEmbeddings?: typeof generateEmbeddings;
|
|
80
|
+
}): Promise<void>;
|
|
81
|
+
/**
|
|
82
|
+
* Which sub-query found this document, and where did it place in that sub-query's own list?
|
|
83
|
+
*
|
|
84
|
+
* Expansion turns one query into a dozen, fuses their lists and reports one number. A single
|
|
85
|
+
* fused rank cannot say whether a result placed first in the hyde list and nowhere else, or
|
|
86
|
+
* middling everywhere — and those call for different fixes. Each expansion gets a line, with
|
|
87
|
+
* the sub-query text it ran, so the fused score can be read back to the query that earned it.
|
|
88
|
+
*/
|
|
89
|
+
export declare function explainGroupLines(contributions: readonly RRFContributionTrace[]): string[];
|
|
90
|
+
export declare function formatScore(score: number): string;
|
|
91
|
+
export declare function buildEditorUri(template: string, absolutePath: string, line: number, col: number): string;
|
|
92
|
+
export declare function termLink(text: string, url: string, isTTY?: boolean): string;
|
|
93
|
+
/**
|
|
94
|
+
* Resolve the effective MCP HTTP bind host (single source of truth).
|
|
95
|
+
* Same precedence the daemon child uses in startMcpHttpServer:
|
|
96
|
+
* `--host` flag ?? QMD_HOST env ?? "localhost". The daemon parent uses
|
|
97
|
+
* this for both the Started announcement and the explicit --host passed
|
|
98
|
+
* to the child, so the announced URL always matches the actual bind.
|
|
99
|
+
*/
|
|
100
|
+
export declare function resolveDaemonHost(flagHost: string | undefined): string;
|
|
101
|
+
export {};
|