toolnet-memory 0.3.2 → 0.3.4
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/toolnet-memory +44 -0
- package/bundle/agy-hook.js +39 -88
- package/bundle/agy.js +38 -104
- package/bundle/auto-integrate.js +131 -707
- package/bundle/codex-context.js +44 -54
- package/bundle/codex.js +47 -83
- package/bundle/context-runtime.js +29 -124
- package/bundle/context.js +14 -15
- package/bundle/doctor.js +3 -6
- package/bundle/full-index.js +8 -18
- package/bundle/graph-index.js +4 -4
- package/bundle/impact.js +3 -3
- package/bundle/incremental.js +4 -4
- package/bundle/learner.js +5 -5
- package/bundle/mcp.js +7 -54
- package/bundle/opencode.js +168 -717
- package/bundle/project-manual.js +6 -6
- package/bundle/runtime.js +11 -11
- package/bundle/semantic.js +6 -6
- package/bundle/setup.js +187 -716
- package/bundle/snapshot.js +4 -4
- package/bundle/update.js +1 -4
- package/bundle/work-continuity.js +6 -6
- package/package.json +1 -1
- package/bundle/api.js +0 -52
- package/bundle/claude-hook.js +0 -78
- package/bundle/claude.js +0 -3
- package/bundle/continuity-certify.js +0 -49
- package/bundle/handoff-refresh.js +0 -12
- package/bundle/init.js +0 -763
- package/bundle/memory-agent.js +0 -48
- package/bundle/memory-query.js +0 -7
- package/bundle/production-certify.js +0 -5
- package/bundle/provider.js +0 -3
- package/bundle/recovery-certify.js +0 -55
package/bundle/claude-hook.js
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import{existsSync as Ye,mkdirSync as We,readFileSync as Ue,renameSync as Qe,rmSync as Ze,writeFileSync as Xe}from"node:fs";import{isAbsolute as qe,join as re,relative as et}from"node:path";import{existsSync as ie,readFileSync as ae}from"node:fs";import{homedir as ce}from"node:os";import{join as ue}from"node:path";function de(e){let t=e.trim();return t.length>=2&&t.startsWith('"')&&t.endsWith('"')?(t=t.slice(1,-1),t.replace(/\\n/g,`
|
|
2
|
-
`).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):t.length>=2&&t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1):t}function le(){let e=process.env.TOOLNET_GLOBAL_ENV??ue(ce(),".config","toolnet-memory",".env");if(!ie(e))return;let t=ae(e,"utf8");for(let n of t.split(/\r?\n/)){let r=n.trim();if(!r||r.startsWith("#"))continue;r.startsWith("export ")&&(r=r.slice(7));let o=r.indexOf("=");if(o<=0)continue;let s=r.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(s)&&process.env[s]===void 0&&(process.env[s]=de(r.slice(o+1)))}}le();import{createHash as fe}from"node:crypto";import{existsSync as w,mkdirSync as pe,readFileSync as me,renameSync as ge,writeFileSync as ye}from"node:fs";import{basename as he,dirname as h,join as x,parse as _,resolve as g}from"node:path";var P=".toolnet",Se="project.json";function xe(e){return fe("sha256").update(e).digest("hex").slice(0,16)}function v(e){return x(e,P,Se)}function ke(e){return w(v(e))}function be(e,t){let n=g(e),r=_(n).root;for(;;){if(ke(n))return n;if(n===r||t&&n===g(t))break;let o=h(n);if(o===n)break;n=o}return null}function we(e){let t=g(e),n=_(t).root,r=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(r.some(s=>w(x(t,s))))return t;if(t===n)break;let o=h(t);if(o===t)break;t=o}return g(e)}function ve(e){let t;try{t=JSON.parse(me(e,"utf8"))}catch(o){throw new Error(`Invalid ToolNet project manifest: ${e}: ${o instanceof Error?o.message:String(o)}`)}if(!t||typeof t!="object")throw new Error(`Invalid ToolNet project manifest: ${e}`);let n=t;if(typeof n.id!="string"||!n.id.trim())throw new Error(`ToolNet project manifest is missing id: ${e}`);if(typeof n.name!="string"||!n.name.trim())throw new Error(`ToolNet project manifest is missing name: ${e}`);let r=new Date().toISOString();return{version:1,id:n.id,name:n.name,remote:typeof n.remote=="string"&&n.remote.trim()?n.remote:n.name,rootPath:typeof n.rootPath=="string"?n.rootPath:h(h(e)),createdAt:typeof n.createdAt=="string"?n.createdAt:r,updatedAt:typeof n.updatedAt=="string"?n.updatedAt:r,graphVersion:typeof n.graphVersion=="number"?n.graphVersion:0,memoryVersion:typeof n.memoryVersion=="number"?n.memoryVersion:0,metadata:n.metadata&&typeof n.metadata=="object"?n.metadata:void 0}}function j(e,t){let n=x(e,P);pe(n,{recursive:!0});let r=v(e),o=`${r}.tmp-${process.pid}`;ye(o,JSON.stringify(t,null,2)+`
|
|
3
|
-
`,{encoding:"utf8",mode:384}),ge(o,r)}function I(e,t){return{id:e.id,name:e.name,remote:e.remote,rootPath:t,createdAt:e.createdAt,updatedAt:e.updatedAt,graphVersion:e.graphVersion,memoryVersion:e.memoryVersion,metadata:e.metadata}}var S=class{detect(t=process.cwd()){let n=g(t),r=we(n),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(p=>w(x(r,p))),i=be(n,s?r:void 0);if(i){let p=v(i),l=ve(p);return l.rootPath!==i&&(l.rootPath=i,l.updatedAt=new Date().toISOString(),j(i,l)),I(l,i)}let c=new Date().toISOString(),a=he(r),u={version:1,id:xe(r),name:a,remote:a,rootPath:r,createdAt:c,updatedAt:c,graphVersion:0,memoryVersion:0};return j(r,u),I(u,r)}};import*as f from"fs";import*as d from"path";function A(e){return e?Math.ceil(e.length/3.5):0}function Ae(e,t){if(!e)return"";if(A(e)<=t)return e;let r=Math.floor(t*3.5),o=e.slice(0,r),s=o.lastIndexOf("."),i=o.lastIndexOf(`
|
|
4
|
-
`),c=Math.max(s,i);return c>r*.7?o.slice(0,c+1):o}function N(e,t){if(!e)return"";let n=e.split(`
|
|
5
|
-
`).filter(s=>s.trim());if(n.length<=t)return e;let r=n.slice(0,t),o=n.length-t;return[...r,`... (${o} more items omitted)`].join(`
|
|
6
|
-
`)}function Ce(e,t){let{maxTokens:n,trimMarker:r="[Context trimmed by ToolNet Memory token budget]"}=t;if(e.length===0)return"";let o=[...e].sort((a,u)=>u.priority-a.priority),s=[],i=0,c=!1;for(let a of o){let u=`# ${a.title}
|
|
7
|
-
|
|
8
|
-
`,p=A(u),l=A(a.content),y=p+l;if(i+y<=n)s.push(u+a.content),i+=y;else{let m=n-i-p;if(m>50){let k=Ae(a.content,m);s.push(u+k),i=n,c=!0}else c=!0;break}}return c&&s.push(`
|
|
9
|
-
${r}
|
|
10
|
-
`),s.join(`
|
|
11
|
-
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
`)}function F(e,t){let n=[{title:"Profile",content:N(e,10),priority:100},{title:"Current Work",content:N(t,15),priority:90}];return Ce(n,{maxTokens:800})}import{chmodSync as Zt,existsSync as Me,mkdirSync as Xt,readFileSync as Ee,renameSync as qt,writeFileSync as en}from"node:fs";import{dirname as nn,join as $}from"node:path";var L="toolnet.context-offload.v1";function Re(e){return $(e,".toolnet","offload")}function Te(e){return $(Re(e),"graph.json")}function C(){return{schema:L,version:1,updatedAt:new Date(0).toISOString(),nodes:[]}}function Oe(e){let t=Te(e);if(!Me(t))return C();try{let n=JSON.parse(Ee(t,"utf8"));return n.schema!==L||n.version!==1||!Array.isArray(n.nodes)?C():n}catch{return C()}}function D(e,t){let n=e.replace(/\s+/gu," ").trim();return n.length<=t?n:n.slice(0,t-1).trimEnd()+"\u2026"}function H(e,t={}){let n=Oe(e);if(n.nodes.length===0)return"";let r=Math.max(1,Math.min(12,t.maxAssets??6)),o=Math.max(320,Math.min(2400,t.maxChars??900)),s=["[TOOLNET CONTEXT OFFLOAD GRAPH]","Large tool/file payloads stay outside prompt context.","Read only a needed asset with MCP context_offload_read."];for(let i of n.nodes.slice(-r).reverse()){let c=D(i.sourceRefs.at(-1)??"unknown",72),a=i.files.length>0?` files=${D(i.files.join(","),120)}`:"",u=`event:${c} --offloads--> asset:${i.id.slice(0,12)} kind=${i.kind} bytes=${i.bytes}${a}`;if([...s,u].join(`
|
|
15
|
-
`).length>o)break;s.push(u)}return s.join(`
|
|
16
|
-
`)}import{existsSync as z,mkdirSync as Ie,readFileSync as V,renameSync as _e,writeFileSync as Pe}from"node:fs";import{dirname as Ne,join as G}from"node:path";import{createHash as je}from"node:crypto";function M(e){return je("sha256").update(e).digest("hex")}function B(e){return G(e.rootPath,".toolnet","context","handoff.md")}function J(e){return G(e.rootPath,".toolnet","context","handoff.json")}function K(e,t){Ie(Ne(e),{recursive:!0});let n=`${e}.tmp-${process.pid}-${Date.now()}`;Pe(n,t.endsWith(`
|
|
17
|
-
`)?t:`${t}
|
|
18
|
-
`,{encoding:"utf8",mode:384}),_e(n,e)}function Fe(e,t){K(e,`${JSON.stringify(t,null,2)}
|
|
19
|
-
`)}function Y(e,t){let n=t.replace(/\r\n/g,`
|
|
20
|
-
`).trim();if(!n)return null;let r={version:1,projectId:e.id,projectName:e.name,text:n,digest:M(n),generatedAt:new Date().toISOString()};return K(B(e),n),Fe(J(e),r),r}function E(e){let t=B(e);if(!z(t))return null;try{let n=V(t,"utf8").trim();if(!n)return null;let r=new Date(0).toISOString(),o=J(e);if(z(o))try{let s=JSON.parse(V(o,"utf8"));typeof s.generatedAt=="string"&&(r=s.generatedAt)}catch{}return{version:1,projectId:e.id,projectName:e.name,text:n,digest:M(n),generatedAt:r}}catch{return null}}function W(e,t=1800){let n=E(e);if(!n?.text)return null;let r=n.text;return r.length>t&&(r=`${r.slice(0,t)}
|
|
21
|
-
|
|
22
|
-
[Fast handoff truncated]`),["[TOOLNET FAST HANDOFF]","",`Project: ${e.name}`,`Updated: ${n.generatedAt}`,"",r].join(`
|
|
23
|
-
`)}var R="memory_agent_ask";function U(){return`
|
|
24
|
-
[TOOLNET MEMORY AGENT]
|
|
25
|
-
|
|
26
|
-
Tool:
|
|
27
|
-
- ${R}
|
|
28
|
-
|
|
29
|
-
For resume/continue requests:
|
|
30
|
-
|
|
31
|
-
1. Use the injected ToolNet continuity handoff FIRST.
|
|
32
|
-
2. If the handoff is missing or ambiguous, invoke
|
|
33
|
-
${R} directly BEFORE repository/history exploration.
|
|
34
|
-
3. NEVER reconstruct prior work from:
|
|
35
|
-
- .toolnet/sessions/**
|
|
36
|
-
- state.json
|
|
37
|
-
- events.jsonl
|
|
38
|
-
- raw transcripts
|
|
39
|
-
4. NEVER search for the implementation/schema of
|
|
40
|
-
${R}; invoke the MCP tool directly.
|
|
41
|
-
5. Inspect git/source only AFTER continuity context is known.
|
|
42
|
-
|
|
43
|
-
Use:
|
|
44
|
-
- mode="local" for current task, last file, blocker or next action.
|
|
45
|
-
- mode="ai" for ambiguous or combined continuity questions.
|
|
46
|
-
|
|
47
|
-
Current repository evidence overrides stale memory.
|
|
48
|
-
`.trim()}function T(e){let t=d.resolve(e),n=d.parse(t).root;for(;t!==n;){let r=d.join(t,".toolnet");if(f.existsSync(r)&&f.statSync(r).isDirectory())return t;t=d.dirname(t)}return null}function Q(e){try{return f.existsSync(e)?f.readFileSync(e,"utf-8").trim():null}catch{return null}}function Z(e){return e.split(`
|
|
49
|
-
`).filter(r=>{let o=r.toUpperCase();return!(o.includes("SECRET")||o.includes("TOKEN")||o.includes("API_KEY")||o.includes("APIKEY")||o.includes("PASSWORD")||o.includes("PASS="))}).join(`
|
|
50
|
-
`)}function X(e={}){let t=e.projectPath||process.cwd(),n=T(t);if(!n)return null;let r=d.join(n,".toolnet","project.json"),o="Unknown";try{if(f.existsSync(r)){let b=JSON.parse(f.readFileSync(r,"utf-8"));o=b.name||b.projectName||"Unknown"}}catch{}let s=d.join(n,".toolnet","profile.md"),i=d.join(n,".toolnet","current.md"),c=Q(s)||"",a=Q(i)||"";c=Z(c),a=Z(a);let u=`[TOOLNET PROJECT CONTEXT]
|
|
51
|
-
|
|
52
|
-
Project: ${o}
|
|
53
|
-
Root: ${n}
|
|
54
|
-
|
|
55
|
-
`,p=F(c,a),l=W({id:"",name:o,rootPath:n},1600),y=l?`
|
|
56
|
-
|
|
57
|
-
${l}
|
|
58
|
-
`:"",m=H(n,{maxAssets:6,maxChars:900}),k=m?`
|
|
59
|
-
|
|
60
|
-
${m}
|
|
61
|
-
`:"",se=`
|
|
62
|
-
|
|
63
|
-
${U()}
|
|
64
|
-
|
|
65
|
-
Forbidden At Startup:
|
|
66
|
-
- Do not run session:agy-recover, handoff:latest, or brief automatically.
|
|
67
|
-
- Do not perform deep recovery merely because an agent starts.
|
|
68
|
-
- If the user asks to resume previous work and fast context is insufficient,
|
|
69
|
-
use memory_agent_ask before guessing.
|
|
70
|
-
`;return u+p+y+k+se}import{existsSync as Ve,readFileSync as Ge}from"node:fs";import{join as Be}from"node:path";import{existsSync as De,mkdirSync as xn,readFileSync as $e,renameSync as kn,writeFileSync as bn}from"node:fs";import{dirname as vn,join as Le}from"node:path";function He(e){return Le(e.rootPath,".toolnet","context","session-origin.json")}function ze(e){let t=He(e);if(!De(t))return null;try{let n=JSON.parse($e(t,"utf8"));return n.version!==1||n.projectId!==e.id?null:n}catch{return null}}function q(e){let t=ze(e);if(!t)return null;let n=["Previous session:",`- Agent: ${t.agent}`,`- Session: ${t.nativeSessionId}`];return t.currentPhase&&n.push(`- Current phase: ${t.currentPhase}`),t.currentTask&&n.push(`- Current task: ${t.currentTask}`),t.lastTouchedFile&&n.push(`- Last touched file: ${t.lastTouchedFile}`),t.latestBlocker&&n.push(`- Blocker: ${t.latestBlocker}`),t.latestNextAction&&n.push(`- Next action: ${t.latestNextAction}`),t.latestDecision&&n.push(`- Important decision: ${t.latestDecision}`),n.join(`
|
|
71
|
-
`)}function Je(e){let t=Be(e.rootPath,".toolnet","current.md");if(!Ve(t))return null;try{return Ge(t,"utf8").trim()}catch{return null}}function Ke(e,t){let n=t.replace(/\r\n/g,`
|
|
72
|
-
`).trim(),r=2400,o=n.length>r?`${n.slice(0,r)}
|
|
73
|
-
|
|
74
|
-
[Current work truncated for fast handoff]`:n,s=q(e);return["# ToolNet Fast Handoff","",`Project: ${e.name}`,"",...s?[s,""]:[],o,"","---","","Continuation rule:","- Continue unfinished work; do not redo completed steps.","- Query ToolNet Memory only when deeper history is needed."].join(`
|
|
75
|
-
`)}function O(e){let t=Je(e);if(t===null)return{updated:!1,reason:"missing-current",chars:0};if(!t.trim())return{updated:!1,reason:"empty-current",chars:0};let n=Ke(e,t);return E(e)?.text.trim()===n.trim()?{updated:!1,reason:"unchanged",chars:n.length}:(Y(e,n),{updated:!0,reason:"updated",chars:n.length})}var ee=3200;function te(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function tt(e){return e.length<=ee?e:`${e.slice(0,ee)}
|
|
76
|
-
|
|
77
|
-
[ToolNet startup context truncated]`}function nt(e){let t=T(e);return t?new S().detect(t)??null:null}function rt(e){return re(e.rootPath,".toolnet","context","session-origin.json")}function ot(e,t){if(!qe(t))return t;let n=et(e.rootPath,t);return n.startsWith("..")?t:n}function ne(e,t){let n=rt(e),r={};if(Ye(n))try{let u=JSON.parse(Ue(n,"utf8"));te(u)&&u.projectId===e.id&&(r=u)}catch{}let o,s=t.tool_input;te(s)&&typeof s.file_path=="string"&&(o=ot(e,s.file_path));let i=typeof t.session_id=="string"?t.session_id:typeof r.nativeSessionId=="string"?r.nativeSessionId:"claude",c={...r,version:1,projectId:e.id,agent:"claude",nativeSessionId:i,updatedAt:new Date().toISOString()};o&&(c.lastTouchedFile=o),We(re(e.rootPath,".toolnet","context"),{recursive:!0,mode:448});let a=`${n}.tmp-${process.pid}-${Date.now()}`;try{Xe(a,JSON.stringify(c,null,2)+`
|
|
78
|
-
`,{encoding:"utf8",mode:384}),Qe(a,n)}finally{Ze(a,{force:!0})}}function oe(e){let t=typeof e.hook_event_name=="string"?e.hook_event_name:"",n=typeof e.cwd=="string"?e.cwd:"";if(!n)return{};let r=nt(n);if(!r)return{};try{if(t==="SessionStart"){let o=X({projectPath:n});return o?.trim()?{hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:tt(o)}}:{}}if(t==="PostToolUse"){ne(r,e);try{O(r)}catch{}return{}}if(t==="Stop"){ne(r,e);try{O(r)}catch{}return{}}return{}}catch{return{}}}async function st(){let e="";for await(let t of process.stdin)e+=t.toString();if(!e.trim())return{};try{return JSON.parse(e)}catch{return{}}}async function it(){let e=await st(),t=oe(e);process.stdout.write(JSON.stringify(t))}it().catch(()=>{process.stdout.write("{}"),process.exitCode=0});
|
package/bundle/claude.js
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{existsSync as I,mkdirSync as v,readFileSync as J,renameSync as E,rmSync as N,writeFileSync as M}from"node:fs";import{dirname as $}from"node:path";import{homedir as y}from"node:os";import{join as g}from"node:path";function x(n={}){return g(n.home??y(),".claude")}function h(n={}){return g(x(n),"settings.json")}function C(n={}){return g(n.home??y(),".claude.json")}function l(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)}function A(n){return/^[A-Za-z0-9_./:-]+$/u.test(n)?n:`'${n.replace(/'/gu,"'\\''")}'`}function R(n){if(!I(n))return{};let t;try{t=JSON.parse(J(n,"utf8"))}catch(e){throw new Error(`Invalid existing Claude settings.json: ${e instanceof Error?e.message:String(e)}`)}if(!l(t))throw new Error("Invalid existing Claude settings.json: root must be a JSON object.");return t}function f(n){if(n===void 0)return[];if(!Array.isArray(n))throw new Error("Invalid existing Claude settings.json: hook event must be an array.");let t=[];for(let e of n){if(!l(e)){t.push(e);continue}let o=e.hooks;if(!Array.isArray(o)){t.push(e);continue}let s=o.filter(r=>{if(!l(r))return!0;let i=r.command;return!(typeof i=="string"&&i.includes("session:claude-hook"))});s.length!==0&&t.push({...e,hooks:s})}return t}function p(n){return{type:"command",command:n,timeout:10}}function T(n,t){v($(n),{recursive:!0,mode:448});let e=`${n}.toolnet-${process.pid}-${Date.now()}.tmp`;try{M(e,JSON.stringify(t,null,2)+`
|
|
2
|
-
`,{encoding:"utf8",mode:384}),E(e,n)}finally{N(e,{force:!0})}}function b(n={}){let t=n.settingsFile??h(),e=n.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=R(t),s=o.hooks;if(s!==void 0&&!l(s))throw new Error("Invalid existing Claude settings.json: hooks must be an object.");let r=l(s)?{...s}:{},i=`${A(e)} session:claude-hook`,a=f(r.SessionStart);a.push({matcher:"startup|resume|clear|compact",hooks:[p(i)]}),r.SessionStart=a;let d=f(r.PostToolUse);d.push({matcher:"Edit|Write",hooks:[p(i)]}),r.PostToolUse=d;let m=f(r.Stop);m.push({hooks:[p(i)]}),r.Stop=m;let c={...o,hooks:r},j=JSON.stringify(o),F=JSON.stringify(c);return j===F?{settingsFile:t,changed:!1}:(T(t,c),{settingsFile:t,changed:!0})}import{existsSync as H,mkdirSync as P,readFileSync as _,renameSync as U,rmSync as B,writeFileSync as D}from"node:fs";import{dirname as L}from"node:path";function u(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)}function O(n){if(!H(n))return{};let t;try{t=JSON.parse(_(n,"utf8"))}catch(e){throw new Error(`Invalid existing Claude Code config: ${e instanceof Error?e.message:String(e)}`)}if(!u(t))throw new Error("Invalid existing Claude Code config: root must be a JSON object.");return t}function S(n,t){if(!u(n))return!1;let e=n.args;return n.type==="stdio"&&n.command===t&&Array.isArray(e)&&e.length===1&&e[0]==="mcp"}function W(n,t){P(L(n),{recursive:!0});let e=`${n}.toolnet-${process.pid}-${Date.now()}.tmp`;try{D(e,JSON.stringify(t,null,2)+`
|
|
3
|
-
`,{encoding:"utf8",mode:384}),U(e,n)}finally{B(e,{force:!0})}}function w(n={}){let t=n.stateFile??C(),e=n.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=n.serverName??"toolnet-memory",s=O(t),r=s.mcpServers;if(r!==void 0&&!u(r))throw new Error("Invalid existing Claude Code config: mcpServers must be an object.");let i=u(r)?{...r}:{},a=i[o];if(S(a,e))return{installed:!0,changed:!1,configFile:t,serverName:o,command:[e,"mcp"],repaired:!1};let d=a!==void 0;i[o]={type:"stdio",command:e,args:["mcp"]},W(t,{...s,mcpServers:i});let c=O(t).mcpServers;if(!u(c)||!S(c[o],e))throw new Error("Claude Code MCP configuration was written but verification failed.");return{installed:!0,changed:!0,configFile:t,serverName:o,command:[e,"mcp"],repaired:d}}function k(n={}){let t=n.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",e=b({binary:t,settingsFile:n.settingsFile}),o=w({binary:t,stateFile:n.stateFile});return{hooks:e,mcp:o,files:[e.settingsFile,o.configFile]}}function Y(){if((process.argv[2]??"install")!=="install")throw new Error("Usage: toolnet-memory integrate:claude");let t=k();console.log("\u2705 Claude Code integration installed"),console.log(`Hooks: ${t.hooks.settingsFile}`),console.log(`MCP: ${t.mcp.configFile}`),console.log("Server: toolnet-memory")}try{Y()}catch(n){console.error(n instanceof Error?n.message:String(n)),process.exitCode=1}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import{mkdirSync as H,mkdtempSync as ft,rmSync as mt,writeFileSync as me}from"node:fs";import{tmpdir as pt}from"node:os";import{join as k}from"node:path";import{z as le}from"zod";import{existsSync as xe,readFileSync as Ie}from"node:fs";import{join as Ce}from"node:path";import{existsSync as he,mkdirSync as bt,readFileSync as ke,renameSync as wt,writeFileSync as Mt}from"node:fs";import{dirname as Et,join as Ae}from"node:path";function ve(r){return Ae(r.rootPath,".toolnet","work","current.json")}function K(r){let e=ve(r);if(!he(e))return null;try{let t=JSON.parse(ke(e,"utf8"));return t.version!==1||t.projectId!==r.id?null:t}catch{return null}}import{existsSync as be,mkdirSync as Ct,readFileSync as we,renameSync as Ot,writeFileSync as Pt}from"node:fs";import{dirname as Rt,join as Me}from"node:path";function _e(r){return Me(r.rootPath,".toolnet","context","session-origin.json")}function B(r){let e=_e(r);if(!be(e))return null;try{let t=JSON.parse(we(e,"utf8"));return t.version!==1||t.projectId!==r.id?null:t}catch{return null}}function Ee(r){return r.normalize("NFKC").toLowerCase().replace(/\s+/g," ").trim()}function I(r){let e=Ee(r),t=/(?:agent trước|previous agent|ai trước|phiên trước)/u.test(e),o=/(?:đang làm gì|làm tới đâu|dừng ở đâu|dở ở đâu|stopped where|working on|current task|file nào|last touched|làm gì tiếp|next action|next step)/u.test(e);return t&&o?"summary":/(?:agent trước là ai|previous agent|ai trước là ai|phiên trước là ai)/u.test(e)?"previous_agent":/(?:task hiện tại|current task|đang làm gì|đang làm task nào|dở ở đâu|dừng ở đâu|stopped where)/u.test(e)?"current_task":/(?:tiếp theo|next action|next step|làm gì tiếp|phải làm gì tiếp)/u.test(e)?"next_action":/(?:file cuối|last file|file nào|đang sửa file|last touched)/u.test(e)?"last_file":/(?:blocker|vướng|kẹt|lỗi gì|blocked)/u.test(e)?"blocker":/(?:decision|quyết định|đã chốt|important decision)/u.test(e)?"decision":/(?:đã xong|completed|hoàn thành|done|todo nào xong)/u.test(e)?"completed":/(?:status|trạng thái|tiến độ|progress)/u.test(e)?"status":"summary"}var Oe={previous_agent:{previous_agent:100,task:35,phase:25},current_task:{task:100,phase:75,file:65,next_action:55,blocker:45},next_action:{next_action:100,todo:80,task:65,blocker:60,file:40},last_file:{file:100,task:65,next_action:40},blocker:{blocker:100,task:60,next_action:50,file:35},decision:{decision:100,task:45,file:30},completed:{completed:100,progress:70,task:30},status:{progress:100,task:85,phase:75,blocker:65,next_action:60},summary:{task:100,next_action:95,file:90,blocker:85,previous_agent:80,phase:75,completed:60,todo:60,decision:55,progress:50,test:45,goal:35}},Pe={handoff:18,"session-origin":14,"work-state":8};function Te(r){return r.normalize("NFKC").toLowerCase().replace(/\s+/g," ").trim()}function l(r,e){let t=e.value.replace(/\s+/g," ").trim();t&&r.push({...e,value:t,score:0})}function Re(r){let e=Ce(r.rootPath,".toolnet","work","handoff-latest.json");if(!xe(e))return null;try{let o=JSON.parse(Ie(e,"utf8")).continuity;return o?.schema!=="toolnet.handoff.v2"||o.project.id!==r.id?null:o}catch{return null}}function Se(r){let e=Re(r);if(!e)return[];let t=[];l(t,{kind:"previous_agent",value:e.source.agent,source:"handoff"}),e.goal&&l(t,{kind:"goal",value:e.goal,source:"handoff"}),e.current.phase&&l(t,{kind:"phase",value:e.current.phase.title,source:"handoff"}),e.current.task&&l(t,{kind:"task",value:e.current.task.title,source:"handoff"}),e.files.current&&l(t,{kind:"file",value:e.files.current,source:"handoff"}),e.nextAction&&l(t,{kind:"next_action",value:e.nextAction,source:"handoff"});for(let o of e.blockers)l(t,{kind:"blocker",value:o,source:"handoff"});for(let o of e.decisions)l(t,{kind:"decision",value:o,source:"handoff"});for(let o of e.completed.tasks)l(t,{kind:"completed",value:o,source:"handoff"});for(let o of e.remaining.todos)l(t,{kind:"todo",value:o,source:"handoff"});return l(t,{kind:"test",value:`Test status: ${e.tests.status}`,source:"handoff"}),l(t,{kind:"progress",value:`${e.progress.tasksCompleted}/${e.progress.tasksTotal} tasks completed; ${e.progress.blocked} blocked`,source:"handoff"}),t}function Le(r){let e=B(r);if(!e)return[];let t=[];return l(t,{kind:"previous_agent",value:e.agent,source:"session-origin"}),e.currentPhase&&l(t,{kind:"phase",value:e.currentPhase,source:"session-origin"}),e.currentTask&&l(t,{kind:"task",value:e.currentTask,source:"session-origin"}),e.lastTouchedFile&&l(t,{kind:"file",value:e.lastTouchedFile,source:"session-origin"}),e.latestNextAction&&l(t,{kind:"next_action",value:e.latestNextAction,source:"session-origin"}),e.latestBlocker&&l(t,{kind:"blocker",value:e.latestBlocker,source:"session-origin"}),e.latestDecision&&l(t,{kind:"decision",value:e.latestDecision,source:"session-origin"}),t}function Fe(r){let e=K(r);if(!e)return[];let t=[];e.goal&&l(t,{kind:"goal",value:e.goal,source:"work-state"}),e.currentPhase&&l(t,{kind:"phase",value:e.currentPhase.title,source:"work-state"}),e.currentTask&&l(t,{kind:"task",value:e.currentTask.title,source:"work-state"});let o=e.filesTouched.at(-1);o&&l(t,{kind:"file",value:o,source:"work-state"});for(let n of e.nextActions.slice(0,5))l(t,{kind:"next_action",value:n,source:"work-state"});for(let n of e.blockers.slice(-5))l(t,{kind:"blocker",value:n,source:"work-state"});for(let n of e.decisions.slice(-5))l(t,{kind:"decision",value:n,source:"work-state"});for(let n of e.tasks.filter(i=>i.status==="completed").slice(-8))l(t,{kind:"completed",value:n.title,source:"work-state"});for(let n of e.tasks.filter(i=>i.status!=="completed"&&i.status!=="cancelled").slice(0,8))l(t,{kind:"todo",value:n.title,source:"work-state"});for(let n of e.tests.slice(-5))l(t,{kind:"test",value:n,source:"work-state"});return l(t,{kind:"progress",value:`${e.progress.tasksCompleted}/${e.progress.tasksTotal} tasks completed; ${e.progress.blocked} blocked`,source:"work-state"}),t}function Ne(r,e){let t=Oe[r];return e.map((o,n)=>({...o,score:(t[o.kind]??10)+Pe[o.source]+Math.max(0,5-n*.01)}))}function De(r){let e=new Map;for(let t of r){let o=`${t.kind}:${Te(t.value)}`,n=e.get(o);(!n||t.score>n.score)&&e.set(o,t)}return[...e.values()]}function y(r,e,t={}){let o=I(e),n=[...Se(r),...Le(r),...Fe(r)],i=De(Ne(o,n)).sort((d,g)=>g.score-d.score),a=Math.max(1,t.maxFacts??12),m=Math.max(300,t.maxChars??3200),c=[],p=0;for(let d of i){if(c.length>=a)break;let g=d.kind.length+d.value.length+d.source.length+16;c.length>0&&p+g>m||(c.push(d),p+=g)}return{intent:o,facts:c,context:{project:{id:r.id,name:r.name},intent:o,selectedFacts:c.map(d=>({kind:d.kind,value:d.value,source:d.source}))},stats:{candidates:n.length,selected:c.length,chars:p}}}var q={previous_agent:"Agent tr\u01B0\u1EDBc",goal:"M\u1EE5c ti\xEAu",phase:"Phase",task:"Task hi\u1EC7n t\u1EA1i",file:"File hi\u1EC7n t\u1EA1i",todo:"TODO",next_action:"Ti\u1EBFp theo",blocker:"Blocker",decision:"Quy\u1EBFt \u0111\u1ECBnh",completed:"\u0110\xE3 ho\xE0n th\xE0nh",test:"Test",progress:"Ti\u1EBFn \u0111\u1ED9"},Y=["previous_agent","phase","task","file","todo","next_action","blocker","decision","completed","test","progress","goal"],$e={previous_agent:["previous_agent"],current_task:["task","file","next_action"],next_action:["next_action","task","file","todo"],last_file:["file","task","next_action"],blocker:["blocker"],decision:["decision"],completed:["completed"],status:["progress","task","next_action","blocker"],summary:Y};function Ue(r){let e=r.normalize("NFKC").toLowerCase().replace(/\s+/gu," ").trim();return/(?:^|\s)(?:todo|to-do)(?:\s|$)/u.test(e)||/(?:việc|task)\s+(?:còn lại|chưa làm|chưa xong|chưa hoàn thành)/u.test(e)}function Ke(r){let e=r.filter(t=>t.source==="handoff");return e.length>0?e:r}function Be(r){let e=new Set,t=[];for(let o of r){let n=`${o.kind}:${o.value.normalize("NFKC").toLowerCase().replace(/\s+/gu," ").trim()}`;e.has(n)||(e.add(n),t.push(o))}return t}function V(r){switch(r){case"todo":case"blocker":case"decision":case"completed":return 3;default:return 1}}function je(r,e){let t=[];for(let o of e){let n=r.filter(i=>i.kind===o).slice(0,V(o));for(let i of n)t.push(`${q[o]}: ${i.value}`)}return t.join(`
|
|
2
|
-
`)}function Ge(r,e){let t=$e[e]??Y,o=[];for(let n of t){let i=r.filter(a=>a.kind===n).slice(0,V(n));for(let a of i)o.push(`${q[n]}: ${a.value}`)}return o.join(`
|
|
3
|
-
`)}function w(r,e){let t=y(r,e,{maxFacts:32,maxChars:6400}),o=Be(Ke(t.facts)),n=Ue(e)?je(o,["todo","task","next_action","file"]):Ge(o,String(t.intent));return n?{intent:String(t.intent),answer:n,source:o[0]?.source??"none"}:{intent:String(t.intent),answer:"ToolNet ch\u01B0a c\xF3 \u0111\u1EE7 memory cho c\xE2u h\u1ECFi n\xE0y.",source:"none"}}var J=[{id:"openai-compatible",label:"OpenAI-compatible",requiresApiKey:!0,requiresBaseUrl:!0,transport:"openai-compatible"},{id:"alibaba",label:"Alibaba / DashScope",requiresApiKey:!0,requiresBaseUrl:!0,transport:"openai-compatible"},{id:"openrouter",label:"OpenRouter",defaultBaseUrl:"https://openrouter.ai/api/v1",requiresApiKey:!0,transport:"openai-compatible"},{id:"groq",label:"Groq",defaultBaseUrl:"https://api.groq.com/openai/v1",requiresApiKey:!0,transport:"openai-compatible"},{id:"deepseek",label:"DeepSeek",defaultBaseUrl:"https://api.deepseek.com",defaultModel:"deepseek-v4-flash",requiresApiKey:!0,transport:"openai-compatible"},{id:"nvidia",label:"NVIDIA NIM",defaultBaseUrl:"https://integrate.api.nvidia.com/v1",defaultModel:"deepseek-ai/deepseek-v4-pro",requiresApiKey:!0,transport:"openai-compatible"},{id:"gemini",label:"Gemini",defaultBaseUrl:"https://generativelanguage.googleapis.com/v1beta",requiresApiKey:!0,transport:"gemini"},{id:"huggingface",label:"Hugging Face",defaultBaseUrl:"https://router.huggingface.co/v1",requiresApiKey:!0,transport:"openai-compatible"},{id:"ollama",label:"Ollama / Local",defaultBaseUrl:"http://127.0.0.1:11434/v1",requiresApiKey:!1,transport:"openai-compatible"},{id:"custom",label:"Custom endpoint",requiresApiKey:!1,requiresBaseUrl:!0,transport:"openai-compatible"},{id:"cloudflare",label:"Cloudflare Workers AI",requiresApiKey:!0,requiresAccountId:!0,transport:"cloudflare"}];function h(r){let e=J.find(t=>t.id===r);if(!e)throw new Error(`Unsupported AI provider: ${r}`);return e}function M(r){return J.some(e=>e.id===r)}function s(r){return process.env[r]?.trim()||void 0}function u(...r){return r.find(e=>!!e?.trim())}function z(){if(s("GROQ_API_KEY"))return"groq";if(s("DEEPSEEK_API_KEY"))return"deepseek";if(s("NVIDIA_API_KEY")||s("NVIDIA_NIM_API_KEY"))return"nvidia";if(s("OPENROUTER_API_KEY"))return"openrouter";if(s("ALIBABA_API_KEY")||s("DASHSCOPE_API_KEY"))return"alibaba";if(s("GEMINI_API_KEY")||s("GOOGLE_API_KEY"))return"gemini";if(s("CLOUDFLARE_API_TOKEN")&&s("CLOUDFLARE_ACCOUNT_ID"))return"cloudflare";if(s("HF_TOKEN"))return"huggingface";if(s("OLLAMA_MODEL")||s("OLLAMA_BASE_URL"))return"ollama"}function X(){let r=s("TOOLNET_LLM_PROVIDER");return r&&M(r)?r:z()??"openai-compatible"}function C(r){let e=h(r);switch(r){case"alibaba":return{provider:r,apiKey:u(s("ALIBABA_API_KEY"),s("DASHSCOPE_API_KEY")),baseUrl:u(s("ALIBABA_BASE_URL"),s("DASHSCOPE_BASE_URL"),e.defaultBaseUrl),model:u(s("ALIBABA_MODEL"),s("DASHSCOPE_MODEL"))};case"openrouter":return{provider:r,apiKey:s("OPENROUTER_API_KEY"),baseUrl:u(s("OPENROUTER_BASE_URL"),e.defaultBaseUrl),model:s("OPENROUTER_MODEL")};case"groq":return{provider:r,apiKey:s("GROQ_API_KEY"),baseUrl:u(s("GROQ_BASE_URL"),e.defaultBaseUrl),model:s("GROQ_MODEL")};case"deepseek":return{provider:r,apiKey:s("DEEPSEEK_API_KEY"),baseUrl:u(s("DEEPSEEK_BASE_URL"),e.defaultBaseUrl),model:u(s("DEEPSEEK_MODEL"),e.defaultModel)};case"nvidia":return{provider:r,apiKey:u(s("NVIDIA_API_KEY"),s("NVIDIA_NIM_API_KEY")),baseUrl:u(s("NVIDIA_BASE_URL"),s("NVIDIA_NIM_BASE_URL"),e.defaultBaseUrl),model:u(s("NVIDIA_MODEL"),s("NVIDIA_NIM_MODEL"),e.defaultModel)};case"gemini":return{provider:r,apiKey:u(s("GEMINI_API_KEY"),s("GOOGLE_API_KEY")),baseUrl:u(s("GEMINI_BASE_URL"),e.defaultBaseUrl),model:s("GEMINI_MODEL")};case"huggingface":return{provider:r,apiKey:s("HF_TOKEN"),baseUrl:u(s("HF_INFERENCE_BASE_URL"),e.defaultBaseUrl),model:u(s("HF_LLM_MODEL"),s("HF_MODEL"))};case"ollama":return{provider:r,apiKey:s("OLLAMA_API_KEY"),baseUrl:u(s("OLLAMA_BASE_URL"),e.defaultBaseUrl),model:s("OLLAMA_MODEL")};case"cloudflare":return{provider:r,accountId:s("CLOUDFLARE_ACCOUNT_ID"),apiKey:s("CLOUDFLARE_API_TOKEN"),baseUrl:s("CLOUDFLARE_AI_BASE_URL"),model:s("CLOUDFLARE_MODEL")};case"custom":return{provider:r,apiKey:s("CUSTOM_AI_API_KEY"),baseUrl:s("CUSTOM_AI_BASE_URL"),model:s("CUSTOM_AI_MODEL")};default:return{provider:"openai-compatible",apiKey:u(s("OPENAI_API_KEY"),s("MODEL_API_KEY")),baseUrl:u(s("OPENAI_BASE_URL"),s("MODEL_BASE_URL")),model:u(s("OPENAI_MODEL"),s("MODEL_NAME"))}}}function We(){let r=s("TOOLNET_LLM_PROVIDER"),e=r&&M(r)?r:X(),t=h(e),o=C(e);return{provider:e,apiKey:u(s("TOOLNET_LLM_API_KEY"),o.apiKey),baseUrl:u(s("TOOLNET_LLM_BASE_URL"),o.baseUrl,t.defaultBaseUrl),model:u(s("TOOLNET_LLM_MODEL"),o.model,t.defaultModel),accountId:u(s("TOOLNET_LLM_ACCOUNT_ID"),o.accountId)}}function He(){let r=s("TOOLNET_EMBEDDING_PROVIDER");return r==="local"?"local":r&&M(r)?r:s("HF_TOKEN")||s("HF_EMBEDDING_MODEL")?"huggingface":"local"}function Qe(){let r=He();if(r==="local")return{provider:"local",model:u(s("TOOLNET_EMBEDDING_MODEL"),s("LOCAL_EMBEDDING_MODEL"))};let e=h(r),t,o,n,i;switch(r){case"huggingface":t=s("HF_TOKEN"),o=s("HF_INFERENCE_BASE_URL"),n=s("HF_EMBEDDING_MODEL");break;case"openai-compatible":t=s("OPENAI_API_KEY"),o=s("OPENAI_BASE_URL"),n=s("OPENAI_EMBEDDING_MODEL");break;case"cloudflare":t=s("CLOUDFLARE_API_TOKEN"),o=s("CLOUDFLARE_AI_BASE_URL"),n=s("CLOUDFLARE_EMBEDDING_MODEL"),i=s("CLOUDFLARE_ACCOUNT_ID");break;default:t=C(r).apiKey,o=C(r).baseUrl,n=s(`${r.toUpperCase().replace(/-/g,"_")}_EMBEDDING_MODEL`)}let a=u(s("TOOLNET_EMBEDDING_ACCOUNT_ID"),i),m=u(s("TOOLNET_EMBEDDING_BASE_URL"),o,e.defaultBaseUrl),c=r==="cloudflare"&&!m&&a?`https://api.cloudflare.com/client/v4/accounts/${a}/ai/v1`:m;return{provider:r,apiKey:u(s("TOOLNET_EMBEDDING_API_KEY"),t),baseUrl:c,model:u(s("TOOLNET_EMBEDDING_MODEL"),n),accountId:a}}function j(){let r=We(),e=Qe();return{llm:r,embedding:e,legacy:{llm:!s("TOOLNET_LLM_PROVIDER")&&!!z(),embedding:!s("TOOLNET_EMBEDDING_PROVIDER")&&!!(s("HF_TOKEN")||s("HF_EMBEDDING_MODEL"))}}}function Z(r=X()){let e=j().llm;if(r===e.provider)return{id:r,apiKey:e.apiKey,baseUrl:e.baseUrl,model:e.model,accountId:e.accountId};let t=C(r);return{id:r,apiKey:t.apiKey,baseUrl:t.baseUrl,model:t.model,accountId:t.accountId}}function _(r,e){return s(`TOOLNET_LLM_FALLBACK_${r}_${e}`)}function ee(){let r=[];for(let e of[1,2]){let t=_(e,"PROVIDER");if(!t||!M(t))continue;let o=h(t);r.push({slot:e,config:{provider:t,apiKey:_(e,"API_KEY"),baseUrl:u(_(e,"BASE_URL"),o.defaultBaseUrl),model:u(_(e,"MODEL"),o.defaultModel),accountId:_(e,"ACCOUNT_ID")}})}return r}function te(){let r=s("TOOLNET_LLM_FALLBACK_COOLDOWN_MS"),e=s("TOOLNET_LLM_MAX_RETRIES"),t=Number(r??6e4),o=Number(e??1);return{cooldownMs:Number.isFinite(t)&&t>=0?t:6e4,maxRetries:Number.isFinite(o)&&o>=0?Math.floor(o):1}}var E=class extends Error{status;constructor(e,t){super(e),this.name="AiHttpError",this.status=t}};async function v(r,e,t=3e4){let o=new AbortController,n=setTimeout(()=>o.abort(),t);n.unref?.();try{let i=await fetch(r,{...e,signal:o.signal}),a=await i.text();if(!i.ok){let m=a;try{let c=JSON.parse(a);m=c.error?.message??c.message??a}catch{}throw new E(m||`HTTP ${i.status}`,i.status)}return a.trim()?JSON.parse(a):{}}finally{clearTimeout(n)}}function O(r,e){return`${r.replace(/\/+$/,"")}/${e.replace(/^\/+/,"")}`}var P=class{constructor(e){this.config=e}config;id="cloudflare";model(){let e=this.config.model?.trim();if(!e)throw new Error("cloudflare: MODEL is not configured");return e}async generate(e){let t=this.config.accountId?.trim(),o=this.config.apiKey?.trim();if(!t)throw new Error("cloudflare: ACCOUNT ID is not configured");if(!o)throw new Error("cloudflare: API TOKEN is not configured");let n=this.model(),a=`${(this.config.baseUrl?.trim()||`https://api.cloudflare.com/client/v4/accounts/${t}/ai/run`).replace(/\/+$/,"")}/${n}`,m=await v(a,{method:"POST",headers:{authorization:`Bearer ${o}`,"content-type":"application/json",...this.config.headers},body:JSON.stringify({messages:e.messages,temperature:e.temperature,max_tokens:e.maxTokens})}),c=m.result?.response?.trim();if(!c)throw new Error(m.errors?.[0]?.message??"cloudflare: empty model response");return{text:c,provider:"cloudflare",model:n}}async healthCheck(){let e=Date.now();try{return{ok:!0,provider:"cloudflare",model:(await this.generate({messages:[{role:"user",content:"Reply exactly: OK"}],temperature:0,maxTokens:8})).model,message:"Provider reachable",latencyMs:Date.now()-e}}catch(t){return{ok:!1,provider:"cloudflare",model:this.config.model,message:t instanceof Error?t.message:String(t),latencyMs:Date.now()-e}}}};var T=class{constructor(e){this.config=e}config;id="gemini";model(){let e=this.config.model?.trim();if(!e)throw new Error("gemini: MODEL is not configured");return e.replace(/^models\//,"")}async generate(e){let t=this.config.apiKey?.trim();if(!t)throw new Error("gemini: API KEY is not configured");let o=this.config.baseUrl?.trim()||"https://generativelanguage.googleapis.com/v1beta",n=this.model(),i=e.messages.filter(d=>d.role==="system"),a=e.messages.filter(d=>d.role!=="system").map(d=>({role:d.role==="assistant"?"model":"user",parts:[{text:d.content}]})),m=`${O(o,`models/${encodeURIComponent(n)}:generateContent`)}?key=${encodeURIComponent(t)}`,c=await v(m,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({...i.length?{systemInstruction:{parts:[{text:i.map(d=>d.content).join(`
|
|
4
|
-
|
|
5
|
-
`)}]}}:{},contents:a,generationConfig:{temperature:e.temperature,maxOutputTokens:e.maxTokens}})}),p=c.candidates?.[0]?.content?.parts?.map(d=>d.text??"").join("").trim();if(!p)throw new Error("gemini: empty model response");return{text:p,provider:"gemini",model:n,usage:c.usageMetadata?{inputTokens:c.usageMetadata.promptTokenCount,outputTokens:c.usageMetadata.candidatesTokenCount,totalTokens:c.usageMetadata.totalTokenCount}:void 0}}async healthCheck(){let e=Date.now();try{return{ok:!0,provider:"gemini",model:(await this.generate({messages:[{role:"user",content:"Reply exactly: OK"}],temperature:0,maxTokens:8})).model,message:"Provider reachable",latencyMs:Date.now()-e}}catch(t){return{ok:!1,provider:"gemini",model:this.config.model,message:t instanceof Error?t.message:String(t),latencyMs:Date.now()-e}}}};var R=class{constructor(e){this.config=e;this.id=e.id}config;id;baseUrl(){let e=this.config.baseUrl?.trim();if(!e)throw new Error(`${this.id}: BASE URL is not configured`);return e}model(){let e=this.config.model?.trim();if(!e)throw new Error(`${this.id}: MODEL is not configured`);return e}headers(){let e={"content-type":"application/json",...this.config.headers};return this.config.apiKey&&(e.authorization=`Bearer ${this.config.apiKey}`),e}async generate(e){let t=this.model(),o=await v(O(this.baseUrl(),"chat/completions"),{method:"POST",headers:this.headers(),body:JSON.stringify({model:t,messages:e.messages,temperature:e.temperature,max_tokens:e.maxTokens,...this.id==="alibaba"?{enable_thinking:!1}:{}})}),n=o.choices?.[0]?.message?.content?.trim();if(!n)throw new Error(`${this.id}: empty model response`);return{text:n,provider:this.id,model:t,usage:o.usage?{inputTokens:o.usage.prompt_tokens,outputTokens:o.usage.completion_tokens,totalTokens:o.usage.total_tokens}:void 0}}async healthCheck(){let e=Date.now();try{let t=await this.generate({messages:[{role:"user",content:"Reply exactly: OK"}],temperature:0,maxTokens:8});return{ok:!0,provider:this.id,model:t.model,message:"Provider reachable",latencyMs:Date.now()-e}}catch(t){return{ok:!1,provider:this.id,model:this.config.model,message:t instanceof Error?t.message:String(t),latencyMs:Date.now()-e}}}};function re(r=Z()){switch(h(r.id).transport){case"gemini":return new T(r);case"cloudflare":return new P(r);default:return new R(r)}}function qe(r){return new Promise(e=>{setTimeout(e,r)})}function Ye(r){if(r instanceof E){let t=r.status;return t===void 0||t===408||t===409||t===425||t===429?!0:t>=500}if(!(r instanceof Error))return!0;let e=r.message.toLowerCase();return e.includes("401")||e.includes("403")||e.includes("400")||e.includes("invalid api key")||e.includes("unauthorized")||e.includes("forbidden")?!1:e.includes("timeout")||e.includes("timed out")||e.includes("abort")||e.includes("network")||e.includes("fetch failed")||e.includes("econnreset")||e.includes("econnrefused")||e.includes("429")||e.includes("rate limit")||e.includes("500")||e.includes("502")||e.includes("503")||e.includes("504")}var S=class{id;config;providers;states=new Map;cooldownMs;maxRetries;constructor(e,t=[],o={}){this.id=e.config.id,this.config=e.config,this.cooldownMs=o.cooldownMs??6e4,this.maxRetries=Math.max(0,o.maxRetries??1),this.providers=[e,...t].map(n=>({provider:re(n.config),label:n.label}))}key(e){let t=this.providers[e];return`${e}:${t.provider.id}:${t.provider.config.model??""}`}state(e){let t=this.key(e),o=this.states.get(t);return o||(o={failures:0,cooldownUntil:0},this.states.set(t,o)),o}available(e){return this.state(e).cooldownUntil<=Date.now()}success(e){let t=this.state(e);t.failures=0,t.cooldownUntil=0,t.lastError=void 0}fail(e,t){let o=this.state(e);o.failures+=1,o.lastError=t instanceof Error?t.message:String(t),o.cooldownUntil=Date.now()+this.cooldownMs}async generate(e){let t=[];for(let o=0;o<this.providers.length;o+=1){if(!this.available(o)){t.push(`${this.providers[o].label}: cooldown`);continue}let n=this.providers[o];for(let i=0;i<=this.maxRetries;i+=1)try{let a=await n.provider.generate(e);return this.success(o),a}catch(a){let m=a instanceof Error?a.message:String(a),c=Ye(a);if(t.push(`${n.label}: ${m}`),!c)throw a;if(i<this.maxRetries){await qe(Math.min(250*2**i,2e3));continue}this.fail(o,a)}}throw new Error(`All ToolNet LLM providers failed: ${t.join(" | ")}`)}async healthCheck(){let e=Date.now();try{let t=await this.generate({messages:[{role:"user",content:"Reply exactly: OK"}],temperature:0,maxTokens:8});return{ok:!0,provider:t.provider,model:t.model,message:"Fallback chain reachable",latencyMs:Date.now()-e}}catch(t){return{ok:!1,provider:this.id,model:this.config.model,message:t instanceof Error?t.message:String(t),latencyMs:Date.now()-e}}}status(){return this.providers.map((e,t)=>{let o=this.state(t);return{position:t,provider:e.provider.id,model:e.provider.config.model,cooldown:o.cooldownUntil>Date.now(),cooldownUntil:o.cooldownUntil||void 0,failures:o.failures,lastError:o.lastError}})}};function oe(){let r=j(),e=ee();return new S({label:`Primary (${r.llm.provider})`,config:{id:r.llm.provider,apiKey:r.llm.apiKey,baseUrl:r.llm.baseUrl,model:r.llm.model,accountId:r.llm.accountId}},e.map(t=>({label:`Fallback ${t.slot} (${t.config.provider})`,config:{id:t.config.provider,apiKey:t.config.apiKey,baseUrl:t.config.baseUrl,model:t.config.model,accountId:t.config.accountId}})),te())}var G=class{constructor(e=oe()){this.provider=e}provider;get activeProvider(){return this.provider}setProvider(e){this.provider=e}generate(e){return this.provider.generate(e)}};function ne(){return new G(oe())}function Ve(r,e){let t=JSON.stringify(r,null,2);return t.length<=e?t:t.slice(0,e)+`
|
|
6
|
-
[truncated]`}function Je(r,e){let t=w(r,e),o=y(r,e,{maxFacts:12,maxChars:3200});return Ve({project:o.context.project,intent:o.intent,directAnswer:t.answer,selectedMemory:o.context.selectedFacts,retrievalStats:o.stats},4200)}async function se(r,e,t={}){let o=w(r,e),n=Je(r,e),i=t.router??ne();try{let a=await i.generate({temperature:.1,maxTokens:450,messages:[{role:"system",content:`
|
|
7
|
-
You are ToolNet Memory Agent.
|
|
8
|
-
|
|
9
|
-
You are NOT the coding agent.
|
|
10
|
-
|
|
11
|
-
Your job is to help another coding AI continue work from previous sessions.
|
|
12
|
-
|
|
13
|
-
Rules:
|
|
14
|
-
|
|
15
|
-
1. Answer only from the supplied ToolNet project memory.
|
|
16
|
-
2. Never invent completed work, files, blockers, decisions, or test results.
|
|
17
|
-
3. Prefer the latest session/work state over old information.
|
|
18
|
-
4. Be concise.
|
|
19
|
-
5. Do not dump raw JSON or full memory files.
|
|
20
|
-
6. Tell the coding agent exactly:
|
|
21
|
-
- what the previous agent was doing,
|
|
22
|
-
- what is already completed,
|
|
23
|
-
- what remains unfinished,
|
|
24
|
-
- what file was last touched,
|
|
25
|
-
- what should be done next,
|
|
26
|
-
when those facts are available.
|
|
27
|
-
7. If information is missing, say it is not recorded.
|
|
28
|
-
8. Do not explain ToolNet internals unless asked.
|
|
29
|
-
9. Do not repeat irrelevant history.
|
|
30
|
-
10. Respond in the same language as the question.
|
|
31
|
-
|
|
32
|
-
Think of yourself as the project's memory coworker talking directly to another AI.
|
|
33
|
-
`.trim()},{role:"user",content:`
|
|
34
|
-
Question from coding agent:
|
|
35
|
-
|
|
36
|
-
${e}
|
|
37
|
-
|
|
38
|
-
ToolNet selected memory:
|
|
39
|
-
|
|
40
|
-
${n}
|
|
41
|
-
|
|
42
|
-
Answer the coding agent directly.
|
|
43
|
-
`.trim()}]}),m=a.text.trim();return m?{answer:m,provider:a.provider,model:a.model,usedAi:!0}:{answer:o.answer,usedAi:!1}}catch{return{answer:o.answer,usedAi:!1}}}import{existsSync as ze,mkdirSync as Xe,readFileSync as Ze,renameSync as et,rmSync as tt,writeFileSync as rt}from"node:fs";import{join as ie}from"node:path";var ot=720*60*1e3;function ae(r){return ie(r.rootPath,".toolnet","context","memory-agent-conversation.json")}function x(r){return r.normalize("NFKC").toLowerCase().replace(/\s+/gu," ").trim()}function nt(r){let e=x(r);return[/\btiếp tục\b/u,/\btiếp tục task\b/u,/\blúc nãy\b/u,/\btrước đó\b/u,/\btask trước\b/u,/\bprevious (?:task|session|work)\b/u,/\bcontinue\b/u,/\bresume\b/u,/\bcurrent task\b/u,/\bđang làm gì\b/u].some(t=>t.test(e))}function st(r){if(nt(r))return!1;let e=x(r),t=e.split(/\s+/u).filter(Boolean);return[/^(?:tại sao|vì sao|sao vậy)\b/u,/^(?:why|why so|why that)\b/u,/^(?:còn|thế|vậy|rồi sao|sau đó)\b/u,/^(?:what about|and what|then what)\b/u,/^(?:cái đó|việc đó|nó|phần đó)\b/u].some(o=>o.test(e))?!0:t.length<=5&&/\b(?:nó|đó|vậy|thế|that|it|this)\b/u.test(e)}function it(r,e,t){let o=ae(r);if(!ze(o))return null;try{let n=JSON.parse(Ze(o,"utf8"));if(n.schema!=="toolnet.memory-conversation.v1"||n.version!==1||n.projectId!==r.id||!Array.isArray(n.focus))return null;let i=Date.parse(n.updatedAt);return!Number.isFinite(i)||e-i>t?null:n}catch{return null}}function at(r,e=8){let t=new Set,o=[];for(let n of r){let i=n.value.replace(/\s+/gu," ").trim();if(!i)continue;let a=[n.kind,x(i)].join(":");if(!t.has(a)&&(t.add(a),o.push({...n,value:i}),o.length>=e))break}return o}function ct(r,e){let t=ae(r),o=ie(r.rootPath,".toolnet","context");Xe(o,{recursive:!0,mode:448});let n=`${t}.${process.pid}.tmp`;try{rt(n,JSON.stringify(e,null,2)+`
|
|
44
|
-
`,{encoding:"utf8",mode:384}),et(n,t)}finally{tt(n,{force:!0})}}function lt(r){return r.focus.slice(0,8).map(e=>`- ${e.kind}: ${e.value}`).join(`
|
|
45
|
-
`)}function ut(r,e){return r.priorFocus.find(t=>t.kind===e)?.value}function W(r){if(!r.usedPriorFocus||r.currentIntent!=="summary"||r.priorFocus.length===0)return null;let e=x(r.originalQuestion),t=/^(?:tại sao|vì sao|sao vậy|why|why so|why that)\b/u.test(e),o=/^(?:rồi sao|sau đó|tiếp thì sao|thế tiếp theo|then what|what next)\b/u.test(e),n=t?["decision","blocker","next_action","task","file"]:o?["next_action","todo","task","blocker","file","decision"]:["task","next_action","file","decision","blocker"],i={task:"Task li\xEAn quan",next_action:"Vi\u1EC7c ti\u1EBFp theo",file:"File li\xEAn quan",decision:"Quy\u1EBFt \u0111\u1ECBnh li\xEAn quan",blocker:"Blocker",todo:"TODO"},a=[];for(let m of n){let c=ut(r,m);if(c&&(a.push(`${i[m]??m}: ${c}.`),t&&a.length>=2||!t&&a.length>=3))break}return a.length===0?null:{answer:a.join(" "),intent:"summary",source:"conversation-focus"}}function dt(r){let e=x(r);return/(?:^|\s)(?:todo|to-do)(?:\s|$)/u.test(e)||/(?:việc|task)\s+(?:còn lại|chưa làm|chưa xong|chưa hoàn thành)/u.test(e)||/(?:còn|các)\s+(?:todo|việc)\b/u.test(e)}function ce(r,e,t={}){let o=t.now??Date.now(),n=t.maxAgeMs??ot,i=it(r,o,n),a=!!(i&&i.focus.length>0&&st(e)&&!dt(e)),m=a&&i?[e.trim(),"","Previous ToolNet Memory conversation focus:",lt(i),"","Use the compact focus only to resolve the current follow-up question.","Do not reconstruct or request raw session transcripts."].join(`
|
|
46
|
-
`):e.trim(),c=I(e),d=y(r,m,{maxFacts:8,maxChars:1800}).facts.map(A=>({kind:A.kind,value:A.value,source:A.source})),g=at(a&&i?[...d,...i.focus]:d);return ct(r,{schema:"toolnet.memory-conversation.v1",version:1,projectId:r.id,updatedAt:new Date(o).toISOString(),lastIntent:c,focus:g}),{question:m,originalQuestion:e,usedPriorFocus:a,previousIntent:i?.lastIntent,currentIntent:c,priorFocus:i?.focus??[],focusCount:g.length}}var xr={question:le.string().min(2).max(4e3).describe("Question about previous project work, unfinished tasks, handoff, blockers, decisions, files or next actions."),mode:le.enum(["ai","local"]).optional().describe("ai uses ToolNet Memory Agent with configured LLM fallback chain. local returns deterministic local memory only.")};async function ue(r,e){let t=ce(r.project,e.question);if(e.mode==="local"){let n=W(t);if(n)return{answer:n.answer,mode:"local",usedAi:!1,source:n.source,intent:n.intent};let i=w(r.project,t.originalQuestion);return{answer:i.answer,mode:"local",usedAi:!1,source:i.source,intent:i.intent}}let o=await se(r.project,t.question);if(!o.usedAi){let n=W(t);if(n)return{answer:n.answer,mode:"ai",usedAi:!1,provider:o.provider,model:o.model}}return{answer:o.answer,mode:"ai",usedAi:o.usedAi,provider:o.provider,model:o.model}}var de="RAW_TRANSCRIPT_MUST_NEVER_ENTER_CONTINUITY_CONTEXT_X1";function fe(r,e){me(r,JSON.stringify(e,null,2)+`
|
|
47
|
-
`,"utf8")}function gt(r,e,t,o,n,i,a){return{continuity:{schema:"toolnet.handoff.v2",version:2,project:{id:r,name:e},source:{agent:t,nativeSessionId:`${t}-x1-source`,sessionKey:`${t}:${t}-x1-source`,sequence:42,reason:"checkpoint"},capturedAt:"2026-08-13T05:30:00.000Z",current:{phase:{id:"x1",title:"Cross-Agent Continuity E2E",status:"in_progress"},task:{id:"x1-cross-agent",title:o,status:"in_progress"},file:n},completed:{phases:["A1 OpenCode Adapter","A2 Agy Adapter","A3 Claude Code Adapter"],tasks:["Build canonical handoff schema","Build intelligent memory retrieval"]},remaining:{phases:["X1 Cross-Agent Continuity E2E"],tasks:[o],todos:[i]},nextAction:a,blockers:[],decisions:["Canonical handoff must override stale session state","Never replay raw transcripts during continuity recovery"],files:{current:n,recent:[n]},tests:{status:"passing",recent:["A1-A3 adapter tests passed"]},attention:[],progress:{phasesTotal:3,phasesCompleted:2,tasksTotal:4,tasksCompleted:3,blocked:0},stateDigest:"a".repeat(64)}}}async function L(r,e){return ue({project:r},{question:e,mode:"local"})}async function yt(r,e){let t=ft(k(pt(),`toolnet-x1-${r}-${e}-`));try{let o=`x1-${r}-${e}`,n=`x1-${r}-to-${e}`,i={id:o,name:n,rootPath:t,createdAt:"2026-08-13T05:30:00.000Z",updatedAt:"2026-08-13T05:30:00.000Z",graphVersion:1,memoryVersion:1},a=k(t,".toolnet","work"),m=k(t,".toolnet","context"),c=k(t,".toolnet","sessions",r,"raw-session-x1");H(a,{recursive:!0}),H(m,{recursive:!0}),H(c,{recursive:!0});let p=`X1 ${r} to ${e} canonical continuity`,d=`src/x1/${r}-to-${e}.ts`,g=`Implement ${e} continuation step`,A=`Continue ${p} in ${d}`;fe(k(a,"handoff-latest.json"),gt(o,n,r,p,d,g,A)),fe(k(m,"session-origin.json"),{version:1,projectId:o,agent:"stale-agent",nativeSessionId:"stale-session",updatedAt:"2026-01-01T00:00:00.000Z",currentTask:"STALE TASK MUST NOT WIN",currentPhase:"STALE PHASE",lastTouchedFile:"src/stale/old.ts",latestNextAction:"STALE NEXT ACTION"}),me(k(c,"events.jsonl"),JSON.stringify({text:de})+`
|
|
48
|
-
`,"utf8");let ge=y(i,"T\xF4i ph\u1EA3i l\xE0m g\xEC ti\u1EBFp?",{maxFacts:12,maxChars:3200}),F=await L(i,"Task hi\u1EC7n t\u1EA1i \u0111ang l\xE0m l\xE0 g\xEC?"),N=await L(i,"File hi\u1EC7n t\u1EA1i l\xE0 file n\xE0o?"),D=await L(i,"TODO c\xF2n l\u1EA1i l\xE0 g\xEC?"),$=await L(i,"T\xF4i ph\u1EA3i l\xE0m g\xEC ti\u1EBFp?"),ye=[F.answer,N.answer,D.answer,$.answer].join(`
|
|
49
|
-
`),U=[F,N,D,$],Q={canonicalHandoffPreferred:ge.facts[0]?.source==="handoff",taskRecovered:F.answer.includes(p),fileRecovered:N.answer.includes(d),todoRecovered:D.answer.includes(g),nextActionRecovered:$.answer.includes(A),localModeOnly:U.every(b=>b.mode==="local"),noAiRequired:U.every(b=>b.usedAi===!1),rawTranscriptIsolated:!ye.includes(de),compact:U.every(b=>b.answer.length<1600)};return{from:r,to:e,passed:Object.values(Q).every(Boolean),checks:Q}}finally{mt(t,{recursive:!0,force:!0})}}async function pe(){let r=[["agy","codex"],["codex","opencode"],["opencode","claude"],["claude","agy"]],e=[];for(let[o,n]of r)e.push(await yt(o,n));let t=e.filter(o=>o.passed).length;return{passed:t===e.length,total:e.length,passedCount:t,cases:e}}var f={reset:"\x1B[0m",green:"\x1B[32m",red:"\x1B[31m",cyan:"\x1B[36m",gray:"\x1B[90m",white:"\x1B[97m"};function ht(r){return r?`${f.green}\u2713${f.reset}`:`${f.red}\u2717${f.reset}`}function kt(r){let e=`${r.from.padEnd(8)} \u2192 ${r.to.padEnd(8)}`;if(console.log(`\u2502 ${ht(r.passed)} ${e}`),!r.passed)for(let[t,o]of Object.entries(r.checks))o||console.log(`\u2502 ${f.red}\u2717${f.reset} ${t}`)}async function At(){console.log(""),console.log(`${f.cyan}\u25C7${f.reset} ${f.white}ToolNet Cross-Agent Continuity Certification${f.reset}`),console.log(`${f.gray}\u2502${f.reset}`);let r=await pe();for(let e of r.cases)kt(e);if(console.log(`${f.gray}\u2502${f.reset}`),r.passed){console.log(`${f.green}\u25C6 PASS${f.reset} ${r.passedCount}/${r.total} cross-agent transitions`),console.log(`${f.green}\u2713${f.reset} canonical handoff`),console.log(`${f.green}\u2713${f.reset} memory_agent_ask`),console.log(`${f.green}\u2713${f.reset} task / file / TODO / next action`),console.log(`${f.green}\u2713${f.reset} raw transcripts isolated`),console.log(`${f.green}\u2713${f.reset} AI not required for direct continuity`);return}console.log(`${f.red}\u25C6 FAIL${f.reset} ${r.passedCount}/${r.total} cross-agent transitions`),process.exitCode=1}At().catch(r=>{console.error(r instanceof Error?r.message:String(r)),process.exitCode=1});
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import*as c from"fs";import*as i from"path";import{existsSync as b,mkdirSync as D,readFileSync as w,renameSync as $,writeFileSync as L}from"node:fs";import{dirname as H,join as v}from"node:path";import{createHash as F}from"node:crypto";function g(n){return F("sha256").update(n).digest("hex")}function A(n){return v(n.rootPath,".toolnet","context","handoff.md")}function C(n){return v(n.rootPath,".toolnet","context","handoff.json")}function M(n,e){D(H(n),{recursive:!0});let t=`${n}.tmp-${process.pid}-${Date.now()}`;L(t,e.endsWith(`
|
|
2
|
-
`)?e:`${e}
|
|
3
|
-
`,{encoding:"utf8",mode:384}),$(t,n)}function z(n,e){M(n,`${JSON.stringify(e,null,2)}
|
|
4
|
-
`)}function E(n,e){let t=e.replace(/\r\n/g,`
|
|
5
|
-
`).trim();if(!t)return null;let r={version:1,projectId:n.id,projectName:n.name,text:t,digest:g(t),generatedAt:new Date().toISOString()};return M(A(n),t),z(C(n),r),r}function T(n){let e=A(n);if(!b(e))return null;try{let t=w(e,"utf8").trim();if(!t)return null;let r=new Date(0).toISOString(),o=C(n);if(b(o))try{let s=JSON.parse(w(o,"utf8"));typeof s.generatedAt=="string"&&(r=s.generatedAt)}catch{}return{version:1,projectId:n.id,projectName:n.name,text:t,digest:g(t),generatedAt:r}}catch{return null}}function R(n){let e=i.resolve(n),t=i.parse(e).root;for(;e!==t;){let r=i.join(e,".toolnet");if(c.existsSync(r)&&c.statSync(r).isDirectory())return e;e=i.dirname(e)}return null}import{existsSync as W,readFileSync as J}from"node:fs";import{join as U}from"node:path";import{existsSync as V,mkdirSync as De,readFileSync as G,renameSync as $e,writeFileSync as Le}from"node:fs";import{dirname as ze,join as B}from"node:path";function K(n){return B(n.rootPath,".toolnet","context","session-origin.json")}function Y(n){let e=K(n);if(!V(e))return null;try{let t=JSON.parse(G(e,"utf8"));return t.version!==1||t.projectId!==n.id?null:t}catch{return null}}function O(n){let e=Y(n);if(!e)return null;let t=["Previous session:",`- Agent: ${e.agent}`,`- Session: ${e.nativeSessionId}`];return e.currentPhase&&t.push(`- Current phase: ${e.currentPhase}`),e.currentTask&&t.push(`- Current task: ${e.currentTask}`),e.lastTouchedFile&&t.push(`- Last touched file: ${e.lastTouchedFile}`),e.latestBlocker&&t.push(`- Blocker: ${e.latestBlocker}`),e.latestNextAction&&t.push(`- Next action: ${e.latestNextAction}`),e.latestDecision&&t.push(`- Important decision: ${e.latestDecision}`),t.join(`
|
|
6
|
-
`)}function Q(n){let e=U(n.rootPath,".toolnet","current.md");if(!W(e))return null;try{return J(e,"utf8").trim()}catch{return null}}function Z(n,e){let t=e.replace(/\r\n/g,`
|
|
7
|
-
`).trim(),r=2400,o=t.length>r?`${t.slice(0,r)}
|
|
8
|
-
|
|
9
|
-
[Current work truncated for fast handoff]`:t,s=O(n);return["# ToolNet Fast Handoff","",`Project: ${n.name}`,"",...s?[s,""]:[],o,"","---","","Continuation rule:","- Continue unfinished work; do not redo completed steps.","- Query ToolNet Memory only when deeper history is needed."].join(`
|
|
10
|
-
`)}function I(n){let e=Q(n);if(e===null)return{updated:!1,reason:"missing-current",chars:0};if(!e.trim())return{updated:!1,reason:"empty-current",chars:0};let t=Z(n,e);return T(n)?.text.trim()===t.trim()?{updated:!1,reason:"unchanged",chars:t.length}:(E(n,t),{updated:!0,reason:"updated",chars:t.length})}import{existsSync as q,readFileSync as X}from"node:fs";import{homedir as ee}from"node:os";import{join as te}from"node:path";function ne(n){let e=n.trim();return e.length>=2&&e.startsWith('"')&&e.endsWith('"')?(e=e.slice(1,-1),e.replace(/\\n/g,`
|
|
11
|
-
`).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function re(){let n=process.env.TOOLNET_GLOBAL_ENV??te(ee(),".config","toolnet-memory",".env");if(!q(n))return;let e=X(n,"utf8");for(let t of e.split(/\r?\n/)){let r=t.trim();if(!r||r.startsWith("#"))continue;r.startsWith("export ")&&(r=r.slice(7));let o=r.indexOf("=");if(o<=0)continue;let s=r.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(s)&&process.env[s]===void 0&&(process.env[s]=ne(r.slice(o+1)))}}re();import{createHash as oe}from"node:crypto";import{existsSync as y,mkdirSync as se,readFileSync as ie,renameSync as ae,writeFileSync as ce}from"node:fs";import{basename as ue,dirname as l,join as p,parse as N,resolve as d}from"node:path";var P=".toolnet",de="project.json";function le(n){return oe("sha256").update(n).digest("hex").slice(0,16)}function h(n){return p(n,P,de)}function fe(n){return y(h(n))}function pe(n,e){let t=d(n),r=N(t).root;for(;;){if(fe(t))return t;if(t===r||e&&t===d(e))break;let o=l(t);if(o===t)break;t=o}return null}function me(n){let e=d(n),t=N(e).root,r=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(r.some(s=>y(p(e,s))))return e;if(e===t)break;let o=l(e);if(o===e)break;e=o}return d(n)}function ge(n){let e;try{e=JSON.parse(ie(n,"utf8"))}catch(o){throw new Error(`Invalid ToolNet project manifest: ${n}: ${o instanceof Error?o.message:String(o)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${n}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${n}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${n}`);let r=new Date().toISOString();return{version:1,id:t.id,name:t.name,remote:typeof t.remote=="string"&&t.remote.trim()?t.remote:t.name,rootPath:typeof t.rootPath=="string"?t.rootPath:l(l(n)),createdAt:typeof t.createdAt=="string"?t.createdAt:r,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:r,graphVersion:typeof t.graphVersion=="number"?t.graphVersion:0,memoryVersion:typeof t.memoryVersion=="number"?t.memoryVersion:0,metadata:t.metadata&&typeof t.metadata=="object"?t.metadata:void 0}}function j(n,e){let t=p(n,P);se(t,{recursive:!0});let r=h(n),o=`${r}.tmp-${process.pid}`;ce(o,JSON.stringify(e,null,2)+`
|
|
12
|
-
`,{encoding:"utf8",mode:384}),ae(o,r)}function _(n,e){return{id:n.id,name:n.name,remote:n.remote,rootPath:e,createdAt:n.createdAt,updatedAt:n.updatedAt,graphVersion:n.graphVersion,memoryVersion:n.memoryVersion,metadata:n.metadata}}var f=class{detect(e=process.cwd()){let t=d(e),r=me(t),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(m=>y(p(r,m))),a=pe(t,s?r:void 0);if(a){let m=h(a),u=ge(m);return u.rootPath!==a&&(u.rootPath=a,u.updatedAt=new Date().toISOString(),j(a,u)),_(u,a)}let S=new Date().toISOString(),x=ue(r),k={version:1,id:le(r),name:x,remote:x,rootPath:r,createdAt:S,updatedAt:S,graphVersion:0,memoryVersion:0};return j(r,k),_(k,r)}};function ye(){let n=process.cwd(),e=R(n);if(!e){console.error("Not in a ToolNet project."),process.exitCode=1;return}let t=new f().detect(e);if(!t){console.error("Unable to detect ToolNet project."),process.exitCode=1;return}let r=I(t);console.log(JSON.stringify({project:t.name,...r},null,2))}ye();
|