@huicai/cli 2026.712.0 → 2026.714.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/bin/huicai.js +5 -5
- package/package.json +3 -2
- package/squads/hc-content.md +50 -0
- package/squads/hc-data.md +50 -0
- package/squads/hc-debug.md +50 -0
- package/squads/hc-fullstack.md +59 -0
- package/squads/hc-product.md +50 -0
- package/squads/hc-refactor.md +50 -0
- package/squads/hc-release.md +55 -0
- package/squads/hc-research.md +50 -0
- package/squads/hc-review.md +49 -0
- package/squads/hc-test.md +49 -0
- package/src/agent.js +1 -1
- package/src/api.js +1 -1
- package/src/commands.js +1 -1
- package/src/compaction.js +1 -1
- package/src/config.js +1 -1
- package/src/consult.js +1 -1
- package/src/device.js +1 -1
- package/src/executors.js +1 -1
- package/src/experts.js +1 -0
- package/src/gitflow.js +1 -1
- package/src/input.js +1 -1
- package/src/render.js +1 -1
- package/src/repl.js +1 -1
- package/src/session.js +1 -1
- package/src/squad.js +1 -0
- package/src/tools.js +1 -1
- package/src/transcript.js +1 -1
package/src/render.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c as t}from"./colors.js";export function toolLabel(t,e={}){switch(t){case"read_file":return`read_file ${e.path??""}`;case"list_files":return`list_files ${e.path??"."}`;case"glob":return`glob ${e.pattern??""}`;case"grep":return`grep ${JSON.stringify(e.pattern??"")}${e.path?" in "+e.path:""}`;case"search_code":return`search_code ${JSON.stringify(e.query??"")}`;case"write_file":return`write_file ${e.path??""}`;case"edit_file":return`edit_file ${e.path??""}`;case"run_bash":return`run_bash: ${String(e.command??"").split("\n")[0]}`;case"fetch_url":return`fetch_url ${e.url??""}`;case"todo_write":return"todo_write(更新任务清单)";case"dispatch_agent":{const t=String(e.objective??"").split("\n")[0];return`dispatch_agent: ${t.length>60?t.slice(0,60)+"…":t}`}case"dispatch_agents":return`dispatch_agents(并行 ${Array.isArray(e.tasks)?e.tasks.length:"?"} 个子任务)`;case"install_packages":return`install_packages ${Array.isArray(e.packages)&&e.packages.length?e.packages.join(" "):"(全部依赖)"}${e.global?" -g":""}${e.dev?" -D":""}`;case"setup_environment":return"setup_environment"+(Array.isArray(e.commands)&&e.commands.length?`(${e.commands.length} 条配置命令)`:"(自动配置运行环境)");case"install_plugin":return`install_plugin ${e.target??""} ${e.id??""}`;case"open_browser":return`open_browser ${e.url??""}`;case"open_path":return`open_path ${e.path??""}`;case"open_app":return`open_app ${e.name??""}${e.target?" "+e.target:""}`;default:return`${t}(${Object.keys(e).join(", ")})`}}export function renderEditDiff(e,
|
|
1
|
+
import{c as t}from"./colors.js";export function toolLabel(t,e={}){switch(t){case"read_file":return`read_file ${e.path??""}`;case"list_files":return`list_files ${e.path??"."}`;case"glob":return`glob ${e.pattern??""}`;case"grep":return`grep ${JSON.stringify(e.pattern??"")}${e.path?" in "+e.path:""}`;case"search_code":return`search_code ${JSON.stringify(e.query??"")}`;case"write_file":return`write_file ${e.path??""}`;case"edit_file":return`edit_file ${e.path??""}`;case"run_bash":return`run_bash: ${String(e.command??"").split("\n")[0]}`;case"fetch_url":return`fetch_url ${e.url??""}`;case"todo_write":return"todo_write(更新任务清单)";case"dispatch_agent":{const t=String(e.objective??"").split("\n")[0];return`dispatch_agent: ${t.length>60?t.slice(0,60)+"…":t}`}case"dispatch_agents":return`dispatch_agents(并行 ${Array.isArray(e.tasks)?e.tasks.length:"?"} 个子任务)`;case"dispatch_member":{const t=String(e.task??"").split("\n")[0];return`dispatch_member ${e.squad??""}·${e.member??""}: ${t.length>48?t.slice(0,48)+"…":t}`}case"consult_expert":{const t=String(e.query??"").split("\n")[0];return`consult_expert ${e.expert??""}: ${t.length>48?t.slice(0,48)+"…":t}`}case"consult_experts":return`consult_experts(并行咨询 ${Array.isArray(e.tasks)?e.tasks.length:"?"} 个平台专家)`;case"install_packages":return`install_packages ${Array.isArray(e.packages)&&e.packages.length?e.packages.join(" "):"(全部依赖)"}${e.global?" -g":""}${e.dev?" -D":""}`;case"setup_environment":return"setup_environment"+(Array.isArray(e.commands)&&e.commands.length?`(${e.commands.length} 条配置命令)`:"(自动配置运行环境)");case"install_plugin":return`install_plugin ${e.target??""} ${e.id??""}`;case"open_browser":return`open_browser ${e.url??""}`;case"open_path":return`open_path ${e.path??""}`;case"open_app":return`open_app ${e.name??""}${e.target?" "+e.target:""}`;default:return`${t}(${Object.keys(e).join(", ")})`}}export function renderEditDiff(e,r){const n=[];for(const r of String(e??"").split("\n"))n.push(t.red("- "+r));for(const e of String(r??"").split("\n"))n.push(t.green("+ "+e));return n.join("\n")}export function diffLines(t,e){const r=t=>null==t||""===t?[]:String(t).split("\n"),n=r(t),s=r(e),a=n.length,i=s.length;if(a*i>4e6)return[...n.map(t=>({type:"-",text:t})),...s.map(t=>({type:"+",text:t}))];const p=Array.from({length:a+1},()=>new Uint32Array(i+1));for(let t=a-1;t>=0;t--)for(let e=i-1;e>=0;e--)p[t][e]=n[t]===s[e]?p[t+1][e+1]+1:Math.max(p[t+1][e],p[t][e+1]);const o=[];let l=0,c=0;for(;l<a&&c<i;)n[l]===s[c]?(o.push({type:" ",text:n[l]}),l++,c++):p[l+1][c]>=p[l][c+1]?(o.push({type:"-",text:n[l]}),l++):(o.push({type:"+",text:s[c]}),c++);for(;l<a;)o.push({type:"-",text:n[l++]});for(;c<i;)o.push({type:"+",text:s[c++]});return o}export function renderFileDiff(e,r,n={}){const{path:s="",context:a=3,maxLines:i=80}=n,p=diffLines(e,r);let o=0,l=0;for(const t of p)"+"===t.type?o++:"-"===t.type&&l++;if(0===o&&0===l)return"";const c=[];let u=0,h=0;for(const t of p)" "===t.type?(u++,h++,c.push({type:" ",text:t.text,no:u})):"-"===t.type?(u++,c.push({type:"-",text:t.text,no:u})):(h++,c.push({type:"+",text:t.text,no:h}));const g=new Array(c.length).fill(!1);c.forEach((t,e)=>{if(" "!==t.type)for(let t=Math.max(0,e-a);t<=Math.min(c.length-1,e+a);t++)g[t]=!0});const f=String(Math.max(u,h)).length,_=t=>String(null==t?"":t).padStart(f),y=[(s?t.bold(s)+" ":"")+t.green("+"+o)+" "+t.red("−"+l)];let d=!1,$=0,x=!1;for(let e=0;e<c.length;e++){if(!g[e]){d||(y.push(t.gray(" "+" ".repeat(f)+" ⋯")),d=!0);continue}if(d=!1,$>=i){x=!0;break}const r=c[e];"+"===r.type?y.push(t.green(" "+_(r.no)+" + ")+t.green(r.text)):"-"===r.type?y.push(t.red(" "+_(r.no)+" - ")+t.red(r.text)):y.push(t.gray(" "+_(r.no)+" "+r.text)),$++}return x&&y.push(t.gray(` …(差异较大,仅显示前 ${i} 行;共 +${o} −${l})`)),y.join("\n")}export function indent(t,e=" "){return String(t).split("\n").map(t=>e+t).join("\n")}export function summarize(e,r=6){const n=String(e).split("\n");return n.length<=r?e:n.slice(0,r).join("\n")+t.gray(`\n …(共 ${n.length} 行)`)}
|
package/src/repl.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import s from"node:readline";import{c as o}from"./colors.js";import{createPermissions as t}from"./permissions.js";import{runTurn as e}from"./agent.js";import{createSession as n,saveSession as r,turnCount as i}from"./session.js";import{createCheckpoints as a}from"./checkpoints.js";import{buildSystemPrompt as c}from"./context.js";import{runVerifyLoop as l}from"./autofix.js";import{runAutoFlow as m}from"./autorun.js";import{loadMcpTools as p,closeMcp as u}from"./mcp.js";import{makeSubagentTool as d,makeDispatchAgentsTool as g}from"./subagent.js";import{
|
|
1
|
+
import s from"node:readline";import{c as o}from"./colors.js";import{createPermissions as t}from"./permissions.js";import{runTurn as e}from"./agent.js";import{createSession as n,saveSession as r,turnCount as i}from"./session.js";import{createCheckpoints as a}from"./checkpoints.js";import{buildSystemPrompt as c}from"./context.js";import{runVerifyLoop as l}from"./autofix.js";import{runAutoFlow as m}from"./autorun.js";import{loadMcpTools as p,closeMcp as u}from"./mcp.js";import{makeSubagentTool as d,makeDispatchAgentsTool as g}from"./subagent.js";import{makeDispatchMemberTool as w}from"./squad.js";import{makeConsultTool as f}from"./consult.js";import{makeExpertTools as h}from"./experts.js";import{makeAdvisorTool as y}from"./advisor.js";import{makeRecallTool as x,readMemoryRaw as T}from"./memory.js";import{detectScanners as j,makeScanTool as k}from"./scan.js";import{runSlash as b}from"./commands.js";import{expandAtMentions as I,buildUserContent as v,runBangCommand as S,loadHistory as P,appendHistory as E}from"./input.js";import{makeSkillTools as $}from"./skills.js";import{renderWelcome as _}from"./banner.js";export async function buildExtraTools(s,t){let e={tools:[],clients:[]};try{e=await p()}catch(s){process.stdout.write(o.yellow("MCP 初始化失败(忽略):"+s.message)+"\n")}const n=[...$(t),...e.tools],r=d({config:s,cwd:t,extraTools:e.tools,depth:0});r&&n.push(r);const i=g({config:s,cwd:t,extraTools:e.tools,depth:0});i&&n.push(i);const a=w({config:s,cwd:t,extraTools:e.tools});a&&n.push(a);const c=f({config:s});c&&n.push(c);try{n.push(...await h({config:s}))}catch{}const l=y({config:s});return l&&n.push(l),!1!==s.memoryEnabled&&T(t)&&n.push(x({cwd:t})),j(t).length&&n.push(k({cwd:t})),{extraTools:n,clients:e.clients}}export async function startRepl({config:i,cwd:p,yolo:d,initialPrompt:g,session:w,version:f}){const h=s.createInterface({input:process.stdin,output:process.stdout,history:P(p).reverse(),historySize:200}),y=s=>new Promise(o=>h.question(s,o));let x=t({yolo:d,ask:y,cwd:p});const T={prompt:0,completion:0},j={list:[]},k=a();let $=null,O=null;const W={config:i,cwd:p,permissions:x,usage:T,session:w,version:f};h.on("SIGINT",()=>{$?$.abort():process.stdout.write("\n"+o.gray("输入 /exit 退出。")+"\n")}),function(s,o,t,e){process.stdout.write(_({model:s.model,cwd:o,version:e,platform:/^(agent|workflow|datahuman|squad)-/.test(String(s.model||"")),yolo:t.isYolo(),consult:s.consult?s.consultLabel||s.consult:null})+"\n")}(i,p,x,f),q(w);let{extraTools:C,clients:J}=await buildExtraTools(i,p);const N=async s=>{const{text:t,mentions:n,images:a}=await I(s,p,i);n.length&&process.stdout.write(o.gray(" · 已展开引用:"+n.join("、")+(a.length?`(含 ${a.length} 张图片)`:""))+"\n");const c=k.snapshot({session:W.session,label:s});W.session.messages.push({role:"user",content:v(t,a)}),$=new AbortController,W.session.compaction||(W.session.compaction={}),i.sessionId=W.session.id;const l=await e({config:i,messages:W.session.messages,permissions:x,cwd:p,signal:$.signal,extraTools:C,todoState:j,compaction:W.session.compaction,fileJournal:c});return l?.usage&&(T.prompt+=l.usage.prompt,T.completion+=l.usage.completion),l?.lastPromptTokens&&(W.session.lastPromptTokens=l.lastPromptTokens),$=null,r(W.session),l},Y=()=>({...W,session:W.session,extraTools:C,checkpoints:k,print:s=>process.stdout.write(String(s).endsWith("\n")?String(s):String(s)+"\n")});for(g&&(process.stdout.write(o.cyan("\nhuicai> ")+g+"\n"),await N(g));;){let s;try{s=(await y(o.cyan("\nhuicai> "))).trim()}catch{break}if(""!==s){if(E(p,s),"/exit"===s||"/quit"===s)break;if(s.startsWith("!")&&s.length>1){const t=s.slice(1).trim(),{output:e,record:n}=await S(t,p);process.stdout.write(o.gray(e.split("\n").map(s=>" "+s).join("\n"))+"\n"),W.session.messages.push({role:"user",content:n}),r(W.session);continue}if(s.startsWith("/")){const e=await b(s,Y());if("exit"===e.action)break;"clear"===e.action&&(W.session=n({cwd:p,model:i.model,messages:[W.session.messages[0]]}),j.list=[],k.list.length=0,process.stdout.write(o.gray("已开启新会话(旧会话已保存,可用 huicai --resume 恢复)。\n"))),"refresh"===e.action&&q(W.session),"cd"===e.action&&(p=e.cwd,W.cwd=p,x=t({yolo:x.isYolo(),ask:y,cwd:p}),W.permissions=x,u(J),({extraTools:C,clients:J}=await buildExtraTools(i,p)),W.session=n({cwd:p,model:i.model,messages:[{role:"system",content:c(p,{memory:!1!==i.memoryEnabled})}]}),j.list=[],k.list.length=0,i.sessionId=W.session.id,process.stdout.write(o.gray("已切换到 "+p+"(新会话,旧会话已保存)。\n"))),"autofix"===e.action&&await l({command:e.command,maxIters:e.maxIters,cwd:p,runTurnOnce:s=>N(s),emit:s=>process.stdout.write(o.gray(s)+"\n")}),"autorun"===e.action&&await m({goal:e.goal,config:i,permissions:x,cwd:p,runTurnOnce:s=>N(s),emit:s=>process.stdout.write(o.gray(" · "+s)+"\n")}),"loop"===e.action&&(O&&(clearInterval(O),O=null),e.stop?process.stdout.write(o.gray("已停止循环。\n")):(process.stdout.write(o.gray(`已开始循环:每 ${e.seconds}s 运行「${e.prompt}」(/loop off 停止)。\n`)),O=setInterval(()=>{$||N(e.prompt)},1e3*e.seconds))),e.prompt&&await N(e.prompt);continue}await N(s)}}O&&clearInterval(O),h.close(),u(J),process.stdout.write(o.gray("\n再见。\n"))}function q(s){const t=i(s.messages);if(0===t)return;process.stdout.write(o.gray(` 已恢复会话「${s.title||""}」(${t} 轮)`)+"\n");const e=[...s.messages].reverse().find(s=>"assistant"===s.role&&s.content);if(e){const s=String(e.content).trim(),t=s.length>400?s.slice(0,400)+"…":s;process.stdout.write(o.gray(" 上次回复:")+t.replace(/\n/g,"\n ")+"\n")}}export async function runOnce({config:s,cwd:o,yolo:n,prompt:a,session:c,persist:l,outputFormat:m,maxTurns:p}){a||(process.stderr.write('用法: huicai -p "你的指令"\n'),process.exit(1));const d="json"===m,g=t({yolo:n,ask:null,cwd:o}),{extraTools:w,clients:f}=await buildExtraTools(s,o),{text:h,images:y}=await I(a,o,s);c.messages.push({role:"user",content:v(h,y)}),c.compaction||(c.compaction={}),s.sessionId=c.id;const x=await e({config:s,messages:c.messages,permissions:g,cwd:o,extraTools:w,maxIters:p,compaction:c.compaction,io:d?{write:()=>{}}:void 0});if(x?.lastPromptTokens&&(c.lastPromptTokens=x.lastPromptTokens),l&&r(c),u(f),d){const o=[...c.messages].reverse().find(s=>"assistant"===s.role&&s.content);process.stdout.write(JSON.stringify({type:"result",is_error:!!x?.error,...x?.error?{error:x.error}:{},result:o?String(o.content):"",usage:{input_tokens:x?.usage?.prompt??0,output_tokens:x?.usage?.completion??0},session_id:c.id,model:s.model,num_turns:i(c.messages)})+"\n")}x?.error&&process.exit(1)}
|
package/src/session.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import t from"node:fs";import e from"node:path";import{CONFIG_DIR as n}from"./config.js";export const SESSIONS_DIR=e.join(n,"sessions");function r(){t.mkdirSync(SESSIONS_DIR,{recursive:!0})}function o(t){return e.join(SESSIONS_DIR,t+".json")}function
|
|
1
|
+
import t from"node:fs";import e from"node:path";import{CONFIG_DIR as n}from"./config.js";export const SESSIONS_DIR=e.join(n,"sessions");function r(){t.mkdirSync(SESSIONS_DIR,{recursive:!0})}function o(t){return e.join(SESSIONS_DIR,t+".json")}function s(t){const e=(t||[]).find(t=>"user"===t.role);return e&&e.content&&String(e.content).replace(/\s+/g," ").trim().slice(0,60)||"(空会话)"}export function turnCount(t){return(t||[]).filter(t=>"user"===t.role).length}export function createSession({cwd:t,model:e,messages:n}){const r=Date.now();return{id:Date.now().toString(36)+"-"+Math.random().toString(36).slice(2,6),cwd:t,model:e,createdAt:r,updatedAt:r,title:"",messages:n||[],compaction:{}}}export function saveSession(e){if(e&&e.id)try{r(),e.updatedAt=Date.now(),e.title||(e.title=s(e.messages)),t.writeFileSync(o(e.id),JSON.stringify(e),{mode:384})}catch(t){process.stderr.write(`(会话保存失败,不影响对话:${t.message})\n`)}}export function loadSession(e){if(!e)return null;try{const n=JSON.parse(t.readFileSync(o(e),"utf8"));return n&&Array.isArray(n.messages)?(n.id||(n.id=e),n.compaction&&"object"==typeof n.compaction||(n.compaction={}),n):null}catch{return null}}export function listSessions(n){let o;try{r(),o=t.readdirSync(SESSIONS_DIR).filter(t=>t.endsWith(".json"))}catch{return[]}const i=[];for(const r of o)try{const o=JSON.parse(t.readFileSync(e.join(SESSIONS_DIR,r),"utf8"));if(n&&o.cwd!==n)continue;i.push({id:o.id||r.replace(/\.json$/,""),title:o.title||s(o.messages),cwd:o.cwd,model:o.model,updatedAt:o.updatedAt||0,turns:turnCount(o.messages),user:o.user?String(o.user):""})}catch{}return i.sort((t,e)=>e.updatedAt-t.updatedAt),i}export function mostRecent(t){const e=listSessions(t);return e.length?e[0].id:null}export function deleteSession(e){try{return t.unlinkSync(o(e)),!0}catch{return!1}}export function relTime(t){if(!t)return"未知时间";const e=Math.max(0,Math.floor((Date.now()-t)/1e3));if(e<60)return e+" 秒前";const n=Math.floor(e/60);if(n<60)return n+" 分钟前";const r=Math.floor(n/60);if(r<24)return r+" 小时前";return Math.floor(r/24)+" 天前"}
|
package/src/squad.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import e from"node:fs";import t from"node:path";import n from"node:os";import{fileURLToPath as r}from"node:url";import{runTurn as s}from"./agent.js";import{createPermissions as o}from"./permissions.js";import{loadAgents as i}from"./subagent.js";import{streamChat as a}from"./api.js";import{discoverExperts as c}from"./experts.js";export const BUNDLED_SQUADS_DIR=t.join(t.dirname(r(import.meta.url)),"..","squads");export function squadDirs(e){const r=[];return e&&r.push(t.join(e,".huicai","squads")),r.push(t.join(n.homedir(),".huicai","squads")),r}export function parseSquadDoc(e,t=""){const{attrs:n,body:r}=function(e){const t={attrs:{},body:String(e||"").trim()},n=/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(t.body);if(!n)return t;t.body=t.body.slice(n[0].length).trim();for(const e of n[1].split(/\r?\n/)){const n=/^([A-Za-z][\w-]*)\s*:\s*(.*)$/.exec(e.trim());n&&(t.attrs[n[1]]=n[2].trim().replace(/^["']|["']$/g,""))}return t}(e),s={name:n.name||t,description:n.description||"",leader:n.leader||"主理人",mode:["dispatch","review","ensemble"].includes(n.mode)?n.mode:"dispatch",when:n.when||"",mission:"",members:[],precheck:[],workflows:[]},o=new Map,i=r.split(/^##\s+/m);s.mission=(i[0]||"").trim();for(const e of i.slice(1)){const t=e.indexOf("\n"),n=(t<0?e:e.slice(0,t)).trim(),r=t<0?"":e.slice(t+1);if(/^成员人设/.test(n))for(const e of r.split(/^###\s+/m).slice(1)){const t=e.indexOf("\n"),n=(t<0?e:e.slice(0,t)).trim(),r=t<0?"":e.slice(t+1).trim();n&&r&&o.set(n,r)}else if(/^成员/.test(n))for(const e of r.split(/\r?\n/)){const t=/^\s*[-*]\s+(.+)$/.exec(e);if(!t)continue;const n=t[1].split("|").map(e=>e.trim());n[0]&&s.members.push({id:n[0],callName:n[1]||n[0],duty:n[2]||"",executor:n[3]||""})}else if(/^预检/.test(n))for(const e of r.split(/\r?\n/)){const t=/^\s*[-*]\s+(.+)$/.exec(e);t&&s.precheck.push(t[1].trim())}else if(/^工作流/.test(n))for(const e of r.split(/^###\s+/m).slice(1)){const t=e.indexOf("\n"),n=(t<0?e:e.slice(0,t)).trim(),r=t<0?"":e.slice(t+1),o=/^(.*?)(?:(触发[::](.*?)))?$/.exec(n)||[],i={name:(o[1]||n).trim(),trigger:(o[2]||"").trim(),stages:[],deliverable:""};for(const e of r.split(/\r?\n/)){const t=/^\s*\d+[.、)]\s*(.+)$/.exec(e);if(t){i.stages.push(t[1].trim());continue}const n=/^\s*交付[::]\s*(.+)$/.exec(e);n&&(i.deliverable=n[1].trim())}i.name&&s.workflows.push(i)}}for(const[e,t]of o){const n=s.members.find(t=>t.id===e);n&&(n.persona=t)}return s}export function loadSquads(n){const r=new Map,s=[...squadDirs(n).map((e,t)=>({dir:e,scope:n&&0===t?"project":"global"})),{dir:BUNDLED_SQUADS_DIR,scope:"builtin"}];for(const{dir:n,scope:o}of s){let s;try{s=e.readdirSync(n).filter(e=>e.endsWith(".md"))}catch{continue}for(const i of s){const s=i.replace(/\.md$/,"").trim();if(s)try{const a=t.join(n,i),c=parseSquadDoc(e.readFileSync(a,"utf8"),s);c.scope=o,c.file=a,r.has(c.name)||r.set(c.name,c)}catch{}}}return[...r.values()]}export function ensureTeamDir(n,r){const s=(new Date).toISOString().slice(0,16).replace(/[-:]/g,"").replace("T","-"),o=t.join(n,".huicai","teams",`${r}-${s}`);try{e.mkdirSync(o,{recursive:!0})}catch{return null}return o}export function buildLeaderPrompt(e,t){const n=[];n.push("**Role Override**:以下专家团主理人定义优先于此前任何身份设定,是你在本任务中唯一、权威的角色。"),n.push(""),n.push(`# 专家团「${e.name}」· 主理人${e.leader}`),n.push(`你是专家团「${e.name}」的主理人${e.leader},负责把用户任务拆解、派发给团队成员并汇总交付。`),e.mission&&n.push("",e.mission),n.push("","## 关键规则:只调度,不代工","- 成员职责范围内的专业产出必须经 dispatch_member 由对应成员完成后采信,你不代写;","- 简单澄清/寒暄/对既有产出的说明可直接回答,无须派发。"),n.push("","## 团队架构(调度依据)","| 成员ID | 称呼 | 职责 | 执行者 |","|---|---|---|---|");for(const t of e.members)n.push(`| ${t.id} | ${t.callName} | ${t.duty} | ${t.executor||"(当前模型)"} |`);if(n.push("","## 任务分配预检(每次派发前必做)","成员不会说「这个我不会」——超能力范围的任务结果无保证。所以:","1. 解析用户意图(研究/分析/生成/编辑/写作/审校…);","2. 对照职责与速查表做能力匹配;","3. 匹配 → 派发;不匹配 → 告知用户团队能力限制与替代建议,禁止硬派;","4. 意图模糊 → 先向用户确认。"),e.precheck.length){n.push("### 速查表(常见误派提醒)");for(const t of e.precheck)n.push(`- ${t}`)}if(e.workflows.length){n.push("","## 预设工作流(识别到触发条件时按阶段执行,不自由发挥)");for(const t of e.workflows)n.push(`### ${t.name}${t.trigger?`(触发:${t.trigger})`:""}`),t.stages.forEach((e,t)=>n.push(`${t+1}. ${e}`)),t.deliverable&&n.push(`交付:${t.deliverable}`)}return n.push("","## 团队协作规范","1. 成员在隔离上下文执行,看不到本对话与其他成员产出——任务说明必须独立自足;","2. 跨成员信息流经你中转:把上一阶段产出的必要部分放进下一位成员的 context;","3. 互相独立的任务同一轮并行派发(一次输出多个 dispatch_member 调用),减少串行等待;","4. 成员结论为准:如实汇编;产出不符时修正任务说明重派,而非替他改写。"),"review"===e.mode?n.push("### 生成-审查模式(本团约束)","每个关键产出完成后,必须把「产出全文+原始要求」派给职责匹配的审查成员评审,按意见让原成员修订至通过;未经审查的产出不得作为最终交付。"):"ensemble"===e.mode&&n.push("### 多候选聚合模式(本团约束)","对开放性核心任务,把同一任务并行派给多位匹配成员(或不同侧重多份),对比候选择优或综合,并向用户说明取舍。"),n.push("","## 异常处理(失败三级升级)","- 一级:修正任务说明(更自足、更具体、补 context)后重派原成员;同一任务累计失败满 2 次系统会终止该任务线;","- 二级:换人或换执行方式——改派职责相近的其他成员;平台资源不可用时由本机成员降级承接(系统会自动降级并注明);","- 三级:降级交付——完成可完成的部分,如实说明缺口与原因,并给出用户可选的替代方案。"),t&&n.push("","## 团队工作区",`本次协作的产物统一放在 ${t}(你可用文件工具写入;成员只回传文本结论,由你负责归档与整合)。`),n.push("","## 交付检查清单(最终答复前逐项自查)","- 用户目标的每一部分是否都有对应成员产出支撑?","- 是否汇总了全部关键产出、无散落遗漏?","- 是否标注了各部分由哪位成员产出?","- 未完成的部分是否如实说明?"),n.join("\n")}const m=/^(agent|workflow|datahuman|squad)-/;async function p({squad:e,member:t,ref:n,task:r,context:s,expectation:o,config:i}){const c=[`【专家团「${e.name}」成员任务】${r}`,s?`\n【上游产出/背景】\n${s}`:"",o?`\n【交付要求】\n${o}`:""].join("");return(await a({baseUrl:i.baseUrl,apiKey:i.apiKey,model:n,messages:[{role:"user",content:c}],client:i.client,sessionId:i.sessionId},{})).content||"(平台无返回)"}async function l({squad:e,member:t,task:n,context:r,expectation:a,config:l,cwd:u,readOnlyExtras:d}){const f=String(t.executor||"").trim();let h="";if(f.startsWith("platform:auto:")){const s=await async function(e,t){const n=/^platform:auto:(agent|workflow|datahuman|squad)(?::(.+))?$/.exec(e);if(!n)return null;const[,r,s]=n;try{const e=await c(t),n=(s||"").trim().toLowerCase();return e.find(e=>e.type===r&&(!n||(String(e.name)+" "+String(e.description)).toLowerCase().includes(n)))||null}catch{return null}}(f,l);if(s){const o=await p({squad:e,member:t,ref:s.id,task:n,context:r,expectation:a,config:l});return`(由平台资源「${s.name}」执行)\n${o}`}h=`(未发现可用的平台${f.split(":")[2]||""}资源,已由本机成员降级执行)\n`}else if(f.startsWith("platform:")){const s=f.slice(9).trim();return m.test(s)?p({squad:e,member:t,ref:s,task:n,context:r,expectation:a,config:l}):`成员「${t.callName}」的执行者 ${s} 不是有效的平台伪模型引用`}const $=["**Role Override**:以下角色定义在本次任务中拥有最高优先级。",""];t.persona&&$.push(t.persona,""),$.push(`你是专家团「${e.name}」的成员「${t.callName}」${t.duty?`(职责:${t.duty})`:""},正在执行主理人下发的一项独立任务。`,"- 只完成本任务,直接输出可被主理人直接采用的成果本身,不要寒暄、不要复述任务;","- 无法反问,信息不全时按合理假设执行并注明假设。"),r&&$.push("","## 主理人提供的上游产出/背景",String(r)),a&&$.push("","## 交付要求",String(a));let g=$.join("\n"),w=l;if(f.startsWith("agent:")){const e=f.slice(6).trim(),t=i(u).find(t=>t.name===e);t&&(g=t.prompt.trim()+"\n\n"+g)}else if(f.startsWith("model:")){const e=f.slice(6).trim();e&&(w={...l,model:e})}g+="\n工作目录:"+u;const x=[{role:"system",content:g},{role:"user",content:n}],b=await s({config:w,messages:x,permissions:o({yolo:!1,ask:null}),cwd:u,extraTools:d,depth:1,maxIters:50,io:{write:()=>{}}});if(b?.error)return`成员执行失败:${b.error}`;const y=[...x].reverse().find(e=>"assistant"===e.role&&e.content);return y?h+y.content:"(成员未产出结论)"}export function makeDispatchMemberTool({config:e,cwd:t,extraTools:n=[]}){const r=loadSquads(t);if(!r.length)return null;const s=n.filter(e=>e.readOnly),o=new Map;return{readOnly:!0,spec:{type:"function",function:{name:"dispatch_member",description:"把一项独立、自足的任务派发给某个专家团的一位成员:成员在隔离上下文中执行并只回传成果。复杂/跨角色任务优先按团开工:对照下方各团「何时使用」自主选团,按其预设工作流分阶段派发(互相独立的任务同一轮多次调用并行);成员产出由你汇总并用本机工具落地,落地后验证。派发前先做任务分配预检(对照成员职责做能力匹配)。\n可用专家团:\n"+r.map(e=>`- ${e.name}:${e.when||e.description||""}(成员 ${e.members.map(e=>e.id).join("/")})`).join("\n"),parameters:{type:"object",properties:{squad:{type:"string",enum:r.map(e=>e.name),description:"专家团名称"},member:{type:"string",description:"成员ID(推荐)或称呼,须来自该团的团队架构表"},task:{type:"string",description:"下发的任务说明——必须独立自足(成员看不到本对话与其他成员产出)"},context:{type:"string",description:"可选:上游产出/背景(跨成员信息流经主理人中转,把必要部分放这里)"},expectation:{type:"string",description:"可选:交付要求/验收标准"}},required:["squad","member","task"]}}},run:async n=>{const i=r.find(e=>e.name===String(n?.squad||"").trim());if(!i)return`未找到专家团「${n?.squad}」(可用:${r.map(e=>e.name).join("、")})`;const a=function(e,t){const n=String(t||"").trim();return e.members.find(e=>e.id===n)||e.members.find(e=>e.callName===n)||null}(i,n?.member);if(!a)return`专家团「${i.name}」中没有成员「${n?.member}」(可用:${i.members.map(e=>`${e.id}(${e.callName})`).join("、")})`;const c=String(n?.task||"").trim();if(!c)return"缺少 task(任务说明)";const m=`${i.name}|${a.id}|${c.slice(0,40)}`,p=o.get(m)||0;if(p>=2)return`该任务线已按异常协议终止(成员「${a.callName}」同一任务累计失败满 2 次)。请向用户说明原因并给出替代方案。`;try{const r=await l({squad:i,member:a,task:c,context:n?.context,expectation:n?.expectation,config:e,cwd:t,readOnlyExtras:s});return/^(成员执行失败|平台调用失败)/.test(r)?(o.set(m,p+1),`${r}(累计失败 ${p+1}/2,可修正任务说明后重试或改派他人)`):`成员「${a.callName}」对任务【${c.replace(/\s+/g," ").slice(0,60)}】的产出:\n${r}`}catch(e){if(e&&"AbortError"===e.name)throw e;return o.set(m,p+1),`成员「${a.callName}」执行失败:${e.message}(累计失败 ${p+1}/2)`}}}}export function squadCommandPrompt(e,t,n){return buildLeaderPrompt(e,n)+"\n\n---\n\n用户任务:"+t+`\n\n请以主理人${e.leader}的身份完成:先做任务分配预检(含预设工作流匹配),然后经 dispatch_member`+`(squad="${e.name}")派发执行(独立任务同轮并行),跨成员信息流经你中转,最后按交付检查清单汇总交付。`}export const SQUAD_TEMPLATE="---\nname: {NAME}\ndescription: 内容创作专家团(示例:改成你的团队)\nleader: 司远\nmode: dispatch\nwhen: 需要成套内容创作/深度文章且涉及调研-写作-审校多角色协作时\n---\n\n擅长把用户的创作需求拆解为具体执行方案,并调度合适的成员高效交付。\n\n## 成员\n- researcher | 调研员 | 资料收集、事实核查、竞品调研 | agent:researcher\n- writer | 笔澜 | 公众号长文、营销文案、脚本 |\n- reviewer | 审校 | 审查产出、给出修订意见 |\n- fetcher | 源泉 | (示例)平台数字人自动绑定查数 | platform:auto:datahuman\n\n## 成员人设\n### writer\n角色:资深中文内容主笔。\n工作方法:先定读者与核心论点,再列结构,后成文;每段只讲一件事;用事实与例子代替空话。\n交付标准:结构清晰可扫读、观点有依据、结尾给行动建议;附 3 条备选标题。\n\n## 预检\n- 写长文/文案 → writer,不要派给 researcher\n- 资料/数据收集 → researcher,产出交给 writer 前先经你提炼要点\n\n## 工作流\n### 深度长文(触发:用户要深度文章/长文)\n1. researcher:收集资料与事实\n2. writer:按资料成稿\n3. reviewer:审查并给修订意见(mode=review 时必经)\n交付:终稿 Markdown + 三条标题备选\n";
|
package/src/tools.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as e from"./executors.js";import{searchCode as t}from"./codeindex.js";import{AUTOMATION_TOOL_SPECS as r,AUTOMATION_EXECUTORS as
|
|
1
|
+
import*as e from"./executors.js";import{searchCode as t}from"./codeindex.js";import{AUTOMATION_TOOL_SPECS as r,AUTOMATION_EXECUTORS as n}from"./automation.js";const i=(e,t)=>({type:"object",properties:e,required:t||[]}),o=e=>({type:"string",description:e}),a=e=>({type:"integer",description:e}),p=e=>({type:"boolean",description:e});export const TOOL_SPECS=[{type:"function",function:{name:"read_file",description:"读取文本文件内容,每行带「行号→」前缀(行号仅供定位,不属于文件内容,编辑时不要包含它)。默认最多 2000 行,可用 offset(起始行,1基)/limit(行数) 分段读取大文件。",parameters:i({path:o("相对项目根的文件路径"),offset:a("可选:起始行(1基)"),limit:a("可选:读取行数")},["path"])}},{type:"function",function:{name:"list_files",description:"列出目录下的文件树(跳过 .git/node_modules 等)。可选 path 指定子目录(默认项目根)。",parameters:i({path:o("可选:子目录路径,默认当前项目根")},[])}},{type:"function",function:{name:"glob",description:'按 glob 模式查找文件(支持 ** 与 *),如 "**/*.ts"、"src/**/*.java"。',parameters:i({pattern:o("glob 模式")},["pattern"])}},{type:"function",function:{name:"search_code",description:"按「概念/功能」检索最相关的代码块(BM25 相关性排序,比 grep 更懂「哪段代码在做这件事」)。适合「登录逻辑在哪」「哪里处理支付回调」这类找相关实现;要找精确字符串仍用 grep。返回 文件:行范围 + 代码块。",parameters:i({query:o("要找的功能/概念(自然语言或关键词组合,多写几个相关词更准)"),k:a("可选:返回块数,默认 8,上限 20")},["query"])}},{type:"function",function:{name:"grep",description:"在文件内容中按正则搜索,返回 匹配文件:行号: 该行。可选 path 限定目录、glob 限定文件名模式、ignore_case 忽略大小写、files_only 只列出命中文件。",parameters:i({pattern:o("正则表达式"),path:o("可选:搜索目录,默认项目根"),glob:o("可选:文件名 glob 过滤"),ignore_case:p("可选:忽略大小写(默认否)"),files_only:p("可选:只返回命中文件列表,不含匹配行(默认否)"),max_results:a("可选:最多返回条数(默认 200,上限 500)")},["pattern"])}},{type:"function",function:{name:"read_document",description:"读取 PDF/Word/Excel/PPT/RTF 等文档的纯文本内容(经平台解析,read_file 读不了这类二进制文档)。大型文档分段通读:段末标注「全文共N字符/本段区间」,继续调用并传 offset 读取后续,直到「已到结尾」。对话附件若标注「已截断」,用本工具读取被省略的部分。",parameters:i({path:o("相对项目根的文档路径"),offset:a("可选:窗口起始字符位置(0 基,默认 0,从头顺序读取)"),max_chars:a("可选:本段最大字符数(默认 16000)")},["path"])}},{type:"function",function:{name:"write_file",description:"把文本写入文件(覆盖式,自动建父目录)。仅限当前项目目录内。仅用于新建文件或全量重写;小改动优先 edit_file。生成超长文件(长文档/PPT/大代码文件)时分段产出:先写首段,后续每段设 append=true 追加到文件末尾,避免单次内容过长被截断。",parameters:i({path:o("相对项目根的文件路径"),content:o("本次写入的内容(append=true 时为追加的续段)"),append:p("可选:true=追加到文件末尾(分段产出大文件);默认覆盖写")},["path","content"])}},{type:"function",function:{name:"edit_file",description:"精确替换文件中的片段:old_string 必须是文件原文(不含 read_file 的行号前缀)且在文件中唯一(除非 replace_all=true)。仅限项目目录内。",parameters:i({path:o("相对项目根的文件路径"),old_string:o("被替换的原文(需在文件中唯一,除非 replace_all;不要包含行号前缀)"),new_string:o("替换为的新内容"),replace_all:p("是否替换全部匹配(默认否)")},["path","old_string","new_string"])}},{type:"function",function:{name:"run_bash",description:"在项目目录下执行一条 shell 命令并返回输出(stdout+stderr+退出码)。用于构建、测试、git、脚手架等。默认超时 120s(可用 timeout 调整)。避免用它读文件/搜索(用 read_file/grep 更高效)。",parameters:i({command:o("要执行的 shell 命令"),timeout:a("可选:超时毫秒")},["command"])}},{type:"function",function:{name:"fetch_url",description:"抓取一个 http/https 网页或接口(GET),HTML 自动转为纯文本。用于查在线文档、API 说明、Issue 等。结果可能很长,尽量带着明确目的使用。",parameters:i({url:o("完整 URL(http/https)"),timeout:a("可选:超时毫秒,默认 30000")},["url"])}},...r];export const READONLY_TOOLS=new Set(["read_file","list_files","glob","grep","search_code","read_document"]);const s={read_file:e.readFile,read_document:e.readDocument,list_files:e.listFiles,glob:e.glob,grep:e.grep,search_code:t,write_file:e.writeFile,edit_file:e.editFile,run_bash:e.runBash,fetch_url:e.fetchUrl,...n};export async function runTool(e,t,r,n){const i=s[e];if(!i)return`未知工具:${e}`;try{return await i(r,t||{},n)}catch(e){return`工具执行失败:${e.message}`}}
|
package/src/transcript.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{streamChat as t}from"./api.js";import{DEFAULT_MODEL as e}from"./config.js";const n=/^(agent|workflow|datahuman)-/;export function textOf(t){return"string"==typeof t?t:Array.isArray(t)?t.map(t=>"text"===t?.type?t.text:"image_url"===t?.type?"[图片]":"").join("\n"):""}export function toMarkdown(t){const e=t||{},n=["# HuiCai 对话记录","",`- 会话:${e.id||""}`,`- 模型:${e.model||""}`,`- 导出时间:${(new Date).toLocaleString()}`,"","---",""];for(const t of e.messages||[])if("system"!==t.role)if("user"===t.role)n.push("### 🧑 用户","",textOf(t.content).trim()||"(空)","");else if("assistant"===t.role){const e=textOf(t.content).trim();e&&n.push("### 🤖 助手","",e,"");for(const e of t.tool_calls||[]){const t=String(e.function?.arguments||"").replace(/\s+/g," ").slice(0,200);n.push(`> 🔧 \`${e.function?.name}(${t})\``,"")}}else if("tool"===t.role){const e=String(t.content||"").slice(0,1500);n.push("> 工具结果:","","```",e,"```","")}return n.join("\n")}export async function distillMemory(o,r
|
|
1
|
+
import{streamChat as t}from"./api.js";import{DEFAULT_MODEL as e}from"./config.js";const n=/^(agent|workflow|datahuman|squad)-/;export function textOf(t){return"string"==typeof t?t:Array.isArray(t)?t.map(t=>"text"===t?.type?t.text:"image_url"===t?.type?"[图片]":"").join("\n"):""}export function toMarkdown(t){const e=t||{},n=["# HuiCai 对话记录","",`- 会话:${e.id||""}`,`- 模型:${e.model||""}`,`- 导出时间:${(new Date).toLocaleString()}`,"","---",""];for(const t of e.messages||[])if("system"!==t.role)if("user"===t.role)n.push("### 🧑 用户","",textOf(t.content).trim()||"(空)","");else if("assistant"===t.role){const e=textOf(t.content).trim();e&&n.push("### 🤖 助手","",e,"");for(const e of t.tool_calls||[]){const t=String(e.function?.arguments||"").replace(/\s+/g," ").slice(0,200);n.push(`> 🔧 \`${e.function?.name}(${t})\``,"")}}else if("tool"===t.role){const e=String(t.content||"").slice(0,1500);n.push("> 工具结果:","","```",e,"```","")}return n.join("\n")}export async function distillMemory(o,s,r=""){const i=n.test(String(s.model||""))?s.compactModel||e:s.model,l=(o||[]).filter(t=>"system"!==t.role).map(t=>`[${t.role}] ${textOf(t.content).replace(/\s+/g," ").slice(0,600)}`).join("\n").slice(0,16e3);if(!l.trim())return[];const c=(r?"【已有记忆(勿重复)】\n"+r+"\n\n":"")+"【本次记录】\n"+l,a=((await t({baseUrl:s.baseUrl,apiKey:s.apiKey,model:i,messages:[{role:"system",content:"你是项目知识提炼器。从下面这段对话/执行记录中,提炼出**值得跨会话长期记住**的稳定事实/约定/结论/踩坑(如构建与测试命令、架构决定、代码约定、易错点、外部依赖要点)。要求:每条一行,以「- 」开头,简洁具体;只输出真正长期有用的,不要一次性细节、不要大段代码;若与「已有记忆」重复则跳过;最多 5 条;没有则输出「(无)」。"},{role:"user",content:c}]})).content||"").trim();return!a||/^[((]无[))]$/.test(a)?[]:a.split("\n").map(t=>t.replace(/^[-*]\s*/,"").trim()).filter(t=>t&&!/^[((]无[))]$/.test(t)).slice(0,5)}export async function recapConversation(o,s){const r=n.test(String(s.model||""))?s.compactModel||e:s.model,i=(o||[]).filter(t=>"system"!==t.role).map(t=>`[${t.role}] ${textOf(t.content).replace(/\s+/g," ").slice(0,500)}`).join("\n").slice(0,12e3);if(!i.trim())return"(对话尚无内容)";return((await t({baseUrl:s.baseUrl,apiKey:s.apiKey,model:r,messages:[{role:"system",content:"用一句话(不超过40个汉字)概括这段对话到目前为止在做什么、进展如何。只输出这一句话,不要引号、不要多余说明。"},{role:"user",content:i}]})).content||"").trim().split("\n")[0]||"(无法生成小结)"}
|