From 0edaa67808ad826124d211e57c54295839fdaafa Mon Sep 17 00:00:00 2001 From: Andrei Hasna Date: Thu, 30 Jul 2026 16:03:29 +0300 Subject: [PATCH 1/2] chore(release): bump to 0.2.92 so the landed multi-tag data-loss fix can ship MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `-t/--tag` silent-data-loss fix landed on main in 78db6e1 (PR #34) on 2026-07-27, but npm 0.2.91 was published on 2026-07-24 and package.json was never bumped. main and the registry therefore both say 0.2.91 while behaving differently: main stores all five tags, the published artifact stores only the last one. Because the version number is identical, nothing signals the difference, and `npm publish` cannot ship the fix at all — the version already exists. Every fleet install is the pre-fix artifact, so 31 other commits merged since the 0.2.91 publish are also unshipped. Bundles are regenerated because they embed the version string; the bundle diff is provably the version string alone (both files are byte-identical to their predecessors once 0.2.92 is reverted to 0.2.91). Refs: 2db5e98d, 78db6e1, #34 Agent: vespasian --- bin/knowledge-mcp.js | 2 +- bin/knowledge.js | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/knowledge-mcp.js b/bin/knowledge-mcp.js index 11bf4f2..93f5a8d 100755 --- a/bin/knowledge-mcp.js +++ b/bin/knowledge-mcp.js @@ -14997,7 +14997,7 @@ import { existsSync as existsSync15, readFileSync as readFileSync13, writeFileSy // package.json var package_default = { name: "@hasna/knowledge", - version: "0.2.91", + version: "0.2.92", description: "Agent-friendly local knowledge CLI with JSON output, pagination, and safe destructive actions", type: "module", exports: { diff --git a/bin/knowledge.js b/bin/knowledge.js index 1b4b03e..858c32d 100755 --- a/bin/knowledge.js +++ b/bin/knowledge.js @@ -1207,7 +1207,7 @@ Pages should be concise, cited, and organized for both humans and agents. ) `)}var DF=pF(jF(),1),{program:qs,createCommand:Os,createArgument:Ls,createOption:Ns,CommanderError:Bs,InvalidArgumentError:Hs,InvalidOptionArgumentError:Vs,Command:zF,Argument:Rs,Option:As,Help:Fs}=DF.default;import{chmod as IO,mkdir as yd,readFile as hd,rename as xd,writeFile as NF}from"fs/promises";import{Buffer as EF}from"buffer";import{existsSync as MF}from"fs";import{homedir as md}from"os";import{join as xW}from"path";import{createHmac as id,timingSafeEqual as ks}from"crypto";import{randomUUID as od}from"crypto";import{spawn as td}from"child_process";import{randomUUID as Uc}from"crypto";function kd($,_){return _.split(".").reduce((U,W)=>{if(U&&typeof U==="object"&&W in U)return U[W];return},$)}function Pd($,_){let U=[],W=(X)=>{if(!U.some((G)=>Object.is(G,X)))U.push(X)};if(_.includes(".")&&_ in $)W($[_]);let J=kd($,_);if(J!==void 0||!_.includes("."))W(J);return U}function Cd($,_={}){let U="";for(let W=0;W<$.length;W+=1){let J=$[W];if(J==="*")if($[W+1]==="*")U+=".*",W+=1;else U+=_.segmentSafe?"[^/]*":".*";else U+=J.replace(/[|\\{}()[\]^$+?.]/g,"\\$&")}return new RegExp(`^${U}$`)}function yW($,_,U={}){if(_===void 0)return!0;if($===void 0)return!1;return(Array.isArray(_)?_:[_]).some((J)=>Cd(J,U).test($))}function qF($,_){if(!_)return!0;return Object.entries(_).every(([U,W])=>{let J=Pd($,U);return Td(J,W,U)})}function Td($,_,U){if(Zd(_))return!$.some((W)=>OF(W,_.not,U));return $.some((W)=>OF(W,_,U))}function OF($,_,U){if(typeof _==="string"||Array.isArray(_))return fd($).some((W)=>yW(W,_,{segmentSafe:U.endsWith("_path")||U.endsWith(".path")}));if(Array.isArray($))return $.some((W)=>W===_);return $===_}function fd($){if($===void 0)return[];if(Array.isArray($))return $.flatMap((_)=>Sd(_)?[String(_)]:[]);return[String($)]}function Sd($){return $===null||typeof $==="string"||typeof $==="number"||typeof $==="boolean"}function Zd($){return Boolean($&&typeof $==="object"&&!Array.isArray($)&&"not"in $)}function vd($,_){return yW($.source,_.source)&&yW($.type,_.type)&&yW($.subject,_.subject)&&yW($.severity,_.severity)&&qF($.data,_.data)&&qF($.metadata,_.metadata)}function LF($,_){if(!$.enabled)return!1;if(!$.filters||$.filters.length===0)return!0;return $.filters.some((U)=>vd(_,U))}var C5="HASNA_EVENTS_DIR",T5="HASNA_EVENTS_HOME",PO="local-json-v1:",ud=100,dd=1000;function bF($){return $||process.env[C5]||process.env[T5]||xW(md(),".hasna","events")}function cd(){if(process.env[C5])return C5;if(process.env[T5])return T5;return null}class f5{dataDir;runtime;channelsPath;eventsPath;deliveriesPath;constructor($=bF()){this.dataDir=$,this.runtime=nd($),this.channelsPath=xW($,"channels.json"),this.eventsPath=xW($,"events.json"),this.deliveriesPath=xW($,"deliveries.json")}async init(){await yd(this.dataDir,{recursive:!0,mode:448}),await IO(this.dataDir,448).catch(()=>{return}),await this.ensureArrayFile(this.channelsPath),await this.ensureArrayFile(this.eventsPath),await this.ensureArrayFile(this.deliveriesPath)}async addChannel($){await this.init();let _=await this.readJson(this.channelsPath,[]),U=_.findIndex((W)=>W.id===$.id);if(U>=0)_[U]={...$,createdAt:_[U].createdAt,updatedAt:new Date().toISOString()};else _.push($);return await this.writeJson(this.channelsPath,_),U>=0?_[U]:$}async listChannels(){return await this.init(),this.readJson(this.channelsPath,[])}async getChannel($){return(await this.listChannels()).find((U)=>U.id===$)}async removeChannel($){await this.init();let _=await this.readJson(this.channelsPath,[]),U=_.filter((W)=>W.id!==$);return await this.writeJson(this.channelsPath,U),U.length!==_.length}async appendEvent($){await this.init();let _=await this.readJson(this.eventsPath,[]);return _.push($),await this.writeJson(this.eventsPath,_),$}async appendEventOnce($,_={}){await this.init();let U=await this.readJson(this.eventsPath,[]);if(_.dedupe!==!1){let J=HF(U,{id:$.id,dedupeKey:$.dedupeKey});if(J)return{event:J,stored:!1,deduped:!0,identity:{id:J.id,dedupeKey:J.dedupeKey}}}return U.push($),await this.writeJson(this.eventsPath,U),{event:$,stored:!0,deduped:!1,identity:{id:$.id,dedupeKey:$.dedupeKey}}}async listEvents($={}){await this.init();let _=await this.readJson(this.eventsPath,[]);return BF(_,$)}async listEventsPage($={}){await this.init();let _=await this.readJson(this.eventsPath,[]),U=BF(_,{eventId:$.eventId,source:$.source,type:$.type}),W=S5($.cursor,$),J=Z5($.limit),X=U.slice(W,W+J),G=W+X.length,Q=G{return})}async readJson($,_){try{let U=await hd($,"utf-8");if(!U.trim())return _;return JSON.parse(U)}catch(U){if(U.code==="ENOENT")return _;throw U}}async writeJson($,_){let U=`${$}.${process.pid}.${Date.now()}.tmp`;await NF(U,`${JSON.stringify(_,null,2)} -`,{encoding:"utf-8",mode:384}),await xd(U,$),await IO($,384).catch(()=>{return})}}function nd($=bF()){return{mode:"local-files",name:"json-events-store",remote:!1,localFiles:!0,localSqlite:!1,postgres:!1,s3:!1,aws:!1,durable:!0,idempotency:"best-effort-local",replayCursors:!0,description:`Local JSON files in ${$}; no SQLite, Postgres, S3, or AWS runtime is configured by this store.`}}function IF($,_={}){if(!Number.isInteger($)||$<0)throw Error(`Invalid event cursor offset: ${$}`);let U={offset:$,eventId:_.eventId,source:_.source,type:_.type};return`${PO}${EF.from(JSON.stringify(U),"utf-8").toString("base64url")}`}function S5($,_={}){if(!$)return 0;if(!$.startsWith(PO))throw Error(`Invalid local JSON event cursor: ${$}`);let U=$.slice(PO.length),W;try{W=JSON.parse(EF.from(U,"base64url").toString("utf-8"))}catch{throw Error(`Invalid local JSON event cursor: ${$}`)}let J=W.offset;if(!Number.isInteger(J)||J<0)throw Error(`Invalid local JSON event cursor: ${$}`);return gO("eventId",W.eventId,_.eventId),gO("source",W.source,_.source),gO("type",W.type,_.type),J}function Z5($){if($===void 0)return ud;if(!Number.isInteger($)||$<1)throw Error(`Event page limit must be a positive integer, got ${$}`);return Math.min($,dd)}function BF($,_){let U=$;if(_.eventId)U=U.filter((W)=>W.id===_.eventId);if(_.source)U=U.filter((W)=>W.source===_.source);if(_.type)U=U.filter((W)=>W.type===_.type);if(_.cursor){let W=S5(_.cursor,_);U=U.slice(W)}if(_.limit!==void 0)U=U.slice(0,Z5(_.limit));return U}function gO($,_,U){if(_!==U)throw Error(`Local JSON event cursor ${$} filter mismatch`)}function HF($,_){return $.find((U)=>_.id!==void 0&&U.id===_.id||_.dedupeKey!==void 0&&U.dedupeKey===_.dedupeKey)}async function ld($){let _=new f5($);await _.init();let[U,W,J]=await Promise.all([_.listChannels(),_.listEvents(),_.listDeliveries()]),X=U.reduce((G,Q)=>{return G[Q.transport]=(G[Q.transport]??0)+1,G},{});return{service:"events",schemaVersion:"1.0",dataDir:_.dataDir,storage:_.runtime,env:{primary:C5,fallback:T5,active:cd()},files:{channels:wO(_.dataDir,"channels.json",U.length),events:wO(_.dataDir,"events.json",W.length),deliveries:wO(_.dataDir,"deliveries.json",J.length)},counts:{channels:U.length,enabledChannels:U.filter((G)=>G.enabled).length,disabledChannels:U.filter((G)=>!G.enabled).length,events:W.length,deliveries:J.length},transports:X,safety:{includesEventPayloads:!1,includesWebhookSecrets:!1,listOutputsRedactSecrets:!0,statusOutputIsMetadataOnly:!0}}}function wO($,_,U){let W=xW($,_);return{path:W,exists:MF(W),records:U}}function rd($,_){return`${$}.${_}`}function pd($,_,U){return`sha256=${id("sha256",$).update(rd(_,U)).digest("hex")}`}function c6(){return new Date().toISOString()}function hW($,_=4096){return $.length>_?`${$.slice(0,_)}...`:$}function ad($,_){if(!_.webhook)throw Error(`Channel ${_.id} has no webhook config`);let U=JSON.stringify($),W=$.time,J={"Content-Type":"application/json","User-Agent":"@hasna/events","X-Hasna-Event-Id":$.id,"X-Hasna-Event-Type":$.type,"X-Hasna-Timestamp":W,..._.webhook.headers};if(_.webhook.secret)J["X-Hasna-Signature"]=pd(_.webhook.secret,W,U);return{body:U,headers:J}}async function sd($,_,U={}){if(!_.webhook)throw Error(`Channel ${_.id} has no webhook config`);let W=c6(),{body:J,headers:X}=ad($,_),G=new AbortController,Q=setTimeout(()=>G.abort(),_.webhook.timeoutMs??15000);try{let Y=await(U.fetchImpl??fetch)(_.webhook.url,{method:"POST",headers:X,body:J,signal:G.signal}),j=hW(await Y.text());return{attempt:1,status:Y.ok?"success":"failed",startedAt:W,completedAt:c6(),responseStatus:Y.status,responseBody:j,error:Y.ok?void 0:`Webhook returned HTTP ${Y.status}`}}catch(Y){return{attempt:1,status:"failed",startedAt:W,completedAt:c6(),error:Y instanceof Error?Y.message:String(Y)}}finally{clearTimeout(Q)}}async function ed($,_){if(!_.command)throw Error(`Channel ${_.id} has no command config`);let U=c6(),W=JSON.stringify($),J={...process.env,..._.command.env,HASNA_CHANNEL_ID:_.id,HASNA_EVENT_ID:$.id,HASNA_EVENT_TYPE:$.type,HASNA_EVENT_SOURCE:$.source,HASNA_EVENT_SUBJECT:$.subject??"",HASNA_EVENT_SEVERITY:$.severity,HASNA_EVENT_TIME:$.time,HASNA_EVENT_DEDUPE_KEY:$.dedupeKey??"",HASNA_EVENT_SCHEMA_VERSION:$.schemaVersion,HASNA_EVENT_JSON:W};return new Promise((X)=>{let G=td(_.command.command,_.command.args??[],{cwd:_.command.cwd,env:J,stdio:["pipe","pipe","pipe"]}),Q="",Y="",j=setTimeout(()=>G.kill("SIGTERM"),_.command.timeoutMs??15000);G.stdin.end(W),G.stdout.on("data",(O)=>{Q+=O.toString()}),G.stderr.on("data",(O)=>{Y+=O.toString()}),G.on("error",(O)=>{clearTimeout(j),X({attempt:1,status:"failed",startedAt:U,completedAt:c6(),stdout:hW(Q),stderr:hW(Y),error:O.message})}),G.on("close",(O,B)=>{clearTimeout(j);let V=O===0;X({attempt:1,status:V?"success":"failed",startedAt:U,completedAt:c6(),stdout:hW(Q),stderr:hW(Y),error:V?void 0:`Command exited with ${B?`signal ${B}`:`code ${O}`}`})})})}async function $c($,_,U={}){if(_.transport==="webhook")return sd($,_,U);if(_.transport==="command")return ed($,_);return{attempt:1,status:"skipped",startedAt:c6(),completedAt:c6(),error:`Unsupported transport: ${_.transport}`}}function VF($,_,U){let W=U.some((J)=>J.status==="success")?"success":U.every((J)=>J.status==="skipped")?"skipped":"failed";return{id:od(),eventId:$.id,channelId:_.id,transport:_.transport,status:W,attempts:U,createdAt:U[0]?.startedAt??c6(),completedAt:U.at(-1)?.completedAt??c6()}}class gF extends Error{eventType;issues;constructor($,_){let U=_.map((W)=>`${W.path||""}: ${W.message}`).join("; ");super(`Event validation failed for type "${$}": ${U}`);this.name="EventValidationError",this.eventType=$,this.issues=_}}class wF{definitions=new Map;register($){return this.definitions.set($.type,$),this}unregister($){return this.definitions.delete($)}has($){return this.definitions.has($)}get($){return this.definitions.get($)}list(){return[...this.definitions.values()]}validateEvent($){let _=this.definitions.get($.type);if(!_)return{ok:!0};return _.validate($.data,$)}assertEventValid($){let _=this.validateEvent($);if(!_.ok)throw new gF($.type,_.issues)}}var _c=new wF;function kO($){return{id:$.id??Uc(),source:$.source,type:$.type,time:Qc($.time),subject:$.subject,severity:$.severity??"info",data:$.data??{},message:$.message,dedupeKey:$.dedupeKey,schemaVersion:$.schemaVersion??"1.0",metadata:$.metadata??{}}}class kF{store;redactors;transportOptions;catalog;validateCatalogTypes;constructor($={}){this.store=$.store??new f5($.dataDir),this.redactors=$.redactors??[],this.transportOptions={fetchImpl:$.fetchImpl},this.catalog=$.catalog??_c,this.validateCatalogTypes=$.validateCatalogTypes??!1}async addChannel($){let _=new Date().toISOString();return this.store.addChannel({...$,createdAt:$.createdAt??_,updatedAt:$.updatedAt??_})}async listChannels(){return this.store.listChannels()}async removeChannel($){return this.store.removeChannel($)}async emit($,_={}){let U=_.redactSensitiveData===!1?kO($):Xc(kO($));if(_.validate??this.validateCatalogTypes)this.catalog.assertEventValid(U);let W=await this.appendEvent(U,{dedupe:_.dedupe!==!1});if(W.deduped)return{event:W.event,deliveries:[],deduped:!0};let J=_.deliver===!1?[]:await this.deliver(W.event);return{event:W.event,deliveries:J,deduped:!1}}async listEvents($={}){if(Object.keys($).length===0)return this.store.listEvents();return RF(await this.store.listEvents(),$)}async listEventsPage($={}){if(this.store.listEventsPage)return this.store.listEventsPage($);let _=RF(await this.store.listEvents(),{eventId:$.eventId,source:$.source,type:$.type}),U=S5($.cursor,$),W=Z5($.limit),J=_.slice(U,U+W),X=U+J.length,G=X<_.length;return{events:J,cursor:$.cursor,nextCursor:G?IF(X,$):void 0,hasMore:G}}async listDeliveries(){return this.store.listDeliveries()}async deliver($){let U=(await this.store.listChannels()).filter((J)=>LF(J,$)),W=[];for(let J of U){let X=await this.applyRedaction($,J),G=await this.deliverWithRetry(X,J);await this.store.appendDelivery(G),W.push(G)}return W}async matchChannel($,_={}){let U=await this.store.getChannel($);if(!U)throw Error(`Channel not found: ${$}`);let W=kO({source:_.source??"hasna.events",type:_.type??"events.test",subject:_.subject??$,severity:_.severity??"info",data:_.data??{test:!0},message:_.message??"Hasna events test delivery",dedupeKey:_.dedupeKey,schemaVersion:_.schemaVersion,metadata:_.metadata,time:_.time,id:_.id}),J=LF(U,W);return{channelId:U.id,matched:J,event:W,filters:U.filters,reason:J?void 0:U.enabled?"event did not match channel filters":"channel is disabled"}}async testChannel($,_={},U={}){let W=await this.store.getChannel($);if(!W)throw Error(`Channel not found: ${$}`);let J=await this.matchChannel($,_),X=J.event;if(U.honorFilters&&!J.matched){let Y=new Date().toISOString(),j=VF(X,W,[{attempt:1,status:"skipped",startedAt:Y,completedAt:Y,error:J.reason}]);return j.metadata={reason:"filter_mismatch"},await this.store.appendDelivery(j),j}let G=await this.applyRedaction(X,W),Q=await this.deliverWithRetry(G,W);return await this.store.appendDelivery(Q),Q}async replay($={}){let _=$.cursor||$.limit!==void 0?await this.listEventsPage($):{events:await this.listEvents($),hasMore:!1};if($.dryRun)return{events:_.events,deliveries:[],cursor:_.cursor,nextCursor:_.nextCursor,hasMore:_.hasMore};let U=[];for(let W of _.events)U.push(...await this.deliver(W));return{events:_.events,deliveries:U,cursor:_.cursor,nextCursor:_.nextCursor,hasMore:_.hasMore}}async appendEvent($,_){if(this.store.appendEventOnce)return this.store.appendEventOnce($,{dedupe:_.dedupe});if(_.dedupe){let W=await this.store.findEventByIdentity({id:$.id,dedupeKey:$.dedupeKey});if(W)return{event:W,stored:!1,deduped:!0,identity:{id:W.id,dedupeKey:W.dedupeKey}}}let U=await this.store.appendEvent($);return{event:U,stored:!0,deduped:!1,identity:{id:U.id,dedupeKey:U.dedupeKey}}}async applyRedaction($,_){let U=Jc($,_.redact?.paths??[],_.redact?.replacement??"[REDACTED]");for(let W of this.redactors)U=await W(U,_);return U}async deliverWithRetry($,_){let U=Yc(_.retry),W=[];for(let J=0;J[U,CF(U)?"[REDACTED]":W]));return _}function Wc($){return $.map(PF)}function Xc($,_="[REDACTED]"){return CO($,_)}function CF($){return/secret|token|password|api[_-]?key|authorization/i.test($)}function CO($,_){if(Array.isArray($))return $.map((U)=>CO(U,_));if(!$||typeof $!=="object")return $;return Object.fromEntries(Object.entries($).map(([U,W])=>[U,CF(U)?_:CO(W,_)]))}function Gc($,_,U){let W=_.split("."),J=$;for(let G of W.slice(0,-1)){let Q=J[G];if(!Q||typeof Q!=="object")return;J=Q}let X=W.at(-1);if(X&&X in J)J[X]=U}function RF($,_){let U=$;if(_.eventId)U=U.filter((W)=>W.id===_.eventId);if(_.source)U=U.filter((W)=>W.source===_.source);if(_.type)U=U.filter((W)=>W.type===_.type);if(_.cursor)U=U.slice(S5(_.cursor,_));if(_.limit!==void 0)U=U.slice(0,Z5(_.limit));return U}function Qc($){if(!$)return new Date().toISOString();return $ instanceof Date?$.toISOString():$}function Yc($){return{maxAttempts:Math.max(1,$?.maxAttempts??1),backoffMs:Math.max(0,$?.backoffMs??250),multiplier:Math.max(1,$?.multiplier??2)}}function P5($,_,U=!1){if(!$?.length)return;let W={};for(let J of $){let X=zc(J,_),G=X.path;if(G in W)throw Error(`Duplicate ${_} filter path: ${G}`);let Q=U?Dc(X.rawValue,_):X.rawValue;W[G]=X.negated?{not:Q}:Q}return W}function jc($){let _={};if($.source)_.source=$.source;if($.type)_.type=$.type;if($.subject)_.subject=$.subject;if($.severity)_.severity=$.severity;let U=AF(P5($.data,"data"),P5($.dataJson,"data-json",!0)),W=AF(P5($.metadata,"metadata"),P5($.metadataJson,"metadata-json",!0));if(Object.keys(U).length>0)_.data=U;if(Object.keys(W).length>0)_.metadata=W;return Object.keys(_).length>0?[_]:void 0}function AF(...$){let _={};for(let U of $){if(!U)continue;for(let[W,J]of Object.entries(U)){if(W in _)throw Error(`Duplicate filter path: ${W}`);_[W]=J}}return _}function Dc($,_){let U=JSON.parse($);if(U===null||typeof U==="string"||typeof U==="number"||typeof U==="boolean"||Array.isArray(U)&&U.every((W)=>typeof W==="string"))return U;throw Error(`${_} filter JSON values must be string, string[], number, boolean, or null`)}function zc($,_){let U=$.indexOf("!=");if(U>0)return{path:$.slice(0,U),rawValue:$.slice(U+2),negated:!0};let W=$.indexOf("=");if(W<=0)throw Error(`Invalid ${_} filter, expected path=value or path!=value: ${$}`);return{path:$.slice(0,W),rawValue:$.slice(W+1),negated:!1}}var qc=100;function MU($,_){if(!$)return _;let U=JSON.parse($);if(!U||typeof U!=="object"||Array.isArray(U))throw Error("Expected a JSON object");return U}function Oc($){if(!$?.length)return;let _={};for(let U of $){let W=U.indexOf("=");if(W===-1)throw Error(`Invalid header, expected name=value: ${U}`);_[U.slice(0,W)]=U.slice(W+1)}return _}function Q0($){if($.createClient)return $.createClient();return new kF({store:new f5($.dataDir)})}function b1($,_,U){if(_)console.log(JSON.stringify($,null,2));else console.log(U)}function FF($,_){let U=$ instanceof Error?$.message:String($);if(_)console.log(JSON.stringify({error:U},null,2));else console.error(U);process.exitCode=1}function KF($){return Boolean($?.json||$?.opts?.().json||$?.optsWithGlobals?.().json||$?.parent?.opts?.().json||$?.parent?.optsWithGlobals?.().json)}function K4($,_){return KF($)||KF(_)}function Lc($,_){let U=$.command(_.channelsCommandName??"channels").description("Manage Hasna event channels");return U.command("add").description("Add or replace a channel").argument("","Webhook URL or command binary").requiredOption("--id ","Channel identifier").option("--transport ","Transport kind: webhook or command","webhook").option("--name ","Display name").option("--type ","Event type filter, e.g. todos.task.*").option("--source ","Event source filter").option("--subject ","Event subject filter").option("--severity ","Event severity filter").option("--data ","Event data field filter; string values, path!=value negatives, array-member matching, dot paths, * segment wildcard, ** recursive wildcard",M1,[]).option("--metadata ","Event metadata field filter; string values, path!=value negatives, array-member matching, dot paths, * segment wildcard, ** recursive wildcard",M1,[]).option("--data-json ","Event data field filter with typed JSON value; path!=json negatives supported",M1,[]).option("--metadata-json ","Event metadata field filter with typed JSON value; path!=json negatives supported",M1,[]).option("--secret ","Webhook HMAC secret").option("--header ","Webhook header",M1,[]).option("--arg ","Command argument",M1,[]).option("--timeout-ms ","Transport timeout in milliseconds",mW).option("--retry-attempts ","Maximum delivery attempts",mW).option("--retry-backoff-ms ","Initial retry backoff in milliseconds",mW).option("--redact ","Event field path to redact before delivery",M1,[]).option("--disabled","Create channel disabled",!1).option("-j, --json","Print JSON output",!1).action(async(W,J,X)=>{let G=new Date().toISOString(),Q={id:J.id,name:J.name,enabled:!J.disabled,transport:J.transport,filters:jc(J),retry:J.retryAttempts||J.retryBackoffMs?{maxAttempts:J.retryAttempts,backoffMs:J.retryBackoffMs}:void 0,redact:J.redact?.length?{paths:J.redact}:void 0,createdAt:G,updatedAt:G};if(J.transport==="webhook")Q.webhook={url:W,secret:J.secret,headers:Oc(J.header),timeoutMs:J.timeoutMs};else if(J.transport==="command")Q.command={command:W,args:J.arg??[],timeoutMs:J.timeoutMs};else throw Error(`Transport ${J.transport} is reserved for future use and cannot be added yet`);let Y=await Q0(_).addChannel(Q);b1(PF(Y),K4(J,X),`Added ${Y.transport} channel ${Y.id}`)}),U.command("list").description("List configured channels").option("-j, --json","Print JSON output",!1).action(async(W,J)=>{let X=await Q0(_).listChannels();if(K4(W,J)){console.log(JSON.stringify(Wc(X),null,2));return}if(!X.length){console.log("No channels configured.");return}for(let G of X)console.log(`${G.id} ${G.enabled?"enabled":"disabled"} ${G.transport} ${G.webhook?.url??G.command?.command??G.transport}`)}),U.command("status").description("Show events channel storage status").option("-j, --json","Print JSON output",!1).action(async(W,J)=>{let X=await ld(_.dataDir);b1(X,K4(W,J),`events dataDir: ${X.dataDir}`)}),U.command("remove").description("Remove a channel").argument("","Channel identifier").option("-j, --json","Print JSON output",!1).action(async(W,J,X)=>{let G=await Q0(_).removeChannel(W);b1({removed:G},K4(J,X),G?`Removed ${W}`:`Channel not found: ${W}`)}),U.command("test").description("Send a test event to one channel").argument("","Channel identifier").option("--source ","Event source override").option("--type ","Event type","events.test").option("--subject ","Event subject").option("--message ","Event message","Hasna events test delivery").option("--data ","Event data JSON object").option("--metadata ","Event metadata JSON object").option("--honor-filters","Skip delivery when the sample event does not match channel filters",!1).option("-j, --json","Print JSON output",!1).action(async(W,J,X)=>{let G=K4(J,X);try{let Q=await Q0(_).testChannel(W,{source:J.source??_.source,type:J.type,subject:J.subject??W,message:J.message,data:MU(J.data,{test:!0}),metadata:MU(J.metadata,{})},{honorFilters:J.honorFilters});b1(Q,G,`${Q.status}: ${Q.channelId}`)}catch(Q){FF(Q,G)}}),U.command("match").description("Check whether a sample event matches one channel without delivering").argument("","Channel identifier").option("--source ","Event source override").option("--type ","Event type","events.test").option("--subject ","Event subject").option("--message ","Event message","Hasna events match preview").option("--data ","Event data JSON object").option("--metadata ","Event metadata JSON object").option("-j, --json","Print JSON output",!1).action(async(W,J,X)=>{let G=K4(J,X);try{let Q=await Q0(_).matchChannel(W,{source:J.source??_.source,type:J.type,subject:J.subject??W,message:J.message,data:MU(J.data,{test:!0}),metadata:MU(J.metadata,{})});b1(Q,G,`${Q.matched?"matched":"skipped"}: ${Q.channelId}`)}catch(Q){FF(Q,G)}}),U}function Nc($,_){let U=$.command(_.eventsCommandName??"events").description("Emit, list, and replay Hasna events");U.command("emit").description("Emit an event from this app").argument("","Event type").option("--source ","Event source override").option("--subject ","Event subject").option("--severity ","Event severity","info").option("--message ","Event message").option("--dedupe-key ","Dedupe key").option("--data ","Event data JSON object").option("--metadata ","Event metadata JSON object").option("--no-deliver","Record without delivering").option("--no-dedupe","Allow duplicate id/dedupeKey events").option("-j, --json","Print JSON output",!1).action(async(J,X,G)=>{let Q=await Q0(_).emit({source:X.source??_.source,type:J,subject:X.subject,severity:X.severity,message:X.message,dedupeKey:X.dedupeKey,data:MU(X.data,{}),metadata:MU(X.metadata,{})},{deliver:X.deliver,dedupe:X.dedupe});b1(Q,K4(X,G),`${Q.deduped?"Deduped":"Emitted"} ${Q.event.id} to ${Q.deliveries.length} channel(s)`)});let W=_.defaultEventListLimit??qc;return U.command("list").description("List recorded events").option("--source ","Filter by source").option("--type ","Filter by type").option("--limit ",`Limit to the most recent events (default ${W}; use 0 for all)`,mW,W).option("-j, --json","Print JSON output",!1).action(async(J,X)=>{let G=await Q0(_).listEvents();if(J.source)G=G.filter((Q)=>Q.source===J.source);if(J.type)G=G.filter((Q)=>Q.type===J.type);if(J.limit)G=G.slice(-J.limit);if(K4(J,X)){console.log(JSON.stringify(G,null,2));return}if(!G.length){console.log("No events recorded.");return}for(let Q of G)console.log(`${Q.time} ${Q.id} ${Q.source} ${Q.type} ${Q.severity}`)}),U.command("replay").description("Replay recorded events").option("--id ","Replay one event id").option("--source ","Filter by source").option("--type ","Filter by type").option("--cursor ","Opaque replay cursor from a previous page").option("--limit ","Maximum events to replay",mW).option("--dry-run","Preview without delivery",!1).option("-j, --json","Print JSON output",!1).action(async(J,X)=>{let G=await Q0(_).replay({eventId:J.id,source:J.source,type:J.type,cursor:J.cursor,limit:J.limit,dryRun:J.dryRun});b1(G,K4(J,X),Bc(G.events.length,G.deliveries.length,G.nextCursor))}),U}function TF($,_){Lc($,_),Nc($,_)}function mW($){let _=Number($);if(!Number.isFinite(_))throw Error(`Expected a number, got ${$}`);return _}function M1($,_){return _.push($),_}function Bc($,_,U){let W=U?`, next cursor: ${U}`:"";return`Replayed ${$} event(s), ${_} delivery result(s)${W}`}import{basename as Vc}from"path";var b6={name:"@hasna/knowledge",version:"0.2.91",description:"Agent-friendly local knowledge CLI with JSON output, pagination, and safe destructive actions",type:"module",exports:{".":{import:"./dist/index.js",types:"./dist/index.d.ts"},"./storage":{import:"./dist/storage.js",types:"./dist/storage.d.ts"},"./serve":{import:"./dist/serve.js",types:"./dist/serve.d.ts"}},main:"./dist/index.js",types:"./dist/index.d.ts",bin:{knowledge:"bin/knowledge.js","knowledge-mcp":"bin/knowledge-mcp.js","knowledge-serve":"bin/knowledge-serve.js"},files:["bin","dist","scripts/apply-cloud-migrations.mjs","scripts/lib/remote-temp-dir.mjs","scripts/smoke-machine-sync-release.mjs","scripts/smoke-machines-adapter.mjs","scripts/smoke-open-files-installed-boundary.mjs","scripts/strip-generated-trailing-whitespace.mjs","scripts/verify-generated-artifacts.mjs","docs/architecture/ai-native-knowledge-base.md","docs/architecture/hosted-wrapper-responsibilities.md","docs/architecture/hybrid-semantic-search.md","docs/architecture/machine-sync-schema.md","docs/examples/app-project-wiki-standard.md","docs/examples/company-wiki-workflow.md","docs/migration/global-rules-provenance-import.md","docs/migration/json-to-sqlite.md","LICENSE","README.md"],scripts:{test:"bun test","test:cli":"bun test tests/cli.test.ts","test:package":"bun test tests/package-release.test.ts","release:pack:check":"node scripts/validate-public-package.mjs","smoke:machines-adapter":"bun scripts/smoke-machines-adapter.mjs","smoke:machine-sync-release":"bun scripts/smoke-machine-sync-release.mjs","smoke:open-files-installed-boundary":"bun scripts/smoke-open-files-installed-boundary.mjs","migrate:cloud":"bun scripts/apply-cloud-migrations.mjs",serve:"bun src/serve-entry.ts","verify:generated":"bun scripts/verify-generated-artifacts.mjs",build:"rm -rf dist && bun build --target=bun --outfile=bin/knowledge.js --minify --external pg --external @hasna/machines --external @hasna/machines/consumer --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek src/cli.ts && bun build --target=bun --outfile=bin/knowledge-mcp.js --external pg --external @hasna/machines --external @hasna/machines/consumer --external @modelcontextprotocol/sdk --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek src/mcp.js && bun build --target=bun --outfile=bin/knowledge-serve.js --external pg --external @hasna/machines --external @hasna/machines/consumer --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek src/serve-entry.ts && bun build ./src/index.ts ./src/storage.ts ./src/serve.ts --outdir ./dist --target bun --external pg --external @hasna/machines --external @hasna/machines/consumer --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek && bun scripts/strip-generated-trailing-whitespace.mjs && bunx tsc -p tsconfig.build.json",prepublishOnly:"bun run build && node scripts/validate-public-package.mjs"},keywords:["knowledge","cli","agents","json","notes","local","store"],license:"Apache-2.0",publishConfig:{registry:"https://registry.npmjs.org",access:"public"},repository:{type:"git",url:"git+https://github.com/hasna/knowledge.git"},bugs:{url:"https://github.com/hasna/knowledge/issues"},author:"Hasna Inc. ",engines:{bun:">=1.0",node:">=18"},dependencies:{"@ai-sdk/anthropic":"^3.0.81","@ai-sdk/deepseek":"^2.0.35","@ai-sdk/openai":"^3.0.68","@aws-sdk/client-s3":"^3.1063.0","@aws-sdk/credential-providers":"^3.1063.0","@hasna/events":"^0.1.3","@modelcontextprotocol/sdk":"^1.29.0","@types/json-schema":"^7.0.15",ai:"^6.0.197",commander:"^13.1.0",pg:"^8.16.3",zod:"^4.3.6"},devDependencies:{"@electric-sql/pglite":"^0.5.4","@hasna/contracts":"0.5.2","@types/bun":"^1.3.14","@types/pg":"^8.15.6"}};var fF={debug:0,info:1,warn:2,error:3},Rc=()=>{if(process.env.DEBUG)return"debug";if(process.env.LOG_LEVEL==="debug")return"debug";if(process.env.LOG_LEVEL==="warn")return"warn";if(process.env.LOG_LEVEL==="error")return"error";return"info"};function g1($,_,U){if(fF[$]W.toLowerCase()));return _.filter((W)=>!U.has(W.toLowerCase()))}function TO($,_,U){if(U===void 0)return{...$,message:_};return{...$,added:U.length,message:`${_} (added ${U.length} tag${U.length===1?"":"s"})`}}function Fc($,_){if(_===void 0)throw Error("Missing value for --tag. Example: knowledge add <content> -t <tag> -t <tag>");let U=_.split(",").map((W)=>W.trim()).filter((W)=>W.length>0);if(U.length===0)throw Error(`Invalid --tag value ${JSON.stringify(_)}: no tag name found. Example: knowledge add <title> <content> -t <tag> -t <tag>`);return Ac([...$??[],...U])}function Kc($){let _=[],U={};for(let W=0;W<$.length;W+=1){let J=$[W];if(!J.startsWith("-")){_.push(J);continue}switch(J){case"--json":U.json=!0;break;case"--verbose":U.verbose=!0;break;case"--yes":case"-y":U.yes=!0;break;case"--help":case"-h":U.help=!0;break;case"--version":case"-v":U.version=!0;break;case"--desc":U.desc=!0;break;case"--page":case"-p":U.page=Number($[W+1]),W+=1;break;case"--limit":case"-l":U.limit=Number($[W+1]),W+=1;break;case"--search":case"-s":U.search=$[W+1],W+=1;break;case"--sort":U.sort=$[W+1],W+=1;break;case"--id":U.id=$[W+1],W+=1;break;case"--store":U.store=$[W+1],W+=1;break;case"--title":U.title=$[W+1],W+=1;break;case"--content":U.content=$[W+1],W+=1;break;case"--url":U.url=$[W+1],W+=1;break;case"--tag":case"-t":U.tag=Fc(U.tag,$[W+1]),U.tagRaw=[...U.tagRaw??[],$[W+1]],W+=1;break;case"--format":U.format=$[W+1],W+=1;break;case"--completions":U.completions=$[W+1],W+=1;break;case"--purpose":U.purpose=$[W+1],W+=1;break;case"--model":U.model=$[W+1],W+=1;break;case"--strategy":U.strategy=$[W+1],W+=1;break;case"--dimensions":U.dimensions=Number($[W+1]),W+=1;break;case"--semantic":U.semantic=!0;break;case"--context":U.context=!0;break;case"--max-tokens":U.maxTokens=Number($[W+1]),W+=1;break;case"--max-items":U.maxItems=Number($[W+1]),W+=1;break;case"--from":U.from=$[W+1],W+=1;break;case"--to":U.to=$[W+1],W+=1;break;case"--rev":U.rev=Number($[W+1]),W+=1;break;case"--since":U.since=$[W+1],W+=1;break;case"--topic":U.topic=$[W+1],W+=1;break;case"--dedupe":U.dedupe=!0;break;case"--generate":U.generate=!0;break;case"--approve-write":U.approveWrite=!0;break;case"--provider":U.provider=$[W+1],W+=1;break;case"--mode":U.mode=$[W+1],W+=1;break;case"--machine":U.machine=$[W+1],W+=1;break;case"--workspace":U.workspace=$[W+1],W+=1;break;case"--api-url":U.apiUrl=$[W+1],W+=1;break;case"--canonical-example":U.canonicalExample=!0;break;case"--api-key":U.apiKey=$[W+1],W+=1;break;case"--email":U.email=$[W+1],W+=1;break;case"--org":U.org=$[W+1],W+=1;break;case"--org-id":U.orgId=$[W+1],W+=1;break;case"--user-id":U.userId=$[W+1],W+=1;break;case"--owner":U.owner=$[W+1],W+=1;break;case"--approved-by":U.approvedBy=$[W+1],W+=1;break;case"--patch-uri":U.patchUri=$[W+1],W+=1;break;case"--domain":U.domain=[...U.domain??[],$[W+1]],W+=1;break;case"--file-results":U.fileResults=!0;break;case"--full":U.full=!0;break;case"--dry-run":U.dryRun=!0;break;case"--fake":U.fake=!0;break;case"--no-tailscale":U.tailscale=!1;break;case"--no-artifact-content":U.artifactContent=!1;break;case"--no-color":U.noColor=!0;break;case"--scope":U.scope=$[W+1],W+=1;break;case"--tables":U.tables=$[W+1],W+=1;break;case"--peer-workspace":U.peerWorkspace=$[W+1],W+=1;break;case"--older-than":U.olderThan=Number($[W+1]),W+=1;break;case"--empty":U.empty=!0;break;case"--archived":U.archived=!0;break;case"--include-archived":U.includeArchived=!0;break;case"--project":U.project=$[W+1],W+=1;break;case"--contract":U.contract=!0;break;case"--source-ref":U.sourceRef=[...U.sourceRef??[],$[W+1]],W+=1;break;case"--allow-global":U.allowGlobal=!0;break;default:throw Error(`Unknown flag: ${J}. Run 'knowledge --help' for valid options.`)}}return{positional:_,flags:U}}function Ec($){if(!$)return"";return mF[$]??$}function Mc($,_){let U=Array.from({length:$.length+1},()=>Array(_.length+1).fill(0));for(let W=0;W<=$.length;W+=1)U[W][0]=W;for(let W=0;W<=_.length;W+=1)U[0][W]=W;for(let W=1;W<=$.length;W+=1)for(let J=1;J<=_.length;J+=1){let X=$[W-1]===_[J-1]?0:1;U[W][J]=Math.min(U[W-1][J]+1,U[W][J-1]+1,U[W-1][J-1]+X)}return U[$.length][_.length]}function bc($){if(!$)return"";let _=[...xF,...Object.keys(mF)],U="",W=Number.POSITIVE_INFINITY;for(let J of _){let X=Mc($,J);if(X<W)W=X,U=J}return W<=3?U:""}function Ic(){return Vc(process.argv[1]??"").replace(/\.(?:js|ts|mjs|cjs)$/,"")==="knowledge"}async function gc($){if(!hF.includes($[0]??""))return!1;let _=new zF;return _.name("knowledge").description("Agent-friendly local knowledge CLI with JSON output, pagination, and safe destructive actions"),TF(_,{source:"knowledge"}),await _.parseAsync($,{from:"user"}),!0}function wc(){console.log(`knowledge - local agent knowledge store +`,{encoding:"utf-8",mode:384}),await xd(U,$),await IO($,384).catch(()=>{return})}}function nd($=bF()){return{mode:"local-files",name:"json-events-store",remote:!1,localFiles:!0,localSqlite:!1,postgres:!1,s3:!1,aws:!1,durable:!0,idempotency:"best-effort-local",replayCursors:!0,description:`Local JSON files in ${$}; no SQLite, Postgres, S3, or AWS runtime is configured by this store.`}}function IF($,_={}){if(!Number.isInteger($)||$<0)throw Error(`Invalid event cursor offset: ${$}`);let U={offset:$,eventId:_.eventId,source:_.source,type:_.type};return`${PO}${EF.from(JSON.stringify(U),"utf-8").toString("base64url")}`}function S5($,_={}){if(!$)return 0;if(!$.startsWith(PO))throw Error(`Invalid local JSON event cursor: ${$}`);let U=$.slice(PO.length),W;try{W=JSON.parse(EF.from(U,"base64url").toString("utf-8"))}catch{throw Error(`Invalid local JSON event cursor: ${$}`)}let J=W.offset;if(!Number.isInteger(J)||J<0)throw Error(`Invalid local JSON event cursor: ${$}`);return gO("eventId",W.eventId,_.eventId),gO("source",W.source,_.source),gO("type",W.type,_.type),J}function Z5($){if($===void 0)return ud;if(!Number.isInteger($)||$<1)throw Error(`Event page limit must be a positive integer, got ${$}`);return Math.min($,dd)}function BF($,_){let U=$;if(_.eventId)U=U.filter((W)=>W.id===_.eventId);if(_.source)U=U.filter((W)=>W.source===_.source);if(_.type)U=U.filter((W)=>W.type===_.type);if(_.cursor){let W=S5(_.cursor,_);U=U.slice(W)}if(_.limit!==void 0)U=U.slice(0,Z5(_.limit));return U}function gO($,_,U){if(_!==U)throw Error(`Local JSON event cursor ${$} filter mismatch`)}function HF($,_){return $.find((U)=>_.id!==void 0&&U.id===_.id||_.dedupeKey!==void 0&&U.dedupeKey===_.dedupeKey)}async function ld($){let _=new f5($);await _.init();let[U,W,J]=await Promise.all([_.listChannels(),_.listEvents(),_.listDeliveries()]),X=U.reduce((G,Q)=>{return G[Q.transport]=(G[Q.transport]??0)+1,G},{});return{service:"events",schemaVersion:"1.0",dataDir:_.dataDir,storage:_.runtime,env:{primary:C5,fallback:T5,active:cd()},files:{channels:wO(_.dataDir,"channels.json",U.length),events:wO(_.dataDir,"events.json",W.length),deliveries:wO(_.dataDir,"deliveries.json",J.length)},counts:{channels:U.length,enabledChannels:U.filter((G)=>G.enabled).length,disabledChannels:U.filter((G)=>!G.enabled).length,events:W.length,deliveries:J.length},transports:X,safety:{includesEventPayloads:!1,includesWebhookSecrets:!1,listOutputsRedactSecrets:!0,statusOutputIsMetadataOnly:!0}}}function wO($,_,U){let W=xW($,_);return{path:W,exists:MF(W),records:U}}function rd($,_){return`${$}.${_}`}function pd($,_,U){return`sha256=${id("sha256",$).update(rd(_,U)).digest("hex")}`}function c6(){return new Date().toISOString()}function hW($,_=4096){return $.length>_?`${$.slice(0,_)}...`:$}function ad($,_){if(!_.webhook)throw Error(`Channel ${_.id} has no webhook config`);let U=JSON.stringify($),W=$.time,J={"Content-Type":"application/json","User-Agent":"@hasna/events","X-Hasna-Event-Id":$.id,"X-Hasna-Event-Type":$.type,"X-Hasna-Timestamp":W,..._.webhook.headers};if(_.webhook.secret)J["X-Hasna-Signature"]=pd(_.webhook.secret,W,U);return{body:U,headers:J}}async function sd($,_,U={}){if(!_.webhook)throw Error(`Channel ${_.id} has no webhook config`);let W=c6(),{body:J,headers:X}=ad($,_),G=new AbortController,Q=setTimeout(()=>G.abort(),_.webhook.timeoutMs??15000);try{let Y=await(U.fetchImpl??fetch)(_.webhook.url,{method:"POST",headers:X,body:J,signal:G.signal}),j=hW(await Y.text());return{attempt:1,status:Y.ok?"success":"failed",startedAt:W,completedAt:c6(),responseStatus:Y.status,responseBody:j,error:Y.ok?void 0:`Webhook returned HTTP ${Y.status}`}}catch(Y){return{attempt:1,status:"failed",startedAt:W,completedAt:c6(),error:Y instanceof Error?Y.message:String(Y)}}finally{clearTimeout(Q)}}async function ed($,_){if(!_.command)throw Error(`Channel ${_.id} has no command config`);let U=c6(),W=JSON.stringify($),J={...process.env,..._.command.env,HASNA_CHANNEL_ID:_.id,HASNA_EVENT_ID:$.id,HASNA_EVENT_TYPE:$.type,HASNA_EVENT_SOURCE:$.source,HASNA_EVENT_SUBJECT:$.subject??"",HASNA_EVENT_SEVERITY:$.severity,HASNA_EVENT_TIME:$.time,HASNA_EVENT_DEDUPE_KEY:$.dedupeKey??"",HASNA_EVENT_SCHEMA_VERSION:$.schemaVersion,HASNA_EVENT_JSON:W};return new Promise((X)=>{let G=td(_.command.command,_.command.args??[],{cwd:_.command.cwd,env:J,stdio:["pipe","pipe","pipe"]}),Q="",Y="",j=setTimeout(()=>G.kill("SIGTERM"),_.command.timeoutMs??15000);G.stdin.end(W),G.stdout.on("data",(O)=>{Q+=O.toString()}),G.stderr.on("data",(O)=>{Y+=O.toString()}),G.on("error",(O)=>{clearTimeout(j),X({attempt:1,status:"failed",startedAt:U,completedAt:c6(),stdout:hW(Q),stderr:hW(Y),error:O.message})}),G.on("close",(O,B)=>{clearTimeout(j);let V=O===0;X({attempt:1,status:V?"success":"failed",startedAt:U,completedAt:c6(),stdout:hW(Q),stderr:hW(Y),error:V?void 0:`Command exited with ${B?`signal ${B}`:`code ${O}`}`})})})}async function $c($,_,U={}){if(_.transport==="webhook")return sd($,_,U);if(_.transport==="command")return ed($,_);return{attempt:1,status:"skipped",startedAt:c6(),completedAt:c6(),error:`Unsupported transport: ${_.transport}`}}function VF($,_,U){let W=U.some((J)=>J.status==="success")?"success":U.every((J)=>J.status==="skipped")?"skipped":"failed";return{id:od(),eventId:$.id,channelId:_.id,transport:_.transport,status:W,attempts:U,createdAt:U[0]?.startedAt??c6(),completedAt:U.at(-1)?.completedAt??c6()}}class gF extends Error{eventType;issues;constructor($,_){let U=_.map((W)=>`${W.path||"<root>"}: ${W.message}`).join("; ");super(`Event validation failed for type "${$}": ${U}`);this.name="EventValidationError",this.eventType=$,this.issues=_}}class wF{definitions=new Map;register($){return this.definitions.set($.type,$),this}unregister($){return this.definitions.delete($)}has($){return this.definitions.has($)}get($){return this.definitions.get($)}list(){return[...this.definitions.values()]}validateEvent($){let _=this.definitions.get($.type);if(!_)return{ok:!0};return _.validate($.data,$)}assertEventValid($){let _=this.validateEvent($);if(!_.ok)throw new gF($.type,_.issues)}}var _c=new wF;function kO($){return{id:$.id??Uc(),source:$.source,type:$.type,time:Qc($.time),subject:$.subject,severity:$.severity??"info",data:$.data??{},message:$.message,dedupeKey:$.dedupeKey,schemaVersion:$.schemaVersion??"1.0",metadata:$.metadata??{}}}class kF{store;redactors;transportOptions;catalog;validateCatalogTypes;constructor($={}){this.store=$.store??new f5($.dataDir),this.redactors=$.redactors??[],this.transportOptions={fetchImpl:$.fetchImpl},this.catalog=$.catalog??_c,this.validateCatalogTypes=$.validateCatalogTypes??!1}async addChannel($){let _=new Date().toISOString();return this.store.addChannel({...$,createdAt:$.createdAt??_,updatedAt:$.updatedAt??_})}async listChannels(){return this.store.listChannels()}async removeChannel($){return this.store.removeChannel($)}async emit($,_={}){let U=_.redactSensitiveData===!1?kO($):Xc(kO($));if(_.validate??this.validateCatalogTypes)this.catalog.assertEventValid(U);let W=await this.appendEvent(U,{dedupe:_.dedupe!==!1});if(W.deduped)return{event:W.event,deliveries:[],deduped:!0};let J=_.deliver===!1?[]:await this.deliver(W.event);return{event:W.event,deliveries:J,deduped:!1}}async listEvents($={}){if(Object.keys($).length===0)return this.store.listEvents();return RF(await this.store.listEvents(),$)}async listEventsPage($={}){if(this.store.listEventsPage)return this.store.listEventsPage($);let _=RF(await this.store.listEvents(),{eventId:$.eventId,source:$.source,type:$.type}),U=S5($.cursor,$),W=Z5($.limit),J=_.slice(U,U+W),X=U+J.length,G=X<_.length;return{events:J,cursor:$.cursor,nextCursor:G?IF(X,$):void 0,hasMore:G}}async listDeliveries(){return this.store.listDeliveries()}async deliver($){let U=(await this.store.listChannels()).filter((J)=>LF(J,$)),W=[];for(let J of U){let X=await this.applyRedaction($,J),G=await this.deliverWithRetry(X,J);await this.store.appendDelivery(G),W.push(G)}return W}async matchChannel($,_={}){let U=await this.store.getChannel($);if(!U)throw Error(`Channel not found: ${$}`);let W=kO({source:_.source??"hasna.events",type:_.type??"events.test",subject:_.subject??$,severity:_.severity??"info",data:_.data??{test:!0},message:_.message??"Hasna events test delivery",dedupeKey:_.dedupeKey,schemaVersion:_.schemaVersion,metadata:_.metadata,time:_.time,id:_.id}),J=LF(U,W);return{channelId:U.id,matched:J,event:W,filters:U.filters,reason:J?void 0:U.enabled?"event did not match channel filters":"channel is disabled"}}async testChannel($,_={},U={}){let W=await this.store.getChannel($);if(!W)throw Error(`Channel not found: ${$}`);let J=await this.matchChannel($,_),X=J.event;if(U.honorFilters&&!J.matched){let Y=new Date().toISOString(),j=VF(X,W,[{attempt:1,status:"skipped",startedAt:Y,completedAt:Y,error:J.reason}]);return j.metadata={reason:"filter_mismatch"},await this.store.appendDelivery(j),j}let G=await this.applyRedaction(X,W),Q=await this.deliverWithRetry(G,W);return await this.store.appendDelivery(Q),Q}async replay($={}){let _=$.cursor||$.limit!==void 0?await this.listEventsPage($):{events:await this.listEvents($),hasMore:!1};if($.dryRun)return{events:_.events,deliveries:[],cursor:_.cursor,nextCursor:_.nextCursor,hasMore:_.hasMore};let U=[];for(let W of _.events)U.push(...await this.deliver(W));return{events:_.events,deliveries:U,cursor:_.cursor,nextCursor:_.nextCursor,hasMore:_.hasMore}}async appendEvent($,_){if(this.store.appendEventOnce)return this.store.appendEventOnce($,{dedupe:_.dedupe});if(_.dedupe){let W=await this.store.findEventByIdentity({id:$.id,dedupeKey:$.dedupeKey});if(W)return{event:W,stored:!1,deduped:!0,identity:{id:W.id,dedupeKey:W.dedupeKey}}}let U=await this.store.appendEvent($);return{event:U,stored:!0,deduped:!1,identity:{id:U.id,dedupeKey:U.dedupeKey}}}async applyRedaction($,_){let U=Jc($,_.redact?.paths??[],_.redact?.replacement??"[REDACTED]");for(let W of this.redactors)U=await W(U,_);return U}async deliverWithRetry($,_){let U=Yc(_.retry),W=[];for(let J=0;J<U.maxAttempts;J+=1){let X=await $c($,_,this.transportOptions);if(X.attempt=J+1,X.status==="failed"&&J+1<U.maxAttempts)X.nextBackoffMs=Math.round(U.backoffMs*U.multiplier**J);if(W.push(X),X.status!=="failed")break;if(X.nextBackoffMs)await Bun.sleep(X.nextBackoffMs)}return VF($,_,W)}}function Jc($,_,U="[REDACTED]"){if(_.length===0)return $;let W=structuredClone($);for(let J of _)Gc(W,J,U);return W}function PF($){let _=structuredClone($);if(_.webhook?.secret)_.webhook.secret="[REDACTED]";if(_.command?.env)_.command.env=Object.fromEntries(Object.entries(_.command.env).map(([U,W])=>[U,CF(U)?"[REDACTED]":W]));return _}function Wc($){return $.map(PF)}function Xc($,_="[REDACTED]"){return CO($,_)}function CF($){return/secret|token|password|api[_-]?key|authorization/i.test($)}function CO($,_){if(Array.isArray($))return $.map((U)=>CO(U,_));if(!$||typeof $!=="object")return $;return Object.fromEntries(Object.entries($).map(([U,W])=>[U,CF(U)?_:CO(W,_)]))}function Gc($,_,U){let W=_.split("."),J=$;for(let G of W.slice(0,-1)){let Q=J[G];if(!Q||typeof Q!=="object")return;J=Q}let X=W.at(-1);if(X&&X in J)J[X]=U}function RF($,_){let U=$;if(_.eventId)U=U.filter((W)=>W.id===_.eventId);if(_.source)U=U.filter((W)=>W.source===_.source);if(_.type)U=U.filter((W)=>W.type===_.type);if(_.cursor)U=U.slice(S5(_.cursor,_));if(_.limit!==void 0)U=U.slice(0,Z5(_.limit));return U}function Qc($){if(!$)return new Date().toISOString();return $ instanceof Date?$.toISOString():$}function Yc($){return{maxAttempts:Math.max(1,$?.maxAttempts??1),backoffMs:Math.max(0,$?.backoffMs??250),multiplier:Math.max(1,$?.multiplier??2)}}function P5($,_,U=!1){if(!$?.length)return;let W={};for(let J of $){let X=zc(J,_),G=X.path;if(G in W)throw Error(`Duplicate ${_} filter path: ${G}`);let Q=U?Dc(X.rawValue,_):X.rawValue;W[G]=X.negated?{not:Q}:Q}return W}function jc($){let _={};if($.source)_.source=$.source;if($.type)_.type=$.type;if($.subject)_.subject=$.subject;if($.severity)_.severity=$.severity;let U=AF(P5($.data,"data"),P5($.dataJson,"data-json",!0)),W=AF(P5($.metadata,"metadata"),P5($.metadataJson,"metadata-json",!0));if(Object.keys(U).length>0)_.data=U;if(Object.keys(W).length>0)_.metadata=W;return Object.keys(_).length>0?[_]:void 0}function AF(...$){let _={};for(let U of $){if(!U)continue;for(let[W,J]of Object.entries(U)){if(W in _)throw Error(`Duplicate filter path: ${W}`);_[W]=J}}return _}function Dc($,_){let U=JSON.parse($);if(U===null||typeof U==="string"||typeof U==="number"||typeof U==="boolean"||Array.isArray(U)&&U.every((W)=>typeof W==="string"))return U;throw Error(`${_} filter JSON values must be string, string[], number, boolean, or null`)}function zc($,_){let U=$.indexOf("!=");if(U>0)return{path:$.slice(0,U),rawValue:$.slice(U+2),negated:!0};let W=$.indexOf("=");if(W<=0)throw Error(`Invalid ${_} filter, expected path=value or path!=value: ${$}`);return{path:$.slice(0,W),rawValue:$.slice(W+1),negated:!1}}var qc=100;function MU($,_){if(!$)return _;let U=JSON.parse($);if(!U||typeof U!=="object"||Array.isArray(U))throw Error("Expected a JSON object");return U}function Oc($){if(!$?.length)return;let _={};for(let U of $){let W=U.indexOf("=");if(W===-1)throw Error(`Invalid header, expected name=value: ${U}`);_[U.slice(0,W)]=U.slice(W+1)}return _}function Q0($){if($.createClient)return $.createClient();return new kF({store:new f5($.dataDir)})}function b1($,_,U){if(_)console.log(JSON.stringify($,null,2));else console.log(U)}function FF($,_){let U=$ instanceof Error?$.message:String($);if(_)console.log(JSON.stringify({error:U},null,2));else console.error(U);process.exitCode=1}function KF($){return Boolean($?.json||$?.opts?.().json||$?.optsWithGlobals?.().json||$?.parent?.opts?.().json||$?.parent?.optsWithGlobals?.().json)}function K4($,_){return KF($)||KF(_)}function Lc($,_){let U=$.command(_.channelsCommandName??"channels").description("Manage Hasna event channels");return U.command("add").description("Add or replace a channel").argument("<target>","Webhook URL or command binary").requiredOption("--id <id>","Channel identifier").option("--transport <kind>","Transport kind: webhook or command","webhook").option("--name <name>","Display name").option("--type <pattern>","Event type filter, e.g. todos.task.*").option("--source <pattern>","Event source filter").option("--subject <pattern>","Event subject filter").option("--severity <pattern>","Event severity filter").option("--data <path=value...>","Event data field filter; string values, path!=value negatives, array-member matching, dot paths, * segment wildcard, ** recursive wildcard",M1,[]).option("--metadata <path=value...>","Event metadata field filter; string values, path!=value negatives, array-member matching, dot paths, * segment wildcard, ** recursive wildcard",M1,[]).option("--data-json <path=json...>","Event data field filter with typed JSON value; path!=json negatives supported",M1,[]).option("--metadata-json <path=json...>","Event metadata field filter with typed JSON value; path!=json negatives supported",M1,[]).option("--secret <secret>","Webhook HMAC secret").option("--header <name=value...>","Webhook header",M1,[]).option("--arg <arg...>","Command argument",M1,[]).option("--timeout-ms <ms>","Transport timeout in milliseconds",mW).option("--retry-attempts <n>","Maximum delivery attempts",mW).option("--retry-backoff-ms <ms>","Initial retry backoff in milliseconds",mW).option("--redact <path...>","Event field path to redact before delivery",M1,[]).option("--disabled","Create channel disabled",!1).option("-j, --json","Print JSON output",!1).action(async(W,J,X)=>{let G=new Date().toISOString(),Q={id:J.id,name:J.name,enabled:!J.disabled,transport:J.transport,filters:jc(J),retry:J.retryAttempts||J.retryBackoffMs?{maxAttempts:J.retryAttempts,backoffMs:J.retryBackoffMs}:void 0,redact:J.redact?.length?{paths:J.redact}:void 0,createdAt:G,updatedAt:G};if(J.transport==="webhook")Q.webhook={url:W,secret:J.secret,headers:Oc(J.header),timeoutMs:J.timeoutMs};else if(J.transport==="command")Q.command={command:W,args:J.arg??[],timeoutMs:J.timeoutMs};else throw Error(`Transport ${J.transport} is reserved for future use and cannot be added yet`);let Y=await Q0(_).addChannel(Q);b1(PF(Y),K4(J,X),`Added ${Y.transport} channel ${Y.id}`)}),U.command("list").description("List configured channels").option("-j, --json","Print JSON output",!1).action(async(W,J)=>{let X=await Q0(_).listChannels();if(K4(W,J)){console.log(JSON.stringify(Wc(X),null,2));return}if(!X.length){console.log("No channels configured.");return}for(let G of X)console.log(`${G.id} ${G.enabled?"enabled":"disabled"} ${G.transport} ${G.webhook?.url??G.command?.command??G.transport}`)}),U.command("status").description("Show events channel storage status").option("-j, --json","Print JSON output",!1).action(async(W,J)=>{let X=await ld(_.dataDir);b1(X,K4(W,J),`events dataDir: ${X.dataDir}`)}),U.command("remove").description("Remove a channel").argument("<id>","Channel identifier").option("-j, --json","Print JSON output",!1).action(async(W,J,X)=>{let G=await Q0(_).removeChannel(W);b1({removed:G},K4(J,X),G?`Removed ${W}`:`Channel not found: ${W}`)}),U.command("test").description("Send a test event to one channel").argument("<id>","Channel identifier").option("--source <source>","Event source override").option("--type <type>","Event type","events.test").option("--subject <subject>","Event subject").option("--message <message>","Event message","Hasna events test delivery").option("--data <json>","Event data JSON object").option("--metadata <json>","Event metadata JSON object").option("--honor-filters","Skip delivery when the sample event does not match channel filters",!1).option("-j, --json","Print JSON output",!1).action(async(W,J,X)=>{let G=K4(J,X);try{let Q=await Q0(_).testChannel(W,{source:J.source??_.source,type:J.type,subject:J.subject??W,message:J.message,data:MU(J.data,{test:!0}),metadata:MU(J.metadata,{})},{honorFilters:J.honorFilters});b1(Q,G,`${Q.status}: ${Q.channelId}`)}catch(Q){FF(Q,G)}}),U.command("match").description("Check whether a sample event matches one channel without delivering").argument("<id>","Channel identifier").option("--source <source>","Event source override").option("--type <type>","Event type","events.test").option("--subject <subject>","Event subject").option("--message <message>","Event message","Hasna events match preview").option("--data <json>","Event data JSON object").option("--metadata <json>","Event metadata JSON object").option("-j, --json","Print JSON output",!1).action(async(W,J,X)=>{let G=K4(J,X);try{let Q=await Q0(_).matchChannel(W,{source:J.source??_.source,type:J.type,subject:J.subject??W,message:J.message,data:MU(J.data,{test:!0}),metadata:MU(J.metadata,{})});b1(Q,G,`${Q.matched?"matched":"skipped"}: ${Q.channelId}`)}catch(Q){FF(Q,G)}}),U}function Nc($,_){let U=$.command(_.eventsCommandName??"events").description("Emit, list, and replay Hasna events");U.command("emit").description("Emit an event from this app").argument("<type>","Event type").option("--source <source>","Event source override").option("--subject <subject>","Event subject").option("--severity <severity>","Event severity","info").option("--message <message>","Event message").option("--dedupe-key <key>","Dedupe key").option("--data <json>","Event data JSON object").option("--metadata <json>","Event metadata JSON object").option("--no-deliver","Record without delivering").option("--no-dedupe","Allow duplicate id/dedupeKey events").option("-j, --json","Print JSON output",!1).action(async(J,X,G)=>{let Q=await Q0(_).emit({source:X.source??_.source,type:J,subject:X.subject,severity:X.severity,message:X.message,dedupeKey:X.dedupeKey,data:MU(X.data,{}),metadata:MU(X.metadata,{})},{deliver:X.deliver,dedupe:X.dedupe});b1(Q,K4(X,G),`${Q.deduped?"Deduped":"Emitted"} ${Q.event.id} to ${Q.deliveries.length} channel(s)`)});let W=_.defaultEventListLimit??qc;return U.command("list").description("List recorded events").option("--source <source>","Filter by source").option("--type <type>","Filter by type").option("--limit <n>",`Limit to the most recent <n> events (default ${W}; use 0 for all)`,mW,W).option("-j, --json","Print JSON output",!1).action(async(J,X)=>{let G=await Q0(_).listEvents();if(J.source)G=G.filter((Q)=>Q.source===J.source);if(J.type)G=G.filter((Q)=>Q.type===J.type);if(J.limit)G=G.slice(-J.limit);if(K4(J,X)){console.log(JSON.stringify(G,null,2));return}if(!G.length){console.log("No events recorded.");return}for(let Q of G)console.log(`${Q.time} ${Q.id} ${Q.source} ${Q.type} ${Q.severity}`)}),U.command("replay").description("Replay recorded events").option("--id <id>","Replay one event id").option("--source <source>","Filter by source").option("--type <type>","Filter by type").option("--cursor <cursor>","Opaque replay cursor from a previous page").option("--limit <n>","Maximum events to replay",mW).option("--dry-run","Preview without delivery",!1).option("-j, --json","Print JSON output",!1).action(async(J,X)=>{let G=await Q0(_).replay({eventId:J.id,source:J.source,type:J.type,cursor:J.cursor,limit:J.limit,dryRun:J.dryRun});b1(G,K4(J,X),Bc(G.events.length,G.deliveries.length,G.nextCursor))}),U}function TF($,_){Lc($,_),Nc($,_)}function mW($){let _=Number($);if(!Number.isFinite(_))throw Error(`Expected a number, got ${$}`);return _}function M1($,_){return _.push($),_}function Bc($,_,U){let W=U?`, next cursor: ${U}`:"";return`Replayed ${$} event(s), ${_} delivery result(s)${W}`}import{basename as Vc}from"path";var b6={name:"@hasna/knowledge",version:"0.2.92",description:"Agent-friendly local knowledge CLI with JSON output, pagination, and safe destructive actions",type:"module",exports:{".":{import:"./dist/index.js",types:"./dist/index.d.ts"},"./storage":{import:"./dist/storage.js",types:"./dist/storage.d.ts"},"./serve":{import:"./dist/serve.js",types:"./dist/serve.d.ts"}},main:"./dist/index.js",types:"./dist/index.d.ts",bin:{knowledge:"bin/knowledge.js","knowledge-mcp":"bin/knowledge-mcp.js","knowledge-serve":"bin/knowledge-serve.js"},files:["bin","dist","scripts/apply-cloud-migrations.mjs","scripts/lib/remote-temp-dir.mjs","scripts/smoke-machine-sync-release.mjs","scripts/smoke-machines-adapter.mjs","scripts/smoke-open-files-installed-boundary.mjs","scripts/strip-generated-trailing-whitespace.mjs","scripts/verify-generated-artifacts.mjs","docs/architecture/ai-native-knowledge-base.md","docs/architecture/hosted-wrapper-responsibilities.md","docs/architecture/hybrid-semantic-search.md","docs/architecture/machine-sync-schema.md","docs/examples/app-project-wiki-standard.md","docs/examples/company-wiki-workflow.md","docs/migration/global-rules-provenance-import.md","docs/migration/json-to-sqlite.md","LICENSE","README.md"],scripts:{test:"bun test","test:cli":"bun test tests/cli.test.ts","test:package":"bun test tests/package-release.test.ts","release:pack:check":"node scripts/validate-public-package.mjs","smoke:machines-adapter":"bun scripts/smoke-machines-adapter.mjs","smoke:machine-sync-release":"bun scripts/smoke-machine-sync-release.mjs","smoke:open-files-installed-boundary":"bun scripts/smoke-open-files-installed-boundary.mjs","migrate:cloud":"bun scripts/apply-cloud-migrations.mjs",serve:"bun src/serve-entry.ts","verify:generated":"bun scripts/verify-generated-artifacts.mjs",build:"rm -rf dist && bun build --target=bun --outfile=bin/knowledge.js --minify --external pg --external @hasna/machines --external @hasna/machines/consumer --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek src/cli.ts && bun build --target=bun --outfile=bin/knowledge-mcp.js --external pg --external @hasna/machines --external @hasna/machines/consumer --external @modelcontextprotocol/sdk --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek src/mcp.js && bun build --target=bun --outfile=bin/knowledge-serve.js --external pg --external @hasna/machines --external @hasna/machines/consumer --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek src/serve-entry.ts && bun build ./src/index.ts ./src/storage.ts ./src/serve.ts --outdir ./dist --target bun --external pg --external @hasna/machines --external @hasna/machines/consumer --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek && bun scripts/strip-generated-trailing-whitespace.mjs && bunx tsc -p tsconfig.build.json",prepublishOnly:"bun run build && node scripts/validate-public-package.mjs"},keywords:["knowledge","cli","agents","json","notes","local","store"],license:"Apache-2.0",publishConfig:{registry:"https://registry.npmjs.org",access:"public"},repository:{type:"git",url:"git+https://github.com/hasna/knowledge.git"},bugs:{url:"https://github.com/hasna/knowledge/issues"},author:"Hasna Inc. <hasna@example.com>",engines:{bun:">=1.0",node:">=18"},dependencies:{"@ai-sdk/anthropic":"^3.0.81","@ai-sdk/deepseek":"^2.0.35","@ai-sdk/openai":"^3.0.68","@aws-sdk/client-s3":"^3.1063.0","@aws-sdk/credential-providers":"^3.1063.0","@hasna/events":"^0.1.3","@modelcontextprotocol/sdk":"^1.29.0","@types/json-schema":"^7.0.15",ai:"^6.0.197",commander:"^13.1.0",pg:"^8.16.3",zod:"^4.3.6"},devDependencies:{"@electric-sql/pglite":"^0.5.4","@hasna/contracts":"0.5.2","@types/bun":"^1.3.14","@types/pg":"^8.15.6"}};var fF={debug:0,info:1,warn:2,error:3},Rc=()=>{if(process.env.DEBUG)return"debug";if(process.env.LOG_LEVEL==="debug")return"debug";if(process.env.LOG_LEVEL==="warn")return"warn";if(process.env.LOG_LEVEL==="error")return"error";return"info"};function g1($,_,U){if(fF[$]<fF[Rc()])return;let W={debug:"[DEBUG]",info:"[INFO]",warn:"[WARN]",error:"[ERROR]"}[$],J=U?`${W} ${_} ${JSON.stringify(U)}`:`${W} ${_}`;if($==="error")console.error(J);else console.error(J)}var hF=["events","webhooks"],xF=["add","list","get","delete","update","archive","restore","upsert","untag","versions","diff","export","prune","dedupe","stats","inventory","project-panel","paths","mode","setup","auth","storage","machines","sync","db","wiki","app-wiki","source","ingest","reindex","search","context","proposals","web","ask","build","embeddings","providers","safety","help",...hF],mF={ls:"list",rm:"delete",edit:"update",unarchive:"restore"};function Ac($){let _=new Set,U=[];for(let W of $){let J=W.toLowerCase();if(_.has(J))continue;_.add(J),U.push(W)}return U}function SF($,_){let U=new Set(($??[]).map((W)=>W.toLowerCase()));return _.filter((W)=>!U.has(W.toLowerCase()))}function TO($,_,U){if(U===void 0)return{...$,message:_};return{...$,added:U.length,message:`${_} (added ${U.length} tag${U.length===1?"":"s"})`}}function Fc($,_){if(_===void 0)throw Error("Missing value for --tag. Example: knowledge add <title> <content> -t <tag> -t <tag>");let U=_.split(",").map((W)=>W.trim()).filter((W)=>W.length>0);if(U.length===0)throw Error(`Invalid --tag value ${JSON.stringify(_)}: no tag name found. Example: knowledge add <title> <content> -t <tag> -t <tag>`);return Ac([...$??[],...U])}function Kc($){let _=[],U={};for(let W=0;W<$.length;W+=1){let J=$[W];if(!J.startsWith("-")){_.push(J);continue}switch(J){case"--json":U.json=!0;break;case"--verbose":U.verbose=!0;break;case"--yes":case"-y":U.yes=!0;break;case"--help":case"-h":U.help=!0;break;case"--version":case"-v":U.version=!0;break;case"--desc":U.desc=!0;break;case"--page":case"-p":U.page=Number($[W+1]),W+=1;break;case"--limit":case"-l":U.limit=Number($[W+1]),W+=1;break;case"--search":case"-s":U.search=$[W+1],W+=1;break;case"--sort":U.sort=$[W+1],W+=1;break;case"--id":U.id=$[W+1],W+=1;break;case"--store":U.store=$[W+1],W+=1;break;case"--title":U.title=$[W+1],W+=1;break;case"--content":U.content=$[W+1],W+=1;break;case"--url":U.url=$[W+1],W+=1;break;case"--tag":case"-t":U.tag=Fc(U.tag,$[W+1]),U.tagRaw=[...U.tagRaw??[],$[W+1]],W+=1;break;case"--format":U.format=$[W+1],W+=1;break;case"--completions":U.completions=$[W+1],W+=1;break;case"--purpose":U.purpose=$[W+1],W+=1;break;case"--model":U.model=$[W+1],W+=1;break;case"--strategy":U.strategy=$[W+1],W+=1;break;case"--dimensions":U.dimensions=Number($[W+1]),W+=1;break;case"--semantic":U.semantic=!0;break;case"--context":U.context=!0;break;case"--max-tokens":U.maxTokens=Number($[W+1]),W+=1;break;case"--max-items":U.maxItems=Number($[W+1]),W+=1;break;case"--from":U.from=$[W+1],W+=1;break;case"--to":U.to=$[W+1],W+=1;break;case"--rev":U.rev=Number($[W+1]),W+=1;break;case"--since":U.since=$[W+1],W+=1;break;case"--topic":U.topic=$[W+1],W+=1;break;case"--dedupe":U.dedupe=!0;break;case"--generate":U.generate=!0;break;case"--approve-write":U.approveWrite=!0;break;case"--provider":U.provider=$[W+1],W+=1;break;case"--mode":U.mode=$[W+1],W+=1;break;case"--machine":U.machine=$[W+1],W+=1;break;case"--workspace":U.workspace=$[W+1],W+=1;break;case"--api-url":U.apiUrl=$[W+1],W+=1;break;case"--canonical-example":U.canonicalExample=!0;break;case"--api-key":U.apiKey=$[W+1],W+=1;break;case"--email":U.email=$[W+1],W+=1;break;case"--org":U.org=$[W+1],W+=1;break;case"--org-id":U.orgId=$[W+1],W+=1;break;case"--user-id":U.userId=$[W+1],W+=1;break;case"--owner":U.owner=$[W+1],W+=1;break;case"--approved-by":U.approvedBy=$[W+1],W+=1;break;case"--patch-uri":U.patchUri=$[W+1],W+=1;break;case"--domain":U.domain=[...U.domain??[],$[W+1]],W+=1;break;case"--file-results":U.fileResults=!0;break;case"--full":U.full=!0;break;case"--dry-run":U.dryRun=!0;break;case"--fake":U.fake=!0;break;case"--no-tailscale":U.tailscale=!1;break;case"--no-artifact-content":U.artifactContent=!1;break;case"--no-color":U.noColor=!0;break;case"--scope":U.scope=$[W+1],W+=1;break;case"--tables":U.tables=$[W+1],W+=1;break;case"--peer-workspace":U.peerWorkspace=$[W+1],W+=1;break;case"--older-than":U.olderThan=Number($[W+1]),W+=1;break;case"--empty":U.empty=!0;break;case"--archived":U.archived=!0;break;case"--include-archived":U.includeArchived=!0;break;case"--project":U.project=$[W+1],W+=1;break;case"--contract":U.contract=!0;break;case"--source-ref":U.sourceRef=[...U.sourceRef??[],$[W+1]],W+=1;break;case"--allow-global":U.allowGlobal=!0;break;default:throw Error(`Unknown flag: ${J}. Run 'knowledge --help' for valid options.`)}}return{positional:_,flags:U}}function Ec($){if(!$)return"";return mF[$]??$}function Mc($,_){let U=Array.from({length:$.length+1},()=>Array(_.length+1).fill(0));for(let W=0;W<=$.length;W+=1)U[W][0]=W;for(let W=0;W<=_.length;W+=1)U[0][W]=W;for(let W=1;W<=$.length;W+=1)for(let J=1;J<=_.length;J+=1){let X=$[W-1]===_[J-1]?0:1;U[W][J]=Math.min(U[W-1][J]+1,U[W][J-1]+1,U[W-1][J-1]+X)}return U[$.length][_.length]}function bc($){if(!$)return"";let _=[...xF,...Object.keys(mF)],U="",W=Number.POSITIVE_INFINITY;for(let J of _){let X=Mc($,J);if(X<W)W=X,U=J}return W<=3?U:""}function Ic(){return Vc(process.argv[1]??"").replace(/\.(?:js|ts|mjs|cjs)$/,"")==="knowledge"}async function gc($){if(!hF.includes($[0]??""))return!1;let _=new zF;return _.name("knowledge").description("Agent-friendly local knowledge CLI with JSON output, pagination, and safe destructive actions"),TF(_,{source:"knowledge"}),await _.parseAsync($,{from:"user"}),!0}function wc(){console.log(`knowledge - local agent knowledge store Usage: knowledge <command> [options] diff --git a/package.json b/package.json index 193b22d..5031216 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@hasna/knowledge", - "version": "0.2.91", + "version": "0.2.92", "description": "Agent-friendly local knowledge CLI with JSON output, pagination, and safe destructive actions", "type": "module", "exports": { From 69ced04594feeea86aa1d2a636666a6c49cf9bd2 Mon Sep 17 00:00:00 2001 From: Andrei Hasna <andrei@hasna.com> Date: Thu, 30 Jul 2026 16:03:29 +0300 Subject: [PATCH 2/2] docs(changelog): record 0.2.92 as the release that ships PR #34 Agent: vespasian --- CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3b416c..094f8d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -125,6 +125,36 @@ here, because both need their own change and review: 0.1.13 does not. Check `node_modules/@hasna/events/package.json` before concluding a bundle is out of sync. +## 0.2.92 + +Release-only bump. No source change: this ships work already merged to `main` +that no published artifact carried. + +The headline is the `-t/--tag` silent data-loss fix from **#34** +(`fix(cli): stop silently dropping repeated -t tags on add/update/upsert`, +merged 2026-07-27T19:39:04Z). That fix landed three days *after* 0.2.91 was +published (2026-07-24T15:52:10Z) and `package.json` was never bumped, so `main` +and the registry both reported `0.2.91` while behaving differently — `main` +stored all five tags, the published artifact stored only the last one — and +`npm publish` could not ship the fix at all, because that version already +existed. Measured against installed 0.2.91: +`knowledge add … -t convention -t naming -t repos -t github -t proposed` +stored `["proposed"]` at exit 0. Because tags are the retrieval surface, such an +entry is invisible to every `--tag` query, which is indistinguishable from never +having been written. + +Since the version number was identical, nothing signalled the difference. The +same gap held back **31 other commits** merged since the 0.2.91 publish, +including the request-boundary guard, the platform-agnostic redaction fix, the +`ok_untag` truthful-removal fix and the generated-artifact checks described under +"Unreleased" above; they ship here too. + +- Bump `0.2.91` → `0.2.92` and regenerate the shipped bundles, which embed the + version string. The bundle diff is provably the version string alone: + `bin/knowledge.js` and `bin/knowledge-mcp.js` are byte-identical to their + predecessors once `0.2.92` is reverted to `0.2.91`, and + `verify:generated` reports all 6 bundles rebuilding byte-identically. + ## 0.2.91 Harden the local JSON item-store against lock corruption and add a sanctioned