noema-cli 0.0.13 → 0.1.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
@@ -1,11 +1,44 @@
1
- type PromptRunner = (sessionId: string, prompt: string, onDelta: (delta: string) => void) => Promise<{
2
- responseText: string;
3
- }>;
1
+ import { PromptRunner as PromptRunner$1, ProjectFileAccess, WorkflowClassifier, WorkflowCatalog, WriteRequest } from '@noema/core';
2
+
3
+ type PromptRunner = PromptRunner$1["runPrompt"];
4
4
  type AdapterRunner = (input: NodeJS.ReadableStream, output: NodeJS.WritableStream, runner: PromptRunner) => void;
5
5
  declare const runMain: (runner: AdapterRunner, args?: string[], output?: NodeJS.WritableStream) => void;
6
6
 
7
+ declare class NodeProjectFileAccess implements ProjectFileAccess {
8
+ statPath(filePath: string): Promise<"dir" | "file" | null>;
9
+ listFiles(root: string): Promise<string[]>;
10
+ readTextFile(filePath: string): Promise<string | null>;
11
+ fileSize(filePath: string): Promise<number | null>;
12
+ private walk;
13
+ }
14
+
15
+ declare class PiAdapter implements PromptRunner$1 {
16
+ private readonly apiKey;
17
+ private readonly model;
18
+ private readonly agents;
19
+ private readonly streamFn;
20
+ private readonly modelDefinition;
21
+ constructor(apiKey: string, model: string);
22
+ runPrompt(sessionId: string, prompt: string, onDelta: (delta: string) => void): Promise<{
23
+ responseText: string;
24
+ }>;
25
+ private getAgent;
26
+ }
27
+
28
+ declare class SimpleWorkflowClassifier implements WorkflowClassifier {
29
+ classify(prompt: string, catalog: WorkflowCatalog): Promise<string>;
30
+ }
31
+
32
+ declare const DEFAULT_CONFIG_PATH: string;
33
+ declare const DEFAULT_CONFIG_DISPLAY_PATH = "~/.noema/config.json";
34
+ declare const loadWorkflowCatalog: (configPath?: string) => Promise<WorkflowCatalog>;
35
+ declare const createWorkflowCatalogLoader: (configPath?: string) => (() => Promise<WorkflowCatalog>);
36
+
37
+ declare const writeFiles: (root: string, writes: WriteRequest[]) => Promise<void>;
38
+ declare const resolveWritePath: (root: string, target: string) => string;
39
+
7
40
  declare const cliVersion = "0.0.1";
8
41
 
9
42
  declare const runCli: (args?: string[], output?: NodeJS.WritableStream) => void;
10
43
 
11
- export { cliVersion, runCli, runMain };
44
+ export { DEFAULT_CONFIG_DISPLAY_PATH, DEFAULT_CONFIG_PATH, NodeProjectFileAccess, PiAdapter, SimpleWorkflowClassifier, cliVersion, createWorkflowCatalogLoader, loadWorkflowCatalog, resolveWritePath, runCli, runMain, writeFiles };
package/dist/index.js CHANGED
@@ -1,5 +1,22 @@
1
- var h=()=>({nextSessionId:1,sessions:new Map}),w=e=>{let t=`session-${e.nextSessionId}`;return e.nextSessionId+=1,t},x=(e,t)=>{e.sessions.has(t)||e.sessions.set(t,{id:t})};var y=async(e,t,r,s)=>{let n=[],o=s??(i=>n.push(i));switch(e.method){case"initialize":{let i=S(e.params),c=N(i);return{responses:[{jsonrpc:"2.0",id:e.id,result:{protocolVersion:c,agentCapabilities:{loadSession:!1,mcpCapabilities:{http:!1,sse:!1},promptCapabilities:{audio:!1,embeddedContext:!1,image:!1},sessionCapabilities:{}},agentInfo:{name:"noema-cli",title:"Noema CLI",version:"0.0.1"},authMethods:[]}}],notifications:n}}case"session/new":{let i=w(t);return x(t,i),{responses:[{jsonrpc:"2.0",id:e.id,result:{sessionId:i}}],notifications:n}}case"session/prompt":{let i=S(e.params),c=typeof i?.sessionId=="string"?i.sessionId:null;if(c===null)return T(e.id);let a=A(r);if(a===null)return v(e.id);x(t,c);let d=O(i);try{await a(c,d,g=>{o(E(c,g))})}catch(g){return U(e.id,g)}return{responses:[{jsonrpc:"2.0",id:e.id,result:{stopReason:"end_turn"}}],notifications:n}}default:return{responses:[{jsonrpc:"2.0",id:e.id,error:{code:-32601,message:"Method not found"}}],notifications:n}}},S=e=>l(e)?e:null,l=e=>typeof e=="object"&&e!==null,A=e=>typeof e=="function"?e:l(e)&&typeof e.runPrompt=="function"?e.runPrompt:null,O=e=>e===null?"":(Array.isArray(e.prompt)?e.prompt:[]).map(I).join(""),E=(e,t)=>({jsonrpc:"2.0",method:"session/update",params:{sessionId:e,update:{sessionUpdate:"agent_message_chunk",content:{type:"text",text:t}}}}),I=e=>{if(!l(e))return"";if(e.type==="text"&&typeof e.text=="string")return e.text;if(l(e.content)){let t=e.content;if(t.type==="text"&&typeof t.text=="string")return t.text}return""},N=e=>e===null?1:typeof e.protocolVersion=="number"?e.protocolVersion:1,T=e=>({responses:[{jsonrpc:"2.0",id:e,error:{code:-32602,message:"Invalid params"}}],notifications:[]}),v=e=>({responses:[{jsonrpc:"2.0",id:e,error:{code:-32e3,message:"Missing prompt runner"}}],notifications:[]}),U=(e,t)=>({responses:[{jsonrpc:"2.0",id:e,error:{code:-32e3,message:t instanceof Error?t.message:"Provider error"}}],notifications:[]});import{createInterface as M}from"readline";var R=(e=process.stdin,t=process.stdout,r=h(),s)=>{M({input:e}).on("line",o=>{J(o,t,r,s)})},J=async(e,t,r,s)=>{let n=e.trim();if(n.length===0)return;let o;try{o=JSON.parse(n)}catch{p(t,{jsonrpc:"2.0",id:null,error:{code:-32700,message:"Parse error"}});return}if(!C(o)){P(o)&&p(t,{jsonrpc:"2.0",id:o.id??null,error:{code:-32600,message:"Invalid Request"}});return}let{responses:i,notifications:c}=await y(o,r,s,a=>{p(t,a)});for(let a of c)p(t,a);for(let a of i)p(t,a)},p=(e,t)=>{e.write(`${JSON.stringify(t)}
2
- `)},C=e=>!b(e)||e.jsonrpc!=="2.0"||typeof e.method!="string"?!1:P(e),P=e=>b(e)?typeof e.id=="string"||typeof e.id=="number"||e.id===null:!1,b=e=>typeof e=="object"&&e!==null;var f=class{config;baseUrl;constructor(t){this.config=t,this.baseUrl=t.baseUrl??"https://openrouter.ai/api/v1"}async*streamPrompt(t){let r=await fetch(`${this.baseUrl}/chat/completions`,{method:"POST",headers:{Authorization:`Bearer ${this.config.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify({model:this.config.model,messages:[{role:"user",content:t}],stream:!0})});if(!r.ok)throw new Error(`OpenRouter error: ${r.status} ${r.statusText}`);if(r.body===null){let i=await r.json(),c=$(i);c.length>0&&(yield c);return}let s=r.body.getReader(),n=new TextDecoder,o="";for(;;){let i=await s.read();if(i.done)break;o+=n.decode(i.value,{stream:!0});let c=o.split(`
3
- `);o=c.pop()??"";for(let a of c){let d=_(a);d!==null&&(yield d)}}}},_=e=>{let t=e.trim();if(t.length===0||!t.startsWith("data:"))return null;let r=t.slice(5).trim();if(r==="[DONE]")return null;try{let s=JSON.parse(r),n=s?.choices?.[0]?.delta?.content??s?.choices?.[0]?.message?.content;return typeof n=="string"&&n.length>0?n:null}catch{return null}},$=e=>{let t=L(e),s=(Array.isArray(t?.choices)?t.choices:[])[0];if(!u(s))return"";let n=u(s.message)?s.message:null,o=u(s.delta)?s.delta:null,i=n?.content??o?.content;return typeof i=="string"?i:""},L=e=>u(e)?e:null,u=e=>typeof e=="object"&&e!==null;var m=class{provider;sessions=new Map;constructor(t){this.provider=t}async runPrompt(t,r,s){let n="";for await(let o of this.provider.streamPrompt(r))n+=o,s(o);return this.sessions.set(t,{lastPrompt:r,lastResponse:n}),{responseText:n}}getLastInteraction(t){return this.sessions.get(t)??null}};import{readFileSync as V}from"fs";var j=(e,t=process.argv.slice(2),r=process.stdout)=>{if(t.includes("--version")||t.includes("-v")){r.write(`${D()}
4
- `);return}e(process.stdin,r,k())},k=()=>{let e=null;return async(t,r,s)=>{let n=process.env.OPENROUTER_API_KEY;if(n===void 0||n.length===0)throw new Error("OPENROUTER_API_KEY is required");let o=process.env.OPENROUTER_MODEL;if(o===void 0||o.length===0)throw new Error("OPENROUTER_MODEL is required");return e===null&&(e=new m(new f({apiKey:n,model:o}))),e.runPrompt(t,r,s)}},D=()=>{try{let e=new URL("../package.json",import.meta.url),t=V(e,"utf8"),r=JSON.parse(t);return typeof r?.version=="string"?r.version:"unknown"}catch{return"unknown"}};var pe="0.0.1";var B=(e=process.argv.slice(2),t=process.stdout)=>{j((r,s,n)=>{R(r,s,void 0,n)},e,t)};import.meta.url===`file://${process.argv[1]}`&&B();export{pe as cliVersion,B as runCli,j as runMain};
1
+ var T=()=>({nextSessionId:1,sessions:new Map}),I=e=>{let t=`session-${e.nextSessionId}`;return e.nextSessionId+=1,t},W=(e,t)=>{e.sessions.has(t)||e.sessions.set(t,{id:t})};var b=async(e,t,s,r)=>{let n=[],o=r??(i=>n.push(i));switch(e.method){case"initialize":{let i=U(e.params),c=ut(i);return{responses:[{jsonrpc:"2.0",id:e.id,result:{protocolVersion:c,agentCapabilities:{loadSession:ft(),mcpCapabilities:{http:!1,sse:!1},promptCapabilities:{audio:!1,embeddedContext:!1,image:!1},sessionCapabilities:{}},agentInfo:{name:"noema-cli",title:"Noema CLI",version:"0.0.1"},authMethods:[]}}],notifications:n}}case"session/new":{let i=I(t);return W(t,i),{responses:[{jsonrpc:"2.0",id:e.id,result:{sessionId:i}}],notifications:n}}case"session/prompt":{let i=U(e.params),c=typeof i?.sessionId=="string"?i.sessionId:null;if(c===null)return pt(e.id);let a=it(s);if(a===null)return dt(e.id);W(t,c);let l=at(i);try{await a(c,l,u=>{o(ct(c,u))})}catch(u){return ht(e.id,u)}return{responses:[{jsonrpc:"2.0",id:e.id,result:{stopReason:"end_turn"}}],notifications:n}}default:return{responses:[{jsonrpc:"2.0",id:e.id,error:{code:-32601,message:"Method not found"}}],notifications:n}}},U=e=>x(e)?e:null,x=e=>typeof e=="object"&&e!==null,it=e=>typeof e=="function"?e:x(e)&&typeof e.runPrompt=="function"?e.runPrompt:null,at=e=>e===null?"":(Array.isArray(e.prompt)?e.prompt:[]).map(lt).join(""),ct=(e,t)=>({jsonrpc:"2.0",method:"session/update",params:{sessionId:e,update:{sessionUpdate:"agent_message_chunk",content:{type:"text",text:t}}}}),lt=e=>{if(!x(e))return"";if(e.type==="text"&&typeof e.text=="string")return e.text;if(x(e.content)){let t=e.content;if(t.type==="text"&&typeof t.text=="string")return t.text}return""},ut=e=>e===null?1:typeof e.protocolVersion=="number"?e.protocolVersion:1,ft=()=>{let e=process.env.NOEMA_SESSION_PERSISTENCE;if(e===void 0||e.length===0)return!0;let t=e.toLowerCase();return!(t==="0"||t==="false"||t==="disabled"||t==="off")},pt=e=>({responses:[{jsonrpc:"2.0",id:e,error:{code:-32602,message:"Invalid params"}}],notifications:[]}),dt=e=>({responses:[{jsonrpc:"2.0",id:e,error:{code:-32e3,message:"Missing prompt runner"}}],notifications:[]}),ht=(e,t)=>({responses:[{jsonrpc:"2.0",id:e,error:{code:-32e3,message:t instanceof Error?t.message:"Provider error"}}],notifications:[]});import{createInterface as mt}from"readline";var v=(e=process.stdin,t=process.stdout,s=T(),r)=>{mt({input:e}).on("line",o=>{gt(o,t,s,r)})},gt=async(e,t,s,r)=>{let n=e.trim();if(n.length===0)return;let o;try{o=JSON.parse(n)}catch{g(t,{jsonrpc:"2.0",id:null,error:{code:-32700,message:"Parse error"}});return}if(!wt(o)){D(o)&&g(t,{jsonrpc:"2.0",id:o.id??null,error:{code:-32600,message:"Invalid Request"}});return}let{responses:i,notifications:c}=await b(o,s,r,a=>{g(t,a)});for(let a of c)g(t,a);for(let a of i)g(t,a)},g=(e,t)=>{e.write(`${JSON.stringify(t)}
2
+ `)},wt=e=>!$(e)||e.jsonrpc!=="2.0"||typeof e.method!="string"?!1:D(e),D=e=>$(e)?typeof e.id=="string"||typeof e.id=="number"||e.id===null:!1,$=e=>typeof e=="object"&&e!==null;import h from"path";var yt=`{
3
+ "defaultWorkflow": "draft",
4
+ "workflows": [
5
+ { "id": "draft", "description": "Draft new content" }
6
+ ]
7
+ }`,Pt=e=>e?`Workflow catalog is missing or invalid. Configure workflows in ${e}.
8
+ Example:
9
+ ${yt}`:"Workflow catalog is missing or invalid.",A=class{deps;sessions=new Map;constructor(t){this.deps=t}setWorkflowCatalog(t){this.deps.workflowCatalog=t}async runPrompt(t,s,r){let n=this.ensureSession(t);if(!St(this.deps.workflowCatalog))return this.respondWithText(n,s,r,Pt(this.deps.workflowCatalogPath));if(n.pending)return this.handlePending(t,n.pending,s,r);let o=await this.deps.workflowClassifier.classify(s,this.deps.workflowCatalog);return this.handlePrompt({onDelta:r,pathSelectionPrompt:s,prompt:s,sessionId:t,workflowId:o})}getLastInteraction(t){return this.sessions.get(t)??null}async handlePending(t,s,r,n){switch(s.type){case"paths":return this.handlePrompt({onDelta:n,pathSelectionPrompt:r,prompt:s.prompt,sessionId:t,workflowId:s.workflowId});case"outsideRoot":return await this.isOutsideRootAllowed(r,s.outsideRootPaths,s.writeIntent.allowMissingTargets)?(this.clearPending(t),this.processParsedPaths({onDelta:n,pathReferences:s.pathReferences,prompt:s.prompt,sessionId:t,workflowId:s.workflowId,writeIntent:s.writeIntent,writePathReferences:s.writePathReferences})):this.respondWithText(this.ensureSession(t),r,n,K(s.outsideRootPaths));case"writePath":return this.handlePrompt({onDelta:n,pathSelectionPrompt:r,prompt:s.prompt,pathReferencesOverride:s.pathReferences,sessionId:t,workflowId:s.workflowId,writeIntentOverride:s.writeIntent});case"overwrite":{if(!await this.isOverwriteAllowed(r,s.writeTargets))return this.respondWithText(this.ensureSession(t),r,n,J(s.writeTargets));this.clearPending(t);let i={...s.writeIntent,overwrite:!0};return this.processParsedPaths({onDelta:n,pathReferences:s.pathReferences,prompt:s.prompt,sessionId:t,workflowId:s.workflowId,writeIntent:i,writePathReferences:s.pathReferences,writeTargetsOverride:s.writeTargets})}}}async handlePrompt({onDelta:t,pathSelectionPrompt:s,prompt:r,pathReferencesOverride:n,sessionId:o,workflowId:i,writeIntentOverride:c,writePathReferenceOverrides:a}){let l=c??kt(r,i),u=n===void 0?await this.parseExistingPaths(s):{outsideRootPaths:[],pathReferences:n},f=a??(l.allowMissingTargets?await this.parseAllPathCandidates(s):u.pathReferences),p=Tt([u.outsideRootPaths,f]);if(p.length>0)return this.setPending(o,{outsideRootPaths:p,pathReferences:u.pathReferences,prompt:r,type:"outsideRoot",workflowId:i,writeIntent:l,writePathReferences:f}),this.respondWithText(this.ensureSession(o),r,t,K(p));if(l.enabled){let d=await this.resolveWriteTargets(f,l.allowMissingTargets);if(d.length===0)return this.setPending(o,{pathReferences:u.pathReferences,prompt:r,type:"writePath",workflowId:i,writeIntent:l}),this.respondWithText(this.ensureSession(o),r,t,"Which file should I write to?");if(d.length>1)return this.respondWithText(this.ensureSession(o),r,t,"Please provide a single target file path.");let m=d[0];return!l.overwrite&&await this.deps.fileAccess.statPath(m)==="file"?(this.setPending(o,{pathReferences:u.pathReferences,prompt:r,type:"overwrite",workflowId:i,writeIntent:l,writeTargets:d}),this.respondWithText(this.ensureSession(o),r,t,J(d))):this.processParsedPaths({onDelta:t,pathReferences:u.pathReferences,prompt:r,sessionId:o,workflowId:i,writeIntent:l,writePathReferences:f,writeTargetsOverride:d})}return u.pathReferences.length===0&&f.length===0?(this.setPending(o,{prompt:r,type:"paths",workflowId:i}),this.respondWithText(this.ensureSession(o),r,t,"Which files/folders should I use?")):this.processParsedPaths({onDelta:t,pathReferences:u.pathReferences,prompt:r,sessionId:o,workflowId:i,writeIntent:l,writePathReferences:f})}async processParsedPaths({onDelta:t,pathReferences:s,prompt:r,sessionId:n,workflowId:o,writeIntent:i,writePathReferences:c,writeTargetsOverride:a}){this.clearPending(n);let l=await this.expandContextPaths(s),u=await Et({fileAccess:this.deps.fileAccess,options:this.deps.contextOptions,paths:l}),f=Wt({context:u,prompt:r,workflowId:o}),p=await this.deps.runner.runPrompt(n,f,t),d=a??(i.enabled?await this.resolveWriteTargets(c,i.allowMissingTargets):[]),m=i.enabled&&d.length===1?[{path:d[0],content:p.responseText}]:void 0;return this.recordResponse(n,r,p.responseText,m)}async parseExistingPaths(t){return R({pathExists:this.createPathExists(!1),prompt:t,root:this.deps.root})}async parseAllPathCandidates(t){let s=await R({pathExists:this.createPathExists(!0),prompt:t,root:this.deps.root});return s.pathReferences.concat(s.outsideRootPaths)}createPathExists(t){return async s=>{if(t)return!0;let r=Mt(s,this.deps.root);return await this.deps.fileAccess.statPath(r)!==null}}async expandContextPaths(t){let s=[],r=new Set;for(let n of t){let o=await this.deps.fileAccess.statPath(n.path);if(o==="file"){r.has(n.path)||(r.add(n.path),s.push(n.path));continue}if(o!=="dir")continue;let i=await this.deps.fileAccess.listFiles(n.path);for(let c of i){let a=h.normalize(h.join(n.path,c));r.has(a)||(r.add(a),s.push(a))}}return s}async resolveWriteTargets(t,s){let r=[];for(let n of t){let o=await this.deps.fileAccess.statPath(n.path);if(o==="file"){r.push(n.path);continue}o===null&&s&&r.push(n.path)}return r}respondWithText(t,s,r,n){return r(n),t.lastPrompt=s,t.lastResponse=n,{responseText:n}}recordResponse(t,s,r,n){let o=this.ensureSession(t);return o.lastPrompt=s,o.lastResponse=r,n?{responseText:r,writes:n}:{responseText:r}}ensureSession(t){let s=this.sessions.get(t);if(s)return s;let r={lastPrompt:"",lastResponse:""};return this.sessions.set(t,r),r}setPending(t,s){let r=this.ensureSession(t);r.pending=s}clearPending(t){let s=this.ensureSession(t);delete s.pending}async isOutsideRootAllowed(t,s,r){if(!bt(t))return!1;let o=(await R({pathExists:this.createPathExists(r),prompt:t,root:this.deps.root})).outsideRootPaths.map(i=>i.absolutePath.toLowerCase());return s.every(i=>o.includes(i.absolutePath.toLowerCase()))}async isOverwriteAllowed(t,s){if(!vt(t))return!1;let r=await R({pathExists:this.createPathExists(!0),prompt:t,root:this.deps.root}),n=r.pathReferences.concat(r.outsideRootPaths).map(o=>o.path.toLowerCase());return s.every(o=>n.includes(o.toLowerCase()))}},xt=1e6,Rt=2*1024*1024,B="[[SKIPPED: unreadable]]",At="[[SKIPPED: file exceeds size limit]]",z="[[SKIPPED: context size limit reached]]",Ct="[[TRUNCATED: context size limit reached]]",R=async({pathExists:e,prompt:t,root:s})=>{let r=_t(t),n=new Map,o=new Map;for(let i of r){let c=Ft(i);if(c===null||!Lt(c.value))continue;let a=h.resolve(s,c.value);if(!await e(a))continue;let l=h.relative(s,a),u=l.startsWith("..")||h.isAbsolute(l),f=u?a:h.normalize(l),p={absolutePath:a,isOutsideRoot:u,path:f,source:c.source};if(u){o.has(a)||o.set(a,p);continue}n.has(a)||n.set(a,p)}return{outsideRootPaths:Array.from(o.values()),pathReferences:Array.from(n.values())}},Et=async({fileAccess:e,paths:t,options:s})=>{let r=s?.maxTotalBytes??xt,n=s?.maxFileBytes??Rt,o=[],i=[],c=0,a=!1;for(let l of t){let u=await e.fileSize(l);if(u===null){o.push({path:l,content:B}),i.push(l);continue}if(u>n){o.push({path:l,content:At}),i.push(l);continue}if(c>=r){o.push({path:l,content:z}),i.push(l),a=!0;continue}let f=await e.readTextFile(l);if(f===null){o.push({path:l,content:B}),i.push(l);continue}let p=Buffer.byteLength(f);if(c+p<=r){o.push({path:l,content:f}),c+=p;continue}let d=r-c;if(d<=0){o.push({path:l,content:z}),i.push(l),a=!0;continue}let m=Nt(f,d);o.push({path:l,content:`${m}
10
+ ${Ct}`}),c=r,a=!0}return{entries:o,skippedPaths:i,totalBytes:c,truncated:a}},St=e=>!e||typeof e.defaultWorkflow!="string"||e.defaultWorkflow.length===0||!Array.isArray(e.workflows)||e.workflows.length===0?!1:e.workflows.some(t=>t.id===e.defaultWorkflow),kt=(e,t)=>{let s=/\b(write|save|overwrite|replace|update|create)\b/i.test(e),r=/\b(overwrite|replace)\b/i.test(e);return{allowMissingTargets:s,enabled:s||t==="rewrite",overwrite:r}},Tt=e=>{let t=new Map;for(let s of e)for(let r of s)r.isOutsideRoot&&(t.has(r.absolutePath)||t.set(r.absolutePath,r));return Array.from(t.values())},Wt=({context:e,prompt:t,workflowId:s})=>{let r=e.entries.length===0?"(none)":e.entries.map(n=>`[[FILE: ${n.path}]]
11
+ ${n.content}`.trimEnd()).join(`
12
+
13
+ `);return`Workflow: ${s}
14
+
15
+ Context:
16
+ ${r}
17
+
18
+ User:
19
+ ${t}`},K=e=>{let t=G(e.map(s=>s.path));return`You requested paths outside the project root: ${t}. Reply with "allow ${t}" to proceed.`},J=e=>{let t=G(e);return`The following files already exist: ${t}. Reply with "overwrite ${t}" to proceed.`},bt=e=>/\b(allow|yes)\b/i.test(e),vt=e=>/\b(overwrite|yes)\b/i.test(e),G=e=>e.map(t=>`"${t}"`).join(", "),Mt=(e,t)=>{let s=h.relative(t,e);return!s.startsWith("..")&&!h.isAbsolute(s)?h.normalize(s):e},_t=e=>e.match(/@"[^"]*"|@'[^']*'|@`[^`]*`|"[^"]*"|'[^']*'|`[^`]*`|\S+/g)??[],Ft=e=>{if(e.length===0||e.includes("://"))return null;let t=Ot(e);if(t!==null)return t;let s=Y(e);if(s.length===0||s.includes("://"))return null;if(s.startsWith("@")){let r=Y(s.slice(1));return r.length===0?null:{source:"@",value:r}}return{source:"plain",value:s}},Ot=e=>{let t="plain",s=e;if(s.startsWith("@")&&(t="@",s=s.slice(1)),s.length<2)return null;let r=s[0];if((r==='"'||r==="'"||r==="`")&&s.endsWith(r)){let n=s.slice(1,-1);return n.length===0||n.includes("://")?null:{source:t,value:n}}return null},Y=e=>e.replace(/^[\s"'([{<]+/,"").replace(/[\s"')\]}>.,;:!?]+$/,""),Lt=e=>e.startsWith("./")||e.startsWith("../")||e.startsWith("/")||e.includes("/")?!0:/^[A-Za-z0-9][A-Za-z0-9._-]*\.[A-Za-z0-9._-]+$/.test(e),Nt=(e,t)=>{let s=Buffer.from(e,"utf8");return s.length<=t?e:s.subarray(0,t).toString("utf8")};import{readFileSync as ae}from"fs";import{promises as E}from"fs";import M from"path";import{TextDecoder as jt}from"util";var X=2*1024*1024,V=6,_=8*1024,It=new Set([".git","node_modules"]),w=class{async statPath(t){let s=await C(t);return s===null||s.isSymbolicLink()?null:s.isFile()?"file":s.isDirectory()?"dir":null}async listFiles(t){let s=[];return await this.walk(t,t,0,s),s}async readTextFile(t){let s=await C(t);if(s===null||!s.isFile()||s.isSymbolicLink()||s.size>X)return null;let r=await $t(t);return r===null||H(r.subarray(0,_))?null:Z(r)}async fileSize(t){let s=await C(t);return s===null||!s.isFile()||s.isSymbolicLink()?null:s.size}async walk(t,s,r,n){if(r>V)return;let o=await Bt(s);if(o!==null){o.sort((i,c)=>i.name.localeCompare(c.name));for(let i of o){if(It.has(i.name))continue;let c=M.join(s,i.name);if(i.isSymbolicLink())continue;if(i.isDirectory()){if(r>=V)continue;await this.walk(t,c,r+1,n);continue}if(!i.isFile()||!await Ut(c))continue;let a=M.relative(t,c);a.length!==0&&n.push(M.normalize(a))}}}},Ut=async e=>{let t=await C(e);if(t===null||!t.isFile()||t.isSymbolicLink()||t.size>X)return!1;let s=await Dt(e);return s===null?!1:!H(s)},H=e=>e.includes(0)?!0:Z(e)===null,Z=e=>{try{return new jt("utf-8",{fatal:!0}).decode(e)}catch{return null}},Dt=async e=>{let t=await zt(e);if(t===null)return null;try{let s=Buffer.alloc(_),{bytesRead:r}=await t.read(s,0,_,0);return s.subarray(0,r)}catch{return null}finally{await t.close()}},$t=async e=>{try{return await E.readFile(e)}catch{return null}},Bt=async e=>{try{return await E.readdir(e,{withFileTypes:!0})}catch{return null}},C=async e=>{try{return await E.lstat(e)}catch{return null}},zt=async e=>{try{return await E.open(e,"r")}catch{return null}};import{Agent as Gt}from"@mariozechner/pi-agent-core";import{getModel as Vt}from"@mariozechner/pi-ai";import{AssistantMessageEventStream as Xt}from"@mariozechner/pi-ai/dist/utils/event-stream.js";var k=class{config;baseUrl;constructor(t){this.config=t,this.baseUrl=t.baseUrl??"https://openrouter.ai/api/v1"}async*streamPrompt(t){let s=[{role:"user",content:t}];yield*this.streamChat({messages:s})}async*streamChat({messages:t,model:s,signal:r}){let n=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:t,stream:!0}),signal:r});if(!n.ok)throw new Error(`OpenRouter error: ${n.status} ${n.statusText}`);if(n.body===null){let a=await n.json(),l=Jt(a);l.length>0&&(yield l);return}let o=n.body.getReader(),i=new TextDecoder,c="";for(;;){let a=await o.read();if(a.done)break;c+=i.decode(a.value,{stream:!0});let l=c.split(`
20
+ `);c=l.pop()??"";for(let u of l){let f=Kt(u);f!==null&&(yield f)}}}},Kt=e=>{let t=e.trim();if(t.length===0||!t.startsWith("data:"))return null;let s=t.slice(5).trim();if(s==="[DONE]")return null;try{let r=JSON.parse(s),n=r?.choices?.[0]?.delta?.content??r?.choices?.[0]?.message?.content;return typeof n=="string"&&n.length>0?n:null}catch{return null}},Jt=e=>{let t=Yt(e),r=(Array.isArray(t?.choices)?t.choices:[])[0];if(!S(r))return"";let n=S(r.message)?r.message:null,o=S(r.delta)?r.delta:null,i=n?.content??o?.content;return typeof i=="string"?i:""},Yt=e=>S(e)?e:null,S=e=>typeof e=="object"&&e!==null;var y=class{constructor(t,s){this.apiKey=t;this.model=s;this.streamFn=Zt(this.apiKey),this.modelDefinition=Ht(this.model)}agents=new Map;streamFn;modelDefinition;async runPrompt(t,s,r){let n=this.getAgent(t),o="",i="",c=n.subscribe(a=>{a.type==="message_update"&&a.assistantMessageEvent.type==="text_delta"&&(o+=a.assistantMessageEvent.delta,r(a.assistantMessageEvent.delta)),a.type==="message_end"&&a.message.role==="assistant"&&(i=Q(a.message.content))});return await n.prompt(s),c(),o.length===0&&i.length>0&&(o=i,r(o)),{responseText:o}}getAgent(t){let s=this.agents.get(t);return s||(s=new Gt({initialState:{model:this.modelDefinition},sessionId:t,streamFn:this.streamFn}),this.agents.set(t,s)),s.sessionId=t,s.setModel(this.modelDefinition),s}},Ht=e=>{let t=Vt("openrouter",e);if(!t)throw new Error(`Unknown OpenRouter model: ${e}`);return t},Zt=e=>(s,r,n)=>{let o=new Xt;return qt({apiKey:n?.apiKey??e,context:r,model:s,signal:n?.signal,stream:o}),o},qt=async({apiKey:e,context:t,model:s,signal:r,stream:n})=>{let o=Qt(t),i=new k({apiKey:e,baseUrl:s.baseUrl,model:s.id}),c=q(s),a="",l=!1;try{n.push({type:"start",partial:c});for await(let u of i.streamChat({messages:o,model:s.id,signal:r}))l||(c.content.push({type:"text",text:""}),n.push({type:"text_start",contentIndex:0,partial:c}),l=!0),a+=u,c.content[0].text=a,n.push({type:"text_delta",contentIndex:0,delta:u,partial:c});l&&n.push({type:"text_end",contentIndex:0,content:a,partial:c}),n.push({type:"done",reason:"stop",message:c})}catch(u){let f=u instanceof Error?u.message:String(u),p=q(s,{content:[{type:"text",text:f}],errorMessage:f,stopReason:"error"});n.push({type:"error",reason:"error",error:p})}},Qt=e=>{let t=[];e.systemPrompt&&e.systemPrompt.trim().length>0&&t.push({role:"system",content:e.systemPrompt});for(let s of e.messages){let r=Q(s.content);if(s.role==="toolResult"){if(r.length===0)continue;t.push({role:"tool",content:r,tool_call_id:s.toolCallId});continue}r.length!==0&&t.push({role:s.role,content:r})}return t},Q=e=>typeof e=="string"?e:Array.isArray(e)?e.map(t=>t.type==="text"?t.text:"").join(""):"",q=(e,t)=>({role:"assistant",content:[],api:e.api,provider:e.provider,model:e.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(),...t});var P=class{async classify(t,s){let r=t.toLowerCase();for(let n of s.workflows)if(te(r,n))return n.id;return s.defaultWorkflow}},te=(e,t)=>{let s=t.id.trim().toLowerCase();if(s.length>0&&ee(e,s))return!0;if(t.description){let r=t.description.toLowerCase().trim();if(r.length>0&&e.includes(r))return!0}return!1},ee=(e,t)=>{let s=se(t);return new RegExp(`\\b${s}\\b`,"i").test(e)},se=e=>e.replace(/[.*+?^${}()|[\\]\\]/g,"\\$&");import{promises as et}from"fs";import re from"os";import ne from"path";var tt={defaultWorkflow:"",workflows:[]},F=ne.join(re.homedir(),".noema","config.json"),O="~/.noema/config.json",st=async(e=F)=>{try{let t=await et.readFile(e,"utf8"),s=JSON.parse(t);return oe(s)?s:tt}catch{return tt}},L=(e=F)=>{let t=null,s=null;return async()=>{let r=await et.stat(e).catch(()=>null),n=r?r.mtimeMs:null;return(t===null||s!==n)&&(t=await st(e),s=n),t}},oe=e=>{if(!e||typeof e!="object")return!1;let t=e;return typeof t.defaultWorkflow!="string"||!Array.isArray(t.workflows)?!1:t.workflows.every(s=>ie(s))},ie=e=>{if(!e||typeof e!="object")return!1;let t=e;return!(typeof t.id!="string"||t.id.length===0||t.description!==void 0&&typeof t.description!="string")};import{promises as rt}from"fs";import N from"path";var j=async(e,t)=>{for(let s of t){let r=nt(e,s.path);await rt.mkdir(N.dirname(r),{recursive:!0}),await rt.writeFile(r,s.content,"utf8")}},nt=(e,t)=>N.isAbsolute(t)?t:N.join(e,t);var ot=(e,t=process.argv.slice(2),s=process.stdout)=>{if(t.includes("--version")||t.includes("-v")){s.write(`${ue()}
21
+ `);return}e(process.stdin,s,ce())},ce=()=>{let e=null,t=process.cwd(),s=L();return async(r,n,o)=>{let i=process.env.OPENROUTER_API_KEY;if(i===void 0||i.length===0)throw new Error("OPENROUTER_API_KEY is required");let c=process.env.OPENROUTER_MODEL;if(c===void 0||c.length===0)throw new Error("OPENROUTER_MODEL is required");let a=await s();e===null&&(e=le({apiKey:i,model:c,root:t,workflowCatalog:a,workflowCatalogPath:O}));let l=await e;l.setWorkflowCatalog(a);let u=await l.runPrompt(r,n,o);return u.writes&&await j(t,u.writes),u}},le=async({apiKey:e,model:t,root:s,workflowCatalog:r,workflowCatalogPath:n})=>{let o=new w,i=new P,c=new y(e,t);return new A({fileAccess:o,root:s,runner:c,workflowCatalog:r,workflowCatalogPath:n,workflowClassifier:i})},ue=()=>{try{let e=new URL("../package.json",import.meta.url),t=ae(e,"utf8"),s=JSON.parse(t);return typeof s?.version=="string"?s.version:"unknown"}catch{return"unknown"}};var es="0.0.1";var fe=(e=process.argv.slice(2),t=process.stdout)=>{ot((s,r,n)=>{v(s,r,void 0,n)},e,t)};import.meta.url===`file://${process.argv[1]}`&&fe();export{O as DEFAULT_CONFIG_DISPLAY_PATH,F as DEFAULT_CONFIG_PATH,w as NodeProjectFileAccess,y as PiAdapter,P as SimpleWorkflowClassifier,es as cliVersion,L as createWorkflowCatalogLoader,st as loadWorkflowCatalog,nt as resolveWritePath,fe as runCli,ot as runMain,j as writeFiles};
5
22
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "noema-cli",
3
- "version": "0.0.13",
3
+ "version": "0.1.0",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "bin": {