@dvquys/qrec 0.2.2 → 0.2.3
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/package.json +1 -1
- package/plugin/scripts/qrec-cli.js +10 -10
- package/plugin/scripts/qrec.cjs +1 -1
package/package.json
CHANGED
|
@@ -48,7 +48,16 @@ if (userArgs.includes("serve") && userArgs.includes("--daemon")) {
|
|
|
48
48
|
process.exit(0);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
//
|
|
51
|
+
// TTY: pass stdin directly so interactive prompts (e.g. teardown Y/N) work.
|
|
52
|
+
if (process.stdin.isTTY) {
|
|
53
|
+
const result = spawnSync(bunPath, bunArgs, {
|
|
54
|
+
stdio: "inherit",
|
|
55
|
+
env: process.env,
|
|
56
|
+
});
|
|
57
|
+
process.exit(result.status ?? 1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Non-TTY (hook/pipe): buffer stdin before passing to bun (avoids libuv pipe crash on Linux).
|
|
52
61
|
const stdinChunks = [];
|
|
53
62
|
process.stdin.on("data", chunk => stdinChunks.push(chunk));
|
|
54
63
|
process.stdin.on("end", () => {
|
|
@@ -60,12 +69,3 @@ process.stdin.on("end", () => {
|
|
|
60
69
|
});
|
|
61
70
|
process.exit(result.status ?? 1);
|
|
62
71
|
});
|
|
63
|
-
|
|
64
|
-
if (process.stdin.isTTY) {
|
|
65
|
-
process.stdin.destroy();
|
|
66
|
-
const result = spawnSync(bunPath, bunArgs, {
|
|
67
|
-
stdio: ["ignore", "inherit", "inherit"],
|
|
68
|
-
env: process.env,
|
|
69
|
-
});
|
|
70
|
-
process.exit(result.status ?? 1);
|
|
71
|
-
}
|
package/plugin/scripts/qrec.cjs
CHANGED
|
@@ -1499,5 +1499,5 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
1499
1499
|
`}var zc,Uk=h(()=>{jn();zc=class{append(t){this._buffer=this._buffer?Buffer.concat([this._buffer,t]):t}readMessage(){if(!this._buffer)return null;let t=this._buffer.indexOf(`
|
|
1500
1500
|
`);if(t===-1)return null;let r=this._buffer.toString("utf8",0,t).replace(/\r$/,"");return this._buffer=this._buffer.subarray(t+1),pR(r)}clear(){this._buffer=void 0}}});var Ih,Ic,Ck=h(()=>{Ih=Yn(require("node:process"),1);Uk();Ic=class{constructor(t=Ih.default.stdin,r=Ih.default.stdout){this._stdin=t,this._stdout=r,this._readBuffer=new zc,this._started=!1,this._ondata=n=>{this._readBuffer.append(n),this.processReadBuffer()},this._onerror=n=>{this.onerror?.(n)}}async start(){if(this._started)throw new Error("StdioServerTransport already started! If using Server class, note that connect() calls start() automatically.");this._started=!0,this._stdin.on("data",this._ondata),this._stdin.on("error",this._onerror)}processReadBuffer(){for(;;)try{let t=this._readBuffer.readMessage();if(t===null)break;this.onmessage?.(t)}catch(t){this.onerror?.(t)}}async close(){this._stdin.off("data",this._ondata),this._stdin.off("error",this._onerror),this._stdin.listenerCount("data")===0&&this._stdin.pause(),this._readBuffer.clear(),this.onclose?.()}send(t){return new Promise(r=>{let n=Ak(t);this._stdout.write(n)?r():this._stdout.once("drain",r)})}}});var Bk={};Et(Bk,{runMcpServer:()=>fR});function mR(e=20){if(!(0,Tc.existsSync)(Mk))return[];try{return(0,Tc.readFileSync)(Mk,"utf-8").split(`
|
|
1501
1501
|
`).filter(n=>n.length>0).slice(-e)}catch{return[]}}async function qk(e,t){let r=e.prepare("SELECT COUNT(*) as count FROM sessions").get(),n=e.prepare("SELECT COUNT(*) as count FROM chunks").get(),o=e.prepare("SELECT MAX(indexed_at) as last FROM sessions").get(),i=Qr();return{health:"ok",session_count:r.count,chunk_count:n.count,last_indexed:o.last?new Date(o.last).toISOString():null,model_loaded:t,daemon_pid:i,log_tail:mR(20)}}async function Fk(e){if(e.endsWith(".jsonl")){let t=await Yr(e);return Wh(t)}return Bun.file(e).text()}async function fR(e=!1){let t=wr(),r=!1,n=await Xr();r=!0;let o=new Sc({name:"qrec",version:"0.1.0"},{capabilities:{tools:{}}});o.setRequestHandler(em,async()=>({tools:[{name:"search",description:"Search indexed sessions using hybrid BM25 + vector search",inputSchema:{type:"object",properties:{query:{type:"string",description:"Search query"},k:{type:"number",description:"Number of results (default: 10)"}},required:["query"]}},{name:"get",description:"Get full session markdown by session ID",inputSchema:{type:"object",properties:{session_id:{type:"string",description:"8-char hex session ID"}},required:["session_id"]}},{name:"status",description:"Get qrec engine status and health information",inputSchema:{type:"object",properties:{}}}]})),o.setRequestHandler(_i,async a=>{let{name:s,arguments:c}=a.params;if(s==="search"){let u=String(c?.query??"").trim();if(!u)return{content:[{type:"text",text:JSON.stringify({error:"Missing required field: query"})}],isError:!0};let l=typeof c?.k=="number"?c.k:10;try{let d=await no(t,n,u,l);return{content:[{type:"text",text:JSON.stringify({results:d})}]}}catch(d){return{content:[{type:"text",text:JSON.stringify({error:String(d)})}],isError:!0}}}if(s==="get"){let u=String(c?.session_id??"").trim();if(!u)return{content:[{type:"text",text:JSON.stringify({error:"Missing required field: session_id"})}],isError:!0};let l=t.prepare("SELECT path FROM sessions WHERE id = ?").get(u);if(!l)return{content:[{type:"text",text:JSON.stringify({error:`Session not found: ${u}`})}],isError:!0};try{return{content:[{type:"text",text:await Fk(l.path)}]}}catch(d){return{content:[{type:"text",text:JSON.stringify({error:`Failed to read session file: ${d}`})}],isError:!0}}}if(s==="status")try{let u=await qk(t,r);return{content:[{type:"text",text:JSON.stringify(u)}]}}catch(u){return{content:[{type:"text",text:JSON.stringify({error:String(u)})}],isError:!0}}return{content:[{type:"text",text:JSON.stringify({error:`Unknown tool: ${s}`})}],isError:!0}});async function i(){console.error("[mcp] Shutting down..."),t.close(),await to(),process.exit(0)}if(process.on("SIGTERM",i),process.on("SIGINT",i),e)console.error(`[mcp] Starting HTTP MCP server on port ${Lk}`),Bun.serve({port:Lk,async fetch(a){if(a.method!=="POST")return Response.json({error:"Method not allowed"},{status:405});let s;try{s=await a.json()}catch{return Response.json({jsonrpc:"2.0",error:{code:-32700,message:"Parse error"},id:null},{status:400})}if(s.method==="tools/list"){let c=[{name:"search",description:"Search indexed sessions using hybrid BM25 + vector search",inputSchema:{type:"object",properties:{query:{type:"string"},k:{type:"number"}},required:["query"]}},{name:"get",description:"Get full session markdown by session ID",inputSchema:{type:"object",properties:{session_id:{type:"string"}},required:["session_id"]}},{name:"status",description:"Get qrec engine status",inputSchema:{type:"object",properties:{}}}];return Response.json({jsonrpc:"2.0",result:{tools:c},id:s.id})}if(s.method==="tools/call"){let c=s.params,u=c?.name??"",l=c?.arguments??{},d;try{if(u==="search"){let p=String(l?.query??"").trim(),m=typeof l?.k=="number"?l.k:10,g=await no(t,n,p,m);d={content:[{type:"text",text:JSON.stringify({results:g})}]}}else if(u==="get"){let p=String(l?.session_id??"").trim(),m=t.prepare("SELECT path FROM sessions WHERE id = ?").get(p);m?d={content:[{type:"text",text:await Fk(m.path)}]}:d={content:[{type:"text",text:JSON.stringify({error:`Session not found: ${p}`})}],isError:!0}}else if(u==="status"){let p=await qk(t,r);d={content:[{type:"text",text:JSON.stringify(p)}]}}else d={content:[{type:"text",text:JSON.stringify({error:`Unknown tool: ${u}`})}],isError:!0}}catch(p){d={content:[{type:"text",text:JSON.stringify({error:String(p)})}],isError:!0}}return Response.json({jsonrpc:"2.0",result:d,id:s.id})}return Response.json({jsonrpc:"2.0",error:{code:-32601,message:"Method not found"},id:s.id},{status:404})}}),await new Promise(()=>{});else{let a=new Ic;await o.connect(a),console.error("[mcp] qrec MCP server running on stdio")}}var Vk,Jk,Tc,Mk,Lk,Gk=h(()=>{"use strict";Zk();Ck();jn();ea();na();ra();Vc();Fc();oa();Vk=require("path"),Jk=require("os"),Tc=require("fs"),Mk=(0,Vk.join)((0,Jk.homedir)(),".qrec","qrec.log"),Lk=3031});ea();Cc();ra();Fc();var Ph=require("path"),Ec=require("os"),Br=require("fs"),[,,Kk,..._t]=process.argv,Hk=(0,Ph.join)((0,Ec.homedir)(),".qrec","qrec.log"),Th=(0,Ph.join)((0,Ec.homedir)(),".qrec");function hR(e=20){if(!(0,Br.existsSync)(Hk))return[];try{return(0,Br.readFileSync)(Hk,"utf-8").split(`
|
|
1502
|
-
`).filter(n=>n.length>0).slice(-e)}catch{return[]}}function Eh(){let e=process.platform==="darwin"?"open":"xdg-open";try{Bun.spawnSync([e,"http://localhost:3030"])}catch{}}async function gR(){switch(Kk){case"--version":case"-v":console.log("qrec 0.2.
|
|
1502
|
+
`).filter(n=>n.length>0).slice(-e)}catch{return[]}}function Eh(){let e=process.platform==="darwin"?"open":"xdg-open";try{Bun.spawnSync([e,"http://localhost:3030"])}catch{}}async function gR(){switch(Kk){case"--version":case"-v":console.log("qrec 0.2.3"),process.exit(0);case"onboard":{let e=_t.includes("--no-open");await Lc(),e||Eh();let t=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],r=22,n=s=>{let c=Math.min(r,Math.round(s*r/100));return"\u2588".repeat(c)+"\u2591".repeat(r-c)},o=0,i=0,a=s=>{let{phase:c,modelDownload:u,indexing:l}=s,d=t[i%t.length],p=["indexing","ready"].includes(c),m=c==="ready",g=c==="indexing",y=46,_=[""," qrec \u2014 setting up",` ${"\u2500".repeat(y)}`];if(c==="model_download"){let x=u.percent;_.push(` ${d} [1/3] Downloading model`),_.push(` ${n(x)} ${x}% (${u.downloadedMB.toFixed(0)} / ${u.totalMB?.toFixed(0)??"?"} MB)`)}else c==="model_loading"?(_.push(` ${d} [1/3] Loading model into memory\u2026`),_.push("")):(_.push(" \u2713 [1/3] Model ready"),_.push(""));if(!p)_.push(" \xB7 [2/3] Index sessions"),_.push("");else if(g){let x=l.total>0?Math.round(l.indexed*100/l.total):0;_.push(` ${d} [2/3] Indexing sessions`),_.push(` ${n(x)} ${x}% (${l.indexed}/${l.total}) ${l.current}`)}else _.push(` \u2713 [2/3] Sessions indexed (${s.sessions} sessions)`),_.push("");m?_.push(" \u2713 [3/3] Ready \u2192 http://localhost:3030"):_.push(" \xB7 [3/3] Ready"),_.push(` ${"\u2500".repeat(y)}`),_.push(""),o>0&&process.stdout.write(`\x1B[${o}A`);for(let x of _)process.stdout.write(`\x1B[2K${x}
|
|
1503
1503
|
`);o=_.length};for(;;){try{let s=await fetch("http://localhost:3030/status");if(s.ok){let c=await s.json();if(a(c),c.phase==="ready")break}}catch{}i++,await Bun.sleep(500)}process.exit(0)}case"teardown":{let e=_t.includes("--yes");await qc(),(0,Br.existsSync)(Th)||(console.log("[teardown] ~/.qrec/ not found, nothing to remove."),process.exit(0)),e||(process.stdout.write(`[teardown] Remove ${Th} (DB, model, logs, pid, activity log)? [y/N] `),(await new Promise(r=>{process.stdin.setEncoding("utf-8"),process.stdin.once("data",n=>r(String(n).trim()))})).toLowerCase()!=="y"&&(console.log("[teardown] Aborted."),process.exit(0))),(0,Br.rmSync)(Th,{recursive:!0,force:!0}),console.log("[teardown] Removed ~/.qrec/"),process.exit(0)}case"index":{let e,t=!1,r,n;if(!_t[0]&&!process.stdin.isTTY){let i=await Bun.stdin.text();try{let a=JSON.parse(i.trim());if(!a.transcript_path)throw new Error("Missing transcript_path");e=a.transcript_path}catch(a){console.error(`[cli] index: failed to parse stdin: ${a}`),process.exit(1)}}else{let i=_t[0]??`${(0,Ec.homedir)()}/.claude/projects/`;t=_t.includes("--force");let a=_t.indexOf("--sessions");r=a!==-1?parseInt(_t[a+1],10):void 0;let s=_t.indexOf("--seed");n=s!==-1?parseInt(_t[s+1],10):void 0,e=i.replace("~",process.env.HOME??"")}console.log(`[cli] Indexing: ${e}${r?` (${r} sessions, seed=${n??42})`:""}`);let o=wr();try{await ia(o,e,{force:t,sessions:r,seed:n})}finally{o.close(),await to()}process.exit(0)}case"serve":{let e=_t.includes("--daemon"),t=_t.includes("--no-open");e?(await Lc(),t||Eh()):(t||setTimeout(Eh,1e3),await Promise.resolve().then(()=>(hg(),Nw)));break}case"stop":{await qc();break}case"mcp":{let e=_t.includes("--http"),{runMcpServer:t}=await Promise.resolve().then(()=>(Gk(),Bk));await t(e);break}case"status":{let e=wr();try{let t=e.prepare("SELECT COUNT(*) as count FROM sessions").get(),r=e.prepare("SELECT COUNT(*) as count FROM chunks").get(),n=e.prepare("SELECT MAX(indexed_at) as last FROM sessions").get(),o=Qr(),i=o!==null,a="not checked";if(i)try{let u=await fetch("http://localhost:3030/health");u.ok?a=(await u.json()).status??"unknown":a=`http error ${u.status}`}catch{a="unreachable"}let s=n.last?new Date(n.last).toISOString():"never";console.log("=== qrec status ==="),console.log(`Daemon PID: ${o??"not running"}`),console.log(`HTTP health: ${a}`),console.log(`Sessions: ${t.count}`),console.log(`Chunks: ${r.count}`),console.log(`Last indexed: ${s}`),console.log(""),console.log("--- Log tail (last 20 lines) ---");let c=hR(20);if(c.length===0)console.log("(no log entries)");else for(let u of c)console.log(u)}finally{e.close()}process.exit(0)}default:console.error(`Unknown command: ${Kk}`),console.error("Usage:"),console.error(" qrec onboard [--no-open] # first-time setup"),console.error(" qrec teardown [--yes] # remove all qrec data"),console.error(" qrec index [path] [--force] # default: ~/.claude/projects/"),console.error(" qrec index # stdin JSON {transcript_path} (hook mode)"),console.error(" qrec serve [--daemon] [--no-open]"),console.error(" qrec stop"),console.error(" qrec mcp [--http]"),console.error(" qrec status"),process.exit(1)}}gR().catch(e=>{console.error("Fatal error:",e),process.exit(1)});
|