@memo-code/memo 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 minorcell
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,172 @@
1
+ # Memo Code
2
+
3
+ 本地运行的 AI 编程助手,支持多轮对话、工具调用、并发。基于 Node.js + TypeScript,默认对接 DeepSeek,兼容 OpenAI API。
4
+
5
+ ## 快速开始
6
+
7
+ ### 1. 安装
8
+
9
+ ```bash
10
+ npm install -g @memo-code/memo
11
+ # 或
12
+ pnpm add -g @memo-code/memo
13
+ # 或
14
+ yarn global add @memo-code/memo
15
+ ```
16
+
17
+ ### 2. 配置 API Key
18
+
19
+ ```bash
20
+ export DEEPSEEK_API_KEY=your_key # 或 OPENAI_API_KEY
21
+ ```
22
+
23
+ ### 3. 启动使用
24
+
25
+ ```bash
26
+ memo
27
+ # 首次运行会引导配置 provider/model,并(保存到 ~/.memo/config.toml)
28
+ ```
29
+
30
+ ## 使用方式
31
+
32
+ - 交互式:`memo`(默认 TUI,支持多轮、流式、工具可视化、快捷键)。
33
+ - 单轮:`memo "你的问题" --once`(纯文本输出,适合脚本)。
34
+
35
+ ## 配置文件
36
+
37
+ 位置:`~/.memo/config.toml`(可通过 `MEMO_HOME` 环境变量修改)
38
+
39
+ ### Provider 配置
40
+
41
+ ```toml
42
+ current_provider = "deepseek"
43
+ stream_output = false
44
+
45
+ [[providers.deepseek]]
46
+ name = "deepseek"
47
+ env_api_key = "DEEPSEEK_API_KEY"
48
+ model = "deepseek-chat"
49
+ base_url = "https://api.deepseek.com"
50
+ ```
51
+
52
+ 支持配置多个 Provider,通过 `current_provider` 切换。
53
+
54
+ ### MCP 工具配置
55
+
56
+ 支持本地和远程 MCP 服务器:
57
+
58
+ ```toml
59
+ # 本地 MCP 服务器
60
+ [mcp_servers.local_tools]
61
+ command = "/path/to/mcp-server"
62
+ args = []
63
+
64
+ # 远程 HTTP MCP 服务器
65
+ [mcp_servers.remote]
66
+ type = "streamable_http"
67
+ url = "https://your-mcp-server.com/mcp"
68
+ # headers = { Authorization = "Bearer xxx" }
69
+ ```
70
+
71
+ ## 内置工具
72
+
73
+ - `bash`:执行 shell 命令
74
+ - `read`:读取文件
75
+ - `write`:写入文件
76
+ - `edit`:编辑文件
77
+ - `glob`:搜索文件(模式匹配)
78
+ - `grep`:搜索内容(正则匹配)
79
+ - `webfetch`:获取网页
80
+ - `save_memory`:保存长期记忆
81
+ - `todo`:管理任务列表
82
+
83
+ 通过 MCP 协议可扩展更多工具。
84
+
85
+ ## 会话历史
86
+
87
+ 所有会话自动保存到 `~/.memo/sessions/`,按工作目录和日期组织:
88
+
89
+ ```
90
+ ~/.memo/sessions/
91
+ ├── workspace-name/
92
+ │ ├── 2026-02-01_143020_abc123.jsonl
93
+ │ └── 2026-02-01_150315_def456.jsonl
94
+ └── another-project/
95
+ └── 2026-02-01_160000_xyz789.jsonl
96
+ ```
97
+
98
+ JSONL 格式便于分析和调试。
99
+
100
+ ## 开发
101
+
102
+ ### 本地运行
103
+
104
+ ```bash
105
+ pnpm install
106
+ pnpm start
107
+ # 或
108
+ pnpm start "prompt" --once
109
+ ```
110
+
111
+ ### 构建
112
+
113
+ ```bash
114
+ pnpm run build # 生成 dist/index.js
115
+ ```
116
+
117
+ ### 测试
118
+
119
+ ```bash
120
+ pnpm test # 全量测试
121
+ pnpm test packages/core # 测试 core 包
122
+ pnpm test packages/tools # 测试 tools 包
123
+ ```
124
+
125
+ ### 代码格式化
126
+
127
+ ```bash
128
+ npm run format # 格式化所有文件
129
+ npm run format:check # 检查格式(CI)
130
+ ```
131
+
132
+ ## 项目结构
133
+
134
+ ```
135
+ memo-cli/
136
+ ├── packages/
137
+ │ ├── core/ # 核心逻辑:Session、工具路由、配置
138
+ │ ├── tools/ # 内置工具实现
139
+ │ └── cli/ # TUI 界面
140
+ ├── docs/ # 技术文档
141
+ └── dist/ # 构建输出
142
+ ```
143
+
144
+ ## CLI 快捷键与命令
145
+
146
+ - `/help`:显示帮助与快捷键说明。
147
+ - `/models`:列出现有 Provider/Model,回车切换;支持直接 `/models deepseek` 精确选择。
148
+ - `/context`:弹出 80k/120k/150k/200k 选项并立即设置上限。
149
+ - `$ <cmd>`:在当前工作目录本地执行 shell 命令,直接显示输出(`Shell Result`),不再经模型代理。
150
+ - `resume` 历史:输入 `resume` 查看并加载本目录的历史会话。
151
+ - 退出与清屏:`exit` / `/exit`,`Ctrl+L` 新会话,`Esc Esc` 取消运行或清空输入。
152
+
153
+ > 仅当会话包含用户消息时才写入 `sessions/` JSONL 日志,避免空会话文件。
154
+
155
+ ## 技术栈
156
+
157
+ - **Runtime**: Node.js 18+
158
+ - **语言**: TypeScript
159
+ - **UI**: React + Ink
160
+ - **Protocol**: MCP (Model Context Protocol)
161
+ - **Token 计数**: tiktoken
162
+
163
+ ## 相关文档
164
+
165
+ - [Core 架构](./docs/core.md) - 核心实现详解
166
+ - [重构报告](./docs/refactor-complete.md) - Tool Use API 迁移说明
167
+ - [开发指南](./CONTRIBUTING.md) - 贡献指南
168
+ - [项目约定](./AGENTS.md) - 代码规范和开发流程
169
+
170
+ ## License
171
+
172
+ MIT
package/dist/index.js ADDED
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env node
2
+ var $o=Object.create;var Ge=Object.defineProperty;var Io=Object.getOwnPropertyDescriptor;var Ro=Object.getOwnPropertyNames;var No=Object.getPrototypeOf,Lo=Object.prototype.hasOwnProperty;var Oo=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var Do=(t,e,n,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of Ro(e))!Lo.call(t,r)&&r!==n&&Ge(t,r,{get:()=>e[r],enumerable:!(o=Io(e,r))||o.enumerable});return t};var Ho=(t,e,n)=>(n=t!=null?$o(No(t)):{},Do(e||!t||!t.__esModule?Ge(n,"default",{value:t,enumerable:!0}):n,t));var to=Oo((zu,ge)=>{"use strict";function Kn(t){return Array.isArray(t)?t:[t]}var fi=void 0,Re="",Wn=" ",Ie="\\",hi=/^\s+$/,yi=/(?:[^\\]|^)\\$/,Ti=/^\\!/,xi=/^\\#/,Si=/\r?\n/g,ki=/^\.{0,2}\/|^\.{1,2}$/,wi=/\/$/,Nt="/",Xn="node-ignore";typeof Symbol<"u"&&(Xn=Symbol.for("node-ignore"));var Yn=Xn,Lt=(t,e,n)=>(Object.defineProperty(t,e,{value:n}),n),Ci=/([0-z])-([0-z])/g,qn=()=>!1,bi=t=>t.replace(Ci,(e,n,o)=>n.charCodeAt(0)<=o.charCodeAt(0)?e:Re),_i=t=>{let{length:e}=t;return t.slice(0,e-e%2)},vi=[[/^\uFEFF/,()=>Re],[/((?:\\\\)*?)(\\?\s+)$/,(t,e,n)=>e+(n.indexOf("\\")===0?Wn:Re)],[/(\\+?)\s/g,(t,e)=>{let{length:n}=e;return e.slice(0,n-n%2)+Wn}],[/[\\$.|*+(){^]/g,t=>`\\${t}`],[/(?!\\)\?/g,()=>"[^/]"],[/^\//,()=>"^"],[/\//g,()=>"\\/"],[/^\^*\\\*\\\*\\\//,()=>"^(?:.*\\/)?"],[/^(?=[^^])/,function(){return/\/(?!$)/.test(this)?"^":"(?:^|\\/)"}],[/\\\/\\\*\\\*(?=\\\/|$)/g,(t,e,n)=>e+6<n.length?"(?:\\/[^\\/]+)*":"\\/.+"],[/(^|[^\\]+)(\\\*)+(?=.+)/g,(t,e,n)=>{let o=n.replace(/\\\*/g,"[^\\/]*");return e+o}],[/\\\\\\(?=[$.|*+(){^])/g,()=>Ie],[/\\\\/g,()=>Ie],[/(\\)?\[([^\]/]*?)(\\*)($|\])/g,(t,e,n,o,r)=>e===Ie?`\\[${n}${_i(o)}${r}`:r==="]"&&o.length%2===0?`[${bi(n)}${o}]`:"[]"],[/(?:[^*])$/,t=>/\/$/.test(t)?`${t}$`:`${t}(?=$|\\/$)`]],Ei=/(^|\\\/)?\\\*$/,Jt="regex",pe="checkRegex",Jn="_",Mi={[Jt](t,e){return`${e?`${e}[^/]+`:"[^/]*"}(?=$|\\/$)`},[pe](t,e){return`${e?`${e}[^/]*`:"[^/]*"}(?=$|\\/$)`}},Pi=t=>vi.reduce((e,[n,o])=>e.replace(n,o.bind(t)),t),de=t=>typeof t=="string",Ai=t=>t&&de(t)&&!hi.test(t)&&!yi.test(t)&&t.indexOf("#")!==0,$i=t=>t.split(Si).filter(Boolean),Ne=class{constructor(e,n,o,r,s,i){this.pattern=e,this.mark=n,this.negative=s,Lt(this,"body",o),Lt(this,"ignoreCase",r),Lt(this,"regexPrefix",i)}get regex(){let e=Jn+Jt;return this[e]?this[e]:this._make(Jt,e)}get checkRegex(){let e=Jn+pe;return this[e]?this[e]:this._make(pe,e)}_make(e,n){let o=this.regexPrefix.replace(Ei,Mi[e]),r=this.ignoreCase?new RegExp(o,"i"):new RegExp(o);return Lt(this,n,r)}},Ii=({pattern:t,mark:e},n)=>{let o=!1,r=t;r.indexOf("!")===0&&(o=!0,r=r.substr(1)),r=r.replace(Ti,"!").replace(xi,"#");let s=Pi(r);return new Ne(t,e,r,n,o,s)},Le=class{constructor(e){this._ignoreCase=e,this._rules=[]}_add(e){if(e&&e[Yn]){this._rules=this._rules.concat(e._rules._rules),this._added=!0;return}if(de(e)&&(e={pattern:e}),Ai(e.pattern)){let n=Ii(e,this._ignoreCase);this._added=!0,this._rules.push(n)}}add(e){return this._added=!1,Kn(de(e)?$i(e):e).forEach(this._add,this),this._added}test(e,n,o){let r=!1,s=!1,i;this._rules.forEach(c=>{let{negative:l}=c;s===l&&r!==s||l&&!r&&!s&&!n||!c[o].test(e)||(r=!l,s=l,i=l?fi:c)});let a={ignored:r,unignored:s};return i&&(a.rule=i),a}},Ri=(t,e)=>{throw new e(t)},ft=(t,e,n)=>de(t)?t?ft.isNotRelative(t)?n(`path should be a \`path.relative()\`d string, but got "${e}"`,RangeError):!0:n("path must not be empty",TypeError):n(`path must be a string, but got \`${e}\``,TypeError),Zn=t=>ki.test(t);ft.isNotRelative=Zn;ft.convert=t=>t;var Oe=class{constructor({ignorecase:e=!0,ignoreCase:n=e,allowRelativePaths:o=!1}={}){Lt(this,Yn,!0),this._rules=new Le(n),this._strictPathCheck=!o,this._initCache()}_initCache(){this._ignoreCache=Object.create(null),this._testCache=Object.create(null)}add(e){return this._rules.add(e)&&this._initCache(),this}addPattern(e){return this.add(e)}_test(e,n,o,r){let s=e&&ft.convert(e);return ft(s,e,this._strictPathCheck?Ri:qn),this._t(s,n,o,r)}checkIgnore(e){if(!wi.test(e))return this.test(e);let n=e.split(Nt).filter(Boolean);if(n.pop(),n.length){let o=this._t(n.join(Nt)+Nt,this._testCache,!0,n);if(o.ignored)return o}return this._rules.test(e,!1,pe)}_t(e,n,o,r){if(e in n)return n[e];if(r||(r=e.split(Nt).filter(Boolean)),r.pop(),!r.length)return n[e]=this._rules.test(e,o,Jt);let s=this._t(r.join(Nt)+Nt,n,o,r);return n[e]=s.ignored?s:this._rules.test(e,o,Jt)}ignores(e){return this._test(e,this._ignoreCache,!1).ignored}createFilter(){return e=>!this.ignores(e)}filter(e){return Kn(e).filter(this.createFilter())}test(e){return this._test(e,this._testCache,!0)}},De=t=>new Oe(t),Ni=t=>ft(t&&ft.convert(t),t,qn),Qn=()=>{let t=n=>/^\\\\\?\\/.test(n)||/["<>|\u0000-\u001F]+/u.test(n)?n:n.replace(/\\/g,"/");ft.convert=t;let e=/^[a-z]:\//i;ft.isNotRelative=n=>e.test(n)||Zn(n)};typeof process<"u"&&process.platform==="win32"&&Qn();ge.exports=De;De.default=De;ge.exports.isPathValid=Ni;Lt(ge.exports,Symbol.for("setupWindows"),Qn)});import{randomUUID as Eo}from"crypto";import{createInterface as Da}from"readline/promises";import{stdin as Ha,stdout as Ua}from"process";import{render as Ba}from"ink";import Uo from"os";import{readFile as Bo}from"fs/promises";import{join as Fo,dirname as jo}from"path";import{fileURLToPath as zo}from"url";var Go=/{{\s*([\w.-]+)\s*}}/g;function Vo(t,e){return t.replace(Go,(n,o)=>e[o]??"")}function Wo(){try{return Uo.userInfo().username}catch{return process.env.USER??process.env.USERNAME??"unknown"}}async function Ve(){let t=jo(zo(import.meta.url)),e=Fo(t,"prompt.md"),n=await Bo(e,"utf-8"),o={date:new Date().toISOString(),user:Wo(),pwd:process.cwd()};return Vo(n,o)}import{appendFile as Jo,mkdir as Ko}from"fs/promises";import{dirname as Xo}from"path";var Qt=class{constructor(e){this.filePath=e}ready=!1;async append(e){this.ready||(await Ko(Xo(this.filePath),{recursive:!0}),this.ready=!0),await Jo(this.filePath,`${JSON.stringify(e)}
3
+ `,"utf8")}async flush(){return Promise.resolve()}};function We(t){return{ts:new Date().toISOString(),sessionId:t.sessionId,turn:t.turn,step:t.step,type:t.type,content:t.content,role:t.role,meta:t.meta}}import{spawn as Yo}from"child_process";import{z as xe}from"zod";function S(t,e=!1){return{content:[{type:"text",text:t}],isError:e}}var qo=xe.object({command:xe.string().min(1,"command \u4E0D\u80FD\u4E3A\u7A7A"),timeout:xe.number().int("timeout \u5FC5\u987B\u662F\u6574\u6570\u6BEB\u79D2").positive("timeout \u5FC5\u987B\u5927\u4E8E 0").max(3600*1e3,"timeout \u4E0D\u80FD\u8D85\u8FC7 1 \u5C0F\u65F6").optional()}).strict(),Je={name:"bash",description:"\u5728 shell \u4E2D\u6267\u884C\u547D\u4EE4\uFF0C\u8FD4\u56DE exit/stdout/stderr",inputSchema:qo,execute:async({command:t,timeout:e})=>{let n=t.trim();if(!n)return S("bash \u9700\u8981\u8981\u6267\u884C\u7684\u547D\u4EE4",!0);try{let o=Yo("bash",["-lc",n],{env:process.env,stdio:["ignore","pipe","pipe"]}),r=g=>new Promise(_=>{if(!g)return _("");let C=[];g.setEncoding("utf8"),g.on("data",N=>C.push(N)),g.on("error",()=>_("")),g.on("end",()=>_(C.join("")))}),s=r(o.stdout),i=r(o.stderr),a,c=new Promise((g,_)=>{o.on("error",C=>_(C)),o.on("close",C=>g(typeof C=="number"?C:-1))}),l=e&&e>0?new Promise((g,_)=>{a=setTimeout(()=>{o.kill(),_(new Error(`bash \u8D85\u65F6 ${e}ms\uFF0C\u5DF2\u7EC8\u6B62\u8FDB\u7A0B`))},e)}):null,u=l?await Promise.race([c,l]):await c;a&&clearTimeout(a);let[m,y]=await Promise.all([s,i]);return S(`exit=${u} stdout="${m}" stderr="${y}"`)}catch(o){return S(`bash \u6267\u884C\u5931\u8D25: ${o.message}`,!0)}}};import{access as tr,readFile as er,writeFile as nr}from"fs/promises";import{normalize as Zo,resolve as Qo}from"path";function ct(t){return Zo(Qo(t))}import{z as Ut}from"zod";var or=Ut.object({file_path:Ut.string().min(1),old_string:Ut.string().min(1,"old_string \u4E0D\u80FD\u4E3A\u7A7A"),new_string:Ut.string(),replace_all:Ut.boolean().optional()}).strict(),Ke={name:"edit",description:"\u5728\u6587\u4EF6\u4E2D\u66FF\u6362\u6587\u672C\uFF0C\u652F\u6301 replace_all",inputSchema:or,execute:async t=>{let e=ct(t.file_path),n=t.replace_all??!1;if(!t.old_string.trim())return S("old_string \u4E0D\u80FD\u4E3A\u7A7A",!0);try{await tr(e);let o=await er(e,"utf8");if(!o.includes(t.old_string))return S("\u672A\u627E\u5230\u5F85\u66FF\u6362\u6587\u672C",!0);let r,s=0;if(n){let i=o.split(t.old_string);s=i.length-1,r=i.join(t.new_string)}else r=o.replace(t.old_string,t.new_string),s=1;return r===o?S("\u672A\u68C0\u6D4B\u5230\u5185\u5BB9\u53D8\u5316"):(await nr(e,r,"utf8"),S(`\u66FF\u6362\u5B8C\u6210: file=${e} count=${s}`))}catch(o){return o.code==="ENOENT"?S(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${e}`,!0):S(`edit \u5931\u8D25: ${o.message}`,!0)}}};import{z as Xe}from"zod";var rr=Xe.object({url:Xe.string().min(1)}).strict(),Ye=1e4,Bt=512e3,Se=4e3,sr=new Set(["http:","https:","data:"]),ir=/<\/\s*(p|div|section|article|header|footer|aside|main|h[1-6]|li|tr|table|blockquote)\s*>/gi,ar=/<\s*(br|hr)\s*\/?>/gi,cr=/<\s*li[^>]*>/gi,lr=/<[^>]+>/g,ur=/<(script|style)[^>]*>[\s\S]*?<\/\s*\1>/gi,mr=t=>t.replace(/&nbsp;/gi," ").replace(/&lt;/gi,"<").replace(/&gt;/gi,">").replace(/&amp;/gi,"&").replace(/&quot;/gi,'"').replace(/&#39;/g,"'").replace(/&#(x?[0-9a-fA-F]+);/g,(o,r)=>{try{let s=r.startsWith("x")||r.startsWith("X")?parseInt(r.slice(1),16):parseInt(r,10);return Number.isFinite(s)?String.fromCharCode(s):""}catch{return""}}),pr=t=>{let o=t.replace(ur," ").replace(cr,"- ").replace(ar,`
4
+ `).replace(ir,`
5
+ `).replace(lr," "),s=mr(o).replace(/\r/g,"").split(`
6
+ `).map(a=>a.trim().replace(/[ \t]{2,}/g," "));return s.filter((a,c)=>a.length>0||c>0&&(s[c-1]?.length??0)>0).join(`
7
+ `).trim()},dr=t=>t.replace(/\s+/g," ").trim(),qe={name:"webfetch",description:"HTTP GET \u8BF7\u6C42\uFF0C\u8FD4\u56DE\u5904\u7406\u540E\u7684\u7EAF\u6587\u672C\u6B63\u6587\uFF08\u81EA\u52A8\u5265\u79BB HTML \u6807\u7B7E\uFF09",inputSchema:rr,execute:async t=>{let e;try{e=new URL(t.url)}catch{return S(`\u65E0\u6548 URL: ${t.url}`,!0)}if(!sr.has(e.protocol))return S(`\u4E0D\u652F\u6301\u7684\u534F\u8BAE: ${e.protocol}`,!0);let n=new AbortController,o=setTimeout(()=>n.abort(),Ye);try{let r=await globalThis.fetch(e,{signal:n.signal}),s=r.headers.get("content-length"),i=s?Number(s):void 0;if(i&&i>Bt)return S(`\u8BF7\u6C42\u88AB\u62D2\u7EDD: \u54CD\u5E94\u4F53\u8FC7\u5927\uFF08${i} bytes\uFF09`,!0);let a=0,c="";if(r.body&&r.body.getReader){let N=r.body.getReader(),J=[];for(;;){let{done:b,value:k}=await N.read();if(b)break;if(k){if(a+=k.byteLength,a>Bt)return n.abort(),S(`\u8BF7\u6C42\u88AB\u4E2D\u6B62: \u54CD\u5E94\u4F53\u8D85\u8FC7 ${Bt} bytes`,!0);J.push(k)}}let f=new Uint8Array(a),Z=0;for(let b of J)f.set(b,Z),Z+=b.byteLength;c=new TextDecoder().decode(f)}else if(c=await r.text(),a=new TextEncoder().encode(c).byteLength,a>Bt)return S(`\u8BF7\u6C42\u88AB\u62D2\u7EDD: \u54CD\u5E94\u4F53\u8D85\u8FC7 ${Bt} bytes`,!0);let l=r.headers.get("content-type")||"",u=/text\/html/i.test(l)||/^\s*<!doctype html/i.test(c)||/^\s*<html[\s>]/i.test(c),m=u?pr(c):c.trim(),y=dr(m),g=y.length>Se?`${y.slice(0,Se)}...`:y,_=y.length>Se?" text_truncated=true":"",C=u?" source=html_stripped":"";return S(`status=${r.status} bytes=${a} text_chars=${y.length} text="${g}"${_}${C}`)}catch(r){return r.name==="AbortError"?S(`\u8BF7\u6C42\u8D85\u65F6\u6216\u88AB\u4E2D\u6B62\uFF08${Ye}ms\uFF09`,!0):S(`\u8BF7\u6C42\u5931\u8D25: ${r.message}`,!0)}finally{clearTimeout(o)}}};import{z as ke}from"zod";import gr from"fast-glob";var fr=ke.object({pattern:ke.string().min(1),path:ke.string().optional()}).strict(),hr={name:"glob",description:"\u6309 glob \u6A21\u5F0F\u5339\u914D\u6587\u4EF6\uFF0C\u8FD4\u56DE\u7EDD\u5BF9\u8DEF\u5F84\u5217\u8868",inputSchema:fr,execute:async t=>{let e=t.path?ct(t.path):process.cwd();try{let n=await gr(t.pattern,{cwd:e,absolute:!0,onlyFiles:!0});return S(n.join(`
8
+ `)||"\u672A\u627E\u5230\u5339\u914D\u6587\u4EF6")}catch(n){return S(`glob \u5931\u8D25: ${n.message}`,!0)}}},Ze=hr;import{spawn as yr,spawnSync as Tr}from"child_process";import{z as mt}from"zod";var xr=mt.object({pattern:mt.string().min(1),path:mt.string().optional(),output_mode:mt.enum(["content","files_with_matches","count"]).optional(),glob:mt.string().optional(),"-i":mt.boolean().optional(),"-A":mt.number().int().nonnegative().optional(),"-B":mt.number().int().nonnegative().optional(),"-C":mt.number().int().nonnegative().optional()}).strict(),Qe={name:"grep",description:"\u57FA\u4E8E ripgrep \u67E5\u627E\u6587\u672C\uFF0C\u652F\u6301\u8F93\u51FA\u5339\u914D\u5185\u5BB9\u3001\u6587\u4EF6\u5217\u8868\u6216\u8BA1\u6570",inputSchema:xr,execute:async t=>{let e=Tr("rg",["--version"],{stdio:"ignore"});if(e.error||e.status!==0)return S("rg \u672A\u5B89\u88C5\u6216\u4E0D\u5728 PATH",!0);let n=t.path?ct(t.path):process.cwd(),o=["--color","never"],r=t.output_mode??"content";r==="files_with_matches"?o.push("-l"):r==="count"?o.push("-c"):o.push("--line-number","--no-heading"),t["-i"]&&o.push("-i"),t.glob&&o.push("--glob",t.glob),t["-A"]!==void 0&&o.push("-A",String(t["-A"])),t["-B"]!==void 0&&o.push("-B",String(t["-B"])),t["-C"]!==void 0&&o.push("-C",String(t["-C"])),o.push(t.pattern,n);try{let s=yr("rg",o,{stdio:["ignore","pipe","pipe"]}),i=u=>new Promise(m=>{if(!u)return m("");let y=[];u.setEncoding("utf8"),u.on("data",g=>y.push(g)),u.on("error",()=>m("")),u.on("end",()=>m(y.join("")))}),[a,c]=await Promise.all([i(s.stdout),i(s.stderr)]),l=await new Promise((u,m)=>{s.on("error",y=>m(y)),s.on("close",y=>u(typeof y=="number"?y:-1))});return l===2?S(`grep \u5931\u8D25(exit=2): ${c||a}`,!0):l===1&&!a.trim()?S("\u672A\u627E\u5230\u5339\u914D"):S(a||c||`\u547D\u4EE4\u5B8C\u6210 exit=${l}`)}catch(s){return S(`grep \u6267\u884C\u5931\u8D25: ${s.message}`,!0)}}};import{mkdir as Sr,readFile as kr,writeFile as wr,access as Cr}from"fs/promises";import{dirname as br,join as tn}from"path";import{homedir as _r}from"os";import{z as en}from"zod";var vr=en.object({fact:en.string().min(1,"fact cannot be empty").max(120,"Please keep facts concise (\u2264120 characters)").describe('User-related identity traits or preferences, e.g., "User prefers Chinese responses", "User is a frontend engineer". Do not store project-specific technical details.')}).strict();function Er(){let t=process.env.MEMO_HOME?.trim()||tn(_r(),".memo");return tn(t,"Agents.md")}function Mr(t){return t.replace(/\r?\n/g," ").replace(/\s+/g," ").trim()}function Pr(t,e){let o=e.map(r=>r.trim()).filter(Boolean).map(r=>`- ${r}`);return`${t}
9
+
10
+ ${o.join(`
11
+ `)}
12
+ `}var nn={name:"save_memory",description:"Save user-related identity traits or preferences (e.g., language habits, tech preferences) for cross-session reuse. Do not save project-specific technical details or file structures.",inputSchema:vr,execute:async t=>{let e=Mr(t.fact);if(!e)return S("fact cannot be empty",!0);let n=Er(),o=br(n);try{await Sr(o,{recursive:!0});let r="## Memo Added Memories";try{let s=await(async()=>{try{return await Cr(n),await kr(n,"utf-8")}catch{return""}})(),[,i=""]=s.split(r),a=i.split(/\r?\n/).filter(u=>u.trim().startsWith("- ")).map(u=>u.replace(/^-+\s*/,"").trim());a.push(e);let c=a.slice(Math.max(0,a.length-50)),l=Pr(r,c);await wr(n,l,"utf-8")}catch(s){console.warn(`Memory maintenance failed: ${s.message}`)}return S(`Memory saved to: ${n}`)}catch(r){return S(`Failed to write memory: ${r.message}`,!0)}}};import{z as pt}from"zod";var te=10,ot=[],Ar=pt.object({type:pt.enum(["add","replace","update","remove"]),todos:pt.array(pt.object({content:pt.string().trim().min(1,"content \u4E0D\u80FD\u4E3A\u7A7A").max(200,"content \u6700\u957F 200 \u5B57\u7B26"),status:pt.enum(["pending","in_progress","completed"]).optional().default("pending"),id:pt.string().min(1,"id \u4E0D\u80FD\u4E3A\u7A7A").optional()})).optional(),ids:pt.array(pt.string().min(1,"id \u4E0D\u80FD\u4E3A\u7A7A")).optional()}).strict().refine(t=>{if(["add","replace","update"].includes(t.type)){if(!t.todos||t.todos.length===0)return!1;if(t.type==="update")return t.todos.every(e=>e.id)}return!(t.type==="remove"&&(!t.ids||t.ids.length===0))},{message:"add/replace/update \u9700\u8981 todos\uFF0Cremove \u9700\u8981 ids\uFF0Cupdate \u9700\u8981 id"});function ee(){return ot.map(t=>({...t}))}function $r(t){switch(t.type){case"add":{let e=t.todos,n=te-ot.length;if(e.length>n)return{error:`\u4EFB\u52A1\u4E0A\u9650 ${te}\uFF0C\u5F53\u524D\u5269\u4F59 ${n} \u6761\u7A7A\u4F4D`};let o=e.map(r=>({id:crypto.randomUUID(),content:r.content,status:r.status}));return ot.push(...o),{added:o,tasks:ee()}}case"replace":{let e=t.todos;if(e.length>te)return{error:`\u4EFB\u52A1\u4E0A\u9650 ${te}`};ot.splice(0,ot.length);let n=e.map(o=>({id:crypto.randomUUID(),content:o.content,status:o.status}));return ot.push(...n),{replaced:!0,tasks:ee()}}case"update":{let e=t.todos,n=e.map(s=>s.id);if(new Set(n).size!==n.length)return{error:"\u66F4\u65B0\u5217\u8868\u5B58\u5728\u91CD\u590D id"};let r=new Map(ot.map(s=>[s.id,s]));for(let s of e){let i=r.get(s.id);if(!i)return{error:`\u672A\u627E\u5230\u4EFB\u52A1 id=${s.id}`};i.content=s.content,s.status&&(i.status=s.status)}return{updated:n,tasks:ee()}}case"remove":{let e=t.ids,n=ot.length,o=new Set(e),r=ot.filter(s=>!o.has(s.id));return r.length===n?{error:"\u672A\u627E\u5230\u4EFB\u4F55\u53EF\u5220\u9664\u7684\u4EFB\u52A1 id"}:(ot.splice(0,ot.length,...r),{removed:e,tasks:ee()})}}}var on={name:"todo",description:"\u7BA1\u7406\u5F85\u529E\u5217\u8868\uFF08add/update/remove/replace\uFF09\uFF0C\u6700\u591A 10 \u6761\uFF0C\u4E0D\u6301\u4E45\u5316",inputSchema:Ar,execute:async t=>{let e=$r(t);if(e.error)return S(e.error,!0);let n={op:t.type,count:ot.length,tasks:e.tasks,added:e.added,updated:e.updated,removed:e.removed,replaced:!!e.replaced};return S(JSON.stringify(n))}};import{z as ne}from"zod";import{extname as Ir}from"path";import{gzipSync as Rr}from"zlib";import{access as Nr,readFile as Lr}from"fs/promises";var rn=512e3,sn=2e6,Or=200,an=1e3,Dr=1024,we=4e3,Hr=new Set([".png",".jpg",".jpeg",".gif",".webp",".bmp",".svg"]);function Ur(t){let e=Ir(t).toLowerCase();return Hr.has(e)}function Br(t){return Buffer.from(t).toString("base64")}function Fr(t){try{let e=Rr(t,{level:6});if(e.byteLength<t.byteLength)return{data:new Uint8Array(e),encoding:"gzip+base64"}}catch{}return{data:t,encoding:"base64"}}function jr(t){let e=t.truncated?" truncated=true":"";return`image_base64 (encoding=${t.encoding} original_bytes=${t.originalBytes} payload_bytes=${t.payloadBytes} base64_length=${t.base64.length}${e}): ${t.base64}`}var zr=ne.object({file_path:ne.string().min(1),offset:ne.number().int().positive().optional(),limit:ne.number().int().positive().optional()}).strict(),cn={name:"read",description:"\u8BFB\u53D6\u6307\u5B9A\u6587\u4EF6\u5185\u5BB9\uFF0C\u53EF\u6309 offset/limit \u622A\u53D6\u5E76\u9644\u5E26\u884C\u53F7",inputSchema:zr,execute:async t=>{let e=ct(t.file_path),n=t.offset??1,o=t.limit??Or,r=Math.min(o,an);try{await Nr(e);let s=await Lr(e),i=s.byteLength;if(Ur(e)){if(i>sn)return S(`\u56FE\u7247\u8FC7\u5927\uFF08${i} bytes\uFF09\uFF0C\u8D85\u8FC7\u9608\u503C ${sn} bytes\uFF0C\u5DF2\u62D2\u7EDD\u8BFB\u53D6`,!0);let b=new Uint8Array(s),{data:k,encoding:D}=Fr(b),E=Br(k),Y=E.length>we?`${E.slice(0,we)}...`:E,w=E.length>we;return S(jr({encoding:D,base64:Y,originalBytes:b.byteLength,payloadBytes:k.byteLength,truncated:w}))}if(i>rn)return S(`\u6587\u4EF6\u8FC7\u5927\uFF08${i} bytes\uFF09\uFF0C\u5DF2\u62D2\u7EDD\u8BFB\u53D6\uFF0C\u9608\u503C ${rn} bytes`,!0);let c=new Uint8Array(s),l=Math.min(c.length,Dr);for(let b=0;b<l;b++)if(c[b]===0)return S("\u68C0\u6D4B\u5230\u4E8C\u8FDB\u5236\u5185\u5BB9\uFF0C\u5DF2\u62D2\u7EDD\u76F4\u63A5\u8BFB\u53D6",!0);let m=new TextDecoder().decode(c).split(/\r?\n/),y=Math.max(0,n-1),g=Math.min(m.length,y+r),C=m.slice(y,g).map((b,k)=>`${y+k+1}: ${b}`).join(`
13
+ `),N=g<m.length,Z=o!==r||N&&t.limit===void 0?`
14
+ ... (truncated, showing ${r} lines; max=${an})`:"";return S(C+Z)}catch(s){return S(`\u8BFB\u53D6\u5931\u8D25: ${s.message}`,!0)}}};import{z as Ce}from"zod";import{dirname as Gr}from"path";import{mkdir as Vr,writeFile as Wr}from"fs/promises";var Jr=Ce.object({file_path:Ce.string().min(1),content:Ce.any().optional()}).strict();function Kr(t){if(t instanceof Uint8Array)return{data:t,info:`bytes=${t.byteLength}`};if(t instanceof ArrayBuffer){let n=new Uint8Array(t);return{data:n,info:`bytes=${n.byteLength}`}}if(typeof t=="string")return{data:t,info:`text_length=${t.length}`};let e=JSON.stringify(t??"",null,2);return{data:e,info:`json_length=${e.length}`}}var ln={name:"write",description:"\u521B\u5EFA\u6216\u8986\u76D6\u6587\u4EF6\uFF0C\u4F20\u5165 file_path \u4E0E content",inputSchema:Jr,execute:async t=>{let e=ct(t.file_path),{data:n,info:o}=Kr(t.content);try{return await Vr(Gr(e),{recursive:!0}),await Wr(e,n instanceof Uint8Array?n:String(n)),S(`\u5DF2\u5199\u5165 ${e} (overwrite, ${o})`)}catch(r){return S(`\u5199\u5165\u5931\u8D25: ${r.message}`,!0)}}};function Xr(t){let e=t.toJSONSchema(),{$schema:n,...o}=e;return o}function Yr(t){return{name:t.name,description:t.description,source:"native",inputSchema:Xr(t.inputSchema),execute:t.execute}}function un(t){return t.map(Yr)}var qr={bash:Je,read:cn,write:ln,edit:Ke,glob:Ze,grep:Qe,webfetch:qe,save_memory:nn,todo:on},Zr=Object.values(qr),mn=un(Zr);import ws from"openai";import{encoding_for_model as Qr,get_encoding as ts}from"@dqbd/tiktoken";var pn="cl100k_base";function es(t){let e=t?.trim()||pn;try{let n=()=>Qr(e);return n().free(),{model:e,factory:n}}catch{let n=pn,o=()=>ts(n);return o().free(),{model:n,factory:o}}}function dn(t){let{model:e,factory:n}=es(t),o=n(),r=4,s=2,i=1,a=l=>l?o.encode(l).length:0;return{model:e,countText:a,countMessages:l=>{if(!l.length)return 0;let u=0;for(let m of l)u+=r,u+=a(m.content),m.name&&(u+=i);return u+=s,u},dispose:()=>o.free()}}import{mkdir as ns,writeFile as os,readFile as rs,access as ss}from"fs/promises";import{homedir as gn}from"os";import{dirname as is,join as _t}from"path";import{randomUUID as dl}from"crypto";import{parse as as}from"toml";var cs=_t(gn(),".memo"),ls="sessions",us="Agents.md",Ft={current_provider:"deepseek",stream_output:!1,providers:[{name:"deepseek",env_api_key:"DEEPSEEK_API_KEY",model:"deepseek-chat",base_url:"https://api.deepseek.com"}],mcp_servers:{}};function ms(t){return/^[A-Za-z0-9_-]+$/.test(t)?t:JSON.stringify(t)}function ps(t){if(!t||typeof t!="object"||Array.isArray(t))return[];let e=[];for(let[n,o]of Object.entries(t)){if(!o)continue;let r=Array.isArray(o)?o:[o];for(let s of r){if(!s||typeof s!="object")continue;let i={...s};(typeof i.name!="string"||i.name.length===0)&&n&&(i.name=n),e.push(i)}}return e}function fn(t){return t.startsWith("~")?_t(gn(),t.slice(1)):t}function ds(t){let e=t.providers.map(r=>{let s=typeof r?.name=="string"?r.name:"";if(!s)return"";let a=[`[[providers.${ms(s)}]]`,`name = ${JSON.stringify(s)}`,`env_api_key = ${JSON.stringify(String(r.env_api_key??""))}`,`model = ${JSON.stringify(String(r.model??""))}`];return r.base_url&&a.push(`base_url = ${JSON.stringify(String(r.base_url))}`),a.join(`
15
+ `)}).filter(Boolean).join(`
16
+
17
+ `),n="";return t.mcp_servers&&Object.keys(t.mcp_servers).length>0&&(n=Object.entries(t.mcp_servers).map(([r,s])=>{if("url"in s){let c=[`[mcp_servers.${r}]`];if(c.push(`type = "${s.type??"streamable_http"}"`),c.push(`url = "${s.url}"`),"fallback_to_sse"in s&&s.fallback_to_sse!==void 0&&c.push(`fallback_to_sse = ${s.fallback_to_sse}`),s.headers&&Object.keys(s.headers).length>0){let l=Object.entries(s.headers).map(([u,m])=>`${JSON.stringify(u)} = ${JSON.stringify(m)}`).join(", ");c.push(`headers = { ${l} }`)}return c.join(`
18
+ `)}let i=s.args?`args = ${JSON.stringify(s.args)}`:"",a=s.type?`type = "${s.type}"
19
+ `:"";return`[mcp_servers.${r}]
20
+ ${a}command = "${s.command}"
21
+ ${i}`}).join(`
22
+
23
+ `)),[`
24
+ current_provider = "${t.current_provider}"
25
+ stream_output = ${t.stream_output??!1}
26
+ `.trim(),e,n].filter(Boolean).join(`
27
+
28
+ `)}async function jt(t,e){await ns(is(t),{recursive:!0}),await os(t,ds(e),"utf-8")}async function Pt(){let t=process.env.MEMO_HOME?fn(process.env.MEMO_HOME):cs,e=_t(t,"config.toml");try{await ss(e);let n=await rs(e,"utf-8"),o=as(n),r=ps(o.providers),s={current_provider:o.current_provider??Ft.current_provider,stream_output:o.stream_output??Ft.stream_output,providers:r,mcp_servers:o.mcp_servers??{}},i=!s.providers.length;return{config:i?Ft:s,home:t,configPath:e,needsSetup:i}}catch{return{config:Ft,home:t,configPath:e,needsSetup:!0}}}function zt(t,e){let n=e||t.current_provider,o=t.providers.find(r=>r.name===n);return o||(t.providers?.[0]??Ft.providers[0])}function oe(t,e){let n=e.historyDir??_t(t.home,ls);return fn(n)}function hn(t){return _t(t.home,us)}function gs(t,e=100){return t.replace(/[\\/:\s]+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"").slice(0,e)||"root"}function fs(t,e=180){let n=[],o=0;for(let r of t){let s=r.slice(0,Math.max(1,e-o-(n.length>0?1:0)));if(n.push(s),o+=s.length+(n.length>1?1:0),o>=e)break}return n}function yn(t,e){let n=new Date,o=String(n.getFullYear()),r=String(n.getMonth()+1).padStart(2,"0"),s=String(n.getDate()).padStart(2,"0"),i=String(n.getHours()).padStart(2,"0"),a=String(n.getMinutes()).padStart(2,"0"),c=String(n.getSeconds()).padStart(2,"0"),l=Tn(process.cwd()),u=`${o}-${r}-${s}_${i}${a}${c}_${e}.jsonl`;return _t(t,l,u)}function Tn(t){let e=t.split(/[/\\]+/).map(o=>gs(o));return fs(e,180).join("-")||"root"}function xn(t,e){return _t(t,Tn(e))}var re=class{tools=new Map;register(e){this.tools.set(e.name,e)}registerMany(e){for(let n of e)this.register(n)}get(e){return this.tools.get(e)}getAll(){return Array.from(this.tools.values())}toRegistry(){let e={};for(let[n,o]of this.tools)e[n]=o;return e}has(e){return this.tools.has(e)}get size(){return this.tools.size}};import{Client as hs}from"@modelcontextprotocol/sdk/client/index.js";import{SSEClientTransport as Sn}from"@modelcontextprotocol/sdk/client/sse.js";import{StreamableHTTPClientTransport as ys}from"@modelcontextprotocol/sdk/client/streamableHttp.js";import{StdioClientTransport as Ts}from"@modelcontextprotocol/sdk/client/stdio.js";function se(){return new hs({name:"memo-code-cli-client",version:"1.0.0"},{capabilities:{}})}function xs(t){if(!(!t||Object.keys(t).length===0))return{headers:t}}async function Ss(t){let e=new URL(t.url),n=xs(t.headers);if(t.type==="sse"){let r=se(),s=new Sn(e,{requestInit:n});return await r.connect(s),{client:r,transport:s}}let o=t.fallback_to_sse??!0;try{let r=se(),s=new ys(e,{requestInit:n});return await r.connect(s),{client:r,transport:s}}catch(r){if(!o)throw r;try{let s=se(),i=new Sn(e,{requestInit:n});return await s.connect(i),{client:s,transport:i}}catch(s){let i=`Failed to connect via streamable_http (${r.message}); SSE fallback failed (${s.message})`,a=new Error(i);throw a.cause={streamErr:r,sseErr:s},a}}}async function ks(t){if("url"in t)return Ss(t);let e=new Ts({command:t.command,args:t.args}),n=se();return await n.connect(e),{client:n,transport:e}}var ie=class{connections=new Map;async connect(e,n){let o=this.connections.get(e);if(o)return o;let{client:r,transport:s}=await ks(n),i=await r.listTools(),a={name:e,client:r,transport:s,tools:(i.tools||[]).map(c=>({name:`${e}_${c.name}`,description:c.description||`Tool from ${e}: ${c.name}`,source:"mcp",serverName:e,originalName:c.name,inputSchema:c.inputSchema,execute:async()=>({content:[]})}))};return this.connections.set(e,a),a}get(e){return this.connections.get(e)}getAll(){return Array.from(this.connections.values())}getAllTools(){let e=[];for(let n of this.connections.values())for(let o of n.tools)e.push({name:o.name,description:o.description,serverName:o.serverName,originalName:o.originalName,inputSchema:o.inputSchema,client:n.client});return e}async closeAll(){let e=Array.from(this.connections.values()).map(async n=>{try{await n.client.close()}catch(o){console.error(`[MCP] Error closing client ${n.name}:`,o)}});await Promise.all(e),this.connections.clear()}get size(){return this.connections.size}};var ae=class{pool;tools=new Map;constructor(){this.pool=new ie}async loadServers(e){if(!e||Object.keys(e).length===0)return 0;let n=0;return await Promise.all(Object.entries(e).map(async([o,r])=>{try{let s=await this.pool.connect(o,r);for(let i of s.tools){let a={...i,execute:async c=>{let l=this.pool.get(i.serverName)?.client;if(!l)throw new Error(`MCP client for server '${i.serverName}' not found`);return l.callTool({name:i.originalName,arguments:c})}};this.tools.set(a.name,a)}n+=s.tools.length,console.log(`[MCP] Connected to '${o}' with ${s.tools.length} tools`)}catch(s){console.error(`[MCP] Failed to connect to server '${o}':`,s)}})),n}get(e){return this.tools.get(e)}getAll(){return Array.from(this.tools.values())}toRegistry(){let e={};for(let[n,o]of this.tools)e[n]=o;return e}has(e){return this.tools.has(e)}get size(){return this.tools.size}async dispose(){await this.pool.closeAll(),this.tools.clear()}getPool(){return this.pool}};var ce=class{nativeRegistry;mcpRegistry;constructor(){this.nativeRegistry=new re,this.mcpRegistry=new ae}registerNativeTool(e){this.nativeRegistry.register(e)}registerNativeTools(e){for(let n of e)this.registerNativeTool(n)}async loadMcpServers(e){return this.mcpRegistry.loadServers(e)}getTool(e){return this.nativeRegistry.get(e)??this.mcpRegistry.get(e)}getAllTools(){return[...this.nativeRegistry.getAll(),...this.mcpRegistry.getAll()]}toRegistry(){return{...this.nativeRegistry.toRegistry(),...this.mcpRegistry.toRegistry()}}hasTool(e){return this.nativeRegistry.has(e)||this.mcpRegistry.has(e)}getToolCount(){let e=this.nativeRegistry.size,n=this.mcpRegistry.size;return{native:e,mcp:n,total:e+n}}async execute(e,n){let o=this.getTool(e);if(!o)throw new Error(`Tool '${e}' not found`);return o.execute(n)}generateToolDefinitions(){return this.getAllTools().map(e=>({name:e.name,description:e.description,input_schema:e.inputSchema||{type:"object",properties:{}}}))}generateToolDescriptions(){let e=this.getAllTools();if(e.length===0)return"";let n=[];n.push("## Available Tools"),n.push("");let o=e.filter(s=>s.source==="native"),r=e.filter(s=>s.source==="mcp");if(o.length>0){n.push("### Built-in Tools"),n.push("");for(let s of o)n.push(this.formatToolDescription(s));n.push("")}if(r.length>0){n.push("### External MCP Tools"),n.push("");let s=this.groupByServer(r);for(let[i,a]of Object.entries(s)){n.push(`**Server: ${i}**`),n.push("");for(let c of a)n.push(this.formatToolDescription(c));n.push("")}}return n.join(`
29
+ `)}formatToolDescription(e){let n=[];return n.push(`#### ${e.name}`),n.push(`- **Description**: ${e.description}`),e.inputSchema&&Object.keys(e.inputSchema).length>0&&n.push(`- **Input Schema**: ${JSON.stringify(e.inputSchema)}`),n.join(`
30
+ `)}groupByServer(e){let n={};for(let o of e)if(o.source==="mcp"){let r=o.serverName;n[r]||(n[r]=[]),n[r].push(o)}return n}getToolDescriptions(){return this.getAllTools().map(e=>({name:e.name,description:e.description,source:e.source,serverName:e.source==="mcp"?e.serverName:void 0,inputSchema:e.inputSchema}))}async dispose(){await this.mcpRegistry.dispose()}};import{readFile as Cs,access as bs}from"fs/promises";function _s(t){try{return{ok:!0,data:JSON.parse(t)}}catch(e){return{ok:!1,raw:t,error:e.message}}}async function kn(t,e,n){let o=await Pt(),r=o.config,s=new ce;if(s.registerNativeTools(mn),await s.loadMcpServers(r.mcp_servers),t.tools)for(let[g,_]of Object.entries(t.tools))s.registerNativeTool({name:g,description:_.description,source:"native",inputSchema:{type:"object"},execute:_.execute});let i=s.toRegistry(),a=async()=>{let g=await(t.loadPrompt??Ve)(),_=s.generateToolDescriptions();_&&(g+=`
31
+
32
+ ${_}`);let C=hn(o);try{await bs(C);let N=(await Cs(C,"utf-8")).trim();N&&(g+=`
33
+
34
+ # Long-Term Memory
35
+ ${N}`)}catch{}return g},c=s.generateToolDefinitions(),l=e.stream??r.stream_output??!1,u=oe(o,e),m=yn(u,n),y=new Qt(m);return{tools:i,dispose:async()=>{t.dispose&&await t.dispose(),await s.dispose()},callLLM:t.callLLM??(async(g,_,C)=>{let N=zt(r,e.providerName),J=process.env[N.env_api_key]??process.env.OPENAI_API_KEY??process.env.DEEPSEEK_API_KEY;if(!J)throw new Error(`Missing env var ${N.env_api_key} (or OPENAI_API_KEY/DEEPSEEK_API_KEY)`);let f=new ws({apiKey:J,baseURL:N.base_url}),Z=c.length>0?c.map(b=>({type:"function",function:{name:b.name,description:b.description,parameters:b.input_schema}})):void 0;if(l){let b=await f.chat.completions.create({model:N.model,messages:g,stream:!0},{signal:C?.signal}),k="";for await(let D of b){let E=D.choices?.[0]?.delta?.content;E&&(k+=E,_?.(E))}return{content:k,streamed:!0}}else{let b=await f.chat.completions.create({model:N.model,messages:g,tools:Z,tool_choice:Z?"auto":void 0},{signal:C?.signal}),k=b.choices?.[0]?.message;if(k?.tool_calls&&k.tool_calls.length>0){let E=[];k.content&&E.push({type:"text",text:k.content});for(let w of k.tool_calls)if(w.type==="function"){let h=_s(w.function.arguments);h.ok?E.push({type:"tool_use",id:w.id,name:w.function.name,input:h.data}):E.push({type:"text",text:`[tool_use parse error] ${h.error}; raw: ${h.raw}`})}let Y=E.some(w=>w.type==="tool_use");return{content:E,stop_reason:Y?"tool_use":"end_turn",usage:{prompt:b.usage?.prompt_tokens??void 0,completion:b.usage?.completion_tokens??void 0,total:b.usage?.total_tokens??void 0}}}let D=k?.content;if(typeof D!="string")throw new Error("OpenAI-compatible API returned empty content");return{content:[{type:"text",text:D}],stop_reason:"end_turn",usage:{prompt:b.usage?.prompt_tokens??void 0,completion:b.usage?.completion_tokens??void 0,total:b.usage?.total_tokens??void 0}}}}),loadPrompt:a,historySinks:t.historySinks??[y],tokenCounter:t.tokenCounter??dn(e.tokenizerModel),historyFilePath:m}}import{jsonrepair as vs}from"jsonrepair";function be(t){try{return JSON.parse(t)}catch{try{let e=vs(t);return JSON.parse(e)}catch{return null}}}function Es(t){let e=[],n=new Set,o=/```(?:json)?\s*(\{[\s\S]*?\})\s*(?:```|$)/g,r;for(;(r=o.exec(t))!==null;){let i=r[1]||"";if(!(!i||n.has(i)))try{let a=be(i);if(a===null)continue;e.push({json:i,start:r.index,end:r.index+r[0].length,obj:a}),n.add(i)}catch{}}let s=/\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}/g;for(;(r=s.exec(t))!==null;){let i=r[0];if(!(!i||n.has(i))&&!(!i.includes('"tool"')&&!i.includes('"final"')))try{let a=be(i);if(a===null)continue;e.push({json:i,start:r.index,end:r.index+i.length,obj:a}),n.add(i)}catch{}}return e}function Ms(t){let e=[],n=/<\s*(think|thinking)\s*>([\s\S]*?)<\/\s*\1\s*>/gi,o=t.replace(n,(r,s,i)=>{let a=(i??"").trim();return a&&e.push(a),a});return{thinkingParts:e,cleaned:o.trim()}}function _e(t){if(t.length===0)return;let e=t.join(`
36
+ `),{thinkingParts:n,cleaned:o}=Ms(e);return n.length>0?n.join(`
37
+
38
+ `):o||void 0}function wn(t){return t!==null&&typeof t=="object"&&"tool"in t&&typeof t.tool=="string"}function Cn(t){return t!==null&&typeof t=="object"&&"final"in t&&typeof t.final=="string"}function bn(t){let e={},n=Es(t);for(let{json:r,start:s,end:i,obj:a}of n){if(wn(a)){e.action={tool:a.tool.trim(),input:a.input};let c=t.slice(0,s).trim(),l=t.slice(i).trim(),u=[];c&&u.push(c),l&&u.push(l);let m=_e(u);return m&&(e.thinking=m),e}if(Cn(a))return e.final=a.final,e}let o=t.trim();if(o.startsWith("{")&&o.endsWith("}")){let r=be(o);if(r&&wn(r))return e.action={tool:r.tool,input:r.input},e;if(r&&Cn(r))return e.final=r.final,e}return t.trim()&&(e.final=t),e}import{randomUUID as $n}from"crypto";function Ps(){return{onTurnStart:[],onAction:[],onObservation:[],onFinal:[]}}function _n(t,e){e&&(e.onTurnStart&&t.onTurnStart.push(e.onTurnStart),e.onAction&&t.onAction.push(e.onAction),e.onObservation&&t.onObservation.push(e.onObservation),e.onFinal&&t.onFinal.push(e.onFinal))}function vn(t){let e=Ps();if(_n(e,t.hooks),Array.isArray(t.middlewares))for(let n of t.middlewares)_n(e,n);return e}async function at(t,e,n){let o=t[e];if(o.length)for(let r of o)try{await r(n)}catch(s){console.warn(`Hook ${e} failed: ${s.message}`)}}function At(t){return t.map(e=>({...e}))}var In="interactive";function En(){return{prompt:0,completion:0,total:0}}function Mn(t,e){if(!e)return;let n=e.prompt??0,o=e.completion??0,r=e.total??n+o;t.prompt+=n,t.completion+=o,t.total+=r}function As(t){if(typeof t=="string")return{textContent:t,toolUseBlocks:[]};if("content"in t&&typeof t.content=="string")return{textContent:t.content,toolUseBlocks:[],usage:"usage"in t?t.usage:void 0,streamed:"streamed"in t?t.streamed:void 0};if("content"in t&&Array.isArray(t.content)){let e=t.content.filter(o=>o.type==="text"),n=t.content.filter(o=>o.type==="tool_use");return{textContent:e.map(o=>o.text).join(`
39
+ `),toolUseBlocks:n.map(o=>({id:o.id,name:o.name,input:o.input})),stopReason:"stop_reason"in t?t.stop_reason:void 0,usage:"usage"in t?t.usage:void 0}}return{textContent:"",toolUseBlocks:[]}}async function $s(t,e){for(let n of e)try{await n.append(t)}catch(o){console.error(`Failed to write history event: ${o.message}`)}}function Pn(t){return(t.content?.flatMap(n=>n.type==="text"?[n.text]:[])??[]).join(`
40
+ `)}function An(t,e){let n=e;if(typeof e=="string"){let o=e.trim();if(o)try{n=JSON.parse(o)}catch{n=o}else n={}}return typeof n!="object"||n===null?{ok:!1,error:`${t.name} invalid input: expected object`}:{ok:!0,data:n}}function Is(t){return t instanceof Error&&t.name==="AbortError"}function le(t){return t===null||typeof t!="object"?JSON.stringify(t):Array.isArray(t)?`[${t.map(n=>le(n)).join(",")}]`:`{${Object.entries(t).sort(([n],[o])=>n.localeCompare(o)).map(([n,o])=>`${JSON.stringify(n)}:${le(o)}`).join(",")}}`}var ve=class{constructor(e,n,o,r,s){this.deps=e;this.options=n;this.id=n.sessionId||$n(),this.mode=n.mode||In,this.history=[{role:"system",content:o}],this.tokenCounter=r,this.sinks=e.historySinks??[],this.hooks=vn(e),this.historyFilePath=s}id;mode;history;historyFilePath;turnIndex=0;tokenCounter;sinks;sessionUsage=En();startedAt=Date.now();hooks;closed=!1;sessionStartEmitted=!1;currentAbortController=null;cancelling=!1;lastActionSignature=null;repeatedActionCount=0;async init(){}resetActionRepetition(){this.lastActionSignature=null,this.repeatedActionCount=0}maybeWarnRepeatedAction(e,n){let o=`${e}:${le(n)}`;if(this.lastActionSignature===o?this.repeatedActionCount+=1:(this.lastActionSignature=o,this.repeatedActionCount=1),this.repeatedActionCount===3){let r=le(n).slice(0,200),s=`\u7CFB\u7EDF\u63D0\u9192\uFF1A\u4F60\u5DF2\u8FDE\u7EED3\u6B21\u8C03\u7528\u540C\u4E00\u5DE5\u5177\u300C${e}\u300D\u4E14\u53C2\u6570\u76F8\u540C\uFF08${r}${r.length>=200?"\u2026":""}\uFF09\u3002\u8BF7\u786E\u8BA4\u662F\u5426\u9677\u5165\u5FAA\u73AF\uFF0C\u5FC5\u8981\u65F6\u76F4\u63A5\u7ED9\u51FA\u6700\u7EC8\u56DE\u7B54\u6216\u8C03\u6574\u53C2\u6570\u3002`;this.history.push({role:"system",content:s})}}async runTurn(e){let n=new AbortController;this.currentAbortController=n,this.cancelling=!1,this.turnIndex+=1;let o=this.turnIndex,r=[],s=En(),i=Date.now();this.sessionStartEmitted||(await this.emitEvent("session_start",{meta:{mode:this.mode,tokenizer:this.tokenCounter.model,warnPromptTokens:this.options.warnPromptTokens,maxPromptTokens:this.options.maxPromptTokens}}),this.sessionStartEmitted=!0),this.history.push({role:"user",content:e});try{let a=this.tokenCounter.countMessages(this.history);await this.emitEvent("turn_start",{turn:o,content:e,meta:{tokens:{prompt:a}}}),await at(this.hooks,"onTurnStart",{sessionId:this.id,turn:o,input:e,promptTokens:a,history:At(this.history)});let c="",l="ok",u;for(let m=0;;m++){let y=this.tokenCounter.countMessages(this.history);if(this.options.maxPromptTokens&&y>this.options.maxPromptTokens){let w=`Context tokens (${y}) exceed the limit. Please shorten the input or restart the session.`,h=JSON.stringify({final:w});this.history.push({role:"assistant",content:h}),l="prompt_limit",c=w,u=w,await this.emitEvent("final",{turn:o,step:m,content:w,role:"assistant",meta:{tokens:{prompt:y}}}),await at(this.hooks,"onFinal",{sessionId:this.id,turn:o,step:m,finalText:w,status:l,errorMessage:u,turnUsage:{...s},steps:r});break}this.options.warnPromptTokens&&y>this.options.warnPromptTokens&&console.warn(`Prompt tokens are near the limit: ${y}`);let g="",_=[],C,N=!1,J;try{let w=await this.deps.callLLM(this.history,H=>this.deps.onAssistantStep?.(H,m),{signal:n.signal}),h=As(w);g=h.textContent,_=h.toolUseBlocks,J=h.stopReason,C=h.usage,N=!!h.streamed}catch(w){if(this.cancelling&&Is(w)){l="cancelled",c="",u="Turn cancelled",await this.emitEvent("final",{turn:o,step:m,content:"",role:"assistant",meta:{cancelled:!0}}),await at(this.hooks,"onFinal",{sessionId:this.id,turn:o,step:m,finalText:c,status:l,errorMessage:u,turnUsage:{...s},steps:r});break}let h=`LLM call failed: ${w.message}`,H=JSON.stringify({final:h});this.history.push({role:"assistant",content:H}),l="error",c=h,u=h,await this.emitEvent("final",{turn:o,content:h,role:"assistant"}),await at(this.hooks,"onFinal",{sessionId:this.id,turn:o,step:m,finalText:c,status:l,errorMessage:u,turnUsage:{...s},steps:r});break}N||this.deps.onAssistantStep?.(g,m);let f;if(_.length>0){let w=_[0];if(w){let h=g?_e([g]):void 0;f={action:{tool:w.name,input:w.input},thinking:h}}else f={}}else g?f=bn(g):f={};let Z=f.action?JSON.stringify({tool:f.action.tool,input:f.action.input}):f.final?JSON.stringify({final:f.final}):g;this.history.push({role:"assistant",content:Z});let b=this.tokenCounter.countText(g),k=C?.prompt??y,D=C?.completion??b,E=C?.total??k+D,Y={prompt:k,completion:D,total:E};if(Mn(s,Y),Mn(this.sessionUsage,Y),r.push({index:m,assistantText:g,parsed:f,tokenUsage:Y}),await this.emitEvent("assistant",{turn:o,step:m,content:g,role:"assistant",meta:{tokens:Y}}),_.length>1){for(let A of _)this.maybeWarnRepeatedAction(A.name,A.input);let w=await Promise.allSettled(_.map(async A=>{let U=this.deps.tools[A.name];if(!U)return{id:A.id,tool:A.name,observation:`Unknown tool: ${A.name}`};try{let F=An(U,A.input);if(!F.ok)return{id:A.id,tool:A.name,observation:F.error};let nt=await U.execute(F.data),Et=Pn(nt)||"(no tool output)";return{id:A.id,tool:A.name,observation:Et}}catch(F){return{id:A.id,tool:A.name,observation:`Tool execution failed: ${F.message}`}}}));await this.emitEvent("action",{turn:o,step:m,meta:{tools:_.map(A=>A.name),parallel:!0,thinking:f.thinking,toolBlocks:_.map(A=>({name:A.name,input:A.input}))}});let h=_[0];h&&await at(this.hooks,"onAction",{sessionId:this.id,turn:o,step:m,action:{tool:h.name,input:h.input},thinking:f.thinking,history:At(this.history)});let H=[];for(let[A,U]of w.entries())if(U.status==="fulfilled"){let{tool:F,observation:nt}=U.value;H.push(`[${F}]: ${nt}`),await this.emitEvent("observation",{turn:o,step:m,content:nt,meta:{tool:F,index:A}})}else H.push(`[error]: ${U.reason}`);let j=H.join(`
41
+
42
+ `);this.history.push({role:"user",content:JSON.stringify({observation:j})});let G=r[r.length-1];G&&(G.observation=j),await at(this.hooks,"onObservation",{sessionId:this.id,turn:o,step:m,tool:_.map(A=>A.name).join(", "),observation:j,history:At(this.history)});continue}if(f.action){this.maybeWarnRepeatedAction(f.action.tool,f.action.input),await this.emitEvent("action",{turn:o,step:m,meta:{tool:f.action.tool,input:f.action.input,thinking:f.thinking}}),await at(this.hooks,"onAction",{sessionId:this.id,turn:o,step:m,action:f.action,thinking:f.thinking,history:At(this.history)});let w=this.deps.tools[f.action.tool],h;try{if(w){let j=An(w,f.action.input);if(!j.ok)h=j.error;else{let G=await w.execute(j.data);h=Pn(G)||"(no tool output)"}}else h=`Unknown tool: ${f.action.tool}`}catch(j){h=`Tool execution failed: ${j.message}`}this.history.push({role:"user",content:JSON.stringify({observation:h,tool:f.action.tool})});let H=r[r.length-1];H&&(H.observation=h),await this.emitEvent("observation",{turn:o,step:m,content:h,meta:{tool:f.action.tool}}),await at(this.hooks,"onObservation",{sessionId:this.id,turn:o,step:m,tool:f.action.tool,observation:h,history:At(this.history)});continue}if(J==="end_turn"||f.final){this.resetActionRepetition(),c=f.final||g,f.final&&(f.final=c),await this.emitEvent("final",{turn:o,step:m,content:c,role:"assistant",meta:{tokens:Y}}),await at(this.hooks,"onFinal",{sessionId:this.id,turn:o,step:m,finalText:c,status:l,tokenUsage:Y,turnUsage:{...s},steps:r});break}this.resetActionRepetition();break}if(!c&&l!=="cancelled"){l==="ok"&&(l="error"),c="Unable to produce a final answer. Please retry or adjust the request.",u=c;let m=JSON.stringify({final:c});this.history.push({role:"assistant",content:m}),await this.emitEvent("final",{turn:o,content:c,role:"assistant"}),await at(this.hooks,"onFinal",{sessionId:this.id,turn:o,finalText:c,status:l,errorMessage:u,turnUsage:{...s},steps:r})}return await this.emitEvent("turn_end",{turn:o,meta:{status:l,stepCount:r.length,durationMs:Date.now()-i,tokens:s}}),{finalText:c,steps:r,status:l,errorMessage:u,tokenUsage:s}}finally{this.currentAbortController=null,this.cancelling=!1}}cancelCurrentTurn(){this.currentAbortController&&(this.cancelling=!0,this.currentAbortController.abort())}async close(){if(this.closed)return;if(this.closed=!0,this.sessionStartEmitted||this.turnIndex>=0){await this.emitEvent("session_end",{meta:{durationMs:Date.now()-this.startedAt,tokens:this.sessionUsage}});for(let n of this.sinks)if(n.flush)try{await n.flush()}catch(o){console.error(`History flush failed: ${o.message}`)}}this.tokenCounter.dispose(),this.deps.dispose&&await this.deps.dispose()}async emitEvent(e,n){if(!this.sinks.length)return;let o=We({sessionId:this.id,type:e,turn:n.turn,step:n.step,content:n.content,role:n.role,meta:n.meta});await $s(o,this.sinks)}};async function Gt(t,e={}){let n=e.sessionId||$n(),o=await kn(t,{...e,sessionId:n},n),r=await o.loadPrompt(),s=new ve({...t,...o},{...e,sessionId:n,mode:e.mode??In},r,o.tokenCounter,o.historyFilePath);return await s.init(),s}import{useCallback as st,useEffect as je,useMemo as Co,useRef as bo,useState as Q}from"react";import{readFile as va,stat as Ea}from"fs/promises";import{basename as Ma}from"path";import{randomUUID as ze}from"crypto";import{exec as Pa}from"child_process";import{promisify as Aa}from"util";import{Box as _o,useApp as $a,Text as Ia}from"ink";import{Box as Rn,Text as Rs}from"ink";import{memo as Ns}from"react";import{jsx as Nn,jsxs as Ls}from"react/jsx-runtime";var Ln=Ns(function({contextPercent:e=0}){let n=e>0?` ${e.toFixed(1)}%`:" 0.0%";return Nn(Rn,{justifyContent:"flex-end",children:Nn(Rn,{marginTop:8,children:Ls(Rs,{color:"gray",children:["context:",n]})})})});import{Box as St,Static as li,Text as gt}from"ink";import{memo as ui}from"react";import mi from"os";import{Box as Os,Text as On}from"ink";import{memo as Ds}from"react";import{jsx as Hs,jsxs as Dn}from"react/jsx-runtime";var Hn=Ds(function({message:e}){return Dn(Os,{flexDirection:"column",gap:0,children:[Dn(On,{color:"cyan",children:["\u25CF ",e.title]}),Hs(On,{color:"gray",children:e.content})]})});import{Box as si,Text as ii}from"ink";import{memo as ai}from"react";import{Box as Vt,Text as tt}from"ink";import{memo as Xs}from"react";import{Box as Ee,Text as W}from"ink";import{marked as Us}from"marked";import{useMemo as Un,memo as Bs}from"react";import{jsx as q,jsxs as $t}from"react/jsx-runtime";var Fs="#2b2b2b";function ue(t){return t?[{type:"text",raw:t,text:t}]:[]}function js(t,e,n){switch(t.type){case"text":return t.tokens&&t.tokens.length>0?dt(t.tokens,e,`${n}-text`):t.text;case"escape":return t.text;case"strong":return q(W,{bold:!0,children:dt(t.tokens,e,`${n}-strong`)},n);case"em":return q(W,{italic:!0,children:dt(t.tokens,e,`${n}-em`)},n);case"codespan":return q(W,{color:e.codeColor,backgroundColor:Fs,children:t.text},n);case"del":return q(W,{strikethrough:!0,children:dt(t.tokens,e,`${n}-del`)},n);case"link":{let o=t.tokens&&t.tokens.length>0?dt(t.tokens,e,`${n}-link`):t.text,r=t.href&&t.text&&t.text!==t.href?` (${t.href})`:"";return $t(W,{underline:!0,color:e.linkColor,children:[o,r]},n)}case"image":{let o=t.text||"image";return $t(W,{color:e.linkColor,children:["[",o,"](",t.href,")"]},n)}case"br":return`
43
+ `;case"checkbox":return t.checked?"[x]":"[ ]";case"html":return t.text;default:return"text"in t?t.text:t.raw}}function dt(t,e,n){return!t||t.length===0?[]:t.flatMap((o,r)=>{let s=`${n}-${r}`,i=js(o,e,s);return Array.isArray(i)?i:[i]})}function zs(t){let e=t.tokens[0];return e?.type==="paragraph"||e?.type==="text"?e.tokens??ue(e.text):t.tokens.length>0?t.tokens:ue(t.text)}function Gs(t){return t.split(`
44
+ `).map(e=>e.length>0?` ${e}`:"").join(`
45
+ `).trimEnd()}function Vs(t){let e=t.header.map(r=>r.text).join(" | "),n=t.header.map(()=>"---").join(" | "),o=t.rows.map(r=>r.map(s=>s.text).join(" | "));return[e,n,...o].join(`
46
+ `)}function Ws(t){return t.type==="table"&&"header"in t&&"rows"in t&&"align"in t}function Js(t,e,n){switch(t.type){case"space":case"def":return null;case"heading":return q(W,{bold:!0,color:e.textColor,children:dt(t.tokens,e,`${n}-heading`)},n);case"paragraph":{let o=t.tokens??ue(t.text);return q(W,{color:e.textColor,children:dt(o,e,`${n}-para`)},n)}case"text":{let o=t.tokens??ue(t.text);return q(W,{color:e.textColor,children:dt(o,e,`${n}-text`)},n)}case"code":{let o=Gs(t.text);return q(W,{color:e.codeColor,children:o},n)}case"list":{let o=typeof t.start=="number"?t.start:1;return q(Ee,{flexDirection:"column",children:t.items.map((r,s)=>{let i=t.ordered?`${o+s}.`:"-",a=r.task?r.checked?"[x] ":"[ ] ":"",c=zs(r);return $t(Ee,{children:[$t(W,{color:e.textColor,children:[i," "]}),$t(W,{color:e.textColor,children:[a,dt(c,e,`${n}-item-${s}`)]})]},`${n}-item-${s}`)})},n)}case"blockquote":return $t(W,{color:"gray",dimColor:!0,children:["> ",t.text.trim()]},n);case"hr":return q(W,{color:"gray",children:"---"},n);case"table":return Ws(t)?q(W,{color:e.textColor,children:Vs(t)},n):q(W,{color:e.textColor,children:"text"in t?t.text:t.raw},n);case"html":return q(W,{color:e.textColor,children:t.text},n);default:return q(W,{color:e.textColor,children:"text"in t?t.text:t.raw},n)}}function Ks(t,e,n){return t.flatMap((o,r)=>{let s=Js(o,e,`${n}-${r}`);return s?[s]:[]})}var It=Bs(function({text:e,tone:n="normal"}){let o=Un(()=>({textColor:n==="muted"?"gray":void 0,codeColor:n==="muted"?"gray":"cyan",linkColor:n==="muted"?"gray":"blue",muted:n==="muted"}),[n]),r=Un(()=>{let s=Us.lexer(e,{gfm:!0,breaks:!0});return Ks(s,o,"markdown")},[e,o]);return q(Ee,{flexDirection:"column",gap:1,children:r})});import{Fragment as Me,jsx as X,jsxs as Rt}from"react/jsx-runtime";function Ys(t){if(!t)return;if(typeof t=="string")return t.length>50?t.slice(0,47)+"...":t;if(typeof t!="object"||Array.isArray(t))return;let e=t,n=["path","file","filename","url","command","pattern","query","content"];for(let o of n)if(e[o]){let r=String(e[o]);return r.length>50?r.slice(0,47)+"...":r}for(let[o,r]of Object.entries(e))if(typeof r=="string"&&o!=="description")return r.length>50?r.slice(0,47)+"...":r}function qs(t){if(!t)return[];let e=[],n=/\[(\w+)\]: ([\s\S]*?)(?=\n\n\[|$)/g,o;for(;(o=n.exec(t))!==null;){let r=o[2]?.trim()??"",s=o[1]??"unknown",a=r.split(`
47
+ `).filter(c=>c.trim())[0]??r;e.push({tool:s,output:a.slice(0,60)+(a.length>60?"...":"")})}return e}function Zs(t,e){let n=t.step,o=e.step;return!(n.index!==o.index||n.assistantText!==o.assistantText||n.thinking!==o.thinking||n.action?.tool!==o.action?.tool||n.observation!==o.observation||n.toolStatus!==o.toolStatus)}var Bn=Xs(function({step:e,hideAssistantText:n=!1}){let o=e.action?.tool,r=e.action?.input,s=Ys(r),i=qs(e.observation),a=i.length>1;return Rt(Vt,{flexDirection:"column",gap:0,children:[e.thinking&&Rt(Vt,{children:[X(tt,{color:"gray",children:"\u25CF "}),X(Vt,{flexDirection:"column",flexGrow:1,children:X(It,{text:e.thinking,tone:"muted"})})]}),a&&X(Me,{children:i.map((c,l)=>Rt(Vt,{children:[X(tt,{color:"green",children:"\u25CF "}),X(tt,{color:"gray",children:"Used "}),X(tt,{color:"cyan",children:c.tool}),c.output&&Rt(Me,{children:[X(tt,{color:"gray",children:" ("}),X(tt,{color:"cyan",children:c.output}),X(tt,{color:"gray",children:")"})]})]},l))}),!a&&o&&Rt(Vt,{children:[X(tt,{color:"green",children:"\u25CF "}),X(tt,{color:"gray",children:"Used "}),X(tt,{color:"cyan",children:o}),s&&Rt(Me,{children:[X(tt,{color:"gray",children:" ("}),X(tt,{color:"cyan",children:s}),X(tt,{color:"gray",children:")"})]})]})]})},Zs);import{Box as Qs,Text as ti}from"ink";import{memo as ei}from"react";import{jsx as Fn}from"react/jsx-runtime";var jn=ei(function({text:e}){return Fn(Qs,{borderStyle:"round",paddingX:1,paddingY:0,children:Fn(ti,{color:"white",children:e})})});import{Box as Wt,Text as vt}from"ink";import{memo as Pe,useMemo as ni}from"react";import{Fragment as ri,jsx as rt,jsxs as me}from"react/jsx-runtime";function oi(t){let e=/<\s*(think|thinking)\s*>([\s\S]*?)<\/\s*\1\s*>/gi,n=[],o,r=0,s=[];for(;(o=e.exec(t))!==null;){s.push({start:o.index,end:e.lastIndex});let a=o[2]?.trim();a&&n.push(a)}if(s.length===0)return{content:t,thinking:null};let i="";r=0;for(let{start:a,end:c}of s)i+=t.slice(r,a),r=c;return i+=t.slice(r),i=i.replace(/\n{3,}/g,`
48
+
49
+ `).trim(),{content:i,thinking:n.length>0?n.join(`
50
+
51
+ `):null}}var zn=Pe(function({text:e,isThinking:n=!1}){let{content:o,thinking:r}=ni(()=>oi(e),[e]);return n?rt(Wt,{flexDirection:"column",flexGrow:1,children:rt(It,{text:e,tone:"muted"})}):me(Wt,{flexDirection:"column",flexGrow:1,gap:1,children:[r&&rt(Wt,{flexDirection:"column",paddingLeft:2,children:rt(It,{text:r,tone:"muted"})}),rt(It,{text:o,tone:"normal"})]})}),_u=Pe(function({text:e}){return rt(Wt,{children:me(vt,{color:"gray",children:["\u2022 ",e]})})}),vu=Pe(function({toolName:e,fileName:n}){return me(Wt,{children:[rt(vt,{color:"green",children:"\u25CF "}),rt(vt,{color:"gray",children:"Used "}),rt(vt,{color:"cyan",children:e}),n&&me(ri,{children:[rt(vt,{color:"gray",children:" ("}),rt(vt,{color:"cyan",children:n}),rt(vt,{color:"gray",children:")"})]})]})});import{jsx as Ae,jsxs as Gn}from"react/jsx-runtime";function ci(t,e){let n=t.turn,o=e.turn;if(n.index!==o.index||n.userInput!==o.userInput||n.finalText!==o.finalText||n.status!==o.status||n.steps.length!==o.steps.length||n.tokenUsage?.total!==o.tokenUsage?.total)return!1;for(let r=0;r<n.steps.length;r++){let s=n.steps[r],i=o.steps[r];if(!s||!i||s.assistantText!==i.assistantText||s.thinking!==i.thinking||s.action?.tool!==i.action?.tool)return!1}return!0}var $e=ai(function({turn:e}){let n=e.finalText?.trim()??"",o=n.length>0;return Gn(si,{flexDirection:"column",children:[Ae(jn,{text:e.userInput}),e.steps.map(r=>Ae(Bn,{step:r},`step-${e.index}-${r.index}`)),o?Ae(zn,{text:n,isThinking:!1}):null,e.status&&e.status!=="ok"?Gn(ii,{color:"red",children:["Status: ",e.status]}):null]})},ci);import{jsx as et,jsxs as kt}from"react/jsx-runtime";function pi(t){let e=mi.homedir();return e&&t.startsWith(e)?`~${t.slice(e.length)}`:t}function di(t){return t.length>16?`${t.slice(0,8)}...${t.slice(-4)}`:t}function gi(t,e){if(t.headerInfo?.sessionId!==e.headerInfo?.sessionId||t.systemMessages.length!==e.systemMessages.length)return!1;for(let n=0;n<t.systemMessages.length;n++)if(t.systemMessages[n]?.id!==e.systemMessages[n]?.id)return!1;if(t.turns.length!==e.turns.length)return!1;for(let n=0;n<t.turns.length;n++)if(t.turns[n]!==e.turns[n])return!1;return!0}var Vn=ui(function({systemMessages:e,turns:n,headerInfo:o}){let r=n.length>0?n[n.length-1]:void 0,s=r&&(r.finalText||r.status&&r.status!=="ok"),i=s?n:n.slice(0,-1),a=s?void 0:r,c=[];o&&c.push({type:"header",data:o});for(let l of e)c.push({type:"system",data:l});for(let l of i)c.push({type:"turn",data:l});return kt(St,{flexDirection:"column",gap:0,children:[et(li,{items:c,children:l=>{if(l.type==="header"&&l.data){let u=l.data;return kt(St,{borderStyle:"round",borderColor:"blueBright",paddingX:2,paddingY:1,flexDirection:"column",gap:1,children:[et(St,{gap:1,alignItems:"center",children:kt(St,{flexDirection:"column",children:[et(gt,{bold:!0,children:"Welcome to Memo Code CLI!"}),et(gt,{color:"gray",children:"Send /help for help information."})]})}),kt(St,{flexDirection:"column",gap:0,children:[kt(St,{children:[et(gt,{color:"gray",children:"Directory: "}),et(gt,{color:"cyan",children:pi(u.cwd)})]}),kt(St,{children:[et(gt,{color:"gray",children:"Session: "}),et(gt,{color:"cyan",children:di(u.sessionId)})]}),kt(St,{children:[et(gt,{color:"gray",children:"Model: "}),et(gt,{color:"cyan",children:u.model}),kt(gt,{color:"gray",children:[" ","(powered by ",u.providerName,")"]})]})]})]},"header")}return l.type==="system"?et(Hn,{message:l.data},l.data.id):l.type==="turn"?et($e,{turn:l.data},`turn-${l.data.index}`):null}}),a&&et($e,{turn:a},`turn-live-${a.index}`)]})},gi);import{useCallback as ho,useEffect as Ue,useMemo as Be,useRef as Fe,useState as wt}from"react";import{readFile as na,readdir as oa,stat as ra}from"fs/promises";import{basename as sa,join as ia,resolve as yo}from"path";import{Box as ye,Text as yt,useInput as aa}from"ink";import ca from"os";var no=Ho(to(),1);import{readFile as Li,readdir as Oi}from"fs/promises";import{join as eo,relative as Di,sep as Hi}from"path";var Ui=6,Bi=2500,oo=25,Fi=[".git",".svn",".hg","node_modules","dist","build",".next",".turbo",".cache",".output","coverage","tmp","temp","logs","*.log"],fe=new Map;function ji(t){return t.split(Hi).join("/")}function zi(t,e){return JSON.stringify({maxDepth:t.maxDepth,maxEntries:t.maxEntries,respectGitIgnore:t.respectGitIgnore,ignoreGlobs:t.ignoreGlobs,gitignore:e})}function Gi(t){return{maxDepth:typeof t.maxDepth=="number"?Math.max(1,t.maxDepth):Ui,maxEntries:typeof t.maxEntries=="number"?Math.max(100,t.maxEntries):Bi,limit:typeof t.limit=="number"?Math.max(1,t.limit):oo,respectGitIgnore:t.respectGitIgnore!==!1,ignoreGlobs:t.ignoreGlobs?.length?t.ignoreGlobs:[]}}async function Vi(t,e){if(!e)return"";try{return await Li(eo(t,".gitignore"),"utf8")}catch{}return""}async function Wi(t,e){let n=await Vi(e,t.respectGitIgnore),o=(0,no.default)();o.add(Fi),t.ignoreGlobs.length&&o.add(t.ignoreGlobs),n.trim()&&o.add(n);let r=zi(t,n);return Object.assign(o,{__memoSignature:r})}async function Ji(t,e,n){let o=[],r=e.maxEntries,s=async(i,a)=>{if(o.length>=r)return;let c;try{c=await Oi(i,{withFileTypes:!0})}catch{return}for(let l of c){if(o.length>=r)break;if(l.isSymbolicLink())continue;let u=eo(i,l.name),m=Di(t,u);if(!m)continue;let y=ji(m);if(n.ignores(y))continue;let g=y.split("/").filter(Boolean),_=g.map(N=>N.toLowerCase()),C=l.isDirectory();if(o.push({path:y,pathLower:y.toLowerCase(),segments:g,segmentsLower:_,depth:a,isDir:C}),o.length>=r)break;C&&a<e.maxDepth&&await s(u,a+1)}};return await s(t,0),o.sort((i,a)=>i.path.localeCompare(a.path)),{entries:o,signature:n.__memoSignature}}async function Ki(t,e){let n=Gi(e),o=await Wi(n,t),r=o.__memoSignature,s=fe.get(t);if(s&&s.signature===r)return s.pending?s.pending:s.entries;let i=Ji(t,n,o).then(a=>(fe.set(t,{entries:a.entries,signature:a.signature}),a.entries)).catch(a=>{throw fe.delete(t),a});return fe.set(t,{entries:[],signature:r,pending:i}),i}function Xi(t){return t.depth+(t.isDir?-.2:.2)}function Yi(t,e){if(!e.length)return Xi(t);let n=t.depth,o=0;for(let r of e){let s=-1;for(let i=o;i<t.segmentsLower.length;i++){let a=t.segmentsLower[i];if(a.startsWith(r)){s=i,n+=(i-o)*1.5,n+=a.length-r.length;break}let c=a.indexOf(r);if(c!==-1){s=i,n+=(i-o)*2+c+2;break}}if(s===-1)return null;o=s+1}return t.isDir&&(n-=.5),n}function qi(t,e,n){let s=e.trim().replace(/\\/g,"/").split("/").filter(Boolean).map(a=>a.toLowerCase()),i=[];for(let a of t){let c=Yi(a,s);c!==null&&i.push({entry:a,score:c})}return i.sort((a,c)=>{let l=a.score-c.score;return l!==0?l:a.entry.path.localeCompare(c.entry.path)}),i.slice(0,n).map(({entry:a})=>({id:a.path,path:a.path,name:a.segments[a.segments.length-1]??a.path,parent:a.segments.length>1?a.segments.slice(0,-1).join("/"):void 0,isDir:a.isDir}))}async function ro(t){let e=await Ki(t.cwd,t),n=typeof t.limit=="number"?Math.max(1,t.limit):oo;return qi(e,t.query,n)}import{mkdir as Ku,readFile as Xu,writeFile as Yu}from"fs/promises";import{dirname as Zu}from"path";import{randomUUID as tm}from"crypto";import{Box as Kt,Text as Ot}from"ink";import{jsx as ht,jsxs as io}from"react/jsx-runtime";var Zi="#3a3a3a",he="#2b2b2b",so="#888888",Qi="#666666";function ao({items:t,activeIndex:e,loading:n}){return n?ht(Kt,{flexDirection:"column",paddingX:1,backgroundColor:he,children:ht(Ot,{color:"gray",children:"Loading..."})}):t.length?ht(Kt,{flexDirection:"column",backgroundColor:he,children:t.map((o,r)=>{let s=r===e,i=s?Zi:he;return o.kind==="slash"?io(Kt,{flexDirection:"row",gap:2,paddingX:1,backgroundColor:i,children:[ht(Ot,{color:s?"cyan":"white",bold:s,children:o.title}),o.subtitle?ht(Ot,{color:so,children:o.subtitle}):null]},o.id):io(Kt,{flexDirection:"row",gap:1,paddingX:1,backgroundColor:i,children:[ht(Ot,{color:s?"cyan":"white",bold:s,children:o.title}),o.subtitle?ht(Ot,{color:so,children:o.subtitle}):null]},o.id)})}):ht(Kt,{flexDirection:"column",paddingX:1,backgroundColor:he,children:ht(Ot,{color:Qi,children:"No matches"})})}var co={name:"new",description:"\u5F00\u542F\u4E00\u4E2A\u65B0\u5BF9\u8BDD",run:({closeSuggestions:t,setInputValue:e,clearScreen:n,showSystemMessage:o,newSession:r})=>{t(),e(""),n(),o("New Session","Starting a new session..."),r?.()}};var lo={name:"exit",description:"\u9000\u51FA\u5F53\u524D\u4F1A\u8BDD",run:({closeSuggestions:t,exitApp:e})=>{t(),e()}};var uo={name:"resume",description:"\u6062\u590D\u5386\u53F2\u8F93\u5165",run:({closeSuggestions:t,setInputValue:e,showSystemMessage:n})=>{t(!1),e("resume "),n("Resume",'Type "resume" followed by keywords to filter and select from session history.')}};var mo={name:"models",description:"\u9009\u62E9\u6A21\u578B\uFF08\u5C55\u793A\u914D\u7F6E\u91CC\u7684 providers\uFF09",run:({closeSuggestions:t,setInputValue:e,showSystemMessage:n,data:o})=>{t(!1);let{providers:r,providerName:s,model:i}=o;if(!r.length){n("Models",`No providers configured. Check ${o.configPath}`),e("");return}let a=r.map(c=>{let l=c.name===s&&c.model===i?" (current)":"",u=c.base_url?` @ ${c.base_url}`:"";return`- ${c.name}: ${c.model}${u}${l}`});e("/models "),n("Models",`Available models:
52
+ ${a.join(`
53
+ `)}`)}};var ta=`Available commands:
54
+ /help Show help and shortcuts
55
+ /exit Exit the session
56
+ exit Exit the session (no slash)
57
+ /new Start a new session
58
+ /models Pick a model from config
59
+ /resume Resume session history
60
+ /context Show or set context length (e.g. /context 120k)
61
+ /mcp Show configured MCP servers
62
+ /init Generate AGENTS.md for current project
63
+
64
+ Shortcuts:
65
+ Enter Send message
66
+ Shift+Enter New line in input
67
+ Up/Down Browse input history
68
+ Tab Accept suggestion
69
+ Ctrl+L Start a new session
70
+ Ctrl+C Exit
71
+ exit Type in input to exit
72
+ Esc Esc Cancel / Clear input`,po={name:"help",description:"\u663E\u793A\u5E2E\u52A9\u4FE1\u606F",run:({closeSuggestions:t,setInputValue:e,showSystemMessage:n})=>{t(),e(""),n("Help",ta)}};var go={name:"context",description:"\u8BBE\u7F6E\u4E0A\u4E0B\u6587\u957F\u5EA6\u9650\u5236 (80k/120k/150k/200k)",run:({closeSuggestions:t,setInputValue:e,showSystemMessage:n,data:o})=>{t();let r="80k, 120k, 150k, 200k",s=`Current: ${(o.contextLimit/1e3).toFixed(0)}k`;e("/context "),n("Context",`${s}
73
+ Usage: /context <length>
74
+ Choices: ${r}`)}};function ea(t,e){let n=[];if(n.push(`- **${t}**`),"url"in e){if(n.push(` - Type: ${e.type??"streamable_http"}`),n.push(` - URL: ${e.url}`),e.type!=="sse"&&e.fallback_to_sse!==void 0&&n.push(` - Fallback to SSE: ${e.fallback_to_sse}`),e.headers&&Object.keys(e.headers).length>0){let o=Object.entries(e.headers).map(([r,s])=>`${r}=${s}`).join(", ");n.push(` - Headers: ${o}`)}}else n.push(` - Type: ${e.type??"stdio"}`),n.push(` - Command: ${e.command}`),e.args&&e.args.length>0&&n.push(` - Args: ${e.args.join(" ")}`);return n.join(`
75
+ `)}var fo={name:"mcp",description:"\u67E5\u770B\u5F53\u524D\u914D\u7F6E\u7684 MCP servers",run:({closeSuggestions:t,setInputValue:e,showSystemMessage:n,data:o})=>{t();let{mcpServers:r,configPath:s}=o,i=Object.keys(r);if(i.length===0){n("MCP Servers",`No MCP servers configured.
76
+
77
+ Add servers to ${s}`),e("");return}let a=[];a.push(`Total: ${i.length} server(s)
78
+ `);for(let[c,l]of Object.entries(r))a.push(ea(c,l)),a.push("");e(""),n("MCP Servers",a.join(`
79
+ `))}};var He=[po,lo,co,uo,mo,go,fo];import{Fragment as Ca,jsx as lt,jsxs as Xt}from"react/jsx-runtime";var la=400;function ua(){try{return ca.userInfo().username||"user"}catch{return"user"}}function To({disabled:t,onSubmit:e,onExit:n,onClear:o,onNewSession:r,onCancelRun:s,onModelSelect:i,onSystemMessage:a,onSetContextLimit:c,history:l,cwd:u,sessionsDir:m,currentSessionFile:y,onHistorySelect:g,providers:_,configPath:C,providerName:N,model:J,contextLimit:f,mcpServers:Z}){let[b,k]=wt(""),[D,E]=wt(null),[Y,w]=wt(""),[h,H]=wt("none"),[j,G]=wt([]),[A,U]=wt(0),[F,nt]=wt(!1),[Et,Ct]=wt(!1),Tt=Fe(0),Dt=Fe(0),R=Fe(""),L=Be(()=>ua(),[]),bt=Be(()=>u.split("/").pop()||u,[u]);Ue(()=>{R.current=b,Ct(!1)},[b]);let B=Be(()=>Et||t?null:fa(b),[t,Et,b]),V=ho((M=!0)=>{M&&Ct(!0),H("none"),G([]),U(0),nt(!1)},[]);Ue(()=>{t&&V(!1)},[t,V]),Ue(()=>{if(!B){H("none"),G([]),U(0),nt(!1);return}let M=!1,I=++Tt.current;return nt(!0),(async()=>{try{if(B.type==="file"){let P=await ro({cwd:u,query:B.query,limit:8});if(M||I!==Tt.current)return;let O=P.map(T=>{let v=T.isDir?`${T.path}/`:T.path;return{id:T.id,title:v,kind:"file",value:v,meta:{isDir:T.isDir}}});H("file"),G(O),U(T=>O.length?Math.min(T,O.length-1):0);return}if(B.type==="history"){let P=await ma({sessionsDir:m,cwd:u,keyword:B.keyword,activeSessionFile:y});if(M||I!==Tt.current)return;let O=P.map(ka);H("history"),G(O),U(T=>O.length?Math.min(T,O.length-1):0);return}if(B.type==="models"){let P=B.keyword.toLowerCase(),T=(_??[]).filter(v=>{let it=v.name?.toLowerCase()??"",Mt=v.model?.toLowerCase()??"";return P?it.includes(P)||Mt.includes(P):!0}).map(v=>({id:v.name,title:`${v.name}: ${v.model}`,subtitle:v.base_url??v.env_api_key??"",kind:"model",value:`/models ${v.name}`,meta:{provider:v}}));H("model"),G(T),U(v=>T.length?Math.min(v,T.length-1):0);return}if(B.type==="context"){let O=[8e4,12e4,15e4,2e5].map(T=>({id:`${T}`,title:`${(T/1e3).toFixed(0)}k tokens`,subtitle:T===f?"Current":void 0,kind:"context",value:`/context ${(T/1e3).toFixed(0)}k`,meta:{contextValue:T}}));H("context"),G(O),U(T=>O.length?Math.min(T,O.length-1):0);return}if(B.type==="slash"){let P=B.keyword.toLowerCase(),T=(P?He.filter(v=>v.matches?v.matches(P):v.name.startsWith(P)):He).map(v=>({id:v.name,title:`/${v.name}`,subtitle:v.description,kind:"slash",value:`/${v.name} `,meta:{slashCommand:v}}));H("slash"),G(T),U(v=>T.length?Math.min(v,T.length-1):0);return}}catch{!M&&I===Tt.current&&G([])}finally{!M&&I===Tt.current&&nt(!1)}})(),()=>{M=!0}},[B,u,m,y,_,f]);let Ht=ho(M=>{if(M){if(h==="file"&&B?.type==="file"){let I=b.slice(0,B.tokenStart),P=b.slice(B.tokenStart+B.query.length),O=`${I}${M.value}${P}`;R.current=O,k(O),E(null),w(""),M.meta?.isDir||V();return}if(h==="history"){M.meta?.historyEntry&&g?.(M.meta.historyEntry),R.current=M.value,k(M.value),E(null),w(""),V();return}if(h==="model"&&M.meta?.provider){i?.(M.meta.provider),R.current="",k(""),E(null),w(""),V();return}if(h==="slash"&&M.meta?.slashCommand){M.meta.slashCommand.run({setInputValue:P=>{R.current=P,k(P),E(null),w("")},closeSuggestions:V,clearScreen:()=>{o()},newSession:()=>{r?.()},exitApp:()=>{n()},showSystemMessage:(P,O)=>{a?.(P,O)},switchModel:P=>{i?.(P)},setContextLimit:P=>{c?.(P)},loadHistory:P=>{g?.(P)},data:{configPath:C,providerName:N,model:J,contextLimit:f,providers:_,mcpServers:Z}});return}if(h==="context"&&M.meta?.contextValue){let I=M.meta.contextValue;c?.(I),a?.("Context",`\u5DF2\u8BBE\u7F6E\u4E0A\u4E0B\u6587\u4E0A\u9650\u4E3A ${(I/1e3).toFixed(0)}k tokens`),R.current="",k(""),E(null),w(""),V();return}}},[V,o,n,i,a,c,g,h,B,b,C,N,J,f,_,Z]);aa((M,I)=>{if(I.ctrl&&M==="c"){n();return}if(I.ctrl&&M==="l"){R.current="",k(""),E(null),w(""),V(),o(),r?.();return}let P=h!=="none",O=P&&j.length>0;if(I.escape){let T=Date.now();if(T-Dt.current<=la){Dt.current=0,t?s():(R.current="",k(""),E(null),w(""),V());return}Dt.current=T,P&&V();return}if(!t){if(I.upArrow){if(O){U(it=>it<=0?j.length-1:it-1);return}if(!l.length)return;if(D===null){w(R.current);let it=l.length-1;E(it);let Mt=l[it]??"";R.current=Mt,k(Mt);return}let T=Math.max(0,D-1);E(T);let v=l[T]??"";R.current=v,k(v);return}if(I.downArrow){if(O){U(it=>(it+1)%j.length);return}if(D===null)return;let T=D+1;if(T>=l.length){E(null),R.current=Y,k(Y),w("");return}E(T);let v=l[T]??"";R.current=v,k(v);return}if(I.tab&&O){Ht(j[A]);return}if(I.return){if(O){Ht(j[A]);return}if(I.shift){let v=R.current+`
80
+ `;R.current=v,k(v);return}let T=R.current.trim();T&&(e(T),R.current="",k(""),E(null),w(""),V(!1));return}if(I.backspace||I.delete){let T=R.current.slice(0,Math.max(0,R.current.length-1));R.current=T,k(T);return}if(M){let T=R.current+M;R.current=T,k(T)}}});let qt=b,Zt=t?" ":"\u258A",xt=qt.split(`
81
+ `),Te=j.map(({value:M,meta:I,...P})=>P);return Xt(ye,{flexDirection:"column",gap:1,children:[Xt(ye,{flexDirection:"column",children:[Xt(ye,{children:[lt(yt,{color:"cyan",children:L}),lt(yt,{color:"gray",children:"@"}),lt(yt,{color:"cyan",children:bt}),lt(yt,{color:"yellow",children:" "}),t?lt(yt,{color:"gray",children:xt[0]}):Xt(Ca,{children:[lt(yt,{color:"white",children:xt[0]}),xt.length===1&&lt(yt,{color:"cyan",children:Zt})]})]}),xt.slice(1).map((M,I)=>Xt(ye,{children:[lt(yt,{color:"white",children:M}),I===xt.length-2&&lt(yt,{color:"cyan",children:Zt})]},`line-${I}`))]}),h!=="none"?lt(ao,{items:Te,activeIndex:A,loading:F}):null]})}async function ma(t){let e=xn(t.sessionsDir,t.cwd),n;try{n=await oa(e,{withFileTypes:!0})}catch(l){return l?.code==="ENOENT"?[]:[]}let o=t.activeSessionFile?yo(t.activeSessionFile):null,s=(await Promise.all(n.filter(l=>l.isFile()&&l.name.endsWith(".jsonl")).map(async l=>{let u=ia(e,l.name);if(o&&yo(u)===o)return null;try{let m=await ra(u);return{path:u,mtimeMs:m.mtimeMs}}catch{return null}}))).filter(l=>!!l).sort((l,u)=>u.mtimeMs-l.mtimeMs),i=t.limit??10,a=t.keyword?.trim().toLowerCase(),c=[];for(let l of s){if(c.length>=i)break;let u=await pa(l.path,t.cwd,l.mtimeMs);if(u&&!(a&&!u.input.toLowerCase().includes(a))&&(c.push(u),c.length>=i))break}return c}async function pa(t,e,n){try{let o=await na(t,"utf8"),s=da(o)?.trim()||ga(t);return{id:t,cwd:e,input:s,ts:n,sessionFile:t}}catch{return null}}function da(t){for(let e of t.split(`
82
+ `)){let n=e.trim();if(!n)continue;let o;try{o=JSON.parse(n)}catch{continue}if(o&&typeof o=="object"&&o.type==="turn_start"){let r=typeof o.content=="string"?o.content.trim():"";if(r)return r}}return null}function ga(t){return sa(t).replace(/\.jsonl$/i,"")}function fa(t){let e=xa(t);if(e)return e;let n=Ta(t);if(n)return n;let o=Sa(t);if(o)return o;let r=ha(t);return r||ya(t)}function ha(t){let e=t.lastIndexOf("@");if(e===-1)return null;if(e>0){let o=t[e-1];if(o&&!/\s/.test(o))return null}let n=t.slice(e+1);return/\s/.test(n)?null:{type:"file",query:n,tokenStart:e+1}}function ya(t){let e=t.trimStart(),n=t.length-e.length;if(e.length===0)return null;let o=e;if(o.startsWith("/")&&(o=o.slice(1)),!o.toLowerCase().startsWith("resume")||t.slice(0,n).trim().length>0)return null;let s=o.slice(6);return s&&!s.startsWith(" ")?null:{type:"history",keyword:s.trim()}}function Ta(t){let e=t.trimStart();if(!e.startsWith("/models"))return null;let n=e.slice(7);return n&&!n.startsWith(" ")?null:{type:"models",keyword:n.trim()}}function xa(t){let e=t.trimStart();if(!e.startsWith("/context"))return null;let n=e.slice(8);return n&&!n.startsWith(" ")?null:{type:"context"}}function Sa(t){let e=t.trimStart();if(!e.startsWith("/"))return null;let n=e.slice(1);return n.includes(" ")?null:/^[a-zA-Z]*$/.test(n)?{type:"slash",keyword:n.toLowerCase()}:n.length===0?{type:"slash",keyword:""}:null}function ka(t){return{id:t.id,title:t.input,subtitle:wa(t.ts),kind:"history",badge:"HIS",value:t.input,meta:{historyEntry:t}}}function wa(t){if(!t)return"";let e=new Date(t);if(Number.isNaN(e.getTime()))return"";let n=String(e.getFullYear()),o=String(e.getMonth()+1).padStart(2,"0"),r=String(e.getDate()).padStart(2,"0"),s=String(e.getHours()).padStart(2,"0"),i=String(e.getMinutes()).padStart(2,"0");return`${n}-${o}-${r} ${s}:${i}`}import Om from"string-width";var xo={"gpt-4o-mini":128e3,"gpt-4o":128e3,"gpt-4":8192,"gpt-3.5":16384,"claude-3":2e5,claude:2e5,"deepseek-coder":64e3,"deepseek-chat":64e3,deepseek:64e3,"kimi-k2":2e5,kimi:2e5,default:12e4};function ba(t){let e=t.toLowerCase(),n=Object.entries(xo).filter(([o])=>o!=="default");for(let[o,r]of n.sort((s,i)=>i[0].length-s[0].length))if(e.includes(o))return r;return xo.default}function So(t,e){if(t==null)return 0;let n=typeof t=="number"?t:t.prompt??t.total??0;if(n<=0)return 0;let o=e&&e>0?e:ba("");return Math.min(100,n/o*100)}function ko(t){return t?`${t.total} tokens`:""}var _a=`Available commands:
83
+ /help Show help and shortcuts
84
+ /exit Exit the session
85
+ exit Exit the session (no slash)
86
+ /new Start a new session
87
+ /models Pick a model from config
88
+ /resume Resume session history
89
+ /context Show or set context length (e.g. /context 120k)
90
+ /mcp Show configured MCP servers
91
+ /init Generate AGENTS.md for current project
92
+ $ Execute shell command (e.g. $ git status)
93
+
94
+ Shortcuts:
95
+ Enter Send message
96
+ Shift+Enter New line in input
97
+ Up/Down Browse input history
98
+ Ctrl+L Start a new session
99
+ Ctrl+C Exit
100
+ exit Type in input to exit
101
+ Ctrl+X Toggle mode
102
+ Ctrl+/ Show help`;function wo(t,e){let[n,...o]=t.trim().slice(1).split(/\s+/),r=(n??"").toLowerCase(),s=[8e4,12e4,15e4,2e5],i=a=>{if(!a)return null;let l=a.toLowerCase().replace(/,/g,"").match(/^(\d+)(k)?$/);if(!l)return null;let u=Number(l[1])*(l[2]?1e3:1);return Number.isFinite(u)?u:null};switch(r){case"exit":return{kind:"exit"};case"new":return{kind:"new"};case"help":return{kind:"message",title:"Help",content:_a};case"config":return{kind:"message",title:"Config",content:`Config file: ${e.configPath}
103
+ Current provider: ${e.providerName}
104
+ Current model: ${e.model}`};case"resume":return{kind:"message",title:"Resume",content:'Type "resume" to filter and select from session history.'};case"context":{let m=i(o[0]),y=s.map(g=>`${g/1e3}k`).join(", ");return m===null?{kind:"message",title:"Context",content:`Current: ${(e.contextLimit/1e3).toFixed(0)}k
105
+ Usage: /context <length>
106
+ Choices: ${y}`}:s.includes(m)?{kind:"set_context_limit",limit:m}:{kind:"message",title:"Context",content:`Unsupported length: ${m}. Pick one of: ${y}`}}case"init":return{kind:"init_agents_md"};case"$":{let m=o.join(" ").trim();return m?{kind:"shell_command",command:m}:{kind:"message",title:"Shell Command",content:"Usage: $ <command> (e.g. $ git status)"}}case"models":if(!e.providers.length)return{kind:"message",title:"Models",content:`No providers configured. Check ${e.configPath}`};let a=o.join(" ").trim(),c=e.providers.find(m=>m.name===a)??e.providers.find(m=>m.model===a);if(c)return{kind:"switch_model",provider:c};let l=e.providers.map(m=>{let y=m.base_url?` @ ${m.base_url}`:"";return`- ${m.name}: ${m.model}${y}`});return{kind:"message",title:"Models",content:`${a?`Not found: ${a}, `:""}Available models:
107
+ ${l.join(`
108
+ `)}`};default:return{kind:"message",title:"Unknown",content:`Unknown command: ${t}
109
+ Type /help for available commands.`}}}import{jsx as Yt,jsxs as Oa}from"react/jsx-runtime";var Ra=Aa(Pa);function Na(t){return{index:t,userInput:"",steps:[]}}function vo({sessionOptions:t,providerName:e,model:n,configPath:o,mcpServers:r,cwd:s,sessionsDir:i,providers:a}){let{exit:c}=$a(),[l,u]=Q(e),[m,y]=Q(n),[g,_]=Q({...t,providerName:e}),[C,N]=Q(null),[J,f]=Q([]),[Z,b]=Q([]),[k,D]=Q(!1),E=bo(null),[Y,w]=Q([]),[h,H]=Q(null),[j,G]=Q([]),[A,U]=Q(null),F=bo(null),[nt,Et]=Q(null),[Ct,Tt]=Q(t.maxPromptTokens??12e4),[Dt,R]=Q(0),L=st((p,d)=>{let x=`${Date.now()}-${Math.random().toString(16).slice(2)}`;b($=>[...$,{id:x,title:p,content:d}])},[]),bt=st((p,d)=>{f(x=>{let $=[...x],z=$.findIndex(ut=>ut.index===p);z===-1&&($.push(Na(p)),z=$.length-1);let K=$[z];return K&&($[z]=d(K)),$})},[]),B=Co(()=>({onAssistantStep:(p,d)=>{let x=E.current;x&&bt(x,$=>{let z=$.steps.slice();for(;z.length<=d;)z.push({index:z.length,assistantText:""});let K=z[d];if(!K)return $;let ut={...K,assistantText:K.assistantText+p};return z[d]=ut,{...$,steps:z}})},hooks:{onTurnStart:({turn:p,input:d,promptTokens:x})=>{E.current=p,x&&x>0&&R(x),bt(p,$=>({...$,index:p,userInput:d,steps:[],startedAt:Date.now(),contextPromptTokens:x??$.contextPromptTokens}))},onAction:({turn:p,step:d,action:x,thinking:$})=>{bt(p,z=>{let K=z.steps.slice();for(;K.length<=d;)K.push({index:K.length,assistantText:""});let ut=K[d];return ut?(K[d]={...ut,action:x,thinking:$,toolStatus:"executing"},{...z,steps:K}):z})},onObservation:({turn:p,step:d,observation:x})=>{},onFinal:({turn:p,finalText:d,status:x,turnUsage:$,tokenUsage:z})=>{bt(p,K=>{let ut=K.startedAt??Date.now(),Po=Math.max(0,Date.now()-ut),Ao=z?.prompt??K.contextPromptTokens;return{...K,finalText:d,status:x,tokenUsage:$,contextPromptTokens:Ao,startedAt:ut,durationMs:Po}}),D(!1)}}}),[bt]);je(()=>{let p=!1;return(async()=>{let d=F.current;d&&await d.close();let x=await Gt(B,g);if(p){await x.close();return}F.current=x,N(x),H(x.historyFilePath??null)})(),()=>{p=!0}},[B,g]),je(()=>()=>{F.current&&F.current.close()},[]);let V=st(async()=>{F.current&&await F.current.close();let p="";if(h)try{(await Ea(h)).size>0&&(p=`
110
+ Session saved: ${Ma(h)}`)}catch{}Et(`Bye!${p}`),setTimeout(()=>{c()},600)},[c,h]),Ht=st(()=>{f([]),b([]),G([]),U(null),R(0)},[]),qt=st(async()=>{f([]),b([]),G([]),U(null),R(0);let p=ze(),d={...g,sessionId:p};F.current&&await F.current.close();let x=await Gt(B,d);F.current=x,N(x),H(x.historyFilePath??null),_(d),L("New Session","Started a new session with fresh context.")},[B,g,L]),Zt=st(async p=>{if(!p.sessionFile){L("\u5386\u53F2\u8BB0\u5F55","\u8BE5\u8BB0\u5F55\u6CA1\u6709\u53EF\u52A0\u8F7D\u7684\u4E0A\u4E0B\u6587\u6587\u4EF6\u3002");return}try{let d=await va(p.sessionFile,"utf8"),x=La(d);G(x.turns),U(x.messages),D(!1),f([]),N(null),H(null),R(0),E.current=null,_($=>({...$,sessionId:ze()})),L("\u5386\u53F2\u8BB0\u5F55\u5DF2\u52A0\u8F7D",x.summary||p.input)}catch(d){L("\u5386\u53F2\u8BB0\u5F55\u52A0\u8F7D\u5931\u8D25",`\u65E0\u6CD5\u8BFB\u53D6 ${p.sessionFile}: ${d.message}`)}},[L]),xt=st(async p=>{try{let d=await Pt(),x={...d.config,current_provider:p};await jt(d.configPath,x)}catch(d){L("\u914D\u7F6E\u4FDD\u5B58\u5931\u8D25",`\u672A\u80FD\u4FDD\u5B58\u6A21\u578B\u9009\u62E9: ${d.message}`)}},[L]),Te=st(()=>{k&&C?.cancelCurrentTurn?.()},[k,C]),M=st(async p=>{if(p.name===l&&p.model===m){L("\u6A21\u578B\u5207\u6362",`\u5DF2\u5728\u4F7F\u7528 ${p.name} (${p.model})`);return}if(k){L("\u6A21\u578B\u5207\u6362","\u5F53\u524D\u6B63\u5728\u8FD0\u884C\uFF0C\u6309 Esc Esc \u53D6\u6D88\u540E\u518D\u5207\u6362\u6A21\u578B\u3002");return}f([]),G([]),U(null),R(0),E.current=null,N(null),H(null),u(p.name),y(p.model),_(d=>({...d,sessionId:ze(),providerName:p.name})),await xt(p.name),L("\u6A21\u578B\u5207\u6362",`\u5DF2\u5207\u6362\u5230 ${p.name} (${p.model})`)},[L,k,m,l,xt]),I=st(async p=>{if(!p.trim()){L("Shell Command","Usage: $ <command> (e.g. $ git status)");return}D(!0);try{let{stdout:d,stderr:x}=await Ra(p,{cwd:s,maxBuffer:5242880}),$=[d?.trim(),x?.trim()].filter(Boolean).join(`
111
+ `);L("Shell Result",$||"(no output)")}catch(d){let x=d,z=[x.stdout?.trim(),x.stderr?.trim(),x.message].filter(Boolean).join(`
112
+ `);L("Shell Error",z||"Command failed")}finally{D(!1)}},[L,s]),P=st(async p=>{let d=wo(p,{configPath:o,providerName:l,model:m,mcpServers:r,providers:a,contextLimit:Ct});if(d.kind==="exit"){await V();return}if(d.kind==="new"){await qt();return}if(d.kind==="switch_model"){await M(d.provider);return}if(d.kind==="set_context_limit"){Tt(d.limit),L("Context length",`\u5DF2\u8BBE\u7F6E\u4E0A\u4E0B\u6587\u4E0A\u9650\u4E3A ${(d.limit/1e3).toFixed(0)}k tokens`);return}if(d.kind==="init_agents_md"){L("Init","Analyzing project structure and generating AGENTS.md...");let x=`Please analyze the current project and create an AGENTS.md file at the project root.
113
+
114
+ The AGENTS.md should include:
115
+ 1. Project name and brief description
116
+ 2. Directory structure overview
117
+ 3. Key technologies and stack
118
+ 4. Coding conventions and style guidelines
119
+ 5. Build/test/development commands
120
+ 6. Any project-specific notes for AI assistants
121
+
122
+ Steps:
123
+ 1. First explore the project structure using glob and bash tools
124
+ 2. Read key configuration files (package.json, tsconfig.json, etc.)
125
+ 3. Understand the tech stack and conventions
126
+ 4. Create the AGENTS.md file using the write tool
127
+
128
+ Make the AGENTS.md concise but informative, following best practices for AI agent guidelines.`;if(w($=>[...$,"/init"]),!C){L("Error","Session not initialized");return}D(!0);try{await C.runTurn(x)}catch{D(!1)}return}if(d.kind==="shell_command"){await I(d.command);return}L(d.title,d.content)},[L,o,Ht,V,M,r,m,l,Ct,a,I]);je(()=>{if(!C||!A?.length)return;let p=C.history[0];p&&(C.history.splice(0,C.history.length,p,...A),U(null))},[A,C]);let O=st(async p=>{if(p.trim().toLowerCase()==="exit"){await V();return}if(!C||k)return;let d=p.trim();if(d.startsWith("$")){let x=d.slice(1).trim();w($=>[...$,p]),await I(x);return}if(p.startsWith("/")){await P(p);return}w(x=>[...x,p]),D(!0);try{await C.runTurn(p)}catch{D(!1)}},[k,P,V,C]),T=J[J.length-1],v=ko(T?.tokenUsage),it=So(Dt,Ct),Mt=Co(()=>[...j,...J],[j,J]);if(nt){let p=nt.split(`
129
+ `);return Yt(_o,{flexDirection:"column",children:p.map((d,x)=>Yt(Ia,{color:"green",children:d},x))})}return Oa(_o,{flexDirection:"column",children:[Yt(Vn,{systemMessages:Z,turns:Mt,headerInfo:{providerName:l,model:m,cwd:s,sessionId:g.sessionId??"unknown"}}),Yt(To,{disabled:!C||k,onSubmit:O,onExit:V,onClear:Ht,onNewSession:qt,onCancelRun:Te,onHistorySelect:Zt,onModelSelect:M,onSystemMessage:L,onSetContextLimit:p=>{Tt(p),L("Context length",`\u5DF2\u8BBE\u7F6E\u4E0A\u4E0B\u6587\u4E0A\u9650\u4E3A ${(p/1e3).toFixed(0)}k tokens`)},history:Y,cwd:s,sessionsDir:i,currentSessionFile:h??void 0,providers:a,configPath:o,providerName:l,model:m,contextLimit:Ct,mcpServers:r}),Yt(Ln,{contextPercent:it})]})}function La(t){let e=[],n=[],o=[],r=t.split(`
130
+ `).map(a=>a.trim()).filter(Boolean),s=null,i=0;for(let a of r){let c;try{c=JSON.parse(a)}catch{continue}if(!(!c||typeof c!="object")){if(c.type==="turn_start"){let l=typeof c.content=="string"?c.content:"";s={index:-(i+1),userInput:l,steps:[],status:"ok"},n.push(s),l&&(e.push({role:"user",content:l}),o.push(`User: ${l}`)),i+=1;continue}if(c.type==="assistant"){let l=typeof c.content=="string"?c.content:"";if(l&&(e.push({role:"assistant",content:l}),o.push(`Assistant: ${l}`),s)){let u={index:s.steps.length,assistantText:l};s.steps=[...s.steps,u],s.finalText=l}continue}if(c.type==="action"&&s){let l=c.meta;if(l&&typeof l=="object"){let u=typeof l.tool=="string"?l.tool:"",m=l.input,y=typeof l.thinking=="string"?l.thinking:"",g=s.steps[s.steps.length-1];g&&(g.action={tool:u,input:m},y&&(g.thinking=y))}continue}if(c.type==="observation"&&s){let l=typeof c.content=="string"?c.content:"",u=s.steps[s.steps.length-1];u&&(u.observation=l);continue}}}return{summary:o.join(`
131
+ `),messages:e,turns:n}}import{jsx as Wa}from"react/jsx-runtime";function Fa(t){let e={once:!1},n=[];for(let o=0;o<t.length;o++){let r=t[o];if(r!==void 0){if(r==="--once"){e.once=!0;continue}n.push(r)}}return{question:n.join(" "),options:e}}async function Mo(){let t=await Pt();if(!t.needsSetup)return t;let e=t.config.providers[0],o=[e?.env_api_key,"OPENAI_API_KEY","DEEPSEEK_API_KEY"].filter(Boolean).some(i=>!!process.env[i]);if(e&&o)return await jt(t.configPath,t.config),console.log(`Detected API key in env. Wrote default provider (${e.name}) to ${t.configPath}`),{...t,needsSetup:!1};let r=Da({input:Ha,output:Ua}),s=async(i,a)=>(await r.question(i)).trim()||a;try{console.log("No provider config found. Please answer the prompts:");let i=await s("Provider name [deepseek]: ","deepseek"),a=await s("API key env var [DEEPSEEK_API_KEY]: ","DEEPSEEK_API_KEY"),c=await s("Model name [deepseek-chat]: ","deepseek-chat"),l=await s("Base URL [https://api.deepseek.com]: ","https://api.deepseek.com"),u={current_provider:i,providers:[{name:i,env_api_key:a,model:c,base_url:l||void 0}]};return await jt(t.configPath,u),console.log(`Config written to ${t.configPath}
132
+ `),{...t,config:u,needsSetup:!1}}finally{r.close()}}async function ja(t){let e=await Mo(),n=zt(e.config),r={sessionId:Eo(),mode:"once",stream:e.config.stream_output??!1},i=await Gt({onAssistantStep:c=>{process.stdout.write(c)},hooks:{onAction:({action:c})=>{console.log(`
133
+ [tool] ${c.tool}`),c.input!==void 0&&console.log(`[input] ${JSON.stringify(c.input)}`)},onObservation:()=>{}}},r),a=t.question;if(!a&&!process.stdin.isTTY&&(a=await Va()),!a&&t.options.once&&(a="Give me a quick self-introduction."),!a){console.error("No input provided. Pass a question or use stdin."),await i.close();return}try{console.log(`User: ${a}
134
+ `);let c=await i.runTurn(a);e.config.stream_output||console.log(`
135
+ ${c.finalText}`),console.log(`
136
+ [tokens] prompt=${c.tokenUsage.prompt} completion=${c.tokenUsage.completion} total=${c.tokenUsage.total}`),console.log(`
137
+ provider=${n.name} model=${n.model}`)}catch(c){console.error(`Run failed: ${c.message}`)}finally{await i.close()}}async function za(t){let e=await Mo(),n=zt(e.config),r={sessionId:Eo(),mode:"interactive",stream:e.config.stream_output??!1},s=oe(e,r);await Ba(Wa(vo,{sessionOptions:r,providerName:n.name,model:n.model,configPath:e.configPath,mcpServers:e.config.mcp_servers??{},cwd:process.cwd(),sessionsDir:s,providers:e.config.providers}),{exitOnCtrlC:!1,patchConsole:!1}).waitUntilExit()}async function Ga(){let t=Fa(process.argv.slice(2));if(!(process.stdin.isTTY&&process.stdout.isTTY)||t.options.once){await ja(t);return}await za(t)}Ga();async function Va(){return new Promise(t=>{let e="";process.stdin.setEncoding("utf8"),process.stdin.on("data",n=>{e+=n}),process.stdin.on("end",()=>{t(e.trim())}),process.stdin.resume()})}
138
+ //# sourceMappingURL=index.js.map
package/dist/prompt.md ADDED
@@ -0,0 +1,363 @@
1
+ You are **Memo Code**, an interactive CLI tool that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user.
2
+
3
+ **IMPORTANT**: Refuse to write or explain code that may be used maliciously. When working on files, if they seem related to malware, refuse to work on it, even if the request seems benign.
4
+
5
+ ---
6
+
7
+ # Core Identity
8
+
9
+ - **Local First**: You operate directly on the user's machine. File operations and commands happen in the real environment.
10
+ - **Project Aware**: Read and follow `CLAUDE.md` (or `AGENTS.md`) files containing project structure, conventions, and preferences.
11
+ - **Tool Rich**: Use your comprehensive toolkit liberally to gather information and complete tasks.
12
+ - **Safety Conscious**: The environment is NOT sandboxed. Your actions have immediate effects.
13
+
14
+ # Session Context (auto-injected)
15
+
16
+ - Date: {{date}}
17
+ - User: {{user}}
18
+ - PWD: {{pwd}}
19
+
20
+ ---
21
+
22
+ # Tone and Style
23
+
24
+ **CRITICAL - Output Discipline**: Keep your responses short and concise. You MUST answer with **fewer than 4 lines of text** (not including tool calls or code generation), unless the user asks for detail.
25
+
26
+ - Answer directly without preamble or postamble
27
+ - Avoid phrases like "The answer is...", "Here is...", "Based on...", "I will now..."
28
+ - One word answers are best when appropriate
29
+ - Only explain when the user explicitly asks
30
+
31
+ **Examples**:
32
+
33
+ <example>
34
+ user: 2 + 2
35
+ assistant: 4
36
+ </example>
37
+
38
+ <example>
39
+ user: is 11 a prime number?
40
+ assistant: Yes
41
+ </example>
42
+
43
+ <example>
44
+ user: what command lists files?
45
+ assistant: ls
46
+ </example>
47
+
48
+ <example>
49
+ user: which file contains the implementation of foo?
50
+ assistant: [runs search]
51
+ src/foo.c
52
+ </example>
53
+
54
+ **Communication Rules**:
55
+
56
+ - Output text to communicate with the user
57
+ - All text outside tool use is displayed to the user
58
+ - Never use Bash or code comments to communicate
59
+ - Never add code summaries unless requested
60
+ - If you cannot help, keep refusal to 1-2 sentences without explanation
61
+
62
+ ---
63
+
64
+ # Tool Usage Policy
65
+
66
+ ## Parallel Tool Calls (CRITICAL)
67
+
68
+ **You MUST call multiple tools in parallel when they are independent**. This is a CRITICAL requirement for performance.
69
+
70
+ When making multiple tool calls:
71
+
72
+ - If tools are independent, send a SINGLE message with MULTIPLE tool calls
73
+ - If tools depend on each other, run them sequentially
74
+ - Never make sequential calls for independent operations
75
+
76
+ **Examples**:
77
+
78
+ <example>
79
+ user: Run git status and git diff
80
+ assistant: [Makes ONE message with TWO Bash tool calls in parallel]
81
+ </example>
82
+
83
+ <example>
84
+ user: Read package.json and tsconfig.json
85
+ assistant: [Makes ONE message with TWO Read tool calls in parallel]
86
+ </example>
87
+
88
+ <example>
89
+ user: Show me TypeScript files and test files
90
+ assistant: [Makes ONE message with TWO Glob tool calls in parallel]
91
+ </example>
92
+
93
+ ## Tool Selection
94
+
95
+ - Prefer specialized tools over bash: Read instead of cat, Edit instead of sed, Glob/Grep instead of find/grep
96
+ - Use Task tool for open-ended searches requiring multiple rounds
97
+ - Use Bash only for actual shell commands and operations
98
+
99
+ ## Tool JSON Formatting (CRITICAL)
100
+
101
+ - Wrap every tool call payload in a ```json fenced block.
102
+ - Payload must be valid JSON; no stray newlines or unescaped quotes inside strings.
103
+ - For shell commands use a single-line string; if you need newlines, encode them as `\\n`, not raw line breaks.
104
+
105
+ ---
106
+
107
+ # Task Management (Todo Tool)
108
+
109
+ Use the TodoWrite tool **VERY frequently** for complex tasks. This is EXTREMELY important for tracking progress and preventing you from forgetting critical steps.
110
+
111
+ ## When to Use Todo Tool
112
+
113
+ Use proactively in these scenarios:
114
+
115
+ 1. **Complex multi-step tasks** - 3+ distinct steps
116
+ 2. **Non-trivial tasks** - Require careful planning
117
+ 3. **User provides multiple tasks** - Numbered or comma-separated list
118
+ 4. **After receiving instructions** - Immediately capture requirements
119
+ 5. **When starting work** - Mark todo as in_progress
120
+ 6. **After completing work** - Mark todo as completed immediately
121
+
122
+ ## When NOT to Use
123
+
124
+ Skip for:
125
+
126
+ - Single straightforward tasks
127
+ - Trivial tasks completable in < 3 steps
128
+ - Purely conversational requests
129
+
130
+ ## Task Management Rules
131
+
132
+ **CRITICAL**:
133
+
134
+ - Update status in real-time as you work
135
+ - Mark tasks completed IMMEDIATELY after finishing (don't batch)
136
+ - Only ONE task in_progress at a time
137
+ - Complete current tasks before starting new ones
138
+
139
+ **Task States**:
140
+
141
+ - `pending`: Not yet started
142
+ - `in_progress`: Currently working (limit to ONE)
143
+ - `completed`: Finished successfully
144
+
145
+ **Example**:
146
+
147
+ <example>
148
+ user: Run the build and fix any type errors
149
+ assistant: [Creates todos: "Run build", "Fix type errors"]
150
+ [Runs build]
151
+ Found 10 type errors. [Updates todo list with 10 specific items]
152
+ [Marks first todo in_progress]
153
+ [Fixes first error, marks completed, moves to second]
154
+ ...
155
+ </example>
156
+
157
+ ---
158
+
159
+ # Doing Tasks
160
+
161
+ For software engineering tasks (bugs, features, refactoring, explaining):
162
+
163
+ 1. **Understand first** - NEVER propose changes to code you haven't read
164
+ 2. **Plan if complex** - Use TodoWrite tool to break down the task
165
+ 3. **Use tools extensively** - Search, read, and understand the codebase
166
+ 4. **Follow conventions** - Match existing code style, libraries, and patterns
167
+ 5. **Implement solution** - Make only necessary changes, avoid over-engineering
168
+ 6. **Verify your work** - VERY IMPORTANT: Run lint and typecheck commands when done
169
+
170
+ **CRITICAL - Code Quality**:
171
+
172
+ - After completing tasks, you MUST run lint and typecheck commands (e.g., `npm run lint`, `npm run typecheck`)
173
+ - If commands unknown, ask user and suggest adding to CLAUDE.md
174
+ - NEVER commit changes unless explicitly asked
175
+
176
+ **Following Conventions**:
177
+
178
+ - NEVER assume libraries are available - check package.json first
179
+ - Look at existing code to understand patterns
180
+ - Match code style, naming, and structure
181
+ - Follow security best practices - never log secrets or commit credentials
182
+ - DO NOT ADD COMMENTS unless asked
183
+
184
+ **Avoid Over-engineering**:
185
+
186
+ - Only make changes directly requested or clearly necessary
187
+ - Don't add features, refactor unrelated code, or make "improvements"
188
+ - Don't add error handling for scenarios that can't happen
189
+ - Don't create abstractions for one-time operations
190
+ - Three similar lines is better than a premature abstraction
191
+
192
+ **Backwards Compatibility**:
193
+
194
+ - Avoid hacks like renaming unused `_vars` or `// removed` comments
195
+ - If something is unused, delete it completely
196
+
197
+ ---
198
+
199
+ # Code References
200
+
201
+ When referencing code, use the pattern `file_path:line_number`:
202
+
203
+ <example>
204
+ user: Where are errors handled?
205
+ assistant: Clients are marked as failed in the `connectToServer` function in src/services/process.ts:712.
206
+ </example>
207
+
208
+ ---
209
+
210
+ # Proactiveness
211
+
212
+ Balance between:
213
+
214
+ 1. Doing the right thing when asked
215
+ 2. Not surprising the user with unexpected actions
216
+ 3. Not adding explanations unless requested
217
+
218
+ - If user asks how to approach something, answer first - don't immediately act
219
+ - After working on a file, just stop - don't explain what you did
220
+
221
+ ---
222
+
223
+ # Working Environment
224
+
225
+ ## Safety
226
+
227
+ ⚠️ **WARNING**: Environment is NOT SANDBOXED. Actions immediately affect the user's system.
228
+
229
+ - Never access files outside working directory unless instructed
230
+ - Be careful with destructive operations (rm, overwrite)
231
+ - Avoid superuser commands unless instructed
232
+ - Validate inputs before shell commands
233
+
234
+ ## Project Context (CLAUDE.md / AGENTS.md)
235
+
236
+ Files named `CLAUDE.md` or `AGENTS.md` may exist with project-specific guidance:
237
+
238
+ - Project structure and conventions
239
+ - Build, test, and development workflows
240
+ - Security notes and configuration
241
+
242
+ **IMPORTANT**: If you modify anything mentioned in these files, UPDATE them to keep current.
243
+
244
+ ---
245
+
246
+ # Git Operations
247
+
248
+ ## Creating Commits
249
+
250
+ When user asks to create a commit:
251
+
252
+ 1. **You MUST run these commands IN PARALLEL**:
253
+ - `git status` (never use -uall flag)
254
+ - `git diff` (see staged and unstaged changes)
255
+ - `git log` (see recent commit style)
256
+
257
+ 2. **Analyze changes**:
258
+ - Summarize nature of changes (feature, fix, refactor, etc.)
259
+ - Do not commit secrets (.env, credentials, etc.)
260
+ - Draft concise 1-2 sentence message focusing on "why" not "what"
261
+
262
+ 3. **Execute commit** (run commands in parallel where independent):
263
+ - Add relevant untracked files
264
+ - Create commit with message ending: `Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>`
265
+ - Run git status to verify
266
+
267
+ **Git Safety**:
268
+
269
+ - NEVER update git config
270
+ - NEVER run destructive commands (force push, hard reset) unless explicitly requested
271
+ - NEVER skip hooks (--no-verify) unless requested
272
+ - NEVER use -i flag commands (git rebase -i, git add -i)
273
+ - CRITICAL: ALWAYS create NEW commits, never use --amend unless requested
274
+ - NEVER commit unless explicitly asked
275
+
276
+ **Commit Message Format** (use HEREDOC):
277
+
278
+ ```bash
279
+ git commit -m "$(cat <<'EOF'
280
+ Commit message here.
281
+
282
+ Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
283
+ EOF
284
+ )"
285
+ ```
286
+
287
+ ## Creating Pull Requests
288
+
289
+ Use `gh` command for GitHub operations.
290
+
291
+ When user asks to create a PR:
292
+
293
+ 1. **Run these commands IN PARALLEL**:
294
+ - `git status`
295
+ - `git diff`
296
+ - Check if branch tracks remote
297
+ - `git log` and `git diff [base-branch]...HEAD`
298
+
299
+ 2. **Analyze ALL commits** that will be in the PR (not just latest)
300
+
301
+ 3. **Create PR** (run in parallel where independent):
302
+ - Create new branch if needed
303
+ - Push to remote with -u if needed
304
+ - Create PR with `gh pr create`
305
+
306
+ **PR Format** (use HEREDOC):
307
+
308
+ ```bash
309
+ gh pr create --title "title" --body "$(cat <<'EOF'
310
+ ## Summary
311
+ <1-3 bullet points>
312
+
313
+ ## Test plan
314
+ [Checklist for testing]
315
+
316
+ 🤖 Generated with [Memo Code](https://github.com/yourusername/memo-cli)
317
+ EOF
318
+ )"
319
+ ```
320
+
321
+ ---
322
+
323
+ # Available Tools Reference
324
+
325
+ Your available tools will be provided separately. Use them liberally and in parallel when appropriate.
326
+
327
+ Common tools include:
328
+
329
+ - **bash**: Execute shell commands
330
+ - **read**: Read file contents
331
+ - **write**: Create/overwrite files
332
+ - **edit**: Replace text in files
333
+ - **glob**: Find files by pattern
334
+ - **grep**: Search file contents
335
+ - **todo**: Manage task lists
336
+ - **webfetch**: Fetch web pages
337
+
338
+ ---
339
+
340
+ # Ultimate Reminders
341
+
342
+ At all times:
343
+
344
+ - **Concise**: < 4 lines of text (not including tools/code)
345
+ - **Parallel**: Multiple independent tool calls in ONE message
346
+ - **Todo-driven**: Use TodoWrite for complex tasks
347
+ - **Quality-focused**: Run lint/typecheck after changes
348
+ - **Reference precisely**: Use `file:line` format
349
+ - **Safety conscious**: Actions have real consequences
350
+ - **Focused**: Only make necessary changes
351
+
352
+ **Core Mantras**:
353
+
354
+ - Don't deviate from user needs
355
+ - Don't give more than asked for
356
+ - Verify when uncertain
357
+ - Think twice before acting
358
+ - Keep it simple
359
+ - No time estimates or predictions
360
+
361
+ ---
362
+
363
+ **IMPORTANT**: You MUST answer concisely with fewer than 4 lines of text (not including tool calls or code generation), unless user explicitly asks for detail.
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@memo-code/memo",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "type": "module",
6
+ "description": "AI programming assistant with multi-turn conversations, tool calling, and concurrent execution",
7
+ "bin": {
8
+ "memo": "./dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist/index.js",
12
+ "dist/prompt.md",
13
+ "README.md",
14
+ "LICENSE"
15
+ ],
16
+ "engines": {
17
+ "node": ">=18.0.0"
18
+ },
19
+ "scripts": {
20
+ "start": "tsx packages/cli/src/index.tsx",
21
+ "build": "tsup",
22
+ "dev": "tsup --watch",
23
+ "format": "prettier --write .",
24
+ "format:check": "prettier --check .",
25
+ "test": "vitest run",
26
+ "test:core": "vitest run packages/core",
27
+ "test:tools": "vitest run packages/tools",
28
+ "test:cli": "vitest run packages/cli",
29
+ "ci": "pnpm run format:check && pnpm run test:core && pnpm run test:tools && pnpm run build",
30
+ "prepublishOnly": "pnpm run build && chmod +x dist/index.js"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^22.10.2",
34
+ "@types/react": "^18.2.79",
35
+ "prettier": "^3.3.3",
36
+ "tsup": "^8.3.5",
37
+ "tsx": "^4.19.2",
38
+ "typescript": "^5.7.2",
39
+ "vite-tsconfig-paths": "^6.0.5",
40
+ "vitest": "^2.1.8"
41
+ },
42
+ "dependencies": {
43
+ "@dqbd/tiktoken": "^1.0.22",
44
+ "@modelcontextprotocol/sdk": "^1.24.3",
45
+ "fast-glob": "^3.3.3",
46
+ "ink": "^5.0.0",
47
+ "jsonrepair": "^3.13.2",
48
+ "marked": "^17.0.1",
49
+ "openai": "^6.10.0",
50
+ "react": "^18.2.0",
51
+ "react-reconciler": "^0.29.0",
52
+ "string-width": "^7.2.0",
53
+ "toml": "^3.0.0",
54
+ "zod": "^4.3.6",
55
+ "zod-to-json-schema": "^3.25.1"
56
+ }
57
+ }