toolnet-memory 0.2.19 → 0.2.21

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.
@@ -1,20 +1,20 @@
1
1
  #!/usr/bin/env node
2
- import*as $ from"fs";import*as we from"path";import*as h from"fs";import*as g from"path";import*as X from"crypto";function j(r){return r?Math.ceil(r.length/3.5):0}function _(r,t){if(!r)return"";if(j(r)<=t)return r;let o=Math.floor(t*3.5),n=r.slice(0,o),s=n.lastIndexOf("."),c=n.lastIndexOf(`
3
- `),l=Math.max(s,c);return l>o*.7?n.slice(0,l+1):n}function J(r,t){if(!r)return"";let e=r.split(`
4
- `).filter(s=>s.trim());if(e.length<=t)return r;let o=e.slice(0,t),n=e.length-t;return[...o,`... (${n} more items omitted)`].join(`
5
- `)}function be(r,t){let{maxTokens:e,trimMarker:o="[Context trimmed by ToolNet Memory token budget]"}=t;if(r.length===0)return"";let n=[...r].sort((i,p)=>p.priority-i.priority),s=[],c=0,l=!1;for(let i of n){let p=`# ${i.title}
2
+ import*as $ from"fs";import*as xe from"path";import*as h from"fs";import*as g from"path";import*as X from"crypto";function k(r){return r?Math.ceil(r.length/3.5):0}function _(r,t){if(!r)return"";if(k(r)<=t)return r;let n=Math.floor(t*3.5),o=r.slice(0,n),s=o.lastIndexOf("."),c=o.lastIndexOf(`
3
+ `),l=Math.max(s,c);return l>n*.7?o.slice(0,l+1):o}function H(r,t){if(!r)return"";let e=r.split(`
4
+ `).filter(s=>s.trim());if(e.length<=t)return r;let n=e.slice(0,t),o=e.length-t;return[...n,`... (${o} more items omitted)`].join(`
5
+ `)}function be(r,t){let{maxTokens:e,trimMarker:n="[Context trimmed by ToolNet Memory token budget]"}=t;if(r.length===0)return"";let o=[...r].sort((i,p)=>p.priority-i.priority),s=[],c=0,l=!1;for(let i of o){let p=`# ${i.title}
6
6
 
7
- `,f=j(p),a=j(i.content),y=f+a;if(c+y<=e)s.push(p+i.content),c+=y;else{let u=e-c-f;if(u>50){let m=_(i.content,u);s.push(p+m),c=e,l=!0}else l=!0;break}}return l&&s.push(`
8
- ${o}
7
+ `,f=k(p),a=k(i.content),S=f+a;if(c+S<=e)s.push(p+i.content),c+=S;else{let u=e-c-f;if(u>50){let m=_(i.content,u);s.push(p+m),c=e,l=!0}else l=!0;break}}return l&&s.push(`
8
+ ${n}
9
9
  `),s.join(`
10
10
 
11
11
  ---
12
12
 
13
- `)}function z(r,t){let e=[{title:"Profile",content:J(r,10),priority:100},{title:"Current Work",content:J(t,15),priority:90}];return be(e,{maxTokens:800})}import{existsSync as U,mkdirSync as tr,readFileSync as H,renameSync as rr,writeFileSync as or}from"node:fs";import{dirname as sr,join as G}from"node:path";import{createHash as Pe}from"node:crypto";import{dirname as Te}from"node:path";import{mkdirSync as Ae,readFileSync as Zt,renameSync as Ce,writeFileSync as Oe}from"node:fs";function S(r){return Pe("sha256").update(r).digest("hex")}function k(r,t){Ae(Te(r),{recursive:!0});let e=`${r}.${process.pid}.tmp`;Oe(e,JSON.stringify(t,null,2)+`
14
- `,{encoding:"utf8",mode:384}),Ce(e,r)}function Ee(r){return G(r.rootPath,".toolnet","context","handoff.md")}function Re(r){return G(r.rootPath,".toolnet","context","handoff.json")}function Ie(r){let t=Ee(r);if(!U(t))return null;try{let e=H(t,"utf8").trim();if(!e)return null;let o=new Date(0).toISOString(),n=Re(r);if(U(n))try{let s=JSON.parse(H(n,"utf8"));typeof s.generatedAt=="string"&&(o=s.generatedAt)}catch{}return{version:1,projectId:r.id,projectName:r.name,text:e,digest:S(e),generatedAt:o}}catch{return null}}function Y(r,t=1800){let e=Ie(r);if(!e?.text)return null;let o=e.text;return o.length>t&&(o=`${o.slice(0,t)}
13
+ `)}function z(r,t){let e=[{title:"Profile",content:H(r,10),priority:100},{title:"Current Work",content:H(t,15),priority:90}];return be(e,{maxTokens:800})}import{existsSync as J,mkdirSync as tr,readFileSync as U,renameSync as rr,writeFileSync as nr}from"node:fs";import{dirname as sr,join as G}from"node:path";import{createHash as Pe}from"node:crypto";import{dirname as Te}from"node:path";import{mkdirSync as Ae,readFileSync as Zt,renameSync as Ce,writeFileSync as Me}from"node:fs";function y(r){return Pe("sha256").update(r).digest("hex")}function j(r,t){Ae(Te(r),{recursive:!0});let e=`${r}.${process.pid}.tmp`;Me(e,JSON.stringify(t,null,2)+`
14
+ `,{encoding:"utf8",mode:384}),Ce(e,r)}function Ie(r){return G(r.rootPath,".toolnet","context","handoff.md")}function Re(r){return G(r.rootPath,".toolnet","context","handoff.json")}function Oe(r){let t=Ie(r);if(!J(t))return null;try{let e=U(t,"utf8").trim();if(!e)return null;let n=new Date(0).toISOString(),o=Re(r);if(J(o))try{let s=JSON.parse(U(o,"utf8"));typeof s.generatedAt=="string"&&(n=s.generatedAt)}catch{}return{version:1,projectId:r.id,projectName:r.name,text:e,digest:y(e),generatedAt:n}}catch{return null}}function Y(r,t=1800){let e=Oe(r);if(!e?.text)return null;let n=e.text;return n.length>t&&(n=`${n.slice(0,t)}
15
15
 
16
- [Fast handoff truncated]`),["[TOOLNET FAST HANDOFF]","",`Project: ${r.name}`,`Updated: ${e.generatedAt}`,"",o].join(`
17
- `)}var A="memory_agent_ask";function q(){return`
16
+ [Fast handoff truncated]`),["[TOOLNET FAST HANDOFF]","",`Project: ${r.name}`,`Updated: ${e.generatedAt}`,"",n].join(`
17
+ `)}var A="memory_agent_ask";function Q(){return`
18
18
  [TOOLNET MEMORY AGENT]
19
19
 
20
20
  Tool available:
@@ -65,7 +65,7 @@ Rules:
65
65
  - After receiving the ToolNet answer, continue the task
66
66
  instead of asking the user to repeat known context.
67
67
  - If ToolNet says information is not recorded, say so.
68
- `.trim()}function Q(){return`
68
+ `.trim()}function q(){return`
69
69
  [TOOLNET MEMORY AGENT]
70
70
 
71
71
  Tool:
@@ -90,26 +90,26 @@ Use:
90
90
  - mode="ai" for ambiguous or combined continuity questions.
91
91
 
92
92
  Current repository evidence overrides stale memory.
93
- `.trim()}function v(r){let t=g.resolve(r),e=g.parse(t).root;for(;t!==e;){let o=g.join(t,".toolnet");if(h.existsSync(o)&&h.statSync(o).isDirectory())return t;t=g.dirname(t)}return null}function C(r){try{return h.existsSync(r)?h.readFileSync(r,"utf-8").trim():null}catch{return null}}function Z(r){return r.split(`
94
- `).filter(o=>{let n=o.toUpperCase();return!(n.includes("SECRET")||n.includes("TOKEN")||n.includes("API_KEY")||n.includes("APIKEY")||n.includes("PASSWORD")||n.includes("PASS="))}).join(`
95
- `)}function K(r={}){let t=r.projectPath||process.cwd(),e=v(t);if(!e)return null;let o=g.join(e,".toolnet","project.json"),n="Unknown";try{if(h.existsSync(o)){let m=JSON.parse(h.readFileSync(o,"utf-8"));n=m.name||m.projectName||"Unknown"}}catch{}let s=g.join(e,".toolnet","profile.md"),c=g.join(e,".toolnet","current.md"),l=C(s)||"",i=C(c)||"";l=Z(l),i=Z(i);let p=`[TOOLNET PROJECT CONTEXT]
93
+ `.trim()}function v(r){let t=g.resolve(r),e=g.parse(t).root;for(;t!==e;){let n=g.join(t,".toolnet");if(h.existsSync(n)&&h.statSync(n).isDirectory())return t;t=g.dirname(t)}return null}function C(r){try{return h.existsSync(r)?h.readFileSync(r,"utf-8").trim():null}catch{return null}}function Z(r){return r.split(`
94
+ `).filter(n=>{let o=n.toUpperCase();return!(o.includes("SECRET")||o.includes("TOKEN")||o.includes("API_KEY")||o.includes("APIKEY")||o.includes("PASSWORD")||o.includes("PASS="))}).join(`
95
+ `)}function D(r={}){let t=r.projectPath||process.cwd(),e=v(t);if(!e)return null;let n=g.join(e,".toolnet","project.json"),o="Unknown";try{if(h.existsSync(n)){let m=JSON.parse(h.readFileSync(n,"utf-8"));o=m.name||m.projectName||"Unknown"}}catch{}let s=g.join(e,".toolnet","profile.md"),c=g.join(e,".toolnet","current.md"),l=C(s)||"",i=C(c)||"";l=Z(l),i=Z(i);let p=`[TOOLNET PROJECT CONTEXT]
96
96
 
97
- Project: ${n}
97
+ Project: ${o}
98
98
  Root: ${e}
99
99
 
100
- `,f=z(l,i),a=Y({id:"",name:n,rootPath:e},1600),y=a?`
100
+ `,f=z(l,i),a=Y({id:"",name:o,rootPath:e},1600),S=a?`
101
101
 
102
102
  ${a}
103
103
  `:"",u=`
104
104
 
105
- ${Q()}
105
+ ${q()}
106
106
 
107
107
  Forbidden At Startup:
108
108
  - Do not run session:agy-recover, handoff:latest, or brief automatically.
109
109
  - Do not perform deep recovery merely because an agent starts.
110
110
  - If the user asks to resume previous work and fast context is insufficient,
111
111
  use memory_agent_ask before guessing.
112
- `;return p+f+y+u}function ee(r={}){let t=r.projectPath||process.cwd(),e=v(t);if(!e)throw new Error("Not in a ToolNet project (no .toolnet directory found)");let o=g.join(e,".toolnet","profile.md"),n=g.join(e,".toolnet","current.md"),s=C(o)||"",c=C(n)||"",l=`# AI Startup Instructions
112
+ `;return p+f+S+u}function ee(r={}){let t=r.projectPath||process.cwd(),e=v(t);if(!e)throw new Error("Not in a ToolNet project (no .toolnet directory found)");let n=g.join(e,".toolnet","profile.md"),o=g.join(e,".toolnet","current.md"),s=C(n)||"",c=C(o)||"",l=`# AI Startup Instructions
113
113
 
114
114
  Read and follow:
115
115
  - .toolnet/profile.md
@@ -121,7 +121,7 @@ Rules:
121
121
  - Do not invent previous-session state.
122
122
  - Current repository evidence overrides stale memory.
123
123
 
124
- ${q()}
124
+ ${Q()}
125
125
 
126
126
  ---
127
127
 
@@ -134,7 +134,7 @@ ${s}
134
134
  ## Current Work
135
135
 
136
136
  ${c}
137
- `,i=["GEMINI.md","AGENTS.md","CLAUDE.md"],p=[];for(let f of i){let a=g.join(e,f);try{h.writeFileSync(a,l,"utf-8"),p.push(f)}catch(y){console.error(`Failed to write ${f}:`,y)}}return p}function te(r){return X.createHash("sha256").update(r,"utf-8").digest("hex")}import{existsSync as Ir,mkdirSync as ze,readFileSync as Mr,writeFileSync as Ue}from"node:fs";import{dirname as He,join as ce}from"node:path";import{existsSync as ne,mkdirSync as Me,readFileSync as Ne,statSync as re,writeFileSync as $e}from"node:fs";import{dirname as _e,join as Ke}from"node:path";var oe=64*1024,De=`# ToolNet Project Operating Manual
137
+ `,i=["GEMINI.md","AGENTS.md","CLAUDE.md"],p=[];for(let f of i){let a=g.join(e,f);try{h.writeFileSync(a,l,"utf-8"),p.push(f)}catch(S){console.error(`Failed to write ${f}:`,S)}}return p}function te(r){return X.createHash("sha256").update(r,"utf-8").digest("hex")}import{existsSync as _r,mkdirSync as ze,readFileSync as Dr,writeFileSync as Je}from"node:fs";import{dirname as Ue,join as ce}from"node:path";import{existsSync as oe,mkdirSync as Ee,readFileSync as Ne,statSync as re,writeFileSync as $e}from"node:fs";import{dirname as _e,join as De}from"node:path";var ne=64*1024,Ke=`# ToolNet Project Operating Manual
138
138
 
139
139
  This file contains persistent instructions for AI agents working on this project.
140
140
 
@@ -177,17 +177,17 @@ Things an AI agent should always remember.
177
177
  <!--
178
178
  - [advisory] Prefer small focused files.
179
179
  -->
180
- `;function O(r){return Ke(r.rootPath,".toolnet","PROJECT.md")}function Fe(r){return r.normalize("NFKC").replace(/\s+/g," ").trim()}function We(r){let t=[],e=new Set,o=/^\s*[-*]\s+\[(enforce|advisory)\]\s+(.+?)\s*$/gimu,n;for(;n=o.exec(r);){let s=n[1].toLowerCase(),c=Fe(n[2]);if(!c)continue;let l=`${s}:${c.toLowerCase()}`;e.has(l)||(e.add(l),t.push({id:S(l).slice(0,24),mode:s,text:c,source:"manual"}))}return t}function Be(r){let t=O(r);return ne(t)||(Me(_e(t),{recursive:!0}),$e(t,De,{encoding:"utf8",mode:384})),t}function D(r,t=!1){let e=t?Be(r):O(r);if(!ne(e))return null;if(re(e).size>oe)throw new Error(`PROJECT.md exceeds ${oe} bytes`);let n=Ne(e,"utf8");return{path:e,content:n,digest:S(n),rules:We(n),bytes:Buffer.byteLength(n,"utf8"),updatedAt:new Date(re(e).mtimeMs).toISOString()}}async function F(r,t){let e=await t.getText(`projects/${r.id}/work/current.json`);if(!e)return null;try{return JSON.parse(e)}catch{return null}}async function se(r,t){let e=await t.getText(`projects/${r.id}/work/handoff-latest.json`);if(!e)return null;try{return JSON.parse(e)}catch{return null}}async function ie(r,t){let e=await t.getText(`projects/${r.id}/work/semantic/current.json`);if(!e)return null;try{return JSON.parse(e)}catch{return null}}function W(r){if(!r)return 0;let t=Array.from(r).length,e=r.trim().split(/\s+/u).filter(Boolean).length;return Math.ceil(Math.max(t/3.5,e*1.3))}function d(r,t){let e=r.replace(/\s+/g," ").trim();return e.length<=t?e:e.slice(0,Math.max(0,t-1)).trimEnd()+"\u2026"}function Le(r){let t=[],e=!1;for(let o of r.split(/\r?\n/u)){let n=o.trim();if(n.includes("<!--")&&(e=!0),e){n.includes("-->")&&(e=!1);continue}let s=n.toLowerCase();if(!(!n||n.startsWith("#")||n==="```"||s.startsWith("- [enforce]")||s.startsWith("* [enforce]")||s.startsWith("- [advisory]")||s.startsWith("* [advisory]"))&&(n=n.replace(/^[-*]\s+/u,""),n&&t.push(d(n,280)),t.length>=16))break}return t}function Ve(r){let t=[],e=[];for(let o of r.split(/\\r?\\n/u)){let n=o.trim(),s=n.toLowerCase(),l=["- [enforce]","* [enforce]","- [advisory]","* [advisory]"].find(p=>s.startsWith(p));if(!l)continue;let i=n.slice(l.length).trim();i&&(l.includes("enforce")?t.push(i):e.push(i))}return{enforce:t,advisory:e}}function Je(r,t){let e=[];for(let o of r){let n=[...e,o].join(`
181
- `);if(W(n)<=t){e.push(o);continue}let s=W(e.join(`
182
- `)),c=Math.max(0,t-s);if(c>=16){let l=Math.floor(c*3.2),i=d(o,l);i&&e.push(i)}break}return e.join(`
183
- `).trim()}async function ae(r){let t=Math.max(256,Math.min(2e3,r.maxTokens??1e3)),e=D(r.project,!1),o=e?.content??"";o||(o=await r.storage.getText(`projects/${r.project.id}/project/manual.md`)??"");let n=Ve(o),s=e?e.rules.filter(u=>u.mode==="enforce").map(u=>u.text):n.enforce,c=e?e.rules.filter(u=>u.mode==="advisory").map(u=>u.text):n.advisory,l=o?Le(o):[],i=await F(r.project,r.storage),p=await ie(r.project,r.storage),f=await se(r.project,r.storage),a=[];if(a.push("[TOOLNET PROJECT CONTEXT]"),a.push(`Project: ${r.project.name}`),a.push("Continue existing project state. Do not restart completed work unless evidence shows it is necessary."),o&&a.push(`Full operating manual: ${O(r.project)}`),s.length){a.push("","PROJECT RULES \u2014 MUST FOLLOW");for(let u of s.slice(0,24))a.push(`- [ENFORCE] ${d(u,240)}`)}if(c.length){a.push("","PROJECT PREFERENCES");for(let u of c.slice(0,10))a.push(`- ${d(u,220)}`)}if(p&&(p.mission&&a.push("","MISSION",d(p.mission.value,420)),p.activeObjective&&a.push("","CURRENT OBJECTIVE",d(p.activeObjective.value,420)),p.why&&a.push("","WHY THIS WORK MATTERS",d(p.why.value,420)),p.desiredOutcome&&a.push("","DESIRED OUTCOME",d(p.desiredOutcome.value,420)),p.planRationale&&a.push("","WHY THIS APPROACH",d(p.planRationale.value,420))),i){if(a.push("","ACTIVE WORK"),i.goal&&a.push(`Goal: ${d(i.goal,320)}`),i.plan&&a.push(`Plan: ${d(i.plan,320)}`),a.push(`Progress: phases ${i.progress.phasesCompleted}/${i.progress.phasesTotal}; tasks ${i.progress.tasksCompleted}/${i.progress.tasksTotal}; blocked ${i.progress.blocked}`),i.currentPhase&&a.push(`Current phase: ${i.currentPhase.title} [${i.currentPhase.status}]`),i.currentPhase&&p){let u=p.phases.find(m=>m.order===i.currentPhase?.order);u&&(u.objective&&a.push(`Phase objective: ${d(u.objective.value,340)}`),u.why?a.push(`Why this phase: ${d(u.why.value,340)}`):a.push("Why this phase: not explicitly recorded. Inspect existing implementation before assuming intent."),u.deliverable&&a.push(`Deliverable: ${d(u.deliverable.value,340)}`),u.dependencies.length&&a.push(`Depends on: ${u.dependencies.slice(0,4).map(m=>d(m.value,180)).join("; ")}`),u.acceptanceCriteria.length&&(a.push("","DEFINITION OF DONE"),u.acceptanceCriteria.slice(0,6).forEach(m=>{a.push(`- ${d(m.value,260)}`)})),u.openQuestions.length&&(a.push("","OPEN QUESTIONS FOR CURRENT PHASE"),u.openQuestions.slice(0,4).forEach(m=>{a.push(`- ${d(m.value,260)}`)})))}i.currentTask&&a.push(`Current task: ${i.currentTask.title} [${i.currentTask.status}]`),i.nextActions.length&&(a.push("","NEXT ACTIONS"),i.nextActions.slice(0,6).forEach((u,m)=>{a.push(`${m+1}. ${d(u,260)}`)})),i.blockers.length&&(a.push("","BLOCKERS"),i.blockers.slice(0,5).forEach(u=>{a.push(`- ${d(u,260)}`)})),i.warnings.length&&(a.push("","ATTENTION"),i.warnings.slice(-5).forEach(u=>{a.push(`- ${d(u,260)}`)})),i.decisions.length&&(a.push("","RECENT DECISIONS"),i.decisions.slice(-5).forEach(u=>{a.push(`- ${d(u,260)}`)})),i.lastSession&&a.push("",`Last work session: ${i.lastSession.agent} / ${i.lastSession.nativeSessionId}`)}if(p&&p.openQuestions.length&&(a.push("","UNRESOLVED QUESTIONS"),p.openQuestions.slice(0,5).forEach(u=>{a.push(`- ${d(u.value,260)}`)})),f&&a.push(`Latest handoff: ${f.reason} / ${f.sourceSession.agent}`),l.length){a.push("","OPERATING NOTES");for(let u of l)a.push(`- ${u}`)}a.push("","Before changing anything: verify the current repository state and continue from the active phase/task above.");let y=Je(a,t);return{version:1,projectId:r.project.id,projectName:r.project.name,text:y,estimatedTokens:W(y),maxTokens:t,hasManual:!!o,hasWorkState:!!i,hasHandoff:!!f,generatedAt:new Date().toISOString()}}function Ge(r){return ce(r.rootPath,".toolnet","context","startup.md")}function Ye(r){return ce(r.rootPath,".toolnet","context","startup.json")}function qe(r,t){let e=Ge(r);ze(He(e),{recursive:!0}),Ue(e,t.text.endsWith(`
180
+ `;function M(r){return De(r.rootPath,".toolnet","PROJECT.md")}function Fe(r){return r.normalize("NFKC").replace(/\s+/g," ").trim()}function We(r){let t=[],e=new Set,n=/^\s*[-*]\s+\[(enforce|advisory)\]\s+(.+?)\s*$/gimu,o;for(;o=n.exec(r);){let s=o[1].toLowerCase(),c=Fe(o[2]);if(!c)continue;let l=`${s}:${c.toLowerCase()}`;e.has(l)||(e.add(l),t.push({id:y(l).slice(0,24),mode:s,text:c,source:"manual"}))}return t}function Be(r){let t=M(r);return oe(t)||(Ee(_e(t),{recursive:!0}),$e(t,Ke,{encoding:"utf8",mode:384})),t}function K(r,t=!1){let e=t?Be(r):M(r);if(!oe(e))return null;if(re(e).size>ne)throw new Error(`PROJECT.md exceeds ${ne} bytes`);let o=Ne(e,"utf8");return{path:e,content:o,digest:y(o),rules:We(o),bytes:Buffer.byteLength(o,"utf8"),updatedAt:new Date(re(e).mtimeMs).toISOString()}}async function F(r,t){let e=await t.getText(`projects/${r.id}/work/current.json`);if(!e)return null;try{return JSON.parse(e)}catch{return null}}async function se(r,t){let e=await t.getText(`projects/${r.id}/work/handoff-latest.json`);if(!e)return null;try{return JSON.parse(e)}catch{return null}}async function ie(r,t){let e=await t.getText(`projects/${r.id}/work/semantic/current.json`);if(!e)return null;try{return JSON.parse(e)}catch{return null}}function W(r){if(!r)return 0;let t=Array.from(r).length,e=r.trim().split(/\s+/u).filter(Boolean).length;return Math.ceil(Math.max(t/3.5,e*1.3))}function d(r,t){let e=r.replace(/\s+/g," ").trim();return e.length<=t?e:e.slice(0,Math.max(0,t-1)).trimEnd()+"\u2026"}function Le(r){let t=[],e=!1;for(let n of r.split(/\r?\n/u)){let o=n.trim();if(o.includes("<!--")&&(e=!0),e){o.includes("-->")&&(e=!1);continue}let s=o.toLowerCase();if(!(!o||o.startsWith("#")||o==="```"||s.startsWith("- [enforce]")||s.startsWith("* [enforce]")||s.startsWith("- [advisory]")||s.startsWith("* [advisory]"))&&(o=o.replace(/^[-*]\s+/u,""),o&&t.push(d(o,280)),t.length>=16))break}return t}function Ve(r){let t=[],e=[];for(let n of r.split(/\\r?\\n/u)){let o=n.trim(),s=o.toLowerCase(),l=["- [enforce]","* [enforce]","- [advisory]","* [advisory]"].find(p=>s.startsWith(p));if(!l)continue;let i=o.slice(l.length).trim();i&&(l.includes("enforce")?t.push(i):e.push(i))}return{enforce:t,advisory:e}}function He(r,t){let e=[];for(let n of r){let o=[...e,n].join(`
181
+ `);if(W(o)<=t){e.push(n);continue}let s=W(e.join(`
182
+ `)),c=Math.max(0,t-s);if(c>=16){let l=Math.floor(c*3.2),i=d(n,l);i&&e.push(i)}break}return e.join(`
183
+ `).trim()}async function ae(r){let t=Math.max(256,Math.min(2e3,r.maxTokens??1e3)),e=K(r.project,!1),n=e?.content??"";n||(n=await r.storage.getText(`projects/${r.project.id}/project/manual.md`)??"");let o=Ve(n),s=e?e.rules.filter(u=>u.mode==="enforce").map(u=>u.text):o.enforce,c=e?e.rules.filter(u=>u.mode==="advisory").map(u=>u.text):o.advisory,l=n?Le(n):[],i=await F(r.project,r.storage),p=await ie(r.project,r.storage),f=await se(r.project,r.storage),a=[];if(a.push("[TOOLNET PROJECT CONTEXT]"),a.push(`Project: ${r.project.name}`),a.push("Continue existing project state. Do not restart completed work unless evidence shows it is necessary."),n&&a.push(`Full operating manual: ${M(r.project)}`),s.length){a.push("","PROJECT RULES \u2014 MUST FOLLOW");for(let u of s.slice(0,24))a.push(`- [ENFORCE] ${d(u,240)}`)}if(c.length){a.push("","PROJECT PREFERENCES");for(let u of c.slice(0,10))a.push(`- ${d(u,220)}`)}if(p&&(p.mission&&a.push("","MISSION",d(p.mission.value,420)),p.activeObjective&&a.push("","CURRENT OBJECTIVE",d(p.activeObjective.value,420)),p.why&&a.push("","WHY THIS WORK MATTERS",d(p.why.value,420)),p.desiredOutcome&&a.push("","DESIRED OUTCOME",d(p.desiredOutcome.value,420)),p.planRationale&&a.push("","WHY THIS APPROACH",d(p.planRationale.value,420))),i){if(a.push("","ACTIVE WORK"),i.goal&&a.push(`Goal: ${d(i.goal,320)}`),i.plan&&a.push(`Plan: ${d(i.plan,320)}`),a.push(`Progress: phases ${i.progress.phasesCompleted}/${i.progress.phasesTotal}; tasks ${i.progress.tasksCompleted}/${i.progress.tasksTotal}; blocked ${i.progress.blocked}`),i.currentPhase&&a.push(`Current phase: ${i.currentPhase.title} [${i.currentPhase.status}]`),i.currentPhase&&p){let u=p.phases.find(m=>m.order===i.currentPhase?.order);u&&(u.objective&&a.push(`Phase objective: ${d(u.objective.value,340)}`),u.why?a.push(`Why this phase: ${d(u.why.value,340)}`):a.push("Why this phase: not explicitly recorded. Inspect existing implementation before assuming intent."),u.deliverable&&a.push(`Deliverable: ${d(u.deliverable.value,340)}`),u.dependencies.length&&a.push(`Depends on: ${u.dependencies.slice(0,4).map(m=>d(m.value,180)).join("; ")}`),u.acceptanceCriteria.length&&(a.push("","DEFINITION OF DONE"),u.acceptanceCriteria.slice(0,6).forEach(m=>{a.push(`- ${d(m.value,260)}`)})),u.openQuestions.length&&(a.push("","OPEN QUESTIONS FOR CURRENT PHASE"),u.openQuestions.slice(0,4).forEach(m=>{a.push(`- ${d(m.value,260)}`)})))}i.currentTask&&a.push(`Current task: ${i.currentTask.title} [${i.currentTask.status}]`),i.nextActions.length&&(a.push("","NEXT ACTIONS"),i.nextActions.slice(0,6).forEach((u,m)=>{a.push(`${m+1}. ${d(u,260)}`)})),i.blockers.length&&(a.push("","BLOCKERS"),i.blockers.slice(0,5).forEach(u=>{a.push(`- ${d(u,260)}`)})),i.warnings.length&&(a.push("","ATTENTION"),i.warnings.slice(-5).forEach(u=>{a.push(`- ${d(u,260)}`)})),i.decisions.length&&(a.push("","RECENT DECISIONS"),i.decisions.slice(-5).forEach(u=>{a.push(`- ${d(u,260)}`)})),i.lastSession&&a.push("",`Last work session: ${i.lastSession.agent} / ${i.lastSession.nativeSessionId}`)}if(p&&p.openQuestions.length&&(a.push("","UNRESOLVED QUESTIONS"),p.openQuestions.slice(0,5).forEach(u=>{a.push(`- ${d(u.value,260)}`)})),f&&a.push(`Latest handoff: ${f.reason} / ${f.sourceSession.agent}`),l.length){a.push("","OPERATING NOTES");for(let u of l)a.push(`- ${u}`)}a.push("","Before changing anything: verify the current repository state and continue from the active phase/task above.");let S=He(a,t);return{version:1,projectId:r.project.id,projectName:r.project.name,text:S,estimatedTokens:W(S),maxTokens:t,hasManual:!!n,hasWorkState:!!i,hasHandoff:!!f,generatedAt:new Date().toISOString()}}function Ge(r){return ce(r.rootPath,".toolnet","context","startup.md")}function Ye(r){return ce(r.rootPath,".toolnet","context","startup.json")}function Qe(r,t){let e=Ge(r);ze(Ue(e),{recursive:!0}),Je(e,t.text.endsWith(`
184
184
  `)?t.text:t.text+`
185
- `,{encoding:"utf8",mode:384}),k(Ye(r),t)}async function ue(r,t,e=800){let n=(await ae({project:r,storage:t,maxTokens:e})).text;j(n)>e&&(n=_(n,e),n+=`
185
+ `,{encoding:"utf8",mode:384}),j(Ye(r),t)}async function ue(r,t,e=800){let o=(await ae({project:r,storage:t,maxTokens:e})).text;k(o)>e&&(o=_(o,e),o+=`
186
186
 
187
187
  [Context trimmed by ToolNet Memory token budget]
188
- `);let c={version:1,projectId:r.id,projectName:r.name,text:n,digest:S(n),estimatedTokens:j(n),generatedAt:new Date().toISOString()};return qe(r,c),await t.put(`projects/${r.id}/context/startup.md`,c.text+`
188
+ `);let c={version:1,projectId:r.id,projectName:r.name,text:o,digest:y(o),estimatedTokens:k(o),generatedAt:new Date().toISOString()};return Qe(r,c),await t.put(`projects/${r.id}/context/startup.md`,c.text+`
189
189
  `,"text/markdown"),await t.put(`projects/${r.id}/context/startup.json`,JSON.stringify(c,null,2)+`
190
- `,"application/json"),c}import{existsSync as Qe,readFileSync as Ze}from"node:fs";import{homedir as Xe}from"node:os";import{join as et}from"node:path";function tt(r){let t=r.trim();return t.length>=2&&t.startsWith('"')&&t.endsWith('"')?(t=t.slice(1,-1),t.replace(/\\n/g,`
191
- `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):t.length>=2&&t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1):t}function rt(){let r=process.env.TOOLNET_GLOBAL_ENV??et(Xe(),".config","toolnet-memory",".env");if(!Qe(r))return;let t=Ze(r,"utf8");for(let e of t.split(/\r?\n/)){let o=e.trim();if(!o||o.startsWith("#"))continue;o.startsWith("export ")&&(o=o.slice(7));let n=o.indexOf("=");if(n<=0)continue;let s=o.slice(0,n).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(s)&&process.env[s]===void 0&&(process.env[s]=tt(o.slice(n+1)))}}rt();function x(r,t){return r===void 0?t:["1","true","yes","on"].includes(r.toLowerCase())}function w(r,t){if(!r)return t;let e=Number(r);return Number.isFinite(e)?e:t}function pe(){return{memory:{autoCapture:x(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:x(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:x(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:x(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:w(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:w(process.env.MEMORY_RERANK_TOP,10),finalContext:w(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:w(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",r2:{accountId:process.env.R2_ACCOUNT_ID,bucket:process.env.R2_BUCKET,accessKeyId:process.env.R2_ACCESS_KEY_ID,secretAccessKey:process.env.R2_SECRET_ACCESS_KEY},s3:{endpoint:process.env.S3_ENDPOINT,region:process.env.S3_REGION,bucket:process.env.S3_BUCKET,accessKeyId:process.env.S3_ACCESS_KEY_ID,secretAccessKey:process.env.S3_SECRET_ACCESS_KEY,forcePathStyle:x(process.env.S3_FORCE_PATH_STYLE,!1)},huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:w(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as ot}from"node:crypto";import{existsSync as B,mkdirSync as nt,readFileSync as st,renameSync as it,writeFileSync as at}from"node:fs";import{basename as ct,dirname as E,join as I,parse as fe,resolve as b}from"node:path";var me=".toolnet",ut="project.json";function pt(r){return ot("sha256").update(r).digest("hex").slice(0,16)}function L(r){return I(r,me,ut)}function lt(r){return B(L(r))}function dt(r,t){let e=b(r),o=fe(e).root;for(;;){if(lt(e))return e;if(e===o||t&&e===b(t))break;let n=E(e);if(n===e)break;e=n}return null}function ft(r){let t=b(r),e=fe(t).root,o=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(o.some(s=>B(I(t,s))))return t;if(t===e)break;let n=E(t);if(n===t)break;t=n}return b(r)}function mt(r){let t;try{t=JSON.parse(st(r,"utf8"))}catch(n){throw new Error(`Invalid ToolNet project manifest: ${r}: ${n instanceof Error?n.message:String(n)}`)}if(!t||typeof t!="object")throw new Error(`Invalid ToolNet project manifest: ${r}`);let e=t;if(typeof e.id!="string"||!e.id.trim())throw new Error(`ToolNet project manifest is missing id: ${r}`);if(typeof e.name!="string"||!e.name.trim())throw new Error(`ToolNet project manifest is missing name: ${r}`);let o=new Date().toISOString();return{version:1,id:e.id,name:e.name,remote:typeof e.remote=="string"&&e.remote.trim()?e.remote:e.name,rootPath:typeof e.rootPath=="string"?e.rootPath:E(E(r)),createdAt:typeof e.createdAt=="string"?e.createdAt:o,updatedAt:typeof e.updatedAt=="string"?e.updatedAt:o,graphVersion:typeof e.graphVersion=="number"?e.graphVersion:0,memoryVersion:typeof e.memoryVersion=="number"?e.memoryVersion:0,metadata:e.metadata&&typeof e.metadata=="object"?e.metadata:void 0}}function le(r,t){let e=I(r,me);nt(e,{recursive:!0});let o=L(r),n=`${o}.tmp-${process.pid}`;at(n,JSON.stringify(t,null,2)+`
192
- `,{encoding:"utf8",mode:384}),it(n,o)}function de(r,t){return{id:r.id,name:r.name,remote:r.remote,rootPath:t,createdAt:r.createdAt,updatedAt:r.updatedAt,graphVersion:r.graphVersion,memoryVersion:r.memoryVersion,metadata:r.metadata}}var R=class{detect(t=process.cwd()){let e=b(t),o=ft(e),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(f=>B(I(o,f))),c=dt(e,s?o:void 0);if(c){let f=L(c),a=mt(f);return a.rootPath!==c&&(a.rootPath=c,a.updatedAt=new Date().toISOString(),le(c,a)),de(a,c)}let l=new Date().toISOString(),i=ct(o),p={version:1,id:pt(o),name:i,remote:i,rootPath:o,createdAt:l,updatedAt:l,graphVersion:0,memoryVersion:0};return le(o,p),de(p,o)}};import{homedir as Kt}from"node:os";import{join as Dt}from"node:path";import{DeleteObjectCommand as gt,GetObjectCommand as ht,HeadObjectCommand as yt,ListObjectsV2Command as St,PutObjectCommand as jt,S3Client as kt}from"@aws-sdk/client-s3";import{getSignedUrl as vt}from"@aws-sdk/s3-request-presigner";var M=class{name="huggingface";client;bucket;constructor(t){this.bucket=t.bucket,this.client=new kt({region:"us-east-1",endpoint:`https://s3.hf.co/${t.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}})}async put(t,e,o="application/octet-stream"){let n=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new jt({Bucket:this.bucket,Key:t,Body:n,ContentType:o}))}async get(t){let e=await vt(this.client,new ht({Bucket:this.bucket,Key:t}),{expiresIn:60}),o=await fetch(e,{redirect:"follow"});if(o.status===404)return null;if(!o.ok)throw new Error(`HF download failed: ${o.status} ${o.statusText}`);return new Uint8Array(await o.arrayBuffer())}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await this.client.send(new yt({Bucket:this.bucket,Key:t})),!0}catch(e){if(typeof e=="object"&&e!==null&&"$metadata"in e&&e.$metadata?.httpStatusCode===404)return!1;throw e}}async delete(t){await this.client.send(new gt({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],o;do{let n=await this.client.send(new St({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:o}));for(let s of n.Contents??[])s.Key&&e.push({key:s.Key,size:s.Size,updatedAt:s.LastModified?.toISOString()});o=n.IsTruncated?n.NextContinuationToken:void 0}while(o);return e}};import{access as ge,mkdir as xt,readFile as wt,readdir as bt,rm as Pt,stat as he,writeFile as Tt}from"node:fs/promises";import{dirname as At,join as Ct,relative as ye,resolve as Ot}from"node:path";var P=class{constructor(t){this.root=t}root;name="local";path(t){let e=t.replace(/^\/+/,"");return Ot(this.root,e)}async put(t,e){let o=this.path(t);await xt(At(o),{recursive:!0}),await Tt(o,e)}async get(t){try{return await wt(this.path(t))}catch(e){if(typeof e=="object"&&e!==null&&"code"in e&&e.code==="ENOENT")return null;throw e}}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await ge(this.path(t)),!0}catch{return!1}}async delete(t){await Pt(this.path(t),{force:!0})}async list(t=""){let e=this.path(t),o=[];try{await ge(e)}catch{return o}let n=async c=>{let l=await bt(c,{withFileTypes:!0});for(let i of l){let p=Ct(c,i.name);if(i.isDirectory()){await n(p);continue}let f=await he(p);o.push({key:ye(this.root,p),size:f.size,updatedAt:f.mtime.toISOString()})}},s=await he(e);return s.isDirectory()?await n(e):o.push({key:ye(this.root,e),size:s.size,updatedAt:s.mtime.toISOString()}),o}};import{DeleteObjectCommand as Et,GetObjectCommand as Rt,HeadObjectCommand as It,ListObjectsV2Command as Mt,PutObjectCommand as Nt,S3Client as $t}from"@aws-sdk/client-s3";import{getSignedUrl as _t}from"@aws-sdk/s3-request-presigner";var T=class{name;client;bucket;constructor(t){this.name=t.name??"s3",this.bucket=t.bucket,this.client=new $t({region:t.region??"us-east-1",endpoint:t.endpoint||void 0,forcePathStyle:t.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}})}async put(t,e,o="application/octet-stream"){let n=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new Nt({Bucket:this.bucket,Key:t,Body:n,ContentType:o}))}async get(t){let e=await _t(this.client,new Rt({Bucket:this.bucket,Key:t}),{expiresIn:60}),o=await fetch(e,{redirect:"follow"});if(o.status===404)return null;if(!o.ok)throw new Error(`${this.name} download failed: ${o.status} ${o.statusText}`);return new Uint8Array(await o.arrayBuffer())}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await this.client.send(new It({Bucket:this.bucket,Key:t})),!0}catch(e){if(typeof e=="object"&&e!==null&&"$metadata"in e&&e.$metadata?.httpStatusCode===404)return!1;throw e}}async delete(t){await this.client.send(new Et({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],o;do{let n=await this.client.send(new Mt({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:o}));for(let s of n.Contents??[])s.Key&&e.push({key:s.Key,size:s.Size,updatedAt:s.LastModified?.toISOString()});o=n.IsTruncated?n.NextContinuationToken:void 0}while(o);return e}};function V(r,t){return console.warn(t),new P(r)}function Se(r){let t=r.localRoot??Dt(Kt(),".toolnet-memory","storage");if(r.provider==="r2"){let e=r.r2;return e?.accountId&&e.bucket&&e.accessKeyId&&e.secretAccessKey?new T({name:"r2",endpoint:`https://${e.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:e.bucket,forcePathStyle:!0,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):V(t,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(r.provider==="s3"){let e=r.s3;return e?.bucket&&e.accessKeyId&&e.secretAccessKey?new T({name:"s3",endpoint:e.endpoint,region:e.region??"us-east-1",bucket:e.bucket,forcePathStyle:e.forcePathStyle??!1,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):V(t,"[storage] S3 credentials missing. Using local fallback.")}if(r.provider==="huggingface"){let e=r.huggingface;return e?.namespace&&e.bucket&&e.accessKeyId&&e.secretAccessKey?new M({namespace:e.namespace,bucket:e.bucket,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):V(t,"[storage] Hugging Face credentials missing. Using local fallback.")}return new P(t)}function Ft(r){return new Promise(t=>setTimeout(t,r))}async function je(r,t={}){let e=Math.max(1,t.attempts??3),o=t.baseDelayMs??150,n=t.maxDelayMs??2e3,s;for(let c=1;c<=e;c++)try{return await r()}catch(l){if(s=l,c>=e)break;let i=Math.min(n,o*2**(c-1)),p=Math.floor(Math.random()*Math.max(1,i*.2));await Ft(i+p)}throw s}var Wt=new Set(["put","get","getText","delete","list"]);function ke(r,t={}){return new Proxy(r,{get(e,o){let n=Reflect.get(e,o,e);return typeof n!="function"?n:Wt.has(o)?(...s)=>je(()=>Promise.resolve(n.apply(e,s)),t):n.bind(e)}})}function ve(r){let t=r.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!t||t==="."||t==="..")throw new Error("Invalid project storage folder");return t}function xe(r){let t=r.replace(/^\/+/,"");if(t.startsWith("memories/"))return"memory/records/"+t.slice(9);if(t.startsWith("vectors/"))return"memory/vectors/"+t.slice(8);if(t.startsWith("graph/"))return"code/graph/"+t.slice(6);let e=t.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(e)return`${e[1]}memory/records/${e[2]}`;if(e=t.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),e)return`${e[1]}memory/vectors/${e[2]}`;if(e=t.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),e)return`${e[1]}code/graph/${e[2]}`;let o=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(o)return`${o[1]}memory/records/${o[2]}`;if(o=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),o)return`${o[1]}memory/vectors/${o[2]}`;if(o=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),o)return`${o[1]}code/graph/${o[2]}`;let n=t.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return n?`${n[1]}memory/records/${n[2]}`:(n=t.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),n?`${n[1]}memory/vectors/${n[2]}`:(n=t.match(/^(projects\/[^/]+\/)graph\/(.+)$/),n?`${n[1]}code/graph/${n[2]}`:t))}var N=class{constructor(t,e,o,n){this.provider=t;this.name=t.name,this.projectId=e,this.projectName=o,this.folder=ve(n??o),this.sourcePrefix=`projects/${e}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let t=`${this.targetPrefix}/project.json`,e=new Date().toISOString(),o=e,n=await this.provider.getText(t);if(n){let c;try{c=JSON.parse(n)}catch(l){throw new Error(`Invalid remote ToolNet project manifest at ${t}: ${l instanceof Error?l.message:String(l)}`)}if(typeof c.id=="string"&&c.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${c.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof c.createdAt=="string"&&(o=c.createdAt)}let s={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:o,updatedAt:e};await this.provider.put(t,JSON.stringify(s,null,2)+`
193
- `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(t){if(t=xe(t),t===this.sourcePrefix)return this.targetPrefix;if(t.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+t.slice(this.sourcePrefix.length);if(t===this.targetPrefix||t.startsWith(`${this.targetPrefix}/`))return t;if(t.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${t}`].join(" "));return t}async put(t,e,o){return await this.ensureRegistered(),this.provider.put(this.key(t),e,o)}async get(t){return await this.ensureRegistered(),this.provider.get(this.key(t))}async getText(t){return await this.ensureRegistered(),this.provider.getText(this.key(t))}async delete(t){return await this.ensureRegistered(),this.provider.delete(this.key(t))}async exists(t){return await this.ensureRegistered(),this.provider.exists(this.key(t))}async list(t){return await this.ensureRegistered(),this.provider.list(this.key(t))}};function Bt(){let r=process.argv.slice(2),t=r[0]||"print",e={mode:"minimal"};for(let o=1;o<r.length;o++)r[o]==="--project"&&r[o+1]?(e.project=r[o+1],o++):r[o]==="--limit"&&r[o+1]?(e.limit=parseInt(r[o+1],10),o++):r[o]==="--focused"&&r[o+1]?(e.mode="focused",e.query=r[o+1],o++):r[o]==="--deep"&&(e.mode="deep");return{command:t,options:e}}async function Lt(){let{command:r,options:t}=Bt();try{switch(r){case"print":await Vt(t);break;case"sync":await Jt(t);break;case"refresh":await zt(t);break;case"profile-show":await Ut(t);break;case"profile-sync":await Ht(t);break;default:console.error(`Unknown command: ${r}`),console.error("Available commands: print, sync, refresh, profile-show, profile-sync"),process.exit(1)}}catch(e){console.error("Error:",e instanceof Error?e.message:String(e)),process.exit(1)}}async function Vt(r){let t=r.mode||"minimal";if(t==="minimal"){let e=K({projectPath:r.project});e||(console.error("No ToolNet project found. Run toolnet-memory init first."),process.exit(1)),process.stdout.write(e)}else(t==="focused"||t==="deep")&&(console.error("Focused and deep modes require storage access."),console.error("Use: toolnet-memory brief --deep for deep context"),process.exit(1))}async function Jt(r){let t=K({projectPath:r.project});t||(console.error("No ToolNet project found."),process.exit(1));let e=te(t);console.log(`Context hash: ${e}`),console.log(`Context size: ${t.length} chars`)}async function zt(r){console.log("Refreshing deep startup brief cache...");let t=r.project||process.cwd(),e=v(t);e||(console.error("No ToolNet project found."),process.exit(1));let o=new R().detect(e),n=pe(),s=ke(Se({provider:n.storage.provider,huggingface:n.storage.huggingface,localRoot:n.storage.localRoot}),{attempts:2}),c=new N(s,o.id,o.name,o.remote??o.name);await ue(o,c),console.log("Deep startup brief cache refreshed.")}async function Ut(r){let t=r.project||process.cwd(),e=v(t);e||(console.error("No ToolNet project found."),process.exit(1));let o=we.join(e,".toolnet","profile.md");$.existsSync(o)||(console.error("No profile.md found in .toolnet directory."),process.exit(1));let n=$.readFileSync(o,"utf-8");process.stdout.write(n)}async function Ht(r){let t=ee({projectPath:r.project});console.log("Created/updated:");for(let e of t)console.log(`- ${e}`)}Lt();
190
+ `,"application/json"),c}import{existsSync as qe,readFileSync as Ze}from"node:fs";import{homedir as Xe}from"node:os";import{join as et}from"node:path";function tt(r){let t=r.trim();return t.length>=2&&t.startsWith('"')&&t.endsWith('"')?(t=t.slice(1,-1),t.replace(/\\n/g,`
191
+ `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):t.length>=2&&t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1):t}function rt(){let r=process.env.TOOLNET_GLOBAL_ENV??et(Xe(),".config","toolnet-memory",".env");if(!qe(r))return;let t=Ze(r,"utf8");for(let e of t.split(/\r?\n/)){let n=e.trim();if(!n||n.startsWith("#"))continue;n.startsWith("export ")&&(n=n.slice(7));let o=n.indexOf("=");if(o<=0)continue;let s=n.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(s)&&process.env[s]===void 0&&(process.env[s]=tt(n.slice(o+1)))}}rt();function w(r,t){return r===void 0?t:["1","true","yes","on"].includes(r.toLowerCase())}function x(r,t){if(!r)return t;let e=Number(r);return Number.isFinite(e)?e:t}function pe(){return{memory:{autoCapture:w(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:w(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:w(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:w(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:x(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:x(process.env.MEMORY_RERANK_TOP,10),finalContext:x(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:x(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",r2:{accountId:process.env.R2_ACCOUNT_ID,bucket:process.env.R2_BUCKET,accessKeyId:process.env.R2_ACCESS_KEY_ID,secretAccessKey:process.env.R2_SECRET_ACCESS_KEY},s3:{endpoint:process.env.S3_ENDPOINT,region:process.env.S3_REGION,bucket:process.env.S3_BUCKET,accessKeyId:process.env.S3_ACCESS_KEY_ID,secretAccessKey:process.env.S3_SECRET_ACCESS_KEY,forcePathStyle:w(process.env.S3_FORCE_PATH_STYLE,!1)},huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:x(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as nt}from"node:crypto";import{existsSync as B,mkdirSync as ot,readFileSync as st,renameSync as it,writeFileSync as at}from"node:fs";import{basename as ct,dirname as I,join as O,parse as fe,resolve as b}from"node:path";var me=".toolnet",ut="project.json";function pt(r){return nt("sha256").update(r).digest("hex").slice(0,16)}function L(r){return O(r,me,ut)}function lt(r){return B(L(r))}function dt(r,t){let e=b(r),n=fe(e).root;for(;;){if(lt(e))return e;if(e===n||t&&e===b(t))break;let o=I(e);if(o===e)break;e=o}return null}function ft(r){let t=b(r),e=fe(t).root,n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(n.some(s=>B(O(t,s))))return t;if(t===e)break;let o=I(t);if(o===t)break;t=o}return b(r)}function mt(r){let t;try{t=JSON.parse(st(r,"utf8"))}catch(o){throw new Error(`Invalid ToolNet project manifest: ${r}: ${o instanceof Error?o.message:String(o)}`)}if(!t||typeof t!="object")throw new Error(`Invalid ToolNet project manifest: ${r}`);let e=t;if(typeof e.id!="string"||!e.id.trim())throw new Error(`ToolNet project manifest is missing id: ${r}`);if(typeof e.name!="string"||!e.name.trim())throw new Error(`ToolNet project manifest is missing name: ${r}`);let n=new Date().toISOString();return{version:1,id:e.id,name:e.name,remote:typeof e.remote=="string"&&e.remote.trim()?e.remote:e.name,rootPath:typeof e.rootPath=="string"?e.rootPath:I(I(r)),createdAt:typeof e.createdAt=="string"?e.createdAt:n,updatedAt:typeof e.updatedAt=="string"?e.updatedAt:n,graphVersion:typeof e.graphVersion=="number"?e.graphVersion:0,memoryVersion:typeof e.memoryVersion=="number"?e.memoryVersion:0,metadata:e.metadata&&typeof e.metadata=="object"?e.metadata:void 0}}function le(r,t){let e=O(r,me);ot(e,{recursive:!0});let n=L(r),o=`${n}.tmp-${process.pid}`;at(o,JSON.stringify(t,null,2)+`
192
+ `,{encoding:"utf8",mode:384}),it(o,n)}function de(r,t){return{id:r.id,name:r.name,remote:r.remote,rootPath:t,createdAt:r.createdAt,updatedAt:r.updatedAt,graphVersion:r.graphVersion,memoryVersion:r.memoryVersion,metadata:r.metadata}}var R=class{detect(t=process.cwd()){let e=b(t),n=ft(e),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(f=>B(O(n,f))),c=dt(e,s?n:void 0);if(c){let f=L(c),a=mt(f);return a.rootPath!==c&&(a.rootPath=c,a.updatedAt=new Date().toISOString(),le(c,a)),de(a,c)}let l=new Date().toISOString(),i=ct(n),p={version:1,id:pt(n),name:i,remote:i,rootPath:n,createdAt:l,updatedAt:l,graphVersion:0,memoryVersion:0};return le(n,p),de(p,n)}};import{homedir as Dt}from"node:os";import{join as Kt}from"node:path";import{DeleteObjectCommand as gt,GetObjectCommand as ht,HeadObjectCommand as yt,ListObjectsV2Command as St,PutObjectCommand as kt,S3Client as jt}from"@aws-sdk/client-s3";import{getSignedUrl as vt}from"@aws-sdk/s3-request-presigner";var E=class{name="huggingface";client;bucket;constructor(t){this.bucket=t.bucket,this.client=new jt({region:"us-east-1",endpoint:`https://s3.hf.co/${t.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}})}async put(t,e,n="application/octet-stream"){let o=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new kt({Bucket:this.bucket,Key:t,Body:o,ContentType:n}))}async get(t){let e=await vt(this.client,new ht({Bucket:this.bucket,Key:t}),{expiresIn:60}),n=await fetch(e,{redirect:"follow"});if(n.status===404)return null;if(!n.ok)throw new Error(`HF download failed: ${n.status} ${n.statusText}`);return new Uint8Array(await n.arrayBuffer())}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await this.client.send(new yt({Bucket:this.bucket,Key:t})),!0}catch(e){if(typeof e=="object"&&e!==null&&"$metadata"in e&&e.$metadata?.httpStatusCode===404)return!1;throw e}}async delete(t){await this.client.send(new gt({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],n;do{let o=await this.client.send(new St({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:n}));for(let s of o.Contents??[])s.Key&&e.push({key:s.Key,size:s.Size,updatedAt:s.LastModified?.toISOString()});n=o.IsTruncated?o.NextContinuationToken:void 0}while(n);return e}};import{access as ge,mkdir as wt,readFile as xt,readdir as bt,rm as Pt,stat as he,writeFile as Tt}from"node:fs/promises";import{dirname as At,join as Ct,relative as ye,resolve as Mt}from"node:path";var P=class{constructor(t){this.root=t}root;name="local";path(t){let e=t.replace(/^\/+/,"");return Mt(this.root,e)}async put(t,e){let n=this.path(t);await wt(At(n),{recursive:!0}),await Tt(n,e)}async get(t){try{return await xt(this.path(t))}catch(e){if(typeof e=="object"&&e!==null&&"code"in e&&e.code==="ENOENT")return null;throw e}}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await ge(this.path(t)),!0}catch{return!1}}async delete(t){await Pt(this.path(t),{force:!0})}async list(t=""){let e=this.path(t),n=[];try{await ge(e)}catch{return n}let o=async c=>{let l=await bt(c,{withFileTypes:!0});for(let i of l){let p=Ct(c,i.name);if(i.isDirectory()){await o(p);continue}let f=await he(p);n.push({key:ye(this.root,p),size:f.size,updatedAt:f.mtime.toISOString()})}},s=await he(e);return s.isDirectory()?await o(e):n.push({key:ye(this.root,e),size:s.size,updatedAt:s.mtime.toISOString()}),n}};import{DeleteObjectCommand as It,GetObjectCommand as Rt,HeadObjectCommand as Ot,ListObjectsV2Command as Et,PutObjectCommand as Nt,S3Client as $t}from"@aws-sdk/client-s3";import{getSignedUrl as _t}from"@aws-sdk/s3-request-presigner";var T=class{name;client;bucket;constructor(t){this.name=t.name??"s3",this.bucket=t.bucket,this.client=new $t({region:t.region??"us-east-1",endpoint:t.endpoint||void 0,forcePathStyle:t.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}})}async put(t,e,n="application/octet-stream"){let o=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new Nt({Bucket:this.bucket,Key:t,Body:o,ContentType:n}))}async get(t){let e=await _t(this.client,new Rt({Bucket:this.bucket,Key:t}),{expiresIn:60}),n=await fetch(e,{redirect:"follow"});if(n.status===404)return null;if(!n.ok)throw new Error(`${this.name} download failed: ${n.status} ${n.statusText}`);return new Uint8Array(await n.arrayBuffer())}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await this.client.send(new Ot({Bucket:this.bucket,Key:t})),!0}catch(e){if(typeof e=="object"&&e!==null&&"$metadata"in e&&e.$metadata?.httpStatusCode===404)return!1;throw e}}async delete(t){await this.client.send(new It({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],n;do{let o=await this.client.send(new Et({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:n}));for(let s of o.Contents??[])s.Key&&e.push({key:s.Key,size:s.Size,updatedAt:s.LastModified?.toISOString()});n=o.IsTruncated?o.NextContinuationToken:void 0}while(n);return e}};function V(r,t){return console.warn(t),new P(r)}function Se(r){let t=r.localRoot??Kt(Dt(),".toolnet-memory","storage");if(r.provider==="r2"){let e=r.r2;return e?.accountId&&e.bucket&&e.accessKeyId&&e.secretAccessKey?new T({name:"r2",endpoint:`https://${e.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:e.bucket,forcePathStyle:!0,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):V(t,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(r.provider==="s3"){let e=r.s3;return e?.bucket&&e.accessKeyId&&e.secretAccessKey?new T({name:"s3",endpoint:e.endpoint,region:e.region??"us-east-1",bucket:e.bucket,forcePathStyle:e.forcePathStyle??!1,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):V(t,"[storage] S3 credentials missing. Using local fallback.")}if(r.provider==="huggingface"){let e=r.huggingface;return e?.namespace&&e.bucket&&e.accessKeyId&&e.secretAccessKey?new E({namespace:e.namespace,bucket:e.bucket,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):V(t,"[storage] Hugging Face credentials missing. Using local fallback.")}return new P(t)}function Ft(r){return new Promise(t=>setTimeout(t,r))}async function ke(r,t={}){let e=Math.max(1,t.attempts??3),n=t.baseDelayMs??150,o=t.maxDelayMs??2e3,s;for(let c=1;c<=e;c++)try{return await r()}catch(l){if(s=l,c>=e)break;let i=Math.min(o,n*2**(c-1)),p=Math.floor(Math.random()*Math.max(1,i*.2));await Ft(i+p)}throw s}var Wt=new Set(["put","get","getText","delete","list"]);function je(r,t={}){return new Proxy(r,{get(e,n){let o=Reflect.get(e,n,e);return typeof o!="function"?o:Wt.has(n)?(...s)=>ke(()=>Promise.resolve(o.apply(e,s)),t):o.bind(e)}})}function ve(r){let t=r.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!t||t==="."||t==="..")throw new Error("Invalid project storage folder");return t}function we(r){let t=r.replace(/^\/+/,"");if(t.startsWith("memories/"))return"memory/records/"+t.slice(9);if(t.startsWith("vectors/"))return"memory/vectors/"+t.slice(8);if(t.startsWith("graph/"))return"code/graph/"+t.slice(6);let e=t.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(e)return`${e[1]}memory/records/${e[2]}`;if(e=t.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),e)return`${e[1]}memory/vectors/${e[2]}`;if(e=t.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),e)return`${e[1]}code/graph/${e[2]}`;let n=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(n)return`${n[1]}memory/records/${n[2]}`;if(n=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),n)return`${n[1]}memory/vectors/${n[2]}`;if(n=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),n)return`${n[1]}code/graph/${n[2]}`;let o=t.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return o?`${o[1]}memory/records/${o[2]}`:(o=t.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),o?`${o[1]}memory/vectors/${o[2]}`:(o=t.match(/^(projects\/[^/]+\/)graph\/(.+)$/),o?`${o[1]}code/graph/${o[2]}`:t))}var N=class{constructor(t,e,n,o){this.provider=t;this.name=t.name,this.projectId=e,this.projectName=n,this.folder=ve(o??n),this.sourcePrefix=`projects/${e}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let t=`${this.targetPrefix}/project.json`,e=new Date().toISOString(),n=e,o=await this.provider.getText(t);if(o){let c;try{c=JSON.parse(o)}catch(l){throw new Error(`Invalid remote ToolNet project manifest at ${t}: ${l instanceof Error?l.message:String(l)}`)}if(typeof c.id=="string"&&c.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${c.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof c.createdAt=="string"&&(n=c.createdAt)}let s={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:n,updatedAt:e};await this.provider.put(t,JSON.stringify(s,null,2)+`
193
+ `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(t){if(t=we(t),t===this.sourcePrefix)return this.targetPrefix;if(t.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+t.slice(this.sourcePrefix.length);if(t===this.targetPrefix||t.startsWith(`${this.targetPrefix}/`))return t;if(t.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${t}`].join(" "));return t}async put(t,e,n){return await this.ensureRegistered(),this.provider.put(this.key(t),e,n)}async get(t){return await this.ensureRegistered(),this.provider.get(this.key(t))}async getText(t){return await this.ensureRegistered(),this.provider.getText(this.key(t))}async delete(t){return await this.ensureRegistered(),this.provider.delete(this.key(t))}async exists(t){return await this.ensureRegistered(),this.provider.exists(this.key(t))}async list(t){return await this.ensureRegistered(),this.provider.list(this.key(t))}};function Bt(){let r=process.argv.slice(2),t=r[0]||"print",e={mode:"minimal"};for(let n=1;n<r.length;n++)r[n]==="--project"&&r[n+1]?(e.project=r[n+1],n++):r[n]==="--limit"&&r[n+1]?(e.limit=parseInt(r[n+1],10),n++):r[n]==="--focused"&&r[n+1]?(e.mode="focused",e.query=r[n+1],n++):r[n]==="--deep"&&(e.mode="deep");return{command:t,options:e}}async function Lt(){let{command:r,options:t}=Bt();try{switch(r){case"print":await Vt(t);break;case"sync":await Ht(t);break;case"refresh":await zt(t);break;case"profile-show":await Jt(t);break;case"profile-sync":await Ut(t);break;default:console.error(`Unknown command: ${r}`),console.error("Available commands: print, sync, refresh, profile-show, profile-sync"),process.exit(1)}}catch(e){console.error("Error:",e instanceof Error?e.message:String(e)),process.exit(1)}}async function Vt(r){let t=r.mode||"minimal";if(t==="minimal"){let e=D({projectPath:r.project});e||(console.error("No ToolNet project found. Run toolnet-memory init first."),process.exit(1)),process.stdout.write(e)}else(t==="focused"||t==="deep")&&(console.error("Focused and deep modes require storage access."),console.error("Use: toolnet-memory brief --deep for deep context"),process.exit(1))}async function Ht(r){let t=D({projectPath:r.project});t||(console.error("No ToolNet project found."),process.exit(1));let e=te(t);console.log(`Context hash: ${e}`),console.log(`Context size: ${t.length} chars`)}async function zt(r){console.log("Refreshing deep startup brief cache...");let t=r.project||process.cwd(),e=v(t);e||(console.error("No ToolNet project found."),process.exit(1));let n=new R().detect(e),o=pe(),s=je(Se({provider:o.storage.provider,huggingface:o.storage.huggingface,localRoot:o.storage.localRoot}),{attempts:2}),c=new N(s,n.id,n.name,n.remote??n.name);await ue(n,c),console.log("Deep startup brief cache refreshed.")}async function Jt(r){let t=r.project||process.cwd(),e=v(t);e||(console.error("No ToolNet project found."),process.exit(1));let n=xe.join(e,".toolnet","profile.md");$.existsSync(n)||(console.error("No profile.md found in .toolnet directory."),process.exit(1));let o=$.readFileSync(n,"utf-8");process.stdout.write(o)}async function Ut(r){let t=ee({projectPath:r.project});console.log("Created/updated:");for(let e of t)console.log(`- ${e}`)}Lt();
package/bundle/context.js CHANGED
@@ -1,8 +1,8 @@
1
- import{existsSync as ve,readFileSync as we}from"node:fs";import{homedir as be}from"node:os";import{join as xe}from"node:path";function Pe(r){let e=r.trim();return e.length>=2&&e.startsWith('"')&&e.endsWith('"')?(e=e.slice(1,-1),e.replace(/\\n/g,`
2
- `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function Ae(){let r=process.env.TOOLNET_GLOBAL_ENV??xe(be(),".config","toolnet-memory",".env");if(!ve(r))return;let e=we(r,"utf8");for(let t of e.split(/\r?\n/)){let s=t.trim();if(!s||s.startsWith("#"))continue;s.startsWith("export ")&&(s=s.slice(7));let o=s.indexOf("=");if(o<=0)continue;let n=s.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(n)&&process.env[n]===void 0&&(process.env[n]=Pe(s.slice(o+1)))}}Ae();function b(r,e){return r===void 0?e:["1","true","yes","on"].includes(r.toLowerCase())}function x(r,e){if(!r)return e;let t=Number(r);return Number.isFinite(t)?t:e}function q(){return{memory:{autoCapture:b(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:b(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:b(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:b(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:x(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:x(process.env.MEMORY_RERANK_TOP,10),finalContext:x(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:x(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",r2:{accountId:process.env.R2_ACCOUNT_ID,bucket:process.env.R2_BUCKET,accessKeyId:process.env.R2_ACCESS_KEY_ID,secretAccessKey:process.env.R2_SECRET_ACCESS_KEY},s3:{endpoint:process.env.S3_ENDPOINT,region:process.env.S3_REGION,bucket:process.env.S3_BUCKET,accessKeyId:process.env.S3_ACCESS_KEY_ID,secretAccessKey:process.env.S3_SECRET_ACCESS_KEY,forcePathStyle:b(process.env.S3_FORCE_PATH_STYLE,!1)},huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:x(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as Te}from"node:crypto";import{existsSync as B,mkdirSync as Re,readFileSync as Ee,renameSync as Ie,writeFileSync as Oe}from"node:fs";import{basename as Ce,dirname as I,join as C,parse as ee,resolve as P}from"node:path";var te=".toolnet",Me="project.json";function Ne(r){return Te("sha256").update(r).digest("hex").slice(0,16)}function J(r){return C(r,te,Me)}function $e(r){return B(J(r))}function _e(r,e){let t=P(r),s=ee(t).root;for(;;){if($e(t))return t;if(t===s||e&&t===P(e))break;let o=I(t);if(o===t)break;t=o}return null}function Ke(r){let e=P(r),t=ee(e).root,s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(s.some(n=>B(C(e,n))))return e;if(e===t)break;let o=I(e);if(o===e)break;e=o}return P(r)}function We(r){let e;try{e=JSON.parse(Ee(r,"utf8"))}catch(o){throw new Error(`Invalid ToolNet project manifest: ${r}: ${o instanceof Error?o.message:String(o)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${r}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${r}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${r}`);let s=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:I(I(r)),createdAt:typeof t.createdAt=="string"?t.createdAt:s,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:s,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 Z(r,e){let t=C(r,te);Re(t,{recursive:!0});let s=J(r),o=`${s}.tmp-${process.pid}`;Oe(o,JSON.stringify(e,null,2)+`
3
- `,{encoding:"utf8",mode:384}),Ie(o,s)}function X(r,e){return{id:r.id,name:r.name,remote:r.remote,rootPath:e,createdAt:r.createdAt,updatedAt:r.updatedAt,graphVersion:r.graphVersion,memoryVersion:r.memoryVersion,metadata:r.metadata}}var O=class{detect(e=process.cwd()){let t=P(e),s=Ke(t),n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(f=>B(C(s,f))),p=_e(t,n?s:void 0);if(p){let f=J(p),a=We(f);return a.rootPath!==p&&(a.rootPath=p,a.updatedAt=new Date().toISOString(),Z(p,a)),X(a,p)}let l=new Date().toISOString(),c=Ce(s),d={version:1,id:Ne(s),name:c,remote:c,rootPath:s,createdAt:l,updatedAt:l,graphVersion:0,memoryVersion:0};return Z(s,d),X(d,s)}};import{homedir as at}from"node:os";import{join as ct}from"node:path";import{DeleteObjectCommand as De,GetObjectCommand as Le,HeadObjectCommand as Ve,ListObjectsV2Command as Be,PutObjectCommand as Je,S3Client as ze}from"@aws-sdk/client-s3";import{getSignedUrl as Fe}from"@aws-sdk/s3-request-presigner";var M=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new ze({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,s="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Je({Bucket:this.bucket,Key:e,Body:o,ContentType:s}))}async get(e){let t=await Fe(this.client,new Le({Bucket:this.bucket,Key:e}),{expiresIn:60}),s=await fetch(t,{redirect:"follow"});if(s.status===404)return null;if(!s.ok)throw new Error(`HF download failed: ${s.status} ${s.statusText}`);return new Uint8Array(await s.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new Ve({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new De({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],s;do{let o=await this.client.send(new Be({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:s}));for(let n of o.Contents??[])n.Key&&t.push({key:n.Key,size:n.Size,updatedAt:n.LastModified?.toISOString()});s=o.IsTruncated?o.NextContinuationToken:void 0}while(s);return t}};import{access as re,mkdir as Ue,readFile as He,readdir as Ye,rm as Ge,stat as se,writeFile as Qe}from"node:fs/promises";import{dirname as qe,join as Ze,relative as oe,resolve as Xe}from"node:path";var A=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return Xe(this.root,t)}async put(e,t){let s=this.path(e);await Ue(qe(s),{recursive:!0}),await Qe(s,t)}async get(e){try{return await He(this.path(e))}catch(t){if(typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT")return null;throw t}}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await re(this.path(e)),!0}catch{return!1}}async delete(e){await Ge(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),s=[];try{await re(t)}catch{return s}let o=async p=>{let l=await Ye(p,{withFileTypes:!0});for(let c of l){let d=Ze(p,c.name);if(c.isDirectory()){await o(d);continue}let f=await se(d);s.push({key:oe(this.root,d),size:f.size,updatedAt:f.mtime.toISOString()})}},n=await se(t);return n.isDirectory()?await o(t):s.push({key:oe(this.root,t),size:n.size,updatedAt:n.mtime.toISOString()}),s}};import{DeleteObjectCommand as et,GetObjectCommand as tt,HeadObjectCommand as rt,ListObjectsV2Command as st,PutObjectCommand as ot,S3Client as nt}from"@aws-sdk/client-s3";import{getSignedUrl as it}from"@aws-sdk/s3-request-presigner";var T=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new nt({region:e.region??"us-east-1",endpoint:e.endpoint||void 0,forcePathStyle:e.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,s="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new ot({Bucket:this.bucket,Key:e,Body:o,ContentType:s}))}async get(e){let t=await it(this.client,new tt({Bucket:this.bucket,Key:e}),{expiresIn:60}),s=await fetch(t,{redirect:"follow"});if(s.status===404)return null;if(!s.ok)throw new Error(`${this.name} download failed: ${s.status} ${s.statusText}`);return new Uint8Array(await s.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new rt({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new et({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],s;do{let o=await this.client.send(new st({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:s}));for(let n of o.Contents??[])n.Key&&t.push({key:n.Key,size:n.Size,updatedAt:n.LastModified?.toISOString()});s=o.IsTruncated?o.NextContinuationToken:void 0}while(s);return t}};function z(r,e){return console.warn(e),new A(r)}function ne(r){let e=r.localRoot??ct(at(),".toolnet-memory","storage");if(r.provider==="r2"){let t=r.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new T({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):z(e,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(r.provider==="s3"){let t=r.s3;return t?.bucket&&t.accessKeyId&&t.secretAccessKey?new T({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):z(e,"[storage] S3 credentials missing. Using local fallback.")}if(r.provider==="huggingface"){let t=r.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new M({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):z(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new A(e)}function pt(r){return new Promise(e=>setTimeout(e,r))}async function ie(r,e={}){let t=Math.max(1,e.attempts??3),s=e.baseDelayMs??150,o=e.maxDelayMs??2e3,n;for(let p=1;p<=t;p++)try{return await r()}catch(l){if(n=l,p>=t)break;let c=Math.min(o,s*2**(p-1)),d=Math.floor(Math.random()*Math.max(1,c*.2));await pt(c+d)}throw n}var ut=new Set(["put","get","getText","delete","list"]);function ae(r,e={}){return new Proxy(r,{get(t,s){let o=Reflect.get(t,s,t);return typeof o!="function"?o:ut.has(s)?(...n)=>ie(()=>Promise.resolve(o.apply(t,n)),e):o.bind(t)}})}function ce(r){let e=r.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!e||e==="."||e==="..")throw new Error("Invalid project storage folder");return e}function pe(r){let e=r.replace(/^\/+/,"");if(e.startsWith("memories/"))return"memory/records/"+e.slice(9);if(e.startsWith("vectors/"))return"memory/vectors/"+e.slice(8);if(e.startsWith("graph/"))return"code/graph/"+e.slice(6);let t=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(t)return`${t[1]}memory/records/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),t)return`${t[1]}memory/vectors/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),t)return`${t[1]}code/graph/${t[2]}`;let s=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(s)return`${s[1]}memory/records/${s[2]}`;if(s=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),s)return`${s[1]}memory/vectors/${s[2]}`;if(s=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),s)return`${s[1]}code/graph/${s[2]}`;let o=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return o?`${o[1]}memory/records/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),o?`${o[1]}memory/vectors/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),o?`${o[1]}code/graph/${o[2]}`:e))}var N=class{constructor(e,t,s,o){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=s,this.folder=ce(o??s),this.sourcePrefix=`projects/${t}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,t=new Date().toISOString(),s=t,o=await this.provider.getText(e);if(o){let p;try{p=JSON.parse(o)}catch(l){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${l instanceof Error?l.message:String(l)}`)}if(typeof p.id=="string"&&p.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${p.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof p.createdAt=="string"&&(s=p.createdAt)}let n={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:s,updatedAt:t};await this.provider.put(e,JSON.stringify(n,null,2)+`
4
- `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=pe(e),e===this.sourcePrefix)return this.targetPrefix;if(e.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+e.slice(this.sourcePrefix.length);if(e===this.targetPrefix||e.startsWith(`${this.targetPrefix}/`))return e;if(e.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${e}`].join(" "));return e}async put(e,t,s){return await this.ensureRegistered(),this.provider.put(this.key(e),t,s)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};import{join as ht}from"node:path";import{createHash as lt}from"node:crypto";import{dirname as dt}from"node:path";import{mkdirSync as mt,readFileSync as Is,renameSync as ft,writeFileSync as gt}from"node:fs";function y(r){return lt("sha256").update(r).digest("hex")}function v(r,e){mt(dt(r),{recursive:!0});let t=`${r}.${process.pid}.tmp`;gt(t,JSON.stringify(e,null,2)+`
5
- `,{encoding:"utf8",mode:384}),ft(t,r)}function ue(r,e){let t=r.trim(),s=t.replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,""),o=y(t).slice(0,12);if(!s||s==="."||s==="..")return`${e}--${o}`;let n=s.slice(0,100);return n===t&&t.length<=100?n:`${n.slice(0,85)}--${o}`}function le(r,e,t){let s=e.trim(),o=t.trim();if(!s)throw new Error("Session agent is required");if(!o)throw new Error("Native session ID is required");let n=ue(s.toLowerCase(),"agent"),p=ue(o,"session");return{projectId:r.id,projectName:r.name,projectRoot:r.rootPath,agent:s,nativeSessionId:o,sessionKey:`${s}:${o}`,remotePrefix:["projects",r.id,"sessions",n,p].join("/"),localDirectory:ht(r.rootPath,".toolnet","sessions",n,p)}}import{existsSync as fe,mkdirSync as yt,readFileSync as St,statSync as de,writeFileSync as jt}from"node:fs";import{dirname as kt,join as vt}from"node:path";var me=64*1024,wt=`# ToolNet Project Operating Manual
1
+ import{existsSync as xe,readFileSync as Pe}from"node:fs";import{homedir as Ae}from"node:os";import{join as Te}from"node:path";function Ie(r){let e=r.trim();return e.length>=2&&e.startsWith('"')&&e.endsWith('"')?(e=e.slice(1,-1),e.replace(/\\n/g,`
2
+ `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function Me(){let r=process.env.TOOLNET_GLOBAL_ENV??Te(Ae(),".config","toolnet-memory",".env");if(!xe(r))return;let e=Pe(r,"utf8");for(let t of e.split(/\r?\n/)){let s=t.trim();if(!s||s.startsWith("#"))continue;s.startsWith("export ")&&(s=s.slice(7));let n=s.indexOf("=");if(n<=0)continue;let o=s.slice(0,n).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(o)&&process.env[o]===void 0&&(process.env[o]=Ie(s.slice(n+1)))}}Me();function P(r,e){return r===void 0?e:["1","true","yes","on"].includes(r.toLowerCase())}function A(r,e){if(!r)return e;let t=Number(r);return Number.isFinite(t)?t:e}function Z(){return{memory:{autoCapture:P(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:P(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:P(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:P(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:A(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:A(process.env.MEMORY_RERANK_TOP,10),finalContext:A(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:A(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",r2:{accountId:process.env.R2_ACCOUNT_ID,bucket:process.env.R2_BUCKET,accessKeyId:process.env.R2_ACCESS_KEY_ID,secretAccessKey:process.env.R2_SECRET_ACCESS_KEY},s3:{endpoint:process.env.S3_ENDPOINT,region:process.env.S3_REGION,bucket:process.env.S3_BUCKET,accessKeyId:process.env.S3_ACCESS_KEY_ID,secretAccessKey:process.env.S3_SECRET_ACCESS_KEY,forcePathStyle:P(process.env.S3_FORCE_PATH_STYLE,!1)},huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:A(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as Re}from"node:crypto";import{existsSync as z,mkdirSync as Ce,readFileSync as Ee,renameSync as Oe,writeFileSync as Ne}from"node:fs";import{basename as $e,dirname as E,join as N,parse as te,resolve as T}from"node:path";var re=".toolnet",_e="project.json";function Ke(r){return Re("sha256").update(r).digest("hex").slice(0,16)}function F(r){return N(r,re,_e)}function De(r){return z(F(r))}function We(r,e){let t=T(r),s=te(t).root;for(;;){if(De(t))return t;if(t===s||e&&t===T(e))break;let n=E(t);if(n===t)break;t=n}return null}function Le(r){let e=T(r),t=te(e).root,s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(s.some(o=>z(N(e,o))))return e;if(e===t)break;let n=E(e);if(n===e)break;e=n}return T(r)}function Ve(r){let e;try{e=JSON.parse(Ee(r,"utf8"))}catch(n){throw new Error(`Invalid ToolNet project manifest: ${r}: ${n instanceof Error?n.message:String(n)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${r}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${r}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${r}`);let s=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:E(E(r)),createdAt:typeof t.createdAt=="string"?t.createdAt:s,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:s,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 X(r,e){let t=N(r,re);Ce(t,{recursive:!0});let s=F(r),n=`${s}.tmp-${process.pid}`;Ne(n,JSON.stringify(e,null,2)+`
3
+ `,{encoding:"utf8",mode:384}),Oe(n,s)}function ee(r,e){return{id:r.id,name:r.name,remote:r.remote,rootPath:e,createdAt:r.createdAt,updatedAt:r.updatedAt,graphVersion:r.graphVersion,memoryVersion:r.memoryVersion,metadata:r.metadata}}var O=class{detect(e=process.cwd()){let t=T(e),s=Le(t),o=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(y=>z(N(s,y))),u=We(t,o?s:void 0);if(u){let y=F(u),i=Ve(y);return i.rootPath!==u&&(i.rootPath=u,i.updatedAt=new Date().toISOString(),X(u,i)),ee(i,u)}let l=new Date().toISOString(),c=$e(s),d={version:1,id:Ke(s),name:c,remote:c,rootPath:s,createdAt:l,updatedAt:l,graphVersion:0,memoryVersion:0};return X(s,d),ee(d,s)}};import{homedir as pt}from"node:os";import{join as lt}from"node:path";import{DeleteObjectCommand as Be,GetObjectCommand as ze,HeadObjectCommand as Fe,ListObjectsV2Command as He,PutObjectCommand as Je,S3Client as Ue}from"@aws-sdk/client-s3";import{getSignedUrl as Ye}from"@aws-sdk/s3-request-presigner";var $=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new Ue({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,s="application/octet-stream"){let n=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Je({Bucket:this.bucket,Key:e,Body:n,ContentType:s}))}async get(e){let t=await Ye(this.client,new ze({Bucket:this.bucket,Key:e}),{expiresIn:60}),s=await fetch(t,{redirect:"follow"});if(s.status===404)return null;if(!s.ok)throw new Error(`HF download failed: ${s.status} ${s.statusText}`);return new Uint8Array(await s.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new Fe({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new Be({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],s;do{let n=await this.client.send(new He({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:s}));for(let o of n.Contents??[])o.Key&&t.push({key:o.Key,size:o.Size,updatedAt:o.LastModified?.toISOString()});s=n.IsTruncated?n.NextContinuationToken:void 0}while(s);return t}};import{access as se,mkdir as Ge,readFile as Qe,readdir as qe,rm as Ze,stat as ne,writeFile as Xe}from"node:fs/promises";import{dirname as et,join as tt,relative as oe,resolve as rt}from"node:path";var I=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return rt(this.root,t)}async put(e,t){let s=this.path(e);await Ge(et(s),{recursive:!0}),await Xe(s,t)}async get(e){try{return await Qe(this.path(e))}catch(t){if(typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT")return null;throw t}}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await se(this.path(e)),!0}catch{return!1}}async delete(e){await Ze(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),s=[];try{await se(t)}catch{return s}let n=async u=>{let l=await qe(u,{withFileTypes:!0});for(let c of l){let d=tt(u,c.name);if(c.isDirectory()){await n(d);continue}let y=await ne(d);s.push({key:oe(this.root,d),size:y.size,updatedAt:y.mtime.toISOString()})}},o=await ne(t);return o.isDirectory()?await n(t):s.push({key:oe(this.root,t),size:o.size,updatedAt:o.mtime.toISOString()}),s}};import{DeleteObjectCommand as st,GetObjectCommand as nt,HeadObjectCommand as ot,ListObjectsV2Command as it,PutObjectCommand as at,S3Client as ct}from"@aws-sdk/client-s3";import{getSignedUrl as ut}from"@aws-sdk/s3-request-presigner";var M=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new ct({region:e.region??"us-east-1",endpoint:e.endpoint||void 0,forcePathStyle:e.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,s="application/octet-stream"){let n=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new at({Bucket:this.bucket,Key:e,Body:n,ContentType:s}))}async get(e){let t=await ut(this.client,new nt({Bucket:this.bucket,Key:e}),{expiresIn:60}),s=await fetch(t,{redirect:"follow"});if(s.status===404)return null;if(!s.ok)throw new Error(`${this.name} download failed: ${s.status} ${s.statusText}`);return new Uint8Array(await s.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new ot({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new st({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],s;do{let n=await this.client.send(new it({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:s}));for(let o of n.Contents??[])o.Key&&t.push({key:o.Key,size:o.Size,updatedAt:o.LastModified?.toISOString()});s=n.IsTruncated?n.NextContinuationToken:void 0}while(s);return t}};function H(r,e){return console.warn(e),new I(r)}function ie(r){let e=r.localRoot??lt(pt(),".toolnet-memory","storage");if(r.provider==="r2"){let t=r.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new M({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):H(e,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(r.provider==="s3"){let t=r.s3;return t?.bucket&&t.accessKeyId&&t.secretAccessKey?new M({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):H(e,"[storage] S3 credentials missing. Using local fallback.")}if(r.provider==="huggingface"){let t=r.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new $({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):H(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new I(e)}function dt(r){return new Promise(e=>setTimeout(e,r))}async function ae(r,e={}){let t=Math.max(1,e.attempts??3),s=e.baseDelayMs??150,n=e.maxDelayMs??2e3,o;for(let u=1;u<=t;u++)try{return await r()}catch(l){if(o=l,u>=t)break;let c=Math.min(n,s*2**(u-1)),d=Math.floor(Math.random()*Math.max(1,c*.2));await dt(c+d)}throw o}var mt=new Set(["put","get","getText","delete","list"]);function ce(r,e={}){return new Proxy(r,{get(t,s){let n=Reflect.get(t,s,t);return typeof n!="function"?n:mt.has(s)?(...o)=>ae(()=>Promise.resolve(n.apply(t,o)),e):n.bind(t)}})}function ue(r){let e=r.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!e||e==="."||e==="..")throw new Error("Invalid project storage folder");return e}function pe(r){let e=r.replace(/^\/+/,"");if(e.startsWith("memories/"))return"memory/records/"+e.slice(9);if(e.startsWith("vectors/"))return"memory/vectors/"+e.slice(8);if(e.startsWith("graph/"))return"code/graph/"+e.slice(6);let t=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(t)return`${t[1]}memory/records/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),t)return`${t[1]}memory/vectors/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),t)return`${t[1]}code/graph/${t[2]}`;let s=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(s)return`${s[1]}memory/records/${s[2]}`;if(s=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),s)return`${s[1]}memory/vectors/${s[2]}`;if(s=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),s)return`${s[1]}code/graph/${s[2]}`;let n=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return n?`${n[1]}memory/records/${n[2]}`:(n=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),n?`${n[1]}memory/vectors/${n[2]}`:(n=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),n?`${n[1]}code/graph/${n[2]}`:e))}var _=class{constructor(e,t,s,n){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=s,this.folder=ue(n??s),this.sourcePrefix=`projects/${t}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,t=new Date().toISOString(),s=t,n=await this.provider.getText(e);if(n){let u;try{u=JSON.parse(n)}catch(l){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${l instanceof Error?l.message:String(l)}`)}if(typeof u.id=="string"&&u.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${u.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof u.createdAt=="string"&&(s=u.createdAt)}let o={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:s,updatedAt:t};await this.provider.put(e,JSON.stringify(o,null,2)+`
4
+ `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=pe(e),e===this.sourcePrefix)return this.targetPrefix;if(e.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+e.slice(this.sourcePrefix.length);if(e===this.targetPrefix||e.startsWith(`${this.targetPrefix}/`))return e;if(e.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${e}`].join(" "));return e}async put(e,t,s){return await this.ensureRegistered(),this.provider.put(this.key(e),t,s)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};import{join as kt}from"node:path";import{createHash as ft}from"node:crypto";import{dirname as gt}from"node:path";import{mkdirSync as yt,readFileSync as Ls,renameSync as ht,writeFileSync as St}from"node:fs";function S(r){return ft("sha256").update(r).digest("hex")}function b(r,e){yt(gt(r),{recursive:!0});let t=`${r}.${process.pid}.tmp`;St(t,JSON.stringify(e,null,2)+`
5
+ `,{encoding:"utf8",mode:384}),ht(t,r)}function le(r,e){let t=r.trim(),s=t.replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,""),n=S(t).slice(0,12);if(!s||s==="."||s==="..")return`${e}--${n}`;let o=s.slice(0,100);return o===t&&t.length<=100?o:`${o.slice(0,85)}--${n}`}function de(r,e,t){let s=e.trim(),n=t.trim();if(!s)throw new Error("Session agent is required");if(!n)throw new Error("Native session ID is required");let o=le(s.toLowerCase(),"agent"),u=le(n,"session");return{projectId:r.id,projectName:r.name,projectRoot:r.rootPath,agent:s,nativeSessionId:n,sessionKey:`${s}:${n}`,remotePrefix:["projects",r.id,"sessions",o,u].join("/"),localDirectory:kt(r.rootPath,".toolnet","sessions",o,u)}}import{existsSync as ge,mkdirSync as jt,readFileSync as vt,statSync as me,writeFileSync as wt}from"node:fs";import{dirname as bt,join as xt}from"node:path";var fe=64*1024,Pt=`# ToolNet Project Operating Manual
6
6
 
7
7
  This file contains persistent instructions for AI agents working on this project.
8
8
 
@@ -45,18 +45,19 @@ Things an AI agent should always remember.
45
45
  <!--
46
46
  - [advisory] Prefer small focused files.
47
47
  -->
48
- `;function $(r){return vt(r.rootPath,".toolnet","PROJECT.md")}function bt(r){return r.normalize("NFKC").replace(/\s+/g," ").trim()}function xt(r){let e=[],t=new Set,s=/^\s*[-*]\s+\[(enforce|advisory)\]\s+(.+?)\s*$/gimu,o;for(;o=s.exec(r);){let n=o[1].toLowerCase(),p=bt(o[2]);if(!p)continue;let l=`${n}:${p.toLowerCase()}`;t.has(l)||(t.add(l),e.push({id:y(l).slice(0,24),mode:n,text:p,source:"manual"}))}return e}function Pt(r){let e=$(r);return fe(e)||(yt(kt(e),{recursive:!0}),jt(e,wt,{encoding:"utf8",mode:384})),e}function _(r,e=!1){let t=e?Pt(r):$(r);if(!fe(t))return null;if(de(t).size>me)throw new Error(`PROJECT.md exceeds ${me} bytes`);let o=St(t,"utf8");return{path:t,content:o,digest:y(o),rules:xt(o),bytes:Buffer.byteLength(o,"utf8"),updatedAt:new Date(de(t).mtimeMs).toISOString()}}import{mkdirSync as It}from"node:fs";import{join as F}from"node:path";import{join as ge}from"node:path";import{mkdirSync as At}from"node:fs";function Tt(r){return r.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}]+/gu," ").replace(/\s+/g," ").trim()}function w(r,e=20){let t=[],s=new Set;for(let o of r.slice().reverse()){let n=Tt(o);if(!(!n||s.has(n))&&(s.add(n),t.push(o),t.length>=e))break}return t.reverse()}function Rt(r){return r.kind==="phase"?/^Phase\s+\d+$/iu.test(r.text):r.kind==="task"?/^(?:TODO|Task|Việc)\s+\d+$/iu.test(r.text):!1}function he(r,e){let t=e.status??r?.status??"pending",s=t;r&&(r.status==="completed"&&t!=="completed"?s="completed":t==="pending"&&(r.status==="in_progress"||r.status==="blocked")&&(s=r.status));let o=r&&Rt(e)?r.title:e.text;return{id:r?.id??y(e.key).slice(0,24),title:o,status:s,order:e.order??r?.order,confidence:Math.max(e.confidence,r?.confidence??0),updatedAt:e.occurredAt,updatedBy:{agent:e.agent,nativeSessionId:e.nativeSessionId,eventId:e.eventId}}}async function Et(r,e){let t=`projects/${r.id}/work/observations/`,s=await e.list(t),o=[];for(let n of s.filter(p=>p.key.endsWith(".json")).sort((p,l)=>p.key.localeCompare(l.key))){let p=await e.getText(n.key);if(p)try{let l=JSON.parse(p);l.version===1&&Array.isArray(l.observations)&&o.push(l)}catch{}}return o}async function ye(r,e){let s=(await Et(r,e)).flatMap(i=>i.observations).sort((i,k)=>{let G=i.occurredAt.localeCompare(k.occurredAt);if(G!==0)return G;let Q=i.sequence-k.sequence;return Q!==0?Q:i.id.localeCompare(k.id)}),o=new Map,n=new Map,p,l,c,d=[],f=[],a=[],g=[],u=[],h=[];for(let i of s)switch(i.kind){case"goal":p=i.text;break;case"plan":l=i.text;break;case"phase":o.set(i.key,he(o.get(i.key),i));break;case"task":n.set(i.key,he(n.get(i.key),i));break;case"decision":d.push(i.text);break;case"blocker":f.push(i.text);break;case"warning":a.push(i.text);break;case"next_action":g.push(i.text);break;case"file":u.push(i.text);break;case"test":h.push(i.text);break;case"session":c={agent:i.agent,nativeSessionId:i.nativeSessionId,sessionKey:i.sessionKey,updatedAt:i.occurredAt};break}let j=Array.from(o.values()).sort((i,k)=>(i.order??Number.MAX_SAFE_INTEGER)-(k.order??Number.MAX_SAFE_INTEGER)),S=Array.from(n.values()).sort((i,k)=>(i.order??Number.MAX_SAFE_INTEGER)-(k.order??Number.MAX_SAFE_INTEGER)),L=j.find(i=>i.status==="in_progress")??j.find(i=>i.status==="blocked")??j.find(i=>i.status==="pending"),E=S.find(i=>i.status==="in_progress")??S.find(i=>i.status==="blocked")??S.find(i=>i.status==="pending"),je=w([...g,...E?[E.title]:[],...!E&&L?[L.title]:[],...S.filter(i=>i.status==="pending").slice(0,5).map(i=>i.title)],8),ke=w([...f,...j.filter(i=>i.status==="blocked").map(i=>i.title),...S.filter(i=>i.status==="blocked").map(i=>i.title)],20),V={version:1,projectId:r.id,projectName:r.name,goal:p,plan:l,phases:j,tasks:S,decisions:w(d,20),blockers:ke,warnings:w(a,20),nextActions:je,filesTouched:w(u,30),tests:w(h,20),currentPhase:L,currentTask:E,progress:{phasesTotal:j.length,phasesCompleted:j.filter(i=>i.status==="completed").length,tasksTotal:S.length,tasksCompleted:S.filter(i=>i.status==="completed").length,blocked:j.filter(i=>i.status==="blocked").length+S.filter(i=>i.status==="blocked").length},lastSession:c,updatedAt:s.length?s[s.length-1].occurredAt:new Date().toISOString()},Y=ge(r.rootPath,".toolnet","work");return At(Y,{recursive:!0}),v(ge(Y,"current.json"),V),await e.put(`projects/${r.id}/work/current.json`,JSON.stringify(V,null,2)+`
49
- `,"application/json"),V}async function K(r,e){let t=await e.getText(`projects/${r.id}/work/current.json`);if(!t)return null;try{return JSON.parse(t)}catch{return null}}function Ot(r){return{goal:r.goal,plan:r.plan,phases:r.phases.map(e=>({id:e.id,title:e.title,status:e.status,order:e.order})),tasks:r.tasks.map(e=>({id:e.id,title:e.title,status:e.status,order:e.order})),decisions:r.decisions,blockers:r.blockers,warnings:r.warnings,nextActions:r.nextActions,filesTouched:r.filesTouched,tests:r.tests}}function Ct(r){return r.phases.some(e=>e.status==="pending"||e.status==="in_progress"||e.status==="blocked")||r.tasks.some(e=>e.status==="pending"||e.status==="in_progress"||e.status==="blocked")||r.nextActions.length>0||r.blockers.length>0}var W=class{constructor(e){this.options=e}options;async capture(e,t){let s=await K(this.options.project,this.options.storage);if(s||(s=await ye(this.options.project,this.options.storage)),!Ct(s))return null;let o=_(this.options.project,!1),n=o?o.rules.filter(g=>g.mode==="enforce").map(g=>g.text):[],p=y(JSON.stringify(Ot(s))),l=y([this.options.project.id,this.options.identity.sessionKey,p].join("|")).slice(0,24),c=new Date().toISOString(),d={version:1,id:l,projectId:this.options.project.id,projectName:this.options.project.name,createdAt:c,reason:e,sourceSession:{agent:this.options.identity.agent,nativeSessionId:this.options.identity.nativeSessionId,sessionKey:this.options.identity.sessionKey,sequence:t},goal:s.goal,plan:s.plan,progress:s.progress,currentPhase:s.currentPhase,currentTask:s.currentTask,incompletePhases:s.phases.filter(g=>g.status!=="completed"&&g.status!=="cancelled"),incompleteTasks:s.tasks.filter(g=>g.status!=="completed"&&g.status!=="cancelled"),nextActions:s.nextActions.slice(0,10),blockers:s.blockers.slice(0,10),decisions:s.decisions.slice(-10),warnings:s.warnings.slice(-10),attention:[...n,...s.warnings].slice(0,20),filesTouched:s.filesTouched.slice(-20),tests:s.tests.slice(-15),stateDigest:p},f=`projects/${this.options.project.id}/work/handoffs/${l}.json`;await this.options.storage.exists(f)||await this.options.storage.put(f,JSON.stringify(d,null,2)+`
50
- `,"application/json"),await this.options.storage.put(`projects/${this.options.project.id}/work/handoff-latest.json`,JSON.stringify(d,null,2)+`
51
- `,"application/json");let a=F(this.options.project.rootPath,".toolnet","work","handoffs");return It(a,{recursive:!0}),v(F(a,`${l}.json`),d),v(F(this.options.project.rootPath,".toolnet","work","handoff-latest.json"),d),d}};async function D(r,e){let t=await e.getText(`projects/${r.id}/work/handoff-latest.json`);if(!t)return null;try{return JSON.parse(t)}catch{return null}}async function Se(r,e){let t=await e.getText(`projects/${r.id}/work/semantic/current.json`);if(!t)return null;try{return JSON.parse(t)}catch{return null}}function U(r){if(!r)return 0;let e=Array.from(r).length,t=r.trim().split(/\s+/u).filter(Boolean).length;return Math.ceil(Math.max(e/3.5,t*1.3))}function m(r,e){let t=r.replace(/\s+/g," ").trim();return t.length<=e?t:t.slice(0,Math.max(0,e-1)).trimEnd()+"\u2026"}function Mt(r){let e=[],t=!1;for(let s of r.split(/\r?\n/u)){let o=s.trim();if(o.includes("<!--")&&(t=!0),t){o.includes("-->")&&(t=!1);continue}let n=o.toLowerCase();if(!(!o||o.startsWith("#")||o==="```"||n.startsWith("- [enforce]")||n.startsWith("* [enforce]")||n.startsWith("- [advisory]")||n.startsWith("* [advisory]"))&&(o=o.replace(/^[-*]\s+/u,""),o&&e.push(m(o,280)),e.length>=16))break}return e}function Nt(r){let e=[],t=[];for(let s of r.split(/\\r?\\n/u)){let o=s.trim(),n=o.toLowerCase(),l=["- [enforce]","* [enforce]","- [advisory]","* [advisory]"].find(d=>n.startsWith(d));if(!l)continue;let c=o.slice(l.length).trim();c&&(l.includes("enforce")?e.push(c):t.push(c))}return{enforce:e,advisory:t}}function $t(r,e){let t=[];for(let s of r){let o=[...t,s].join(`
52
- `);if(U(o)<=e){t.push(s);continue}let n=U(t.join(`
53
- `)),p=Math.max(0,e-n);if(p>=16){let l=Math.floor(p*3.2),c=m(s,l);c&&t.push(c)}break}return t.join(`
54
- `).trim()}async function H(r){let e=Math.max(256,Math.min(2e3,r.maxTokens??1e3)),t=_(r.project,!1),s=t?.content??"";s||(s=await r.storage.getText(`projects/${r.project.id}/project/manual.md`)??"");let o=Nt(s),n=t?t.rules.filter(u=>u.mode==="enforce").map(u=>u.text):o.enforce,p=t?t.rules.filter(u=>u.mode==="advisory").map(u=>u.text):o.advisory,l=s?Mt(s):[],c=await K(r.project,r.storage),d=await Se(r.project,r.storage),f=await D(r.project,r.storage),a=[];if(a.push("[TOOLNET PROJECT CONTEXT]"),a.push(`Project: ${r.project.name}`),a.push("Continue existing project state. Do not restart completed work unless evidence shows it is necessary."),s&&a.push(`Full operating manual: ${$(r.project)}`),n.length){a.push("","PROJECT RULES \u2014 MUST FOLLOW");for(let u of n.slice(0,24))a.push(`- [ENFORCE] ${m(u,240)}`)}if(p.length){a.push("","PROJECT PREFERENCES");for(let u of p.slice(0,10))a.push(`- ${m(u,220)}`)}if(d&&(d.mission&&a.push("","MISSION",m(d.mission.value,420)),d.activeObjective&&a.push("","CURRENT OBJECTIVE",m(d.activeObjective.value,420)),d.why&&a.push("","WHY THIS WORK MATTERS",m(d.why.value,420)),d.desiredOutcome&&a.push("","DESIRED OUTCOME",m(d.desiredOutcome.value,420)),d.planRationale&&a.push("","WHY THIS APPROACH",m(d.planRationale.value,420))),c){if(a.push("","ACTIVE WORK"),c.goal&&a.push(`Goal: ${m(c.goal,320)}`),c.plan&&a.push(`Plan: ${m(c.plan,320)}`),a.push(`Progress: phases ${c.progress.phasesCompleted}/${c.progress.phasesTotal}; tasks ${c.progress.tasksCompleted}/${c.progress.tasksTotal}; blocked ${c.progress.blocked}`),c.currentPhase&&a.push(`Current phase: ${c.currentPhase.title} [${c.currentPhase.status}]`),c.currentPhase&&d){let u=d.phases.find(h=>h.order===c.currentPhase?.order);u&&(u.objective&&a.push(`Phase objective: ${m(u.objective.value,340)}`),u.why?a.push(`Why this phase: ${m(u.why.value,340)}`):a.push("Why this phase: not explicitly recorded. Inspect existing implementation before assuming intent."),u.deliverable&&a.push(`Deliverable: ${m(u.deliverable.value,340)}`),u.dependencies.length&&a.push(`Depends on: ${u.dependencies.slice(0,4).map(h=>m(h.value,180)).join("; ")}`),u.acceptanceCriteria.length&&(a.push("","DEFINITION OF DONE"),u.acceptanceCriteria.slice(0,6).forEach(h=>{a.push(`- ${m(h.value,260)}`)})),u.openQuestions.length&&(a.push("","OPEN QUESTIONS FOR CURRENT PHASE"),u.openQuestions.slice(0,4).forEach(h=>{a.push(`- ${m(h.value,260)}`)})))}c.currentTask&&a.push(`Current task: ${c.currentTask.title} [${c.currentTask.status}]`),c.nextActions.length&&(a.push("","NEXT ACTIONS"),c.nextActions.slice(0,6).forEach((u,h)=>{a.push(`${h+1}. ${m(u,260)}`)})),c.blockers.length&&(a.push("","BLOCKERS"),c.blockers.slice(0,5).forEach(u=>{a.push(`- ${m(u,260)}`)})),c.warnings.length&&(a.push("","ATTENTION"),c.warnings.slice(-5).forEach(u=>{a.push(`- ${m(u,260)}`)})),c.decisions.length&&(a.push("","RECENT DECISIONS"),c.decisions.slice(-5).forEach(u=>{a.push(`- ${m(u,260)}`)})),c.lastSession&&a.push("",`Last work session: ${c.lastSession.agent} / ${c.lastSession.nativeSessionId}`)}if(d&&d.openQuestions.length&&(a.push("","UNRESOLVED QUESTIONS"),d.openQuestions.slice(0,5).forEach(u=>{a.push(`- ${m(u.value,260)}`)})),f&&a.push(`Latest handoff: ${f.reason} / ${f.sourceSession.agent}`),l.length){a.push("","OPERATING NOTES");for(let u of l)a.push(`- ${u}`)}a.push("","Before changing anything: verify the current repository state and continue from the active phase/task above.");let g=$t(a,e);return{version:1,projectId:r.project.id,projectName:r.project.name,text:g,estimatedTokens:U(g),maxTokens:e,hasManual:!!s,hasWorkState:!!c,hasHandoff:!!f,generatedAt:new Date().toISOString()}}function R(r,e){let t=r.indexOf(e);return t>=0?r[t+1]:void 0}function _t(r){let e=q(),t=ae(ne({provider:e.storage.provider,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3});return new N(t,r.id,r.name,r.remote??r.name)}async function Kt(){let[r="help",...e]=process.argv.slice(2),t=new O().detect(R(e,"--project")??process.cwd()),s=_t(t);if(r==="brief"){let o=Number(R(e,"--tokens")??1e3),n=await H({project:t,storage:s,maxTokens:Number.isFinite(o)?o:1e3});process.stdout.write(n.text+`
55
- `);return}if(r==="brief-json"){let o=await H({project:t,storage:s,maxTokens:Number(R(e,"--tokens")??1e3)});console.log(JSON.stringify(o,null,2));return}if(r==="handoff-latest"){let o=await D(t,s);console.log(JSON.stringify(o,null,2));return}if(r==="handoff-create"){let o=R(e,"--agent")??"manual";if(o==="manual")throw new Error("--agent must be opencode, agy, or codex");let n=R(e,"--session");if(!n)throw new Error("--session is required");let p=le(t,o,n),c=await new W({project:t,storage:s,identity:p}).capture("manual",0);console.log(JSON.stringify(c,null,2));return}console.log(`Work Context
48
+ `;function K(r){return xt(r.rootPath,".toolnet","PROJECT.md")}function At(r){return r.normalize("NFKC").replace(/\s+/g," ").trim()}function Tt(r){let e=[],t=new Set,s=/^\s*[-*]\s+\[(enforce|advisory)\]\s+(.+?)\s*$/gimu,n;for(;n=s.exec(r);){let o=n[1].toLowerCase(),u=At(n[2]);if(!u)continue;let l=`${o}:${u.toLowerCase()}`;t.has(l)||(t.add(l),e.push({id:S(l).slice(0,24),mode:o,text:u,source:"manual"}))}return e}function It(r){let e=K(r);return ge(e)||(jt(bt(e),{recursive:!0}),wt(e,Pt,{encoding:"utf8",mode:384})),e}function D(r,e=!1){let t=e?It(r):K(r);if(!ge(t))return null;if(me(t).size>fe)throw new Error(`PROJECT.md exceeds ${fe} bytes`);let n=vt(t,"utf8");return{path:t,content:n,digest:S(n),rules:Tt(n),bytes:Buffer.byteLength(n,"utf8"),updatedAt:new Date(me(t).mtimeMs).toISOString()}}import{mkdirSync as $t}from"node:fs";import{join as J}from"node:path";import{join as ye}from"node:path";import{mkdirSync as Mt}from"node:fs";function Rt(r){return r.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}]+/gu," ").replace(/\s+/g," ").trim()}function x(r,e=20){let t=[],s=new Set;for(let n of r.slice().reverse()){let o=Rt(n);if(!(!o||s.has(o))&&(s.add(o),t.push(n),t.length>=e))break}return t.reverse()}function Ct(r){return r.kind==="phase"?/^Phase\s+\d+$/iu.test(r.text):r.kind==="task"?/^(?:TODO|Task|Việc)\s+\d+$/iu.test(r.text):!1}function he(r,e){let t=e.status??r?.status??"pending",s=t;r&&(r.status==="completed"&&t!=="completed"?s="completed":t==="pending"&&(r.status==="in_progress"||r.status==="blocked")&&(s=r.status));let n=r&&Ct(e)?r.title:e.text;return{id:r?.id??S(e.key).slice(0,24),title:n,status:s,order:e.order??r?.order,confidence:Math.max(e.confidence,r?.confidence??0),updatedAt:e.occurredAt,updatedBy:{agent:e.agent,nativeSessionId:e.nativeSessionId,eventId:e.eventId}}}async function Et(r,e){let t=`projects/${r.id}/work/observations/`,s=await e.list(t),n=[];for(let o of s.filter(u=>u.key.endsWith(".json")).sort((u,l)=>u.key.localeCompare(l.key))){let u=await e.getText(o.key);if(u)try{let l=JSON.parse(u);l.version===1&&Array.isArray(l.observations)&&n.push(l)}catch{}}return n}async function Se(r,e){let s=(await Et(r,e)).flatMap(a=>a.observations).sort((a,w)=>{let Q=a.occurredAt.localeCompare(w.occurredAt);if(Q!==0)return Q;let q=a.sequence-w.sequence;return q!==0?q:a.id.localeCompare(w.id)}),n=new Map,o=new Map,u,l,c,d=[],y=[],i=[],j=[],p=[],m=[];for(let a of s)switch(a.kind){case"goal":u=a.text;break;case"plan":l=a.text;break;case"phase":n.set(a.key,he(n.get(a.key),a));break;case"task":o.set(a.key,he(o.get(a.key),a));break;case"decision":d.push(a.text);break;case"blocker":y.push(a.text);break;case"warning":i.push(a.text);break;case"next_action":j.push(a.text);break;case"file":p.push(a.text);break;case"test":m.push(a.text);break;case"session":c={agent:a.agent,nativeSessionId:a.nativeSessionId,sessionKey:a.sessionKey,updatedAt:a.occurredAt};break}let v=Array.from(n.values()).sort((a,w)=>(a.order??Number.MAX_SAFE_INTEGER)-(w.order??Number.MAX_SAFE_INTEGER)),h=Array.from(o.values()).sort((a,w)=>(a.order??Number.MAX_SAFE_INTEGER)-(w.order??Number.MAX_SAFE_INTEGER)),g=v.find(a=>a.status==="in_progress")??v.find(a=>a.status==="blocked")??v.find(a=>a.status==="pending"),C=h.find(a=>a.status==="in_progress")??h.find(a=>a.status==="blocked")??h.find(a=>a.status==="pending"),we=x([...j,...C?[C.title]:[],...!C&&g?[g.title]:[],...h.filter(a=>a.status==="pending").slice(0,5).map(a=>a.title)],8),be=x([...y,...v.filter(a=>a.status==="blocked").map(a=>a.title),...h.filter(a=>a.status==="blocked").map(a=>a.title)],20),B={version:1,projectId:r.id,projectName:r.name,goal:u,plan:l,phases:v,tasks:h,decisions:x(d,20),blockers:be,warnings:x(i,20),nextActions:we,filesTouched:x(p,30),tests:x(m,20),currentPhase:g,currentTask:C,progress:{phasesTotal:v.length,phasesCompleted:v.filter(a=>a.status==="completed").length,tasksTotal:h.length,tasksCompleted:h.filter(a=>a.status==="completed").length,blocked:v.filter(a=>a.status==="blocked").length+h.filter(a=>a.status==="blocked").length},lastSession:c,updatedAt:s.length?s[s.length-1].occurredAt:new Date().toISOString()},G=ye(r.rootPath,".toolnet","work");return Mt(G,{recursive:!0}),b(ye(G,"current.json"),B),await e.put(`projects/${r.id}/work/current.json`,JSON.stringify(B,null,2)+`
49
+ `,"application/json"),B}async function W(r,e){let t=await e.getText(`projects/${r.id}/work/current.json`);if(!t)return null;try{return JSON.parse(t)}catch{return null}}function k(r,e){let t=new Set,s=[];for(let n of r){let o=n.replace(/\s+/g," ").trim();if(!o)continue;let u=o.normalize("NFKC").toLowerCase();if(!t.has(u)&&(t.add(u),s.push(o),s.length>=e))break}return s}function ke(r){if(r)return{id:r.id,title:r.title,status:r.status}}function Ot(r){let e=r.slice(-10).join(`
50
+ `).toLowerCase();return/(?:failed|failing|failure|error|✗|❌)/u.test(e)?"failing":/(?:passed|passing|green|success|✓|✅)/u.test(e)?"passing":"unknown"}function Nt(r){return S(JSON.stringify(r))}function je(r){let{project:e,identity:t,state:s}=r,n=s.filesTouched.at(-1),o=s.phases.filter(g=>g.status==="completed").map(g=>g.title),u=s.tasks.filter(g=>g.status==="completed").map(g=>g.title),l=s.phases.filter(g=>g.status!=="completed"&&g.status!=="cancelled").map(g=>g.title),c=s.tasks.filter(g=>g.status!=="completed"&&g.status!=="cancelled").map(g=>g.title),d=k(s.nextActions,10),y=k([...c,...d],15),i=k(s.tests.slice().reverse(),10),j=k(s.filesTouched.slice().reverse(),20),p={schema:"toolnet.handoff.v2",version:2,project:{id:e.id,name:e.name},source:{agent:t.agent,nativeSessionId:t.nativeSessionId,sessionKey:t.sessionKey,sequence:r.sequence,reason:r.reason},capturedAt:r.capturedAt??new Date().toISOString(),goal:s.goal,current:{phase:ke(s.currentPhase),task:ke(s.currentTask),file:n},completed:{phases:k(o,20),tasks:k(u,30)},remaining:{phases:k(l,20),tasks:k(c,30),todos:y},nextAction:d[0],blockers:k(s.blockers.slice().reverse(),10),decisions:k(s.decisions.slice().reverse(),10),files:{current:n,recent:j},tests:{status:Ot(s.tests),recent:i},attention:k(r.attention??[],20),progress:s.progress},{capturedAt:m,source:v,...h}=p;return{...p,stateDigest:Nt(h)}}function _t(r){return r.phases.some(e=>e.status==="pending"||e.status==="in_progress"||e.status==="blocked")||r.tasks.some(e=>e.status==="pending"||e.status==="in_progress"||e.status==="blocked")||r.nextActions.length>0||r.blockers.length>0}var L=class{constructor(e){this.options=e}options;async capture(e,t){let s=await W(this.options.project,this.options.storage);if(s||(s=await Se(this.options.project,this.options.storage)),!_t(s))return null;let n=D(this.options.project,!1),u=[...n?n.rules.filter(m=>m.mode==="enforce").map(m=>m.text):[],...s.warnings].slice(0,20),l=je({project:this.options.project,identity:this.options.identity,state:s,reason:e,sequence:t,attention:u}),c=l.stateDigest,d=S([this.options.project.id,this.options.identity.sessionKey,c].join("|")).slice(0,24),y=new Date().toISOString(),i={version:1,id:d,projectId:this.options.project.id,projectName:this.options.project.name,createdAt:y,reason:e,sourceSession:{agent:this.options.identity.agent,nativeSessionId:this.options.identity.nativeSessionId,sessionKey:this.options.identity.sessionKey,sequence:t},goal:s.goal,plan:s.plan,progress:s.progress,currentPhase:s.currentPhase,currentTask:s.currentTask,incompletePhases:s.phases.filter(m=>m.status!=="completed"&&m.status!=="cancelled"),incompleteTasks:s.tasks.filter(m=>m.status!=="completed"&&m.status!=="cancelled"),nextActions:s.nextActions.slice(0,10),blockers:s.blockers.slice(0,10),decisions:s.decisions.slice(-10),warnings:s.warnings.slice(-10),attention:u,filesTouched:s.filesTouched.slice(-20),tests:s.tests.slice(-15),stateDigest:c,continuity:l},j=`projects/${this.options.project.id}/work/handoffs/${d}.json`;await this.options.storage.exists(j)||await this.options.storage.put(j,JSON.stringify(i,null,2)+`
51
+ `,"application/json"),await this.options.storage.put(`projects/${this.options.project.id}/work/handoff-latest.json`,JSON.stringify(i,null,2)+`
52
+ `,"application/json");let p=J(this.options.project.rootPath,".toolnet","work","handoffs");return $t(p,{recursive:!0}),b(J(p,`${d}.json`),i),b(J(this.options.project.rootPath,".toolnet","work","handoff-latest.json"),i),i}};async function V(r,e){let t=await e.getText(`projects/${r.id}/work/handoff-latest.json`);if(!t)return null;try{return JSON.parse(t)}catch{return null}}async function ve(r,e){let t=await e.getText(`projects/${r.id}/work/semantic/current.json`);if(!t)return null;try{return JSON.parse(t)}catch{return null}}function U(r){if(!r)return 0;let e=Array.from(r).length,t=r.trim().split(/\s+/u).filter(Boolean).length;return Math.ceil(Math.max(e/3.5,t*1.3))}function f(r,e){let t=r.replace(/\s+/g," ").trim();return t.length<=e?t:t.slice(0,Math.max(0,e-1)).trimEnd()+"\u2026"}function Kt(r){let e=[],t=!1;for(let s of r.split(/\r?\n/u)){let n=s.trim();if(n.includes("<!--")&&(t=!0),t){n.includes("-->")&&(t=!1);continue}let o=n.toLowerCase();if(!(!n||n.startsWith("#")||n==="```"||o.startsWith("- [enforce]")||o.startsWith("* [enforce]")||o.startsWith("- [advisory]")||o.startsWith("* [advisory]"))&&(n=n.replace(/^[-*]\s+/u,""),n&&e.push(f(n,280)),e.length>=16))break}return e}function Dt(r){let e=[],t=[];for(let s of r.split(/\\r?\\n/u)){let n=s.trim(),o=n.toLowerCase(),l=["- [enforce]","* [enforce]","- [advisory]","* [advisory]"].find(d=>o.startsWith(d));if(!l)continue;let c=n.slice(l.length).trim();c&&(l.includes("enforce")?e.push(c):t.push(c))}return{enforce:e,advisory:t}}function Wt(r,e){let t=[];for(let s of r){let n=[...t,s].join(`
53
+ `);if(U(n)<=e){t.push(s);continue}let o=U(t.join(`
54
+ `)),u=Math.max(0,e-o);if(u>=16){let l=Math.floor(u*3.2),c=f(s,l);c&&t.push(c)}break}return t.join(`
55
+ `).trim()}async function Y(r){let e=Math.max(256,Math.min(2e3,r.maxTokens??1e3)),t=D(r.project,!1),s=t?.content??"";s||(s=await r.storage.getText(`projects/${r.project.id}/project/manual.md`)??"");let n=Dt(s),o=t?t.rules.filter(p=>p.mode==="enforce").map(p=>p.text):n.enforce,u=t?t.rules.filter(p=>p.mode==="advisory").map(p=>p.text):n.advisory,l=s?Kt(s):[],c=await W(r.project,r.storage),d=await ve(r.project,r.storage),y=await V(r.project,r.storage),i=[];if(i.push("[TOOLNET PROJECT CONTEXT]"),i.push(`Project: ${r.project.name}`),i.push("Continue existing project state. Do not restart completed work unless evidence shows it is necessary."),s&&i.push(`Full operating manual: ${K(r.project)}`),o.length){i.push("","PROJECT RULES \u2014 MUST FOLLOW");for(let p of o.slice(0,24))i.push(`- [ENFORCE] ${f(p,240)}`)}if(u.length){i.push("","PROJECT PREFERENCES");for(let p of u.slice(0,10))i.push(`- ${f(p,220)}`)}if(d&&(d.mission&&i.push("","MISSION",f(d.mission.value,420)),d.activeObjective&&i.push("","CURRENT OBJECTIVE",f(d.activeObjective.value,420)),d.why&&i.push("","WHY THIS WORK MATTERS",f(d.why.value,420)),d.desiredOutcome&&i.push("","DESIRED OUTCOME",f(d.desiredOutcome.value,420)),d.planRationale&&i.push("","WHY THIS APPROACH",f(d.planRationale.value,420))),c){if(i.push("","ACTIVE WORK"),c.goal&&i.push(`Goal: ${f(c.goal,320)}`),c.plan&&i.push(`Plan: ${f(c.plan,320)}`),i.push(`Progress: phases ${c.progress.phasesCompleted}/${c.progress.phasesTotal}; tasks ${c.progress.tasksCompleted}/${c.progress.tasksTotal}; blocked ${c.progress.blocked}`),c.currentPhase&&i.push(`Current phase: ${c.currentPhase.title} [${c.currentPhase.status}]`),c.currentPhase&&d){let p=d.phases.find(m=>m.order===c.currentPhase?.order);p&&(p.objective&&i.push(`Phase objective: ${f(p.objective.value,340)}`),p.why?i.push(`Why this phase: ${f(p.why.value,340)}`):i.push("Why this phase: not explicitly recorded. Inspect existing implementation before assuming intent."),p.deliverable&&i.push(`Deliverable: ${f(p.deliverable.value,340)}`),p.dependencies.length&&i.push(`Depends on: ${p.dependencies.slice(0,4).map(m=>f(m.value,180)).join("; ")}`),p.acceptanceCriteria.length&&(i.push("","DEFINITION OF DONE"),p.acceptanceCriteria.slice(0,6).forEach(m=>{i.push(`- ${f(m.value,260)}`)})),p.openQuestions.length&&(i.push("","OPEN QUESTIONS FOR CURRENT PHASE"),p.openQuestions.slice(0,4).forEach(m=>{i.push(`- ${f(m.value,260)}`)})))}c.currentTask&&i.push(`Current task: ${c.currentTask.title} [${c.currentTask.status}]`),c.nextActions.length&&(i.push("","NEXT ACTIONS"),c.nextActions.slice(0,6).forEach((p,m)=>{i.push(`${m+1}. ${f(p,260)}`)})),c.blockers.length&&(i.push("","BLOCKERS"),c.blockers.slice(0,5).forEach(p=>{i.push(`- ${f(p,260)}`)})),c.warnings.length&&(i.push("","ATTENTION"),c.warnings.slice(-5).forEach(p=>{i.push(`- ${f(p,260)}`)})),c.decisions.length&&(i.push("","RECENT DECISIONS"),c.decisions.slice(-5).forEach(p=>{i.push(`- ${f(p,260)}`)})),c.lastSession&&i.push("",`Last work session: ${c.lastSession.agent} / ${c.lastSession.nativeSessionId}`)}if(d&&d.openQuestions.length&&(i.push("","UNRESOLVED QUESTIONS"),d.openQuestions.slice(0,5).forEach(p=>{i.push(`- ${f(p.value,260)}`)})),y&&i.push(`Latest handoff: ${y.reason} / ${y.sourceSession.agent}`),l.length){i.push("","OPERATING NOTES");for(let p of l)i.push(`- ${p}`)}i.push("","Before changing anything: verify the current repository state and continue from the active phase/task above.");let j=Wt(i,e);return{version:1,projectId:r.project.id,projectName:r.project.name,text:j,estimatedTokens:U(j),maxTokens:e,hasManual:!!s,hasWorkState:!!c,hasHandoff:!!y,generatedAt:new Date().toISOString()}}function R(r,e){let t=r.indexOf(e);return t>=0?r[t+1]:void 0}function Lt(r){let e=Z(),t=ce(ie({provider:e.storage.provider,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3});return new _(t,r.id,r.name,r.remote??r.name)}async function Vt(){let[r="help",...e]=process.argv.slice(2),t=new O().detect(R(e,"--project")??process.cwd()),s=Lt(t);if(r==="brief"){let n=Number(R(e,"--tokens")??1e3),o=await Y({project:t,storage:s,maxTokens:Number.isFinite(n)?n:1e3});process.stdout.write(o.text+`
56
+ `);return}if(r==="brief-json"){let n=await Y({project:t,storage:s,maxTokens:Number(R(e,"--tokens")??1e3)});console.log(JSON.stringify(n,null,2));return}if(r==="handoff-latest"){let n=await V(t,s);console.log(JSON.stringify(n,null,2));return}if(r==="handoff-create"){let n=R(e,"--agent")??"manual";if(n==="manual")throw new Error("--agent must be opencode, agy, or codex");let o=R(e,"--session");if(!o)throw new Error("--session is required");let u=de(t,n,o),c=await new L({project:t,storage:s,identity:u}).capture("manual",0);console.log(JSON.stringify(c,null,2));return}console.log(`Work Context
56
57
 
57
58
  Commands:
58
59
  brief [--project PATH] [--tokens N]
59
60
  brief-json [--project PATH] [--tokens N]
60
61
  handoff-latest [--project PATH]
61
62
  handoff-create --agent <opencode|agy|codex> --session <id>
62
- `)}Kt().catch(r=>{console.error(r instanceof Error?r.message:r),process.exit(1)});
63
+ `)}Vt().catch(r=>{console.error(r instanceof Error?r.message:r),process.exit(1)});