noema-cli 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -25,9 +25,17 @@ declare class PiAdapter implements PromptRunner$1 {
25
25
  private readonly agents;
26
26
  private readonly streamFn;
27
27
  private readonly modelDefinition;
28
+ private isPromptRunning;
29
+ private externalSignal?;
30
+ private capturedError;
31
+ private capturedUsage;
28
32
  constructor(apiKey: string, model: string);
29
- runPrompt(sessionId: string, prompt: string, onDelta: (delta: string) => void): Promise<{
33
+ runPrompt(sessionId: string, prompt: string, onDelta: (delta: string) => void, signal?: AbortSignal): Promise<{
30
34
  responseText: string;
35
+ usage: never;
36
+ } | {
37
+ responseText: string;
38
+ usage?: undefined;
31
39
  }>;
32
40
  private getAgent;
33
41
  }
@@ -44,6 +52,8 @@ type NoemaConfig = WorkflowCatalog & {
44
52
  model?: string;
45
53
  provider?: string;
46
54
  sessionPersistence?: SessionPersistenceConfig;
55
+ showTokenUsage?: boolean;
56
+ timeout?: number;
47
57
  };
48
58
  declare const DEFAULT_CONFIG_PATH: string;
49
59
  declare const DEFAULT_CONFIG_DISPLAY_PATH = "~/.noema/config.json";
@@ -80,8 +90,6 @@ declare const formatSessionList: (entries: SessionListEntry[], now: Date) => str
80
90
  declare const writeFiles: (root: string, writes: WriteRequest[]) => Promise<void>;
81
91
  declare const resolveWritePath: (root: string, target: string) => string;
82
92
 
83
- declare const cliVersion = "0.0.1";
84
-
85
93
  declare const runCli: (args?: string[], output?: NodeJS.WritableStream) => void;
86
94
 
87
- export { DEFAULT_CONFIG_DISPLAY_PATH, DEFAULT_CONFIG_PATH, NodeProjectFileAccess, PiAdapter, type SessionListEntry, type SessionStore, SimpleWorkflowClassifier, type SlashCommandContext, type SlashCommandResult, type WritingAgentFactory, cliVersion, createSessionStore, createWorkflowCatalogLoader, formatSessionList, handleSlashCommand, loadNoemaConfig, loadWorkflowCatalog, resolveWritePath, runCli, runMain, writeFiles };
95
+ export { DEFAULT_CONFIG_DISPLAY_PATH, DEFAULT_CONFIG_PATH, NodeProjectFileAccess, PiAdapter, type SessionListEntry, type SessionStore, SimpleWorkflowClassifier, type SlashCommandContext, type SlashCommandResult, type WritingAgentFactory, createSessionStore, createWorkflowCatalogLoader, formatSessionList, handleSlashCommand, loadNoemaConfig, loadWorkflowCatalog, resolveWritePath, runCli, runMain, writeFiles };
package/dist/index.js CHANGED
@@ -1,26 +1,32 @@
1
- import j from"path";var $=()=>({nextSessionId:1,sessions:new Map}),st=t=>{let e=`session-${t.nextSessionId}`;return t.nextSessionId+=1,e},U=(t,e)=>{t.sessions.has(e)||t.sessions.set(e,{id:e})};var D=async(t,e,s,n)=>{let r=[],o=n??(a=>r.push(a));switch(t.method){case"initialize":{let a=nt(t.params),l=Bt(a);return{responses:[{jsonrpc:"2.0",id:t.id,result:{protocolVersion:l,agentCapabilities:{loadSession:Kt(),mcpCapabilities:{http:!1,sse:!1},promptCapabilities:{audio:!1,embeddedContext:!0,image:!1},sessionCapabilities:{}},agentInfo:{name:"noema-cli",title:"Noema CLI",version:"0.0.1"},authMethods:[]}}],notifications:r}}case"session/new":{let a=st(e);return U(e,a),{responses:[{jsonrpc:"2.0",id:t.id,result:{sessionId:a}}],notifications:r}}case"session/prompt":{let a=nt(t.params),l=typeof a?.sessionId=="string"?a.sessionId:null;if(l===null)return Jt(t.id);let i=_t(s);if(i===null)return o(it(l,"Missing prompt runner")),Yt(t.id,r);U(e,l);let c=Nt(a);try{await i(l,c,u=>{o(at(l,u))})}catch(u){let f=Vt(u);return o(it(l,f)),Gt(t.id,f,r)}return{responses:[{jsonrpc:"2.0",id:t.id,result:{stopReason:"end_turn"}}],notifications:r}}default:return{responses:[{jsonrpc:"2.0",id:t.id,error:{code:-32601,message:"Method not found"}}],notifications:r}}},nt=t=>P(t)?t:null,P=t=>typeof t=="object"&&t!==null,_t=t=>typeof t=="function"?t:P(t)&&typeof t.runPrompt=="function"?t.runPrompt:null,Nt=t=>t===null?"":(Array.isArray(t.prompt)?t.prompt:[]).map(Lt).join(""),at=(t,e)=>({jsonrpc:"2.0",method:"session/update",params:{sessionId:t,update:{sessionUpdate:"agent_message_chunk",content:{type:"text",text:e}}}}),Lt=t=>{let e=It(t);return e?e.type==="text"&&typeof e.text=="string"?e.text:e.type==="resource_link"?rt($t(e)):e.type==="resource"&&P(e.resource)?rt(Ut(e.resource)):"":""},It=t=>P(t)?typeof t.type=="string"?t:P(t.content)&&typeof t.content.type=="string"?t.content:null:null,rt=t=>{let e=typeof t=="string"&&t.length>0?t:null;return e===null||!Dt(e)?"":` ${`@${zt(e)}`}`},$t=t=>{let e=lt(t.uri);if(e!==null)return e;let s=ot(t.name);return s!==null?s:ot(t.title)},Ut=t=>lt(t.uri),lt=t=>{if(typeof t!="string"||t.length===0)return null;if(!t.includes("://"))return t;try{let e=new URL(t);if(e.protocol!=="file:")return null;let s=decodeURIComponent(e.pathname),n=/^\/[A-Za-z]:\//.test(s)?s.slice(1):s;return jt(n)}catch{return null}},ot=t=>typeof t!="string"||t.length===0?null:t,jt=t=>{let e=process.cwd(),s=j.relative(e,t);return!s.startsWith("..")&&!j.isAbsolute(s)?j.normalize(s):t},Dt=t=>t.startsWith("./")||t.startsWith("../")||t.startsWith("/")||t.includes("/")?!0:/^[A-Za-z0-9][A-Za-z0-9._-]*\.[A-Za-z0-9._-]+$/.test(t),zt=t=>/\s/.test(t)?`"${t}"`:t,Bt=t=>t===null?1:typeof t.protocolVersion=="number"?t.protocolVersion:1,Kt=()=>{let t=process.env.NOEMA_SESSION_PERSISTENCE;if(t===void 0||t.length===0)return!0;let e=t.toLowerCase();return!(e==="0"||e==="false"||e==="disabled"||e==="off")},Jt=t=>({responses:[{jsonrpc:"2.0",id:t,error:{code:-32602,message:"Invalid params"}}],notifications:[]}),Yt=(t,e)=>({responses:[{jsonrpc:"2.0",id:t,error:{code:-32e3,message:"Missing prompt runner"}}],notifications:e}),Gt=(t,e,s)=>({responses:[{jsonrpc:"2.0",id:t,error:{code:-32e3,message:e}}],notifications:s}),Vt=t=>t instanceof Error?t.message:typeof t=="string"&&t.length>0?t:"Provider error",it=(t,e)=>at(t,`Error: ${e}`);import{createInterface as Ht}from"readline";var z=(t=process.stdin,e=process.stdout,s=$(),n)=>{Ht({input:t}).on("line",o=>{Zt(o,e,s,n)})},Zt=async(t,e,s,n)=>{let r=t.trim();if(r.length===0)return;let o;try{o=JSON.parse(r)}catch{R(e,{jsonrpc:"2.0",id:null,error:{code:-32700,message:"Parse error"}});return}if(!Xt(o)){ct(o)&&R(e,{jsonrpc:"2.0",id:o.id??null,error:{code:-32600,message:"Invalid Request"}});return}let{responses:a,notifications:l}=await D(o,s,n,i=>{R(e,i)});for(let i of l)R(e,i);for(let i of a)R(e,i)},R=(t,e)=>{t.write(`${JSON.stringify(e)}
2
- `)},Xt=t=>!ut(t)||t.jsonrpc!=="2.0"||typeof t.method!="string"?!1:ct(t),ct=t=>ut(t)?typeof t.id=="string"||typeof t.id=="number"||t.id===null:!1,ut=t=>typeof t=="object"&&t!==null;import m from"path";var qt=`{
1
+ import B from"path";var D=()=>({nextSessionId:1,sessions:new Map}),at=t=>{let e=`session-${t.nextSessionId}`;return t.nextSessionId+=1,e},z=(t,e)=>{t.sessions.has(e)||t.sessions.set(e,{id:e})};var q=async t=>{let{request:e,state:n,runner:s,notify:r,signal:o,options:a}=t,i=[],u=r??(c=>i.push(c));switch(e.method){case"initialize":{let c=lt(e.params),l=Yt(c);return{responses:[{jsonrpc:"2.0",id:e.id,result:{protocolVersion:l,agentCapabilities:{loadSession:Ht(),mcpCapabilities:{http:!1,sse:!1},promptCapabilities:{audio:!1,embeddedContext:!0,image:!1},sessionCapabilities:{}},agentInfo:{name:"noema-cli",title:"Noema",version:a?.agentVersion??"0.0.1"},authMethods:[]}}],notifications:i}}case"session/new":{let c=at(n);return z(n,c),{responses:[{jsonrpc:"2.0",id:e.id,result:{sessionId:c}}],notifications:i}}case"session/prompt":{let c=lt(e.params),l=typeof c?.sessionId=="string"?c.sessionId:null;if(l===null)return Gt(e.id);let f=$t(s);if(f===null)return u(ft(l,"Missing prompt runner")),Xt(e.id,i);z(n,l);let p=jt(c);try{await f(l,p,d=>{u(K(l,d))},o)}catch(d){if(Qt(d))return u(K(l,`
2
+
3
+ [Cancelled]`)),{responses:[{jsonrpc:"2.0",id:e.id,result:{stopReason:"cancelled"}}],notifications:i};let m=te(d);return u(ft(l,m)),Zt(e.id,m,i)}return{responses:[{jsonrpc:"2.0",id:e.id,result:{stopReason:"end_turn"}}],notifications:i}}default:return{responses:[{jsonrpc:"2.0",id:e.id,error:{code:-32601,message:"Method not found"}}],notifications:i}}},lt=t=>S(t)?t:null,S=t=>typeof t=="object"&&t!==null,$t=t=>typeof t=="function"?t:S(t)&&typeof t.runPrompt=="function"?t.runPrompt:null,jt=t=>t===null?"":(Array.isArray(t.prompt)?t.prompt:[]).map(Dt).join(""),K=(t,e)=>({jsonrpc:"2.0",method:"session/update",params:{sessionId:t,update:{sessionUpdate:"agent_message_chunk",content:{type:"text",text:e}}}}),Dt=t=>{let e=zt(t);return e?e.type==="text"&&typeof e.text=="string"?e.text:e.type==="resource_link"?ct(Bt(e)):e.type==="resource"&&S(e.resource)?ct(Kt(e.resource)):"":""},zt=t=>S(t)?typeof t.type=="string"?t:S(t.content)&&typeof t.content.type=="string"?t.content:null:null,ct=t=>{let e=typeof t=="string"&&t.length>0?t:null;return e===null||!Jt(e)?"":` ${`@${Vt(e)}`}`},Bt=t=>{let e=pt(t.uri);if(e!==null)return e;let n=ut(t.name);return n!==null?n:ut(t.title)},Kt=t=>pt(t.uri),pt=t=>{if(typeof t!="string"||t.length===0)return null;if(!t.includes("://"))return t;try{let e=new URL(t);if(e.protocol!=="file:")return null;let n=decodeURIComponent(e.pathname),s=/^\/[A-Za-z]:\//.test(n)?n.slice(1):n;return qt(s)}catch{return null}},ut=t=>typeof t!="string"||t.length===0?null:t,qt=t=>{let e=process.cwd(),n=B.relative(e,t);return!n.startsWith("..")&&!B.isAbsolute(n)?B.normalize(n):t},Jt=t=>t.startsWith("./")||t.startsWith("../")||t.startsWith("/")||t.includes("/")?!0:/^[A-Za-z0-9][A-Za-z0-9._-]*\.[A-Za-z0-9._-]+$/.test(t),Vt=t=>/\s/.test(t)?`"${t}"`:t,Yt=t=>t===null?1:typeof t.protocolVersion=="number"?t.protocolVersion:1,Ht=()=>{let t=process.env.NOEMA_SESSION_PERSISTENCE;if(t===void 0||t.length===0)return!0;let e=t.toLowerCase();return!(e==="0"||e==="false"||e==="disabled"||e==="off")},Gt=t=>({responses:[{jsonrpc:"2.0",id:t,error:{code:-32602,message:"Invalid params"}}],notifications:[]}),Xt=(t,e)=>({responses:[{jsonrpc:"2.0",id:t,error:{code:-32e3,message:"Missing prompt runner"}}],notifications:e}),Zt=(t,e,n)=>({responses:[{jsonrpc:"2.0",id:t,error:{code:-32e3,message:e}}],notifications:n}),Qt=t=>t instanceof DOMException&&t.name==="AbortError",te=t=>{let e=ee(t);if(e==="ENOTFOUND")return"Could not reach the LLM provider. Check your internet connection.";if(e==="ECONNREFUSED")return"LLM provider is not accepting connections. Try again later.";if(t instanceof Error){let n=ne(t.message);if(n!==null){if(n===401||n===403)return"Authentication failed. Check your OPENROUTER_API_KEY.";if(n===429)return"Rate limited by the LLM provider. Try again in a moment.";if(n>=500&&n<600)return`LLM provider error (${n}). Try again later.`}return t.message}return typeof t=="string"&&t.length>0?t:"Provider error"},ee=t=>{if(!(t instanceof Error))return null;let{cause:e}=t;return S(e)&&typeof e.code=="string"?e.code:null},ne=t=>{let e=t.match(/^OpenRouter error: (\d+)/);return e===null?null:parseInt(e[1],10)},ft=(t,e)=>K(t,`Error: ${e}`);import{createInterface as se}from"readline";var J=(t=process.stdin,e=process.stdout,n=D(),s,r)=>{let o=se({input:t}),a=new Map;o.on("line",i=>{re(i,e,n,s,a,r)})},re=async(t,e,n,s,r,o)=>{let a=t.trim();if(a.length===0)return;let i;try{i=JSON.parse(a)}catch{R(e,{jsonrpc:"2.0",id:null,error:{code:-32700,message:"Parse error"}});return}if(ce(i)){oe(i,r);return}if(!le(i)){dt(i)&&R(e,{jsonrpc:"2.0",id:i.id??null,error:{code:-32600,message:"Invalid Request"}});return}let u=i.method==="session/prompt"?ie(r,i.id):void 0,{responses:c,notifications:l}=await q({request:i,state:n,runner:s,notify:f=>{R(e,f)},signal:u?.signal,options:o});u&&ae(r,i.id);for(let f of l)R(e,f);for(let f of c)R(e,f)},oe=(t,e)=>{if(t.method!=="$/cancelRequest")return;let n=v(t.params)?t.params:null;if(n===null)return;let s=n.id;if(typeof s!="string"&&typeof s!="number")return;let r=e.get(s);r&&(r.abort(),e.delete(s))},ie=(t,e)=>{if(typeof e!="string"&&typeof e!="number")return;let n=new AbortController;return t.set(e,n),n},ae=(t,e)=>{(typeof e=="string"||typeof e=="number")&&t.delete(e)},R=(t,e)=>{t.write(`${JSON.stringify(e)}
4
+ `)},le=t=>!v(t)||t.jsonrpc!=="2.0"||typeof t.method!="string"?!1:dt(t),ce=t=>!v(t)||t.jsonrpc!=="2.0"||typeof t.method!="string"?!1:!("id"in t),dt=t=>v(t)?typeof t.id=="string"||typeof t.id=="number"||t.id===null:!1,v=t=>typeof t=="object"&&t!==null;import g from"path";var ue=`{
3
5
  "defaultWorkflow": "draft",
4
6
  "workflows": [
5
7
  { "id": "draft", "description": "Draft new content" }
6
8
  ]
7
- }`,Qt=t=>t?`Workflow catalog is missing or invalid. Configure workflows in ${t}.
9
+ }`,fe=t=>t?`Workflow catalog is missing or invalid. Configure workflows in ${t}.
8
10
  Example:
9
- ${qt}`:"Workflow catalog is missing or invalid.",F=class{deps;sessions=new Map;constructor(e){this.deps=e}setWorkflowCatalog(e){this.deps.workflowCatalog=e}async runPrompt(e,s,n){let r=this.ensureSession(e);if(!oe(this.deps.workflowCatalog))return this.respondWithText(r,s,n,Qt(this.deps.workflowCatalogPath));if(r.pending)return this.handlePending(e,r.pending,s,n);let o=await this.deps.workflowClassifier.classify(s,this.deps.workflowCatalog);return this.handlePrompt({onDelta:n,pathSelectionPrompt:s,prompt:s,sessionId:e,workflowId:o})}getLastInteraction(e){return this.sessions.get(e)??null}getSessionSnapshot(e){let s=this.sessions.get(e);if(!s)return null;let{lastPrompt:n,lastResponse:r,pending:o}=s;return o?{lastPrompt:n,lastResponse:r,pending:o}:{lastPrompt:n,lastResponse:r}}loadSessionSnapshot(e,s){this.sessions.set(e,{lastPrompt:s.lastPrompt,lastResponse:s.lastResponse,pending:s.pending})}async handlePending(e,s,n,r){switch(s.type){case"paths":return this.handlePrompt({onDelta:r,pathSelectionPrompt:n,prompt:s.prompt,sessionId:e,workflowId:s.workflowId});case"outsideRoot":return await this.isOutsideRootAllowed(n,s.outsideRootPaths,s.writeIntent.allowMissingTargets)?(this.clearPending(e),this.processParsedPaths({onDelta:r,pathReferences:s.pathReferences,prompt:s.prompt,sessionId:e,workflowId:s.workflowId,writeIntent:s.writeIntent,writePathReferences:s.writePathReferences})):this.respondWithText(this.ensureSession(e),n,r,dt(s.outsideRootPaths));case"writePath":return this.handlePrompt({onDelta:r,pathSelectionPrompt:n,prompt:s.prompt,pathReferencesOverride:s.pathReferences,sessionId:e,workflowId:s.workflowId,writeIntentOverride:s.writeIntent});case"overwrite":{if(!await this.isOverwriteAllowed(n,s.writeTargets))return this.respondWithText(this.ensureSession(e),n,r,ht(s.writeTargets));this.clearPending(e);let a={...s.writeIntent,overwrite:!0};return this.processParsedPaths({onDelta:r,pathReferences:s.pathReferences,prompt:s.prompt,sessionId:e,workflowId:s.workflowId,writeIntent:a,writePathReferences:s.pathReferences,writeTargetsOverride:s.writeTargets})}}}async handlePrompt({onDelta:e,pathSelectionPrompt:s,prompt:n,pathReferencesOverride:r,sessionId:o,workflowId:a,writeIntentOverride:l,writePathReferenceOverrides:i}){let c=l??ie(n,a),u=r===void 0?await this.parseExistingPaths(s):{outsideRootPaths:[],pathReferences:r},f=i??(c.allowMissingTargets?await this.parseAllPathCandidates(s):u.pathReferences),p=ae([u.outsideRootPaths,f]);if(p.length>0)return this.setPending(o,{outsideRootPaths:p,pathReferences:u.pathReferences,prompt:n,type:"outsideRoot",workflowId:a,writeIntent:c,writePathReferences:f}),this.respondWithText(this.ensureSession(o),n,e,dt(p));if(c.enabled){let d=await this.resolveWriteTargets(f,c.allowMissingTargets);if(d.length===0)return this.setPending(o,{pathReferences:u.pathReferences,prompt:n,type:"writePath",workflowId:a,writeIntent:c}),this.respondWithText(this.ensureSession(o),n,e,"Which file should I write to?");if(d.length>1)return this.respondWithText(this.ensureSession(o),n,e,"Please provide a single target file path.");let h=d[0];return!c.overwrite&&await this.deps.fileAccess.statPath(h)==="file"?(this.setPending(o,{pathReferences:u.pathReferences,prompt:n,type:"overwrite",workflowId:a,writeIntent:c,writeTargets:d}),this.respondWithText(this.ensureSession(o),n,e,ht(d))):this.processParsedPaths({onDelta:e,pathReferences:u.pathReferences,prompt:n,sessionId:o,workflowId:a,writeIntent:c,writePathReferences:f,writeTargetsOverride:d})}return u.pathReferences.length===0&&f.length===0?(this.setPending(o,{prompt:n,type:"paths",workflowId:a}),this.respondWithText(this.ensureSession(o),n,e,"Which files/folders should I use?")):this.processParsedPaths({onDelta:e,pathReferences:u.pathReferences,prompt:n,sessionId:o,workflowId:a,writeIntent:c,writePathReferences:f})}async processParsedPaths({onDelta:e,pathReferences:s,prompt:n,sessionId:r,workflowId:o,writeIntent:a,writePathReferences:l,writeTargetsOverride:i}){this.clearPending(r);let c=await this.expandContextPaths(s),u=await re({fileAccess:this.deps.fileAccess,options:this.deps.contextOptions,paths:c}),f=le({context:u,prompt:n,workflowId:o}),p=await this.deps.runner.runPrompt(r,f,e),d=i??(a.enabled?await this.resolveWriteTargets(l,a.allowMissingTargets):[]),h=a.enabled&&d.length===1?[{path:d[0],content:p.responseText}]:void 0;return this.recordResponse(r,n,p.responseText,h)}async parseExistingPaths(e){return M({pathExists:this.createPathExists(!1),prompt:e,root:this.deps.root})}async parseAllPathCandidates(e){let s=await M({pathExists:this.createPathExists(!0),prompt:e,root:this.deps.root});return s.pathReferences.concat(s.outsideRootPaths)}createPathExists(e){return async s=>{if(e)return!0;let n=fe(s,this.deps.root);return await this.deps.fileAccess.statPath(n)!==null}}async expandContextPaths(e){let s=[],n=new Set;for(let r of e){let o=await this.deps.fileAccess.statPath(r.path);if(o==="file"){n.has(r.path)||(n.add(r.path),s.push(r.path));continue}if(o!=="dir")continue;let a=await this.deps.fileAccess.listFiles(r.path);for(let l of a){let i=m.normalize(m.join(r.path,l));n.has(i)||(n.add(i),s.push(i))}}return s}async resolveWriteTargets(e,s){let n=[];for(let r of e){let o=await this.deps.fileAccess.statPath(r.path);if(o==="file"){n.push(r.path);continue}o===null&&s&&n.push(r.path)}return n}respondWithText(e,s,n,r){return n(r),e.lastPrompt=s,e.lastResponse=r,{responseText:r}}recordResponse(e,s,n,r){let o=this.ensureSession(e);return o.lastPrompt=s,o.lastResponse=n,r?{responseText:n,writes:r}:{responseText:n}}ensureSession(e){let s=this.sessions.get(e);if(s)return s;let n={lastPrompt:"",lastResponse:""};return this.sessions.set(e,n),n}setPending(e,s){let n=this.ensureSession(e);n.pending=s}clearPending(e){let s=this.ensureSession(e);delete s.pending}async isOutsideRootAllowed(e,s,n){if(!ce(e))return!1;let o=(await M({pathExists:this.createPathExists(n),prompt:e,root:this.deps.root})).outsideRootPaths.map(a=>a.absolutePath.toLowerCase());return s.every(a=>o.includes(a.absolutePath.toLowerCase()))}async isOverwriteAllowed(e,s){if(!ue(e))return!1;let n=await M({pathExists:this.createPathExists(!0),prompt:e,root:this.deps.root}),r=n.pathReferences.concat(n.outsideRootPaths).map(o=>o.path.toLowerCase());return s.every(o=>r.includes(o.toLowerCase()))}},te=1e6,ee=2*1024*1024,ft="[[SKIPPED: unreadable]]",se="[[SKIPPED: file exceeds size limit]]",pt="[[SKIPPED: context size limit reached]]",ne="[[TRUNCATED: context size limit reached]]",M=async({pathExists:t,prompt:e,root:s})=>{let n=pe(e),r=new Map,o=new Map;for(let a of n){let l=de(a);if(l===null||!me(l.value))continue;let i=m.resolve(s,l.value);if(!await t(i))continue;let c=m.relative(s,i),u=c.startsWith("..")||m.isAbsolute(c),f=u?i:m.normalize(c),p={absolutePath:i,isOutsideRoot:u,path:f,source:l.source};if(u){o.has(i)||o.set(i,p);continue}r.has(i)||r.set(i,p)}return{outsideRootPaths:Array.from(o.values()),pathReferences:Array.from(r.values())}},re=async({fileAccess:t,paths:e,options:s})=>{let n=s?.maxTotalBytes??te,r=s?.maxFileBytes??ee,o=[],a=[],l=0,i=!1;for(let c of e){let u=await t.fileSize(c);if(u===null){o.push({path:c,content:ft}),a.push(c);continue}if(u>r){o.push({path:c,content:se}),a.push(c);continue}if(l>=n){o.push({path:c,content:pt}),a.push(c),i=!0;continue}let f=await t.readTextFile(c);if(f===null){o.push({path:c,content:ft}),a.push(c);continue}let p=Buffer.byteLength(f);if(l+p<=n){o.push({path:c,content:f}),l+=p;continue}let d=n-l;if(d<=0){o.push({path:c,content:pt}),a.push(c),i=!0;continue}let h=ge(f,d);o.push({path:c,content:`${h}
10
- ${ne}`}),l=n,i=!0}return{entries:o,skippedPaths:a,totalBytes:l,truncated:i}},oe=t=>!t||typeof t.defaultWorkflow!="string"||t.defaultWorkflow.length===0||!Array.isArray(t.workflows)||t.workflows.length===0?!1:t.workflows.some(e=>e.id===t.defaultWorkflow),ie=(t,e)=>{let s=/\b(write|save|overwrite|replace|update|create)\b/i.test(t),n=/\b(overwrite|replace)\b/i.test(t);return{allowMissingTargets:s,enabled:s||e==="rewrite",overwrite:n}},ae=t=>{let e=new Map;for(let s of t)for(let n of s)n.isOutsideRoot&&(e.has(n.absolutePath)||e.set(n.absolutePath,n));return Array.from(e.values())},le=({context:t,prompt:e,workflowId:s})=>{let n=t.entries.length===0?"(none)":t.entries.map(r=>`[[FILE: ${r.path}]]
11
+ ${ue}`:"Workflow catalog is missing or invalid.",M=class{deps;sessions=new Map;constructor(e){this.deps=e}setWorkflowCatalog(e){this.deps.workflowCatalog=e}async runPrompt(e,n,s,r){let o=this.ensureSession(e);if(!we(this.deps.workflowCatalog))return this.respondWithText(o,n,s,fe(this.deps.workflowCatalogPath));if(o.pending)return this.handlePending(e,o.pending,n,s,r);let a=await this.deps.workflowClassifier.classify(n,this.deps.workflowCatalog);return this.handlePrompt({onDelta:s,pathSelectionPrompt:n,prompt:n,sessionId:e,signal:r,workflowId:a})}getLastInteraction(e){return this.sessions.get(e)??null}getSessionSnapshot(e){let n=this.sessions.get(e);if(!n)return null;let{lastPrompt:s,lastResponse:r,pending:o}=n;return o?{lastPrompt:s,lastResponse:r,pending:o}:{lastPrompt:s,lastResponse:r}}loadSessionSnapshot(e,n){this.sessions.set(e,{lastPrompt:n.lastPrompt,lastResponse:n.lastResponse,pending:n.pending})}async handlePending(e,n,s,r,o){switch(n.type){case"paths":return this.handlePrompt({onDelta:r,pathSelectionPrompt:s,prompt:n.prompt,sessionId:e,signal:o,workflowId:n.workflowId});case"outsideRoot":return await this.isOutsideRootAllowed(s,n.outsideRootPaths,n.writeIntent.allowMissingTargets)?(this.clearPending(e),this.processParsedPaths({onDelta:r,pathReferences:n.pathReferences,prompt:n.prompt,sessionId:e,signal:o,workflowId:n.workflowId,writeIntent:n.writeIntent,writePathReferences:n.writePathReferences})):this.respondWithText(this.ensureSession(e),s,r,ht(n.outsideRootPaths));case"writePath":return this.handlePrompt({onDelta:r,pathSelectionPrompt:s,prompt:n.prompt,pathReferencesOverride:n.pathReferences,sessionId:e,signal:o,workflowId:n.workflowId,writeIntentOverride:n.writeIntent});case"overwrite":{if(!await this.isOverwriteAllowed(s,n.writeTargets))return this.respondWithText(this.ensureSession(e),s,r,wt(n.writeTargets));this.clearPending(e);let i={...n.writeIntent,overwrite:!0};return this.processParsedPaths({onDelta:r,pathReferences:n.pathReferences,prompt:n.prompt,sessionId:e,signal:o,workflowId:n.workflowId,writeIntent:i,writePathReferences:n.pathReferences,writeTargetsOverride:n.writeTargets})}}}async handlePrompt({onDelta:e,pathSelectionPrompt:n,prompt:s,pathReferencesOverride:r,sessionId:o,signal:a,workflowId:i,writeIntentOverride:u,writePathReferenceOverrides:c}){let l=u??ye(s,i),f=r===void 0?await this.parseExistingPaths(n):{outsideRootPaths:[],pathReferences:r},p=c??(l.allowMissingTargets?await this.parseAllPathCandidates(n):f.pathReferences),d=Se([f.outsideRootPaths,p]);if(d.length>0)return this.setPending(o,{outsideRootPaths:d,pathReferences:f.pathReferences,prompt:s,type:"outsideRoot",workflowId:i,writeIntent:l,writePathReferences:p}),this.respondWithText(this.ensureSession(o),s,e,ht(d));if(l.enabled){let m=await this.resolveWriteTargets(p,l.allowMissingTargets);if(m.length===0)return this.setPending(o,{pathReferences:f.pathReferences,prompt:s,type:"writePath",workflowId:i,writeIntent:l}),this.respondWithText(this.ensureSession(o),s,e,"Which file should I write to?");if(m.length>1)return this.respondWithText(this.ensureSession(o),s,e,"Please provide a single target file path.");let P=m[0];return!l.overwrite&&await this.deps.fileAccess.statPath(P)==="file"?(this.setPending(o,{pathReferences:f.pathReferences,prompt:s,type:"overwrite",workflowId:i,writeIntent:l,writeTargets:m}),this.respondWithText(this.ensureSession(o),s,e,wt(m))):this.processParsedPaths({onDelta:e,pathReferences:f.pathReferences,prompt:s,sessionId:o,signal:a,workflowId:i,writeIntent:l,writePathReferences:p,writeTargetsOverride:m})}return f.pathReferences.length===0&&p.length===0?(this.setPending(o,{prompt:s,type:"paths",workflowId:i}),this.respondWithText(this.ensureSession(o),s,e,"Which files/folders should I use?")):this.processParsedPaths({onDelta:e,pathReferences:f.pathReferences,prompt:s,sessionId:o,signal:a,workflowId:i,writeIntent:l,writePathReferences:p})}async processParsedPaths({onDelta:e,pathReferences:n,prompt:s,sessionId:r,signal:o,workflowId:a,writeIntent:i,writePathReferences:u,writeTargetsOverride:c}){this.clearPending(r);let l=await this.expandContextPaths(n),f=await he({fileAccess:this.deps.fileAccess,options:this.deps.contextOptions,paths:l}),p=Pe({context:f,prompt:s,workflowId:a}),d=await this.deps.runner.runPrompt(r,p,e,o),m=c??(i.enabled?await this.resolveWriteTargets(u,i.allowMissingTargets):[]),P=i.enabled&&m.length===1?[{path:m[0],content:d.responseText}]:void 0;return this.recordResponse(r,s,d.responseText,P,d.usage)}async parseExistingPaths(e){return U({pathExists:this.createPathExists(!1),prompt:e,root:this.deps.root})}async parseAllPathCandidates(e){let n=await U({pathExists:this.createPathExists(!0),prompt:e,root:this.deps.root});return n.pathReferences.concat(n.outsideRootPaths)}createPathExists(e){return async n=>{if(e)return!0;let s=xe(n,this.deps.root);return await this.deps.fileAccess.statPath(s)!==null}}async expandContextPaths(e){let n=[],s=new Set;for(let r of e){let o=await this.deps.fileAccess.statPath(r.path);if(o==="file"){s.has(r.path)||(s.add(r.path),n.push(r.path));continue}if(o!=="dir")continue;let a=await this.deps.fileAccess.listFiles(r.path);for(let i of a){let u=g.normalize(g.join(r.path,i));s.has(u)||(s.add(u),n.push(u))}}return n}async resolveWriteTargets(e,n){let s=[];for(let r of e){let o=await this.deps.fileAccess.statPath(r.path);if(o==="file"){s.push(r.path);continue}o===null&&n&&s.push(r.path)}return s}respondWithText(e,n,s,r){return s(r),e.lastPrompt=n,e.lastResponse=r,{responseText:r}}recordResponse(e,n,s,r,o){let a=this.ensureSession(e);a.lastPrompt=n,a.lastResponse=s;let i={responseText:s};return o&&(i.usage=o),r&&(i.writes=r),i}ensureSession(e){let n=this.sessions.get(e);if(n)return n;let s={lastPrompt:"",lastResponse:""};return this.sessions.set(e,s),s}setPending(e,n){let s=this.ensureSession(e);s.pending=n}clearPending(e){let n=this.ensureSession(e);delete n.pending}async isOutsideRootAllowed(e,n,s){if(!Re(e))return!1;let o=(await U({pathExists:this.createPathExists(s),prompt:e,root:this.deps.root})).outsideRootPaths.map(a=>a.absolutePath.toLowerCase());return n.every(a=>o.includes(a.absolutePath.toLowerCase()))}async isOverwriteAllowed(e,n){if(!ke(e))return!1;let s=await U({pathExists:this.createPathExists(!0),prompt:e,root:this.deps.root}),r=s.pathReferences.concat(s.outsideRootPaths).map(o=>o.path.toLowerCase());return n.every(o=>r.includes(o.toLowerCase()))}},pe=1e6,de=2*1024*1024,mt="[[SKIPPED: unreadable]]",me="[[SKIPPED: file exceeds size limit]]",gt="[[SKIPPED: context size limit reached]]",ge="[[TRUNCATED: context size limit reached]]",U=async({pathExists:t,prompt:e,root:n})=>{let s=Ae(e),r=new Map,o=new Map;for(let a of s){let i=Ce(a);if(i===null||!be(i.value))continue;let u=g.resolve(n,i.value);if(!await t(u))continue;let c=g.relative(n,u),l=c.startsWith("..")||g.isAbsolute(c),f=l?u:g.normalize(c),p={absolutePath:u,isOutsideRoot:l,path:f,source:i.source};if(l){o.has(u)||o.set(u,p);continue}r.has(u)||r.set(u,p)}return{outsideRootPaths:Array.from(o.values()),pathReferences:Array.from(r.values())}},he=async({fileAccess:t,paths:e,options:n})=>{let s=n?.maxTotalBytes??pe,r=n?.maxFileBytes??de,o=[],a=[],i=0,u=!1;for(let c of e){let l=await t.fileSize(c);if(l===null){o.push({path:c,content:mt}),a.push(c);continue}if(l>r){o.push({path:c,content:me}),a.push(c);continue}if(i>=s){o.push({path:c,content:gt}),a.push(c),u=!0;continue}let f=await t.readTextFile(c);if(f===null){o.push({path:c,content:mt}),a.push(c);continue}let p=Buffer.byteLength(f);if(i+p<=s){o.push({path:c,content:f}),i+=p;continue}let d=s-i;if(d<=0){o.push({path:c,content:gt}),a.push(c),u=!0;continue}let m=Te(f,d);o.push({path:c,content:`${m}
12
+ ${ge}`}),i=s,u=!0}return{entries:o,skippedPaths:a,totalBytes:i,truncated:u}},we=t=>!t||typeof t.defaultWorkflow!="string"||t.defaultWorkflow.length===0||!Array.isArray(t.workflows)||t.workflows.length===0?!1:t.workflows.some(e=>e.id===t.defaultWorkflow),ye=(t,e)=>{let n=/\b(write|save|overwrite|replace|update|create)\b/i.test(t),s=/\b(overwrite|replace)\b/i.test(t);return{allowMissingTargets:n,enabled:n||e==="rewrite",overwrite:s}},Se=t=>{let e=new Map;for(let n of t)for(let s of n)s.isOutsideRoot&&(e.has(s.absolutePath)||e.set(s.absolutePath,s));return Array.from(e.values())},Pe=({context:t,prompt:e,workflowId:n})=>{let s=t.entries.length===0?"(none)":t.entries.map(r=>`[[FILE: ${r.path}]]
11
13
  ${r.content}`.trimEnd()).join(`
12
14
 
13
- `);return`Workflow: ${s}
15
+ `);return`Workflow: ${n}
14
16
 
15
17
  Context:
16
- ${n}
18
+ ${s}
17
19
 
18
20
  User:
19
- ${e}`},dt=t=>{let e=gt(t.map(s=>s.path));return`You requested paths outside the project root: ${e}. Reply with "allow ${e}" to proceed.`},ht=t=>{let e=gt(t);return`The following files already exist: ${e}. Reply with "overwrite ${e}" to proceed.`},ce=t=>/\b(allow|yes)\b/i.test(t),ue=t=>/\b(overwrite|yes)\b/i.test(t),gt=t=>t.map(e=>`"${e}"`).join(", "),fe=(t,e)=>{let s=m.relative(e,t);return!s.startsWith("..")&&!m.isAbsolute(s)?m.normalize(s):t},pe=t=>t.match(/@"[^"]*"|@'[^']*'|@`[^`]*`|"[^"]*"|'[^']*'|`[^`]*`|\S+/g)??[],de=t=>{if(t.length===0||t.includes("://"))return null;let e=he(t);if(e!==null)return e;let s=mt(t);if(s.length===0||s.includes("://"))return null;if(s.startsWith("@")){let n=mt(s.slice(1));return n.length===0?null:{source:"@",value:n}}return{source:"plain",value:s}},he=t=>{let e="plain",s=t;if(s.startsWith("@")&&(e="@",s=s.slice(1)),s.length<2)return null;let n=s[0];if((n==='"'||n==="'"||n==="`")&&s.endsWith(n)){let r=s.slice(1,-1);return r.length===0||r.includes("://")?null:{source:e,value:r}}return null},mt=t=>t.replace(/^[\s"'([{<]+/,"").replace(/[\s"')\]}>.,;:!?]+$/,""),me=t=>t.startsWith("./")||t.startsWith("../")||t.startsWith("/")||t.includes("/")?!0:/^[A-Za-z0-9][A-Za-z0-9._-]*\.[A-Za-z0-9._-]+$/.test(t),ge=(t,e)=>{let s=Buffer.from(t,"utf8");return s.length<=e?t:s.subarray(0,e).toString("utf8")};import{readFileSync as ds,promises as q}from"fs";import hs from"os";import Ft from"path";import{promises as v}from"fs";import B from"path";import{TextDecoder as we}from"util";var yt=2*1024*1024,wt=6,K=8*1024,ye=new Set([".git","node_modules"]),x=class{async statPath(e){let s=await O(e);return s===null||s.isSymbolicLink()?null:s.isFile()?"file":s.isDirectory()?"dir":null}async listFiles(e){let s=[];return await this.walk(e,e,0,s),s}async readTextFile(e){let s=await O(e);if(s===null||!s.isFile()||s.isSymbolicLink()||s.size>yt)return null;let n=await Re(e);return n===null||St(n.subarray(0,K))?null:Pt(n)}async fileSize(e){let s=await O(e);return s===null||!s.isFile()||s.isSymbolicLink()?null:s.size}async walk(e,s,n,r){if(n>wt)return;let o=await xe(s);if(o!==null){o.sort((a,l)=>a.name.localeCompare(l.name));for(let a of o){if(ye.has(a.name))continue;let l=B.join(s,a.name);if(a.isSymbolicLink())continue;if(a.isDirectory()){if(n>=wt)continue;await this.walk(e,l,n+1,r);continue}if(!a.isFile()||!await Se(l))continue;let i=B.relative(e,l);i.length!==0&&r.push(B.normalize(i))}}}},Se=async t=>{let e=await O(t);if(e===null||!e.isFile()||e.isSymbolicLink()||e.size>yt)return!1;let s=await Pe(t);return s===null?!1:!St(s)},St=t=>t.includes(0)?!0:Pt(t)===null,Pt=t=>{try{return new we("utf-8",{fatal:!0}).decode(t)}catch{return null}},Pe=async t=>{let e=await Ce(t);if(e===null)return null;try{let s=Buffer.alloc(K),{bytesRead:n}=await e.read(s,0,K,0);return s.subarray(0,n)}catch{return null}finally{await e.close()}},Re=async t=>{try{return await v.readFile(t)}catch{return null}},xe=async t=>{try{return await v.readdir(t,{withFileTypes:!0})}catch{return null}},O=async t=>{try{return await v.lstat(t)}catch{return null}},Ce=async t=>{try{return await v.open(t,"r")}catch{return null}};import{Agent as We}from"@mariozechner/pi-agent-core";import{getModel as be}from"@mariozechner/pi-ai";import{AssistantMessageEventStream as Te}from"@mariozechner/pi-ai/dist/utils/event-stream.js";var N=class{config;baseUrl;constructor(e){this.config=e,this.baseUrl=e.baseUrl??"https://openrouter.ai/api/v1"}async*streamPrompt(e){let s=[{role:"user",content:e}];yield*this.streamChat({messages:s})}async*streamChat({messages:e,model:s,signal:n}){let r=await fetch(`${this.baseUrl}/chat/completions`,{method:"POST",headers:{Authorization:`Bearer ${this.config.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify({model:s??this.config.model,messages:e,stream:!0}),signal:n});if(!r.ok)throw new Error(`OpenRouter error: ${r.status} ${r.statusText}`);if(r.body===null){let i=await r.json(),c=ke(i);c.length>0&&(yield c);return}let o=r.body.getReader(),a=new TextDecoder,l="";for(;;){let i=await o.read();if(i.done)break;l+=a.decode(i.value,{stream:!0});let c=l.split(`
20
- `);l=c.pop()??"";for(let u of c){let f=Ae(u);f!==null&&(yield f)}}}},Ae=t=>{let e=t.trim();if(e.length===0||!e.startsWith("data:"))return null;let s=e.slice(5).trim();if(s==="[DONE]")return null;try{let n=JSON.parse(s),r=n?.choices?.[0]?.delta?.content??n?.choices?.[0]?.message?.content;return typeof r=="string"&&r.length>0?r:null}catch{return null}},ke=t=>{let e=Ee(t),n=(Array.isArray(e?.choices)?e.choices:[])[0];if(!_(n))return"";let r=_(n.message)?n.message:null,o=_(n.delta)?n.delta:null,a=r?.content??o?.content;return typeof a=="string"?a:""},Ee=t=>_(t)?t:null,_=t=>typeof t=="object"&&t!==null;var C=class{constructor(e,s){this.apiKey=e;this.model=s;this.streamFn=Fe(this.apiKey),this.modelDefinition=Me(this.model)}agents=new Map;streamFn;modelDefinition;async runPrompt(e,s,n){let r=this.getAgent(e),o="",a="",l=r.subscribe(i=>{i.type==="message_update"&&i.assistantMessageEvent.type==="text_delta"&&(o+=i.assistantMessageEvent.delta,n(i.assistantMessageEvent.delta)),i.type==="message_end"&&i.message.role==="assistant"&&(a=xt(i.message.content))});return await r.prompt(s),l(),o.length===0&&a.length>0&&(o=a,n(o)),{responseText:o}}getAgent(e){let s=this.agents.get(e);return s||(s=new We({initialState:{model:this.modelDefinition},sessionId:e,streamFn:this.streamFn}),this.agents.set(e,s)),s.sessionId=e,s.setModel(this.modelDefinition),s}},Me=t=>{let e=be("openrouter",t);if(!e)throw new Error(`Unknown OpenRouter model: ${t}`);return e},Fe=t=>(s,n,r)=>{let o=new Te;return Oe({apiKey:r?.apiKey??t,context:n,model:s,signal:r?.signal,stream:o}),o},Oe=async({apiKey:t,context:e,model:s,signal:n,stream:r})=>{let o=ve(e),a=new N({apiKey:t,baseUrl:s.baseUrl,model:s.id}),l=Rt(s),i="",c=!1;try{r.push({type:"start",partial:l});for await(let u of a.streamChat({messages:o,model:s.id,signal:n}))c||(l.content.push({type:"text",text:""}),r.push({type:"text_start",contentIndex:0,partial:l}),c=!0),i+=u,l.content[0].text=i,r.push({type:"text_delta",contentIndex:0,delta:u,partial:l});c&&r.push({type:"text_end",contentIndex:0,content:i,partial:l}),r.push({type:"done",reason:"stop",message:l})}catch(u){let f=u instanceof Error?u.message:String(u),p=Rt(s,{content:[{type:"text",text:f}],errorMessage:f,stopReason:"error"});r.push({type:"error",reason:"error",error:p})}},ve=t=>{let e=[];t.systemPrompt&&t.systemPrompt.trim().length>0&&e.push({role:"system",content:t.systemPrompt});for(let s of t.messages){let n=xt(s.content);if(s.role==="toolResult"){if(n.length===0)continue;e.push({role:"tool",content:n,tool_call_id:s.toolCallId});continue}n.length!==0&&e.push({role:s.role,content:n})}return e},xt=t=>typeof t=="string"?t:Array.isArray(t)?t.map(e=>e.type==="text"?e.text:"").join(""):"",Rt=(t,e)=>({role:"assistant",content:[],api:t.api,provider:t.provider,model:t.id,usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"stop",timestamp:Date.now(),...e});var A=class{async classify(e,s){let n=e.toLowerCase();for(let r of s.workflows)if(_e(n,r))return r.id;return s.defaultWorkflow}},_e=(t,e)=>{let s=e.id.trim().toLowerCase();if(s.length>0&&Ne(t,s))return!0;if(e.description){let n=e.description.toLowerCase().trim();if(n.length>0&&t.includes(n))return!0}return!1},Ne=(t,e)=>{let s=Le(e);return new RegExp(`\\b${s}\\b`,"i").test(t)},Le=t=>t.replace(/[.*+?^${}()|[\\]\\]/g,"\\$&");import{promises as k}from"fs";import Ie from"os";import Ct from"path";var At={defaultWorkflow:"draft",workflows:[{id:"draft",description:"Draft new content"}]},$e={defaultWorkflow:"",workflows:[]},E=()=>Ct.join(Ie.homedir(),".noema","config.json"),Ue=E(),J="~/.noema/config.json",kt=async(t=E())=>{let e=await Y(t);return e===null?$e:{defaultWorkflow:e.defaultWorkflow,workflows:e.workflows}},Y=async(t=E())=>{try{let e=await k.readFile(t,"utf8"),s=JSON.parse(e);return Je(s)}catch(e){return De(e)?(await je(t),At):null}},je=async t=>{let e=Ct.dirname(t);await k.mkdir(e,{recursive:!0});let s=`${JSON.stringify(At,null,2)}
21
- `;await k.writeFile(t,s,"utf8")},De=t=>typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT",ze=(t=E())=>{let e=null,s=null;return async()=>{let n=await k.stat(t).catch(()=>null),r=n?n.mtimeMs:null;return(e===null||s!==r)&&(e=await kt(t),s=r),e}},Et=(t=E())=>{let e=null,s=null;return async()=>{let n=await k.stat(t).catch(()=>null),r=n?n.mtimeMs:null;return(e===null||s!==r)&&(e=await Y(t),s=r),e}},Be=t=>{if(!t||typeof t!="object")return!1;let e=t;return typeof e.defaultWorkflow!="string"||!Array.isArray(e.workflows)?!1:e.workflows.every(s=>Ke(s))},Ke=t=>{if(!t||typeof t!="object")return!1;let e=t;return!(typeof e.id!="string"||e.id.length===0||e.description!==void 0&&typeof e.description!="string")},Je=t=>{if(!Be(t))return null;let e=t,s={defaultWorkflow:e.defaultWorkflow,workflows:e.workflows},n=typeof e.provider=="string"?e.provider.trim():"";n.length>0&&(s.provider=n);let r=typeof e.model=="string"?e.model.trim():"";r.length>0&&(s.model=r);let o=Ye(e.sessionPersistence);return o&&(s.sessionPersistence=o),s},Ye=t=>{if(!Ge(t))return null;let e=t,s=typeof e.enabled=="boolean"?e.enabled:void 0,n=typeof e.path=="string"&&e.path.trim().length>0?e.path.trim():void 0;if(s===void 0&&n===void 0)return null;let r={};return s!==void 0&&(r.enabled=s),n!==void 0&&(r.path=n),r},Ge=t=>typeof t=="object"&&t!==null;import{promises as L}from"fs";import Ve from"os";import y from"path";var He="sessions.json",V=async(t,e)=>{if(!(ls()??t?.sessionPersistence?.enabled??!0))return null;let r=Xe(t?.sessionPersistence?.path),o=await Ze(e),l=(await qe(r)).sessions;return{listByRoot:(i,c)=>Object.entries(l).filter(([u,f])=>f.root===i&&f.lastUpdated.length>0).sort((u,f)=>f[1].lastUpdated.localeCompare(u[1].lastUpdated)).slice(0,c).map(([u,f])=>({id:u,lastUpdated:f.lastUpdated})),load:i=>l[i]?.snapshot??null,loadForRoot:(i,c)=>{let u=l[i];return!u||u.root!==c||u.lastUpdated.length===0?null:u.snapshot},save:async(i,c)=>{l[i]={lastUpdated:new Date().toISOString(),root:o,snapshot:c},await Qe(r,{version:1,sessions:l})}}},Ze=async t=>{try{return await L.realpath(t)}catch{return t}},Xe=t=>{let e=y.join(Ve.homedir(),".noema"),s=y.join(e,He);if(!t||t.length===0)return s;let n=y.isAbsolute(t)?y.resolve(t):y.resolve(e,t);return n.startsWith(`${e}${y.sep}`)?n:s},qe=async t=>{try{let e=await L.readFile(t,"utf8"),s=JSON.parse(e);return ts(s)?ns(s):{version:1,sessions:{}}}catch{return{version:1,sessions:{}}}},Qe=async(t,e)=>{await L.mkdir(y.dirname(t),{recursive:!0});let s=`${JSON.stringify(e,null,2)}
22
- `;await L.writeFile(t,s,"utf8")},ts=t=>!(!g(t)||t.version!==1||!g(t.sessions)),g=t=>typeof t=="object"&&t!==null,es=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/,ss=t=>{if(!es.test(t))return!1;try{return new Date(t).toISOString()===t}catch{return!1}},ns=t=>{let e={};for(let[s,n]of Object.entries(t.sessions)){let r=rs(n);r&&(e[s]=r)}return{version:1,sessions:e}},rs=t=>{if(!g(t))return null;if(typeof t.root=="string"&&typeof t.lastUpdated=="string"&&g(t.snapshot)){if(!ss(t.lastUpdated))return null;let s=Wt(t.snapshot);return s?{lastUpdated:t.lastUpdated,root:t.root,snapshot:s}:null}let e=Wt(t);return e?{lastUpdated:"",root:"",snapshot:e}:null},Wt=t=>{if(!g(t)||typeof t.lastPrompt!="string"||typeof t.lastResponse!="string")return null;let e={lastPrompt:t.lastPrompt,lastResponse:t.lastResponse};return t.pending===void 0||!os(t.pending)?e:{...e,pending:t.pending}},os=t=>{if(!g(t)||typeof t.type!="string"||typeof t.prompt!="string"||typeof t.workflowId!="string")return!1;switch(t.type){case"paths":return!0;case"outsideRoot":return W(t.outsideRootPaths)&&W(t.pathReferences)&&W(t.writePathReferences)&&G(t.writeIntent);case"writePath":return W(t.pathReferences)&&G(t.writeIntent);case"overwrite":return W(t.pathReferences)&&G(t.writeIntent)&&as(t.writeTargets);default:return!1}},W=t=>Array.isArray(t)?t.every(is):!1,is=t=>!(!g(t)||typeof t.absolutePath!="string"||typeof t.path!="string"||typeof t.isOutsideRoot!="boolean"||t.source!=="plain"&&t.source!=="@"),G=t=>g(t)?typeof t.allowMissingTargets=="boolean"&&typeof t.enabled=="boolean"&&typeof t.overwrite=="boolean":!1,as=t=>Array.isArray(t)?t.every(e=>typeof e=="string"):!1,ls=()=>{let t=process.env.NOEMA_SESSION_PERSISTENCE;if(t===void 0||t.length===0)return null;let e=t.toLowerCase();return!(e==="0"||e==="false"||e==="disabled"||e==="off")};var H=(t,e)=>{let s=t.trim();if(s==="/sessions")return cs(e);if(s==="/resume"||s.startsWith("/resume ")){let n=s.slice(7).trim();return us(e,n)}return null},cs=t=>{if(t.sessionStore===null)return{responseText:"Session persistence is disabled."};let e=t.sessionStore.listByRoot(t.root,20);return e.length===0?{responseText:"No sessions found for current worktree."}:{responseText:bt(e,new Date)}},us=(t,e)=>{if(t.sessionStore===null)return{responseText:"Session persistence is disabled."};if(e.length===0)return{responseText:"Usage: /resume <id>"};let s=t.sessionStore.loadForRoot(e,t.root);return s===null?{responseText:`Session "${e}" not found. Run /sessions to see available sessions.`}:{responseText:`Session "${e}" restored.`,resume:{sessionId:e,snapshot:s}}},bt=(t,e)=>t.filter(s=>!Number.isNaN(new Date(s.lastUpdated).getTime())).map(s=>{let n=new Date(s.lastUpdated),r=fs(n),o=ps(n,e);return`${s.id} \u2014 ${r} (${o})`}).join(`
23
- `),fs=t=>{let e=t.getFullYear(),s=String(t.getMonth()+1).padStart(2,"0"),n=String(t.getDate()).padStart(2,"0"),r=String(t.getHours()).padStart(2,"0"),o=String(t.getMinutes()).padStart(2,"0"),a=String(t.getSeconds()).padStart(2,"0");return`${e}-${s}-${n} ${r}:${o}:${a}`},ps=(t,e)=>{let s=e.getTime()-t.getTime();if(s<0)return"just now";let n=Math.floor(s/1e3);if(n<60)return"just now";let r=Math.floor(n/60);if(r<60)return r===1?"1 minute ago":`${r} minutes ago`;let o=Math.floor(r/60);if(o<24)return o===1?"1 hour ago":`${o} hours ago`;let a=Math.floor(o/24);if(a<30)return a===1?"1 day ago":`${a} days ago`;let l=Math.floor(a/30);if(l<12)return l===1?"1 month ago":`${l} months ago`;let i=Math.floor(a/365);return i===1?"1 year ago":`${i} years ago`};import{promises as Tt}from"fs";import Z from"path";var X=async(t,e)=>{for(let s of e){let n=Mt(t,s.path);await Tt.mkdir(Z.dirname(n),{recursive:!0}),await Tt.writeFile(n,s.content,"utf8")}},Mt=(t,e)=>Z.isAbsolute(e)?e:Z.join(t,e);var Ot=(t,e=process.argv.slice(2),s=process.stdout,n)=>{if(e.includes("--version")||e.includes("-v")){s.write(`${ws()}
24
- `);return}t(process.stdin,s,ms(n))},ms=t=>{let e=null,s=null,n=new Set,r=new Map,o=process.cwd(),a=Et(),l=q.realpath(o).catch(()=>o);return async(i,c,u)=>{try{let f=await a();s===null&&(s=V(f,o));let p=await s,d=await l,h=H(c,{sessionStore:p,root:d});if(h)return h.resume&&p&&(r.set(i,h.resume.snapshot),n.delete(i),await p.save(h.resume.sessionId,h.resume.snapshot)),u(h.responseText),{responseText:h.responseText};let b=process.env.OPENROUTER_API_KEY;if(b===void 0||b.length===0)throw new Error("OPENROUTER_API_KEY is required");let vt=f?.model,I=process.env.OPENROUTER_MODEL??vt;if(I===void 0||I.length===0)throw new Error("OPENROUTER_MODEL is required");let Q=f?{defaultWorkflow:f.defaultWorkflow,workflows:f.workflows}:{defaultWorkflow:"",workflows:[]};e===null&&(e=(t??gs)({apiKey:b,model:I,root:o,workflowCatalog:Q,workflowCatalogPath:J}));let S=await e,tt=r.get(i);if(tt)S.loadSessionSnapshot(i,tt),n.add(i),r.delete(i);else if(p&&!n.has(i)){let w=p.load(i);w&&S.loadSessionSnapshot(i,w),n.add(i)}S.setWorkflowCatalog(Q);let et=async()=>{if(!p)return;let w=S.getSessionSnapshot(i);w&&await p.save(i,w)},T;try{T=await S.runPrompt(i,c,u),T.writes&&await X(o,T.writes)}catch(w){try{await et()}catch{}throw w}return await et(),T}catch(f){throw await ys(f),f}}},gs=async({apiKey:t,model:e,root:s,workflowCatalog:n,workflowCatalogPath:r})=>{let o=new x,a=new A,l=new C(t,e);return new F({fileAccess:o,root:s,runner:l,workflowCatalog:n,workflowCatalogPath:r,workflowClassifier:a})},ws=()=>{try{let t=new URL("../package.json",import.meta.url),e=ds(t,"utf8"),s=JSON.parse(e);return typeof s?.version=="string"?s.version:"unknown"}catch{return"unknown"}},ys=async t=>{let e=t instanceof Error?t.stack??t.message:String(t),s=`[${new Date().toISOString()}] ${e}
25
- `,n=Ft.join(hs.homedir(),".noema","error.log");try{await q.mkdir(Ft.dirname(n),{recursive:!0}),await q.appendFile(n,s)}catch{}};var Sn="0.0.1";var Ss=(t=process.argv.slice(2),e=process.stdout)=>{Ot((s,n,r)=>{z(s,n,void 0,r)},t,e)};import.meta.url===`file://${process.argv[1]}`&&Ss();export{J as DEFAULT_CONFIG_DISPLAY_PATH,Ue as DEFAULT_CONFIG_PATH,x as NodeProjectFileAccess,C as PiAdapter,A as SimpleWorkflowClassifier,Sn as cliVersion,V as createSessionStore,ze as createWorkflowCatalogLoader,bt as formatSessionList,H as handleSlashCommand,Y as loadNoemaConfig,kt as loadWorkflowCatalog,Mt as resolveWritePath,Ss as runCli,Ot as runMain,X as writeFiles};
21
+ ${e}`},ht=t=>{let e=St(t.map(n=>n.path));return`You requested paths outside the project root: ${e}. Reply with "allow ${e}" to proceed.`},wt=t=>{let e=St(t);return`The following files already exist: ${e}. Reply with "overwrite ${e}" to proceed.`},Re=t=>/\b(allow|yes)\b/i.test(t),ke=t=>/\b(overwrite|yes)\b/i.test(t),St=t=>t.map(e=>`"${e}"`).join(", "),xe=(t,e)=>{let n=g.relative(e,t);return!n.startsWith("..")&&!g.isAbsolute(n)?g.normalize(n):t},Ae=t=>t.match(/@"[^"]*"|@'[^']*'|@`[^`]*`|"[^"]*"|'[^']*'|`[^`]*`|\S+/g)??[],Ce=t=>{if(t.length===0||t.includes("://"))return null;let e=Ee(t);if(e!==null)return e;let n=yt(t);if(n.length===0||n.includes("://"))return null;if(n.startsWith("@")){let s=yt(n.slice(1));return s.length===0?null:{source:"@",value:s}}return{source:"plain",value:n}},Ee=t=>{let e="plain",n=t;if(n.startsWith("@")&&(e="@",n=n.slice(1)),n.length<2)return null;let s=n[0];if((s==='"'||s==="'"||s==="`")&&n.endsWith(s)){let r=n.slice(1,-1);return r.length===0||r.includes("://")?null:{source:e,value:r}}return null},yt=t=>t.replace(/^[\s"'([{<]+/,"").replace(/[\s"')\]}>.,;:!?]+$/,""),be=t=>t.startsWith("./")||t.startsWith("../")||t.startsWith("/")||t.includes("/")?!0:/^[A-Za-z0-9][A-Za-z0-9._-]*\.[A-Za-z0-9._-]+$/.test(t),Te=(t,e)=>{let n=Buffer.from(t,"utf8");return n.length<=e?t:n.subarray(0,e).toString("utf8")};import{readFileSync as Tn,promises as T}from"fs";import Ft from"os";import I from"path";import{promises as _}from"fs";import V from"path";import{TextDecoder as We}from"util";var Rt=2*1024*1024,Pt=6,Y=8*1024,ve=new Set([".git","node_modules"]),k=class{async statPath(e){let n=await O(e);return n===null||n.isSymbolicLink()?null:n.isFile()?"file":n.isDirectory()?"dir":null}async listFiles(e){let n=[];return await this.walk(e,e,0,n),n}async readTextFile(e){let n=await O(e);if(n===null||!n.isFile()||n.isSymbolicLink()||n.size>Rt)return null;let s=await Oe(e);return s===null||kt(s.subarray(0,Y))?null:xt(s)}async fileSize(e){let n=await O(e);return n===null||!n.isFile()||n.isSymbolicLink()?null:n.size}async walk(e,n,s,r){if(s>Pt)return;let o=await _e(n);if(o!==null){o.sort((a,i)=>a.name.localeCompare(i.name));for(let a of o){if(ve.has(a.name))continue;let i=V.join(n,a.name);if(a.isSymbolicLink())continue;if(a.isDirectory()){if(s>=Pt)continue;await this.walk(e,i,s+1,r);continue}if(!a.isFile()||!await Ue(i))continue;let u=V.relative(e,i);u.length!==0&&r.push(V.normalize(u))}}}},Ue=async t=>{let e=await O(t);if(e===null||!e.isFile()||e.isSymbolicLink()||e.size>Rt)return!1;let n=await Me(t);return n===null?!1:!kt(n)},kt=t=>t.includes(0)?!0:xt(t)===null,xt=t=>{try{return new We("utf-8",{fatal:!0}).decode(t)}catch{return null}},Me=async t=>{let e=await Fe(t);if(e===null)return null;try{let n=Buffer.alloc(Y),{bytesRead:s}=await e.read(n,0,Y,0);return n.subarray(0,s)}catch{return null}finally{await e.close()}},Oe=async t=>{try{return await _.readFile(t)}catch{return null}},_e=async t=>{try{return await _.readdir(t,{withFileTypes:!0})}catch{return null}},O=async t=>{try{return await _.lstat(t)}catch{return null}},Fe=async t=>{try{return await _.open(t,"r")}catch{return null}};import{Agent as je}from"@mariozechner/pi-agent-core";import{getModel as De}from"@mariozechner/pi-ai";import{AssistantMessageEventStream as ze}from"@mariozechner/pi-ai/dist/utils/event-stream.js";var N=class{config;baseUrl;_lastUsage=null;get lastUsage(){return this._lastUsage}constructor(e){this.config=e,this.baseUrl=e.baseUrl??"https://openrouter.ai/api/v1"}async*streamPrompt(e){let n=[{role:"user",content:e}];yield*this.streamChat({messages:n})}async*streamChat({messages:e,model:n,signal:s}){this._lastUsage=null;let r=await fetch(`${this.baseUrl}/chat/completions`,{method:"POST",headers:{Authorization:`Bearer ${this.config.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify({model:n??this.config.model,messages:e,stream:!0}),signal:s}),o=Ie(r.headers);if(!r.ok)throw new Error(`OpenRouter error: ${r.status} ${r.statusText}`);if(r.body===null){let c=await r.json(),l=$e(c);this._lastUsage=At(c)??o,l.length>0&&(yield l);return}let a=r.body.getReader(),i=new TextDecoder,u="";for(;;){let c=await a.read();if(c.done)break;u+=i.decode(c.value,{stream:!0});let l=u.split(`
22
+ `);u=l.pop()??"";for(let f of l){let p=Le(f);p!==null&&(this._lastUsage=p);let d=Ne(f);d!==null&&(yield d)}}this._lastUsage===null&&(this._lastUsage=o)}},Ne=t=>{let e=t.trim();if(e.length===0||!e.startsWith("data:"))return null;let n=e.slice(5).trim();if(n==="[DONE]")return null;try{let s=JSON.parse(n),r=s?.choices?.[0]?.delta?.content??s?.choices?.[0]?.message?.content;return typeof r=="string"&&r.length>0?r:null}catch{return null}},Le=t=>{let e=t.trim();if(e.length===0||!e.startsWith("data:"))return null;let n=e.slice(5).trim();if(n==="[DONE]")return null;try{let s=JSON.parse(n);return At(s)}catch{return null}},At=t=>{let e=G(t);if(e===null)return null;let n=G(e.usage);if(n===null)return null;let s=typeof n.prompt_tokens=="number"?n.prompt_tokens:0,r=typeof n.completion_tokens=="number"?n.completion_tokens:0,o=typeof n.total_tokens=="number"?n.total_tokens:s+r;return{input:s,output:r,total:o}},Ie=t=>{if(t==null||typeof t.get!="function")return null;let e=H(t,["x-openrouter-prompt-tokens","x-openrouter-input-tokens"]),n=H(t,["x-openrouter-completion-tokens","x-openrouter-output-tokens"]),s=H(t,["x-openrouter-total-tokens","x-openrouter-tokens"]);if(e===null&&n===null&&s===null)return null;let r=e??0,o=n??0;return{input:r,output:o,total:s??r+o}},H=(t,e)=>{for(let n of e){let s=t.get(n);if(s===null||s.length===0)continue;let r=Number(s);if(Number.isFinite(r)&&r>=0)return r}return null},$e=t=>{let e=G(t),s=(Array.isArray(e?.choices)?e.choices:[])[0];if(!F(s))return"";let r=F(s.message)?s.message:null,o=F(s.delta)?s.delta:null,a=r?.content??o?.content;return typeof a=="string"?a:""},G=t=>F(t)?t:null,F=t=>typeof t=="object"&&t!==null;var x=class{constructor(e,n){this.apiKey=e;this.model=n;this.streamFn=Ke(this.apiKey,()=>this.externalSignal,s=>{this.capturedUsage=s},s=>{this.capturedError=s}),this.modelDefinition=Be(this.model)}agents=new Map;streamFn;modelDefinition;isPromptRunning=!1;externalSignal;capturedError=null;capturedUsage=null;async runPrompt(e,n,s,r){if(this.isPromptRunning)throw new Error("Concurrent runPrompt calls are not supported.");this.isPromptRunning=!0,this.externalSignal=r,this.capturedUsage=null,this.capturedError=null;let o="",a="",i=null;try{let u=this.getAgent(e);i=u.subscribe(l=>{l.type==="message_update"&&l.assistantMessageEvent.type==="text_delta"&&(o+=l.assistantMessageEvent.delta,s(l.assistantMessageEvent.delta)),l.type==="message_end"&&l.message.role==="assistant"&&(a=Et(l.message.content))});try{await u.prompt(n)}catch{if(!this.capturedError)throw new Error("Unknown prompt error")}if(this.capturedError){let l=this.capturedError;throw this.capturedError=null,l instanceof Error&&this.capturedUsage&&(l.usage=this.capturedUsage),l}o.length===0&&a.length>0&&(o=a,s(o));let c=this.capturedUsage??void 0;return c?{responseText:o,usage:c}:{responseText:o}}finally{i?.(),this.isPromptRunning=!1,this.externalSignal=void 0}}getAgent(e){let n=this.agents.get(e);return n||(n=new je({initialState:{model:this.modelDefinition},sessionId:e,streamFn:this.streamFn}),this.agents.set(e,n)),n.sessionId=e,n.setModel(this.modelDefinition),n}},Be=t=>{let e=De("openrouter",t);if(!e)throw new Error(`Unknown OpenRouter model: ${t}`);return e},Ke=(t,e,n,s)=>(o,a,i)=>{let u=new ze,c=[i?.signal,e()].filter(f=>f!==void 0),l=c.length>0?AbortSignal.any(c):void 0;return qe({apiKey:i?.apiKey??t,context:a,model:o,onError:s,onUsage:n,signal:l,stream:u}),u},qe=async({apiKey:t,context:e,model:n,onError:s,onUsage:r,signal:o,stream:a})=>{let i=Je(e),u=new N({apiKey:t,baseUrl:n.baseUrl,model:n.id}),c=Ct(n),l="",f=!1;try{a.push({type:"start",partial:c});for await(let p of u.streamChat({messages:i,model:n.id,signal:o}))f||(c.content.push({type:"text",text:""}),a.push({type:"text_start",contentIndex:0,partial:c}),f=!0),l+=p,c.content[0].text=l,a.push({type:"text_delta",contentIndex:0,delta:p,partial:c});f&&a.push({type:"text_end",contentIndex:0,content:l,partial:c}),a.push({type:"done",reason:"stop",message:c})}catch(p){s(p);let d=p instanceof Error?p.message:String(p),m=Ct(n,{content:[{type:"text",text:d}],errorMessage:d,stopReason:"error"});a.push({type:"error",reason:"error",error:m})}finally{u.lastUsage!==null&&r(u.lastUsage)}},Je=t=>{let e=[];t.systemPrompt&&t.systemPrompt.trim().length>0&&e.push({role:"system",content:t.systemPrompt});for(let n of t.messages){let s=Et(n.content);if(n.role==="toolResult"){if(s.length===0)continue;e.push({role:"tool",content:s,tool_call_id:n.toolCallId});continue}s.length!==0&&e.push({role:n.role,content:s})}return e},Et=t=>typeof t=="string"?t:Array.isArray(t)?t.map(e=>e.type==="text"?e.text:"").join(""):"",Ct=(t,e)=>({role:"assistant",content:[],api:t.api,provider:t.provider,model:t.id,usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"stop",timestamp:Date.now(),...e});var A=class{async classify(e,n){let s=e.toLowerCase();for(let r of n.workflows)if(Ve(s,r))return r.id;return n.defaultWorkflow}},Ve=(t,e)=>{let n=e.id.trim().toLowerCase();if(n.length>0&&Ye(t,n))return!0;if(e.description){let s=e.description.toLowerCase().trim();if(s.length>0&&t.includes(s))return!0}return!1},Ye=(t,e)=>{let n=He(e);return new RegExp(`\\b${n}\\b`,"i").test(t)},He=t=>t.replace(/[.*+?^${}()|[\\]\\]/g,"\\$&");import{promises as C}from"fs";import Ge from"os";import bt from"path";var Tt={defaultWorkflow:"draft",workflows:[{id:"draft",description:"Draft new content"}]},Xe={defaultWorkflow:"",workflows:[]},E=()=>bt.join(Ge.homedir(),".noema","config.json"),Ze=E(),X="~/.noema/config.json",Wt=async(t=E())=>{let e=await Z(t);return e===null?Xe:{defaultWorkflow:e.defaultWorkflow,workflows:e.workflows}},Z=async(t=E())=>{try{let e=await C.readFile(t,"utf8"),n=JSON.parse(e);return rn(n)}catch(e){return tn(e)?(await Qe(t),Tt):null}},Qe=async t=>{let e=bt.dirname(t);await C.mkdir(e,{recursive:!0});let n=`${JSON.stringify(Tt,null,2)}
23
+ `;await C.writeFile(t,n,"utf8")},tn=t=>typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT",en=(t=E())=>{let e=null,n=null;return async()=>{let s=await C.stat(t).catch(()=>null),r=s?s.mtimeMs:null;return(e===null||n!==r)&&(e=await Wt(t),n=r),e}},vt=(t=E())=>{let e=null,n=null;return async()=>{let s=await C.stat(t).catch(()=>null),r=s?s.mtimeMs:null;return(e===null||n!==r)&&(e=await Z(t),n=r),e}},nn=t=>{if(!t||typeof t!="object")return!1;let e=t;return typeof e.defaultWorkflow!="string"||!Array.isArray(e.workflows)?!1:e.workflows.every(n=>sn(n))},sn=t=>{if(!t||typeof t!="object")return!1;let e=t;return!(typeof e.id!="string"||e.id.length===0||e.description!==void 0&&typeof e.description!="string")},rn=t=>{if(!nn(t))return null;let e=t,n={defaultWorkflow:e.defaultWorkflow,workflows:e.workflows},s=typeof e.provider=="string"?e.provider.trim():"";s.length>0&&(n.provider=s);let r=typeof e.model=="string"?e.model.trim():"";r.length>0&&(n.model=r);let o=on(e.sessionPersistence);o&&(n.sessionPersistence=o),typeof e.showTokenUsage=="boolean"&&(n.showTokenUsage=e.showTokenUsage);let a=typeof e.timeout=="number"&&Number.isFinite(e.timeout)&&e.timeout>0?e.timeout:void 0;return a!==void 0&&(n.timeout=a),n},on=t=>{if(!an(t))return null;let e=t,n=typeof e.enabled=="boolean"?e.enabled:void 0,s=typeof e.path=="string"&&e.path.trim().length>0?e.path.trim():void 0;if(n===void 0&&s===void 0)return null;let r={};return n!==void 0&&(r.enabled=n),s!==void 0&&(r.path=s),r},an=t=>typeof t=="object"&&t!==null;import{promises as L}from"fs";import ln from"os";import w from"path";var cn="sessions.json",tt=async(t,e)=>{if(!(kn()??t?.sessionPersistence?.enabled??!0))return null;let r=fn(t?.sessionPersistence?.path),o=await un(e),i=(await pn(r)).sessions;return{listByRoot:(u,c)=>Object.entries(i).filter(([l,f])=>f.root===u&&f.lastUpdated.length>0).sort((l,f)=>f[1].lastUpdated.localeCompare(l[1].lastUpdated)).slice(0,c).map(([l,f])=>({id:l,lastUpdated:f.lastUpdated})),load:u=>i[u]?.snapshot??null,loadForRoot:(u,c)=>{let l=i[u];return!l||l.root!==c||l.lastUpdated.length===0?null:l.snapshot},save:async(u,c)=>{i[u]={lastUpdated:new Date().toISOString(),root:o,snapshot:c},await dn(r,{version:1,sessions:i})}}},un=async t=>{try{return await L.realpath(t)}catch{return t}},fn=t=>{let e=w.join(ln.homedir(),".noema"),n=w.join(e,cn);if(!t||t.length===0)return n;let s=w.isAbsolute(t)?w.resolve(t):w.resolve(e,t);return s.startsWith(`${e}${w.sep}`)?s:n},pn=async t=>{try{let e=await L.readFile(t,"utf8"),n=JSON.parse(e);return mn(n)?wn(n):{version:1,sessions:{}}}catch{return{version:1,sessions:{}}}},dn=async(t,e)=>{await L.mkdir(w.dirname(t),{recursive:!0});let n=`${JSON.stringify(e,null,2)}
24
+ `;await L.writeFile(t,n,"utf8")},mn=t=>!(!h(t)||t.version!==1||!h(t.sessions)),h=t=>typeof t=="object"&&t!==null,gn=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/,hn=t=>{if(!gn.test(t))return!1;try{return new Date(t).toISOString()===t}catch{return!1}},wn=t=>{let e={};for(let[n,s]of Object.entries(t.sessions)){let r=yn(s);r&&(e[n]=r)}return{version:1,sessions:e}},yn=t=>{if(!h(t))return null;if(typeof t.root=="string"&&typeof t.lastUpdated=="string"&&h(t.snapshot)){if(!hn(t.lastUpdated))return null;let n=Ut(t.snapshot);return n?{lastUpdated:t.lastUpdated,root:t.root,snapshot:n}:null}let e=Ut(t);return e?{lastUpdated:"",root:"",snapshot:e}:null},Ut=t=>{if(!h(t)||typeof t.lastPrompt!="string"||typeof t.lastResponse!="string")return null;let e={lastPrompt:t.lastPrompt,lastResponse:t.lastResponse};return t.pending===void 0||!Sn(t.pending)?e:{...e,pending:t.pending}},Sn=t=>{if(!h(t)||typeof t.type!="string"||typeof t.prompt!="string"||typeof t.workflowId!="string")return!1;switch(t.type){case"paths":return!0;case"outsideRoot":return b(t.outsideRootPaths)&&b(t.pathReferences)&&b(t.writePathReferences)&&Q(t.writeIntent);case"writePath":return b(t.pathReferences)&&Q(t.writeIntent);case"overwrite":return b(t.pathReferences)&&Q(t.writeIntent)&&Rn(t.writeTargets);default:return!1}},b=t=>Array.isArray(t)?t.every(Pn):!1,Pn=t=>!(!h(t)||typeof t.absolutePath!="string"||typeof t.path!="string"||typeof t.isOutsideRoot!="boolean"||t.source!=="plain"&&t.source!=="@"),Q=t=>h(t)?typeof t.allowMissingTargets=="boolean"&&typeof t.enabled=="boolean"&&typeof t.overwrite=="boolean":!1,Rn=t=>Array.isArray(t)?t.every(e=>typeof e=="string"):!1,kn=()=>{let t=process.env.NOEMA_SESSION_PERSISTENCE;if(t===void 0||t.length===0)return null;let e=t.toLowerCase();return!(e==="0"||e==="false"||e==="disabled"||e==="off")};var et=(t,e)=>{let n=t.trim();if(n==="/help")return xn();if(n==="/sessions")return An(e);if(n==="/resume"||n.startsWith("/resume ")){let s=n.slice(7).trim();return Cn(e,s)}return null},xn=()=>({responseText:["Commands:"," /help \u2014 Show this help message"," /sessions \u2014 List recent sessions for the current worktree"," /resume <id> \u2014 Resume a previous session by ID","","Setup:"," OPENROUTER_API_KEY \u2014 Set this environment variable to your API key"," ~/.noema/config.json \u2014 Optional config (model, workflows, timeout)"].join(`
25
+ `)}),An=t=>{if(t.sessionStore===null)return{responseText:"Session persistence is disabled."};let e=t.sessionStore.listByRoot(t.root,20);return e.length===0?{responseText:"No sessions found for current worktree."}:{responseText:Mt(e,new Date)}},Cn=(t,e)=>{if(t.sessionStore===null)return{responseText:"Session persistence is disabled."};if(e.length===0)return{responseText:"Usage: /resume <id>"};let n=t.sessionStore.loadForRoot(e,t.root);return n===null?{responseText:`Session "${e}" not found. Run /sessions to see available sessions.`}:{responseText:`Session "${e}" restored.`,resume:{sessionId:e,snapshot:n}}},Mt=(t,e)=>t.filter(n=>!Number.isNaN(new Date(n.lastUpdated).getTime())).map(n=>{let s=new Date(n.lastUpdated),r=En(s),o=bn(s,e);return`${n.id} \u2014 ${r} (${o})`}).join(`
26
+ `),En=t=>{let e=t.getFullYear(),n=String(t.getMonth()+1).padStart(2,"0"),s=String(t.getDate()).padStart(2,"0"),r=String(t.getHours()).padStart(2,"0"),o=String(t.getMinutes()).padStart(2,"0"),a=String(t.getSeconds()).padStart(2,"0");return`${e}-${n}-${s} ${r}:${o}:${a}`},bn=(t,e)=>{let n=e.getTime()-t.getTime();if(n<0)return"just now";let s=Math.floor(n/1e3);if(s<60)return"just now";let r=Math.floor(s/60);if(r<60)return r===1?"1 minute ago":`${r} minutes ago`;let o=Math.floor(r/60);if(o<24)return o===1?"1 hour ago":`${o} hours ago`;let a=Math.floor(o/24);if(a<30)return a===1?"1 day ago":`${a} days ago`;let i=Math.floor(a/30);if(i<12)return i===1?"1 month ago":`${i} months ago`;let u=Math.floor(a/365);return u===1?"1 year ago":`${u} years ago`};import{promises as Ot}from"fs";import nt from"path";var st=async(t,e)=>{for(let n of e){let s=_t(t,n.path);await Ot.mkdir(nt.dirname(s),{recursive:!0}),await Ot.writeFile(s,n.content,"utf8")}},_t=(t,e)=>nt.isAbsolute(e)?e:nt.join(t,e);var Nt=(t,e=process.argv.slice(2),n=process.stdout,s)=>{if(e.includes("--version")||e.includes("-v")){n.write(`${rt()}
27
+ `);return}t(process.stdin,n,Wn(s))},Wn=t=>{let e=null,n=null,s=!1,r=new Set,o=new Map,a=process.cwd(),i=vt(),u=T.realpath(a).catch(()=>a);return async(c,l,f,p)=>{try{let d=await i();n===null&&(n=tt(d,a));let m=await n,P=await u,$=await vn({loadedSessions:r,onDelta:f,pendingResumeSnapshots:o,prompt:l,resolvedRoot:P,sessionId:c,sessionStore:m});if($)return $;s||(s=!0,f(Ln));let y=Un(d);e===null&&(e=(t??Nn)({apiKey:y.apiKey,model:y.model,root:a,workflowCatalog:y.workflowCatalog,workflowCatalogPath:X}));let W=await e;Mn({agent:W,loadedSessions:r,pendingResumeSnapshots:o,sessionId:c,sessionStore:m}),W.setWorkflowCatalog(y.workflowCatalog);let ot=async()=>{await On(m,W,c)},it;try{it=await _n({agent:W,onDelta:f,prompt:l,root:a,sessionId:c,signal:p,timeoutSeconds:y.timeoutSeconds})}catch(j){try{await ot()}catch{}let It=j instanceof Error?j.usage:void 0;throw await Lt(It),j}return await ot(),Fn({onDelta:f,result:it,showTokenUsage:y.showTokenUsage})}catch(d){throw await $n(d),d}}},vn=async({loadedSessions:t,onDelta:e,pendingResumeSnapshots:n,prompt:s,resolvedRoot:r,sessionId:o,sessionStore:a})=>{let i=et(s,{sessionStore:a,root:r});return i?(i.resume&&a&&(n.set(o,i.resume.snapshot),t.delete(o),await a.save(i.resume.sessionId,i.resume.snapshot)),e(i.responseText),{responseText:i.responseText}):null},Un=t=>{let e=process.env.OPENROUTER_API_KEY;if(e===void 0||e.length===0)throw new Error("OPENROUTER_API_KEY is required");let n=process.env.OPENROUTER_MODEL??t?.model;if(n===void 0||n.length===0)throw new Error("OPENROUTER_MODEL is required");let s=t?{defaultWorkflow:t.defaultWorkflow,workflows:t.workflows}:{defaultWorkflow:"",workflows:[]};return{apiKey:e,model:n,showTokenUsage:t?.showTokenUsage===!0,timeoutSeconds:In(t),workflowCatalog:s}},Mn=({agent:t,loadedSessions:e,pendingResumeSnapshots:n,sessionId:s,sessionStore:r})=>{let o=n.get(s);if(o){t.loadSessionSnapshot(s,o),e.add(s),n.delete(s);return}if(!r||e.has(s))return;let a=r.load(s);a&&t.loadSessionSnapshot(s,a),e.add(s)},On=async(t,e,n)=>{if(!t)return;let s=e.getSessionSnapshot(n);s&&await t.save(n,s)},_n=async({agent:t,onDelta:e,prompt:n,root:s,sessionId:r,signal:o,timeoutSeconds:a})=>{let i=new AbortController,u=setTimeout(()=>i.abort(),a*1e3);u.unref();let c=o?AbortSignal.any([o,i.signal]):i.signal;try{let l=await t.runPrompt(r,n,e,c);return l.writes&&await st(s,l.writes),l}catch(l){throw i.signal.aborted?new Error(`Request timed out after ${a} seconds.`):l}finally{clearTimeout(u)}},Fn=async({onDelta:t,result:e,showTokenUsage:n})=>{if(await Lt(e.usage),!n||!e.usage)return e;let s=`
28
+ [${e.usage.input} in / ${e.usage.output} out tokens]`;return t(s),{...e,responseText:e.responseText+s}},Nn=async({apiKey:t,model:e,root:n,workflowCatalog:s,workflowCatalogPath:r})=>{let o=new k,a=new A,i=new x(t,e);return new M({fileAccess:o,root:n,runner:i,workflowCatalog:s,workflowCatalogPath:r,workflowClassifier:a})},Ln=["Noema \u2014 an AI writing agent for structured documents.","Type /help for available commands and setup hints.","Setup: OPENROUTER_API_KEY and optional ~/.noema/config.json.",""].join(`
29
+ `),rt=()=>{try{let t=new URL("../package.json",import.meta.url),e=Tn(t,"utf8"),n=JSON.parse(e);return typeof n?.version=="string"?n.version:"unknown"}catch{return"unknown"}},In=t=>{let e=process.env.NOEMA_TIMEOUT;if(e!==void 0&&e.length>0){let n=Number(e);if(Number.isFinite(n)&&n>0)return n}return t!==null&&typeof t.timeout=="number"&&Number.isFinite(t.timeout)&&t.timeout>0?t.timeout:120},Lt=async t=>{let e=t?.input??"unknown",n=t?.output??"unknown",s=t?.total??"unknown",r=`[${new Date().toISOString()}] [info] tokens: ${e} in / ${n} out / ${s} total
30
+ `,o=I.join(Ft.homedir(),".noema","error.log");try{await T.mkdir(I.dirname(o),{recursive:!0}),await T.appendFile(o,r)}catch{}},$n=async t=>{let e=t instanceof Error?t.stack??t.message:String(t),n=`[${new Date().toISOString()}] ${e}
31
+ `,s=I.join(Ft.homedir(),".noema","error.log");try{await T.mkdir(I.dirname(s),{recursive:!0}),await T.appendFile(s,n)}catch{}};var jn=(t=process.argv.slice(2),e=process.stdout)=>{let n=rt();Nt((s,r,o)=>{J(s,r,void 0,o,{agentVersion:n})},t,e)};import.meta.url===`file://${process.argv[1]}`&&jn();export{X as DEFAULT_CONFIG_DISPLAY_PATH,Ze as DEFAULT_CONFIG_PATH,k as NodeProjectFileAccess,x as PiAdapter,A as SimpleWorkflowClassifier,tt as createSessionStore,en as createWorkflowCatalogLoader,Mt as formatSessionList,et as handleSlashCommand,Z as loadNoemaConfig,Wt as loadWorkflowCatalog,_t as resolveWritePath,jn as runCli,Nt as runMain,st as writeFiles};
26
32
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "noema-cli",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "bin": {