toolnet-memory 0.3.3 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,120 +1,27 @@
1
1
  #!/usr/bin/env node
2
- import*as $ from"fs";import*as Oe from"path";import*as h from"fs";import*as m from"path";import*as se from"crypto";function k(r){return r?Math.ceil(r.length/3.5):0}function D(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("."),a=o.lastIndexOf(`
3
- `),p=Math.max(s,a);return p>n*.7?o.slice(0,p+1):o}function z(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 Me(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,l)=>l.priority-i.priority),s=[],a=0,p=!1;for(let i of o){let l=`# ${i.title}
2
+ import*as N from"fs";import*as he from"path";import*as y from"fs";import*as f from"path";import*as U 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 V(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 ye(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,u)=>u.priority-i.priority),s=[],c=0,l=!1;for(let i of n){let u=`# ${i.title}
6
6
 
7
- `,f=k(l),c=k(i.content),S=f+c;if(a+S<=e)s.push(l+i.content),a+=S;else{let u=e-a-f;if(u>50){let g=D(i.content,u);s.push(l+g),a=e,p=!0}else p=!0;break}}return p&&s.push(`
8
- ${n}
7
+ `,m=j(u),a=j(i.content),g=m+a;if(c+g<=e)s.push(u+i.content),c+=g;else{let p=e-c-m;if(p>50){let h=$(i.content,p);s.push(u+h),c=e,l=!0}else l=!0;break}}return l&&s.push(`
8
+ ${o}
9
9
  `),s.join(`
10
10
 
11
11
  ---
12
12
 
13
- `)}function G(r,t){let e=[{title:"Profile",content:z(r,10),priority:100},{title:"Current Work",content:z(t,15),priority:90}];return Me(e,{maxTokens:800})}import{chmodSync as cr,existsSync as Ie,mkdirSync as ur,readFileSync as Re,renameSync as lr,writeFileSync as pr}from"node:fs";import{dirname as fr,join as Y}from"node:path";var Q="toolnet.context-offload.v1";function Ne(r){return Y(r,".toolnet","offload")}function $e(r){return Y(Ne(r),"graph.json")}function K(){return{schema:Q,version:1,updatedAt:new Date(0).toISOString(),nodes:[]}}function _e(r){let t=$e(r);if(!Ie(t))return K();try{let e=JSON.parse(Re(t,"utf8"));return e.schema!==Q||e.version!==1||!Array.isArray(e.nodes)?K():e}catch{return K()}}function U(r,t){let e=r.replace(/\s+/gu," ").trim();return e.length<=t?e:e.slice(0,t-1).trimEnd()+"\u2026"}function q(r,t={}){let e=_e(r);if(e.nodes.length===0)return"";let n=Math.max(1,Math.min(12,t.maxAssets??6)),o=Math.max(320,Math.min(2400,t.maxChars??900)),s=["[TOOLNET CONTEXT OFFLOAD GRAPH]","Large tool/file payloads stay outside prompt context.","Read only a needed asset with MCP context_offload_read."];for(let a of e.nodes.slice(-n).reverse()){let p=U(a.sourceRefs.at(-1)??"unknown",72),i=a.files.length>0?` files=${U(a.files.join(","),120)}`:"",l=`event:${p} --offloads--> asset:${a.id.slice(0,12)} kind=${a.kind} bytes=${a.bytes}${i}`;if([...s,l].join(`
14
- `).length>o)break;s.push(l)}return s.join(`
15
- `)}import{existsSync as X,mkdirSync as jr,readFileSync as Z,renameSync as xr,writeFileSync as wr}from"node:fs";import{dirname as Pr,join as ee}from"node:path";import{createHash as De}from"node:crypto";import{dirname as Ke}from"node:path";import{mkdirSync as Fe,readFileSync as Sr,renameSync as We,writeFileSync as Le}from"node:fs";function y(r){return De("sha256").update(r).digest("hex")}function v(r,t){Fe(Ke(r),{recursive:!0});let e=`${r}.${process.pid}.tmp`;Le(e,JSON.stringify(t,null,2)+`
16
- `,{encoding:"utf8",mode:384}),We(e,r)}function Ve(r){return ee(r.rootPath,".toolnet","context","handoff.md")}function Be(r){return ee(r.rootPath,".toolnet","context","handoff.json")}function He(r){let t=Ve(r);if(!X(t))return null;try{let e=Z(t,"utf8").trim();if(!e)return null;let n=new Date(0).toISOString(),o=Be(r);if(X(o))try{let s=JSON.parse(Z(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 te(r,t=1800){let e=He(r);if(!e?.text)return null;let n=e.text;return n.length>t&&(n=`${n.slice(0,t)}
13
+ `)}function J(r,t){let e=[{title:"Profile",content:V(r,10),priority:100},{title:"Current Work",content:V(t,15),priority:90}];return ye(e,{maxTokens:800})}function k(r){let t=f.resolve(r),e=f.parse(t).root;for(;t!==e;){let o=f.join(t,".toolnet");if(y.existsSync(o)&&y.statSync(o).isDirectory())return t;t=f.dirname(t)}return null}function C(r){try{return y.existsSync(r)?y.readFileSync(r,"utf-8").trim():null}catch{return null}}function z(r){return r.split(`
14
+ `).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(`
15
+ `)}function _(r={}){let t=r.projectPath||process.cwd(),e=k(t);if(!e)return null;let o=f.join(e,".toolnet","project.json"),n="Unknown";try{if(y.existsSync(o)){let g=JSON.parse(y.readFileSync(o,"utf-8"));n=g.name||g.projectName||"Unknown"}}catch{}let s=f.join(e,".toolnet","profile.md"),c=f.join(e,".toolnet","current.md"),l=C(s)||"",i=C(c)||"";l=z(l),i=z(i);let u=`[TOOLNET PROJECT CONTEXT]
17
16
 
18
- [Fast handoff truncated]`),["[TOOLNET FAST HANDOFF]","",`Project: ${r.name}`,`Updated: ${e.generatedAt}`,"",n].join(`
19
- `)}var C="memory_agent_ask";function re(){return`
20
- [TOOLNET MEMORY AGENT]
21
-
22
- Tool available:
23
- - ${C}
24
-
25
- Use it automatically BEFORE guessing when:
26
-
27
- - The user asks to continue, resume, or pick up previous work.
28
- - The user says things like:
29
- - "ti\u1EBFp t\u1EE5c task l\xFAc n\xE3y"
30
- - "l\xE0m ti\u1EBFp ph\u1EA7n \u0111ang d\u1EDF"
31
- - "agent tr\u01B0\u1EDBc \u0111ang l\xE0m g\xEC?"
32
- - "d\u1EEBng \u1EDF \u0111\xE2u?"
33
- - "todo n\xE0o ch\u01B0a xong?"
34
- - "continue the previous task"
35
- - "resume the last session"
36
- - Previous-agent state, unfinished work, blockers,
37
- decisions, touched files, or next actions are unclear.
38
- - Fast startup context is not enough to safely continue.
39
-
40
- Preferred mode:
41
-
42
- - mode="local"
43
- for direct factual questions such as:
44
- current task, last file, blocker, completed TODOs.
45
-
46
- - mode="ai"
47
- for composite or ambiguous continuity questions
48
- that benefit from ToolNet Memory Agent reasoning.
49
-
50
- Do NOT call it automatically when:
51
-
52
- - Normal startup context already gives enough information.
53
- - The question is unrelated to previous project work.
54
- - The answer is obvious from current repository evidence.
55
-
56
- Rules:
57
-
58
- - Never invent previous work.
59
- - Current repository evidence overrides stale memory.
60
- - NEVER reconstruct previous work by reading ToolNet internal session files.
61
- - NEVER read/list/search .toolnet/sessions/**, session state.json,
62
- events.jsonl, or raw transcripts to discover previous-agent state.
63
- - Do not search the filesystem for the implementation/schema of
64
- memory_agent_ask. Invoke the MCP tool directly when deeper
65
- continuity is required.
66
- - Do not dump raw transcripts or full memory.
67
- - After receiving the ToolNet answer, continue the task
68
- instead of asking the user to repeat known context.
69
- - If ToolNet says information is not recorded, say so.
70
- `.trim()}function ne(){return`
71
- [TOOLNET MEMORY AGENT]
72
-
73
- Tool:
74
- - ${C}
75
-
76
- For resume/continue requests:
77
-
78
- 1. Use the injected ToolNet continuity handoff FIRST.
79
- 2. If the handoff is missing or ambiguous, invoke
80
- ${C} directly BEFORE repository/history exploration.
81
- 3. NEVER reconstruct prior work from:
82
- - .toolnet/sessions/**
83
- - state.json
84
- - events.jsonl
85
- - raw transcripts
86
- 4. NEVER search for the implementation/schema of
87
- ${C}; invoke the MCP tool directly.
88
- 5. Inspect git/source only AFTER continuity context is known.
89
-
90
- Use:
91
- - mode="local" for current task, last file, blocker or next action.
92
- - mode="ai" for ambiguous or combined continuity questions.
93
-
94
- Current repository evidence overrides stale memory.
95
- `.trim()}function j(r){let t=m.resolve(r),e=m.parse(t).root;for(;t!==e;){let n=m.join(t,".toolnet");if(h.existsSync(n)&&h.statSync(n).isDirectory())return t;t=m.dirname(t)}return null}function T(r){try{return h.existsSync(r)?h.readFileSync(r,"utf-8").trim():null}catch{return null}}function oe(r){return r.split(`
96
- `).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(`
97
- `)}function F(r={}){let t=r.projectPath||process.cwd(),e=j(t);if(!e)return null;let n=m.join(e,".toolnet","project.json"),o="Unknown";try{if(h.existsSync(n)){let _=JSON.parse(h.readFileSync(n,"utf-8"));o=_.name||_.projectName||"Unknown"}}catch{}let s=m.join(e,".toolnet","profile.md"),a=m.join(e,".toolnet","current.md"),p=T(s)||"",i=T(a)||"";p=oe(p),i=oe(i);let l=`[TOOLNET PROJECT CONTEXT]
98
-
99
- Project: ${o}
17
+ Project: ${n}
100
18
  Root: ${e}
101
19
 
102
- `,f=G(p,i),c=te({id:"",name:o,rootPath:e},1600),S=c?`
103
-
104
- ${c}
105
- `:"",u=q(e,{maxAssets:6,maxChars:900}),g=u?`
106
-
107
- ${u}
108
- `:"",Ee=`
109
-
110
- ${ne()}
111
-
20
+ `,m=J(l,i);return u+m+`
112
21
  Forbidden At Startup:
113
- - Do not run session:agy-recover, handoff:latest, or brief automatically.
114
- - Do not perform deep recovery merely because an agent starts.
115
- - If the user asks to resume previous work and fast context is insufficient,
116
- use memory_agent_ask before guessing.
117
- `;return l+f+S+g+Ee}function ie(r={}){let t=r.projectPath||process.cwd(),e=j(t);if(!e)throw new Error("Not in a ToolNet project (no .toolnet directory found)");let n=m.join(e,".toolnet","profile.md"),o=m.join(e,".toolnet","current.md"),s=T(n)||"",a=T(o)||"",p=`# AI Startup Instructions
22
+ - Do not run session:agy-recover, handoff:latest, or brief automatically
23
+ - Deep memory only when user explicitly asks
24
+ `}function H(r={}){let t=r.projectPath||process.cwd(),e=k(t);if(!e)throw new Error("Not in a ToolNet project (no .toolnet directory found)");let o=f.join(e,".toolnet","profile.md"),n=f.join(e,".toolnet","current.md"),s=C(o)||"",c=C(n)||"",l=`# AI Startup Instructions
118
25
 
119
26
  Read and follow:
120
27
  - .toolnet/profile.md
@@ -123,10 +30,7 @@ Read and follow:
123
30
  Rules:
124
31
  - Fast context first.
125
32
  - Do not run session:agy-recover, handoff:latest, or brief automatically.
126
- - Do not invent previous-session state.
127
- - Current repository evidence overrides stale memory.
128
-
129
- ${re()}
33
+ - Deep memory only when the user explicitly asks.
130
34
 
131
35
  ---
132
36
 
@@ -138,8 +42,9 @@ ${s}
138
42
 
139
43
  ## Current Work
140
44
 
141
- ${a}
142
- `,i=["GEMINI.md","AGENTS.md","CLAUDE.md"],l=[];for(let f of i){let c=m.join(e,f);try{h.writeFileSync(c,p,"utf-8"),l.push(f)}catch(S){console.error(`Failed to write ${f}:`,S)}}return l}function ae(r){return se.createHash("sha256").update(r,"utf-8").digest("hex")}import{existsSync as nn,mkdirSync as nt,readFileSync as on,writeFileSync as ot}from"node:fs";import{dirname as st,join as me}from"node:path";import{existsSync as le,mkdirSync as Je,readFileSync as ze,statSync as ce,writeFileSync as Ge}from"node:fs";import{dirname as Ue,join as Ye}from"node:path";var ue=64*1024,Qe=`# ToolNet Project Operating Manual
45
+ ${c}
46
+ `,i=["GEMINI.md","AGENTS.md","CLAUDE.md"],u=[];for(let m of i){let a=f.join(e,m);try{y.writeFileSync(a,l,"utf-8"),u.push(m)}catch(g){console.error(`Failed to write ${m}:`,g)}}return u}function Y(r){return U.createHash("sha256").update(r,"utf-8").digest("hex")}import{existsSync as dr,mkdirSync as $e,readFileSync as mr,writeFileSync as _e}from"node:fs";import{dirname as Ke,join as te}from"node:path";import{createHash as Se}from"node:crypto";import{dirname as je}from"node:path";import{mkdirSync as ke,readFileSync as zt,renameSync as ve,writeFileSync as xe}from"node:fs";function S(r){return Se("sha256").update(r).digest("hex")}function v(r,t){ke(je(r),{recursive:!0});let e=`${r}.${process.pid}.tmp`;xe(e,JSON.stringify(t,null,2)+`
47
+ `,{encoding:"utf8",mode:384}),ve(e,r)}import{existsSync as q,mkdirSync as we,readFileSync as be,statSync as G,writeFileSync as Pe}from"node:fs";import{dirname as Ae,join as Ce}from"node:path";var Q=64*1024,Te=`# ToolNet Project Operating Manual
143
48
 
144
49
  This file contains persistent instructions for AI agents working on this project.
145
50
 
@@ -182,17 +87,17 @@ Things an AI agent should always remember.
182
87
  <!--
183
88
  - [advisory] Prefer small focused files.
184
89
  -->
185
- `;function O(r){return Ye(r.rootPath,".toolnet","PROJECT.md")}function qe(r){return r.normalize("NFKC").replace(/\s+/g," ").trim()}function Xe(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(),a=qe(o[2]);if(!a)continue;let p=`${s}:${a.toLowerCase()}`;e.has(p)||(e.add(p),t.push({id:y(p).slice(0,24),mode:s,text:a,source:"manual"}))}return t}function Ze(r){let t=O(r);return le(t)||(Je(Ue(t),{recursive:!0}),Ge(t,Qe,{encoding:"utf8",mode:384})),t}function W(r,t=!1){let e=t?Ze(r):O(r);if(!le(e))return null;if(ce(e).size>ue)throw new Error(`PROJECT.md exceeds ${ue} bytes`);let o=ze(e,"utf8");return{path:e,content:o,digest:y(o),rules:Xe(o),bytes:Buffer.byteLength(o,"utf8"),updatedAt:new Date(ce(e).mtimeMs).toISOString()}}async function L(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 pe(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 de(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 V(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 et(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 tt(r){let t=[],e=[];for(let n of r.split(/\\r?\\n/u)){let o=n.trim(),s=o.toLowerCase(),p=["- [enforce]","* [enforce]","- [advisory]","* [advisory]"].find(l=>s.startsWith(l));if(!p)continue;let i=o.slice(p.length).trim();i&&(p.includes("enforce")?t.push(i):e.push(i))}return{enforce:t,advisory:e}}function rt(r,t){let e=[];for(let n of r){let o=[...e,n].join(`
186
- `);if(V(o)<=t){e.push(n);continue}let s=V(e.join(`
187
- `)),a=Math.max(0,t-s);if(a>=16){let p=Math.floor(a*3.2),i=d(n,p);i&&e.push(i)}break}return e.join(`
188
- `).trim()}async function fe(r){let t=Math.max(256,Math.min(2e3,r.maxTokens??1e3)),e=W(r.project,!1),n=e?.content??"";n||(n=await r.storage.getText(`projects/${r.project.id}/project/manual.md`)??"");let o=tt(n),s=e?e.rules.filter(u=>u.mode==="enforce").map(u=>u.text):o.enforce,a=e?e.rules.filter(u=>u.mode==="advisory").map(u=>u.text):o.advisory,p=n?et(n):[],i=await L(r.project,r.storage),l=await de(r.project,r.storage),f=await pe(r.project,r.storage),c=[];if(c.push("[TOOLNET PROJECT CONTEXT]"),c.push(`Project: ${r.project.name}`),c.push("Continue existing project state. Do not restart completed work unless evidence shows it is necessary."),n&&c.push(`Full operating manual: ${O(r.project)}`),s.length){c.push("","PROJECT RULES \u2014 MUST FOLLOW");for(let u of s.slice(0,24))c.push(`- [ENFORCE] ${d(u,240)}`)}if(a.length){c.push("","PROJECT PREFERENCES");for(let u of a.slice(0,10))c.push(`- ${d(u,220)}`)}if(l&&(l.mission&&c.push("","MISSION",d(l.mission.value,420)),l.activeObjective&&c.push("","CURRENT OBJECTIVE",d(l.activeObjective.value,420)),l.why&&c.push("","WHY THIS WORK MATTERS",d(l.why.value,420)),l.desiredOutcome&&c.push("","DESIRED OUTCOME",d(l.desiredOutcome.value,420)),l.planRationale&&c.push("","WHY THIS APPROACH",d(l.planRationale.value,420))),i){if(c.push("","ACTIVE WORK"),i.goal&&c.push(`Goal: ${d(i.goal,320)}`),i.plan&&c.push(`Plan: ${d(i.plan,320)}`),c.push(`Progress: phases ${i.progress.phasesCompleted}/${i.progress.phasesTotal}; tasks ${i.progress.tasksCompleted}/${i.progress.tasksTotal}; blocked ${i.progress.blocked}`),i.currentPhase&&c.push(`Current phase: ${i.currentPhase.title} [${i.currentPhase.status}]`),i.currentPhase&&l){let u=l.phases.find(g=>g.order===i.currentPhase?.order);u&&(u.objective&&c.push(`Phase objective: ${d(u.objective.value,340)}`),u.why?c.push(`Why this phase: ${d(u.why.value,340)}`):c.push("Why this phase: not explicitly recorded. Inspect existing implementation before assuming intent."),u.deliverable&&c.push(`Deliverable: ${d(u.deliverable.value,340)}`),u.dependencies.length&&c.push(`Depends on: ${u.dependencies.slice(0,4).map(g=>d(g.value,180)).join("; ")}`),u.acceptanceCriteria.length&&(c.push("","DEFINITION OF DONE"),u.acceptanceCriteria.slice(0,6).forEach(g=>{c.push(`- ${d(g.value,260)}`)})),u.openQuestions.length&&(c.push("","OPEN QUESTIONS FOR CURRENT PHASE"),u.openQuestions.slice(0,4).forEach(g=>{c.push(`- ${d(g.value,260)}`)})))}i.currentTask&&c.push(`Current task: ${i.currentTask.title} [${i.currentTask.status}]`),i.nextActions.length&&(c.push("","NEXT ACTIONS"),i.nextActions.slice(0,6).forEach((u,g)=>{c.push(`${g+1}. ${d(u,260)}`)})),i.blockers.length&&(c.push("","BLOCKERS"),i.blockers.slice(0,5).forEach(u=>{c.push(`- ${d(u,260)}`)})),i.warnings.length&&(c.push("","ATTENTION"),i.warnings.slice(-5).forEach(u=>{c.push(`- ${d(u,260)}`)})),i.decisions.length&&(c.push("","RECENT DECISIONS"),i.decisions.slice(-5).forEach(u=>{c.push(`- ${d(u,260)}`)})),i.lastSession&&c.push("",`Last work session: ${i.lastSession.agent} / ${i.lastSession.nativeSessionId}`)}if(l&&l.openQuestions.length&&(c.push("","UNRESOLVED QUESTIONS"),l.openQuestions.slice(0,5).forEach(u=>{c.push(`- ${d(u.value,260)}`)})),f&&c.push(`Latest handoff: ${f.reason} / ${f.sourceSession.agent}`),p.length){c.push("","OPERATING NOTES");for(let u of p)c.push(`- ${u}`)}c.push("","Before changing anything: verify the current repository state and continue from the active phase/task above.");let S=rt(c,t);return{version:1,projectId:r.project.id,projectName:r.project.name,text:S,estimatedTokens:V(S),maxTokens:t,hasManual:!!n,hasWorkState:!!i,hasHandoff:!!f,generatedAt:new Date().toISOString()}}function it(r){return me(r.rootPath,".toolnet","context","startup.md")}function at(r){return me(r.rootPath,".toolnet","context","startup.json")}function ct(r,t){let e=it(r);nt(st(e),{recursive:!0}),ot(e,t.text.endsWith(`
90
+ `;function T(r){return Ce(r.rootPath,".toolnet","PROJECT.md")}function Oe(r){return r.normalize("NFKC").replace(/\s+/g," ").trim()}function Re(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=Oe(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 Ee(r){let t=T(r);return q(t)||(we(Ae(t),{recursive:!0}),Pe(t,Te,{encoding:"utf8",mode:384})),t}function K(r,t=!1){let e=t?Ee(r):T(r);if(!q(e))return null;if(G(e).size>Q)throw new Error(`PROJECT.md exceeds ${Q} bytes`);let n=be(e,"utf8");return{path:e,content:n,digest:S(n),rules:Re(n),bytes:Buffer.byteLength(n,"utf8"),updatedAt:new Date(G(e).mtimeMs).toISOString()}}async function D(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 Z(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 X(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 B(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 Ie(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 Me(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(u=>s.startsWith(u));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 Ne(r,t){let e=[];for(let o of r){let n=[...e,o].join(`
91
+ `);if(B(n)<=t){e.push(o);continue}let s=B(e.join(`
92
+ `)),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(`
93
+ `).trim()}async function ee(r){let t=Math.max(256,Math.min(2e3,r.maxTokens??1e3)),e=K(r.project,!1),o=e?.content??"";o||(o=await r.storage.getText(`projects/${r.project.id}/project/manual.md`)??"");let n=Me(o),s=e?e.rules.filter(p=>p.mode==="enforce").map(p=>p.text):n.enforce,c=e?e.rules.filter(p=>p.mode==="advisory").map(p=>p.text):n.advisory,l=o?Ie(o):[],i=await D(r.project,r.storage),u=await X(r.project,r.storage),m=await Z(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: ${T(r.project)}`),s.length){a.push("","PROJECT RULES \u2014 MUST FOLLOW");for(let p of s.slice(0,24))a.push(`- [ENFORCE] ${d(p,240)}`)}if(c.length){a.push("","PROJECT PREFERENCES");for(let p of c.slice(0,10))a.push(`- ${d(p,220)}`)}if(u&&(u.mission&&a.push("","MISSION",d(u.mission.value,420)),u.activeObjective&&a.push("","CURRENT OBJECTIVE",d(u.activeObjective.value,420)),u.why&&a.push("","WHY THIS WORK MATTERS",d(u.why.value,420)),u.desiredOutcome&&a.push("","DESIRED OUTCOME",d(u.desiredOutcome.value,420)),u.planRationale&&a.push("","WHY THIS APPROACH",d(u.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&&u){let p=u.phases.find(h=>h.order===i.currentPhase?.order);p&&(p.objective&&a.push(`Phase objective: ${d(p.objective.value,340)}`),p.why?a.push(`Why this phase: ${d(p.why.value,340)}`):a.push("Why this phase: not explicitly recorded. Inspect existing implementation before assuming intent."),p.deliverable&&a.push(`Deliverable: ${d(p.deliverable.value,340)}`),p.dependencies.length&&a.push(`Depends on: ${p.dependencies.slice(0,4).map(h=>d(h.value,180)).join("; ")}`),p.acceptanceCriteria.length&&(a.push("","DEFINITION OF DONE"),p.acceptanceCriteria.slice(0,6).forEach(h=>{a.push(`- ${d(h.value,260)}`)})),p.openQuestions.length&&(a.push("","OPEN QUESTIONS FOR CURRENT PHASE"),p.openQuestions.slice(0,4).forEach(h=>{a.push(`- ${d(h.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((p,h)=>{a.push(`${h+1}. ${d(p,260)}`)})),i.blockers.length&&(a.push("","BLOCKERS"),i.blockers.slice(0,5).forEach(p=>{a.push(`- ${d(p,260)}`)})),i.warnings.length&&(a.push("","ATTENTION"),i.warnings.slice(-5).forEach(p=>{a.push(`- ${d(p,260)}`)})),i.decisions.length&&(a.push("","RECENT DECISIONS"),i.decisions.slice(-5).forEach(p=>{a.push(`- ${d(p,260)}`)})),i.lastSession&&a.push("",`Last work session: ${i.lastSession.agent} / ${i.lastSession.nativeSessionId}`)}if(u&&u.openQuestions.length&&(a.push("","UNRESOLVED QUESTIONS"),u.openQuestions.slice(0,5).forEach(p=>{a.push(`- ${d(p.value,260)}`)})),m&&a.push(`Latest handoff: ${m.reason} / ${m.sourceSession.agent}`),l.length){a.push("","OPERATING NOTES");for(let p of l)a.push(`- ${p}`)}a.push("","Before changing anything: verify the current repository state and continue from the active phase/task above.");let g=Ne(a,t);return{version:1,projectId:r.project.id,projectName:r.project.name,text:g,estimatedTokens:B(g),maxTokens:t,hasManual:!!o,hasWorkState:!!i,hasHandoff:!!m,generatedAt:new Date().toISOString()}}function De(r){return te(r.rootPath,".toolnet","context","startup.md")}function Be(r){return te(r.rootPath,".toolnet","context","startup.json")}function We(r,t){let e=De(r);$e(Ke(e),{recursive:!0}),_e(e,t.text.endsWith(`
189
94
  `)?t.text:t.text+`
190
- `,{encoding:"utf8",mode:384}),v(at(r),t)}async function ge(r,t,e=800){let o=(await fe({project:r,storage:t,maxTokens:e})).text;k(o)>e&&(o=D(o,e),o+=`
95
+ `,{encoding:"utf8",mode:384}),v(Be(r),t)}async function re(r,t,e=800){let n=(await ee({project:r,storage:t,maxTokens:e})).text;j(n)>e&&(n=$(n,e),n+=`
191
96
 
192
97
  [Context trimmed by ToolNet Memory token budget]
193
- `);let a={version:1,projectId:r.id,projectName:r.name,text:o,digest:y(o),estimatedTokens:k(o),generatedAt:new Date().toISOString()};return ct(r,a),await t.put(`projects/${r.id}/context/startup.md`,a.text+`
194
- `,"text/markdown"),await t.put(`projects/${r.id}/context/startup.json`,JSON.stringify(a,null,2)+`
195
- `,"application/json"),a}import{existsSync as ut,readFileSync as lt}from"node:fs";import{homedir as pt}from"node:os";import{join as dt}from"node:path";function ft(r){let t=r.trim();return t.length>=2&&t.startsWith('"')&&t.endsWith('"')?(t=t.slice(1,-1),t.replace(/\\n/g,`
196
- `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):t.length>=2&&t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1):t}function mt(){let r=process.env.TOOLNET_GLOBAL_ENV??dt(pt(),".config","toolnet-memory",".env");if(!ut(r))return;let t=lt(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]=ft(n.slice(o+1)))}}mt();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 he(){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 gt}from"node:crypto";import{existsSync as B,mkdirSync as ht,readFileSync as yt,renameSync as St,writeFileSync as kt}from"node:fs";import{basename as vt,dirname as E,join as I,parse as ke,resolve as b}from"node:path";var ve=".toolnet",jt="project.json";function xt(r){return gt("sha256").update(r).digest("hex").slice(0,16)}function H(r){return I(r,ve,jt)}function wt(r){return B(H(r))}function bt(r,t){let e=b(r),n=ke(e).root;for(;;){if(wt(e))return e;if(e===n||t&&e===b(t))break;let o=E(e);if(o===e)break;e=o}return null}function Pt(r){let t=b(r),e=ke(t).root,n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(n.some(s=>B(I(t,s))))return t;if(t===e)break;let o=E(t);if(o===t)break;t=o}return b(r)}function At(r){let t;try{t=JSON.parse(yt(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:E(E(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 ye(r,t){let e=I(r,ve);ht(e,{recursive:!0});let n=H(r),o=`${n}.tmp-${process.pid}`;kt(o,JSON.stringify(t,null,2)+`
197
- `,{encoding:"utf8",mode:384}),St(o,n)}function Se(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 M=class{detect(t=process.cwd()){let e=b(t),n=Pt(e),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(f=>B(I(n,f))),a=bt(e,s?n:void 0);if(a){let f=H(a),c=At(f);return c.rootPath!==a&&(c.rootPath=a,c.updatedAt=new Date().toISOString(),ye(a,c)),Se(c,a)}let p=new Date().toISOString(),i=vt(n),l={version:1,id:xt(n),name:i,remote:i,rootPath:n,createdAt:p,updatedAt:p,graphVersion:0,memoryVersion:0};return ye(n,l),Se(l,n)}};import{homedir as Yt}from"node:os";import{join as Qt}from"node:path";import{DeleteObjectCommand as Ct,GetObjectCommand as Tt,HeadObjectCommand as Ot,ListObjectsV2Command as Et,PutObjectCommand as Mt,S3Client as It}from"@aws-sdk/client-s3";import{getSignedUrl as Rt}from"@aws-sdk/s3-request-presigner";var R=class{name="huggingface";client;bucket;constructor(t){this.bucket=t.bucket,this.client=new It({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 Mt({Bucket:this.bucket,Key:t,Body:o,ContentType:n}))}async get(t){let e=await Rt(this.client,new Tt({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 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 Ct({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}};import{access as je,mkdir as Nt,readFile as $t,readdir as _t,rm as Dt,stat as xe,writeFile as Kt}from"node:fs/promises";import{dirname as Ft,join as Wt,relative as we,resolve as Lt}from"node:path";var P=class{constructor(t){this.root=t}root;name="local";path(t){let e=t.replace(/^\/+/,"");return Lt(this.root,e)}async put(t,e){let n=this.path(t);await Nt(Ft(n),{recursive:!0}),await Kt(n,e)}async get(t){try{return await $t(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 je(this.path(t)),!0}catch{return!1}}async delete(t){await Dt(this.path(t),{force:!0})}async list(t=""){let e=this.path(t),n=[];try{await je(e)}catch{return n}let o=async a=>{let p=await _t(a,{withFileTypes:!0});for(let i of p){let l=Wt(a,i.name);if(i.isDirectory()){await o(l);continue}let f=await xe(l);n.push({key:we(this.root,l),size:f.size,updatedAt:f.mtime.toISOString()})}},s=await xe(e);return s.isDirectory()?await o(e):n.push({key:we(this.root,e),size:s.size,updatedAt:s.mtime.toISOString()}),n}};import{DeleteObjectCommand as Vt,GetObjectCommand as Bt,HeadObjectCommand as Ht,ListObjectsV2Command as Jt,PutObjectCommand as zt,S3Client as Gt}from"@aws-sdk/client-s3";import{getSignedUrl as Ut}from"@aws-sdk/s3-request-presigner";var A=class{name;client;bucket;constructor(t){this.name=t.name??"s3",this.bucket=t.bucket,this.client=new Gt({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 zt({Bucket:this.bucket,Key:t,Body:o,ContentType:n}))}async get(t){let e=await Ut(this.client,new Bt({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 Ht({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 Vt({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],n;do{let o=await this.client.send(new Jt({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 J(r,t){return console.warn(t),new P(r)}function be(r){let t=r.localRoot??Qt(Yt(),".toolnet-memory","storage");if(r.provider==="r2"){let e=r.r2;return e?.accountId&&e.bucket&&e.accessKeyId&&e.secretAccessKey?new A({name:"r2",endpoint:`https://${e.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:e.bucket,forcePathStyle:!0,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):J(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 A({name:"s3",endpoint:e.endpoint,region:e.region??"us-east-1",bucket:e.bucket,forcePathStyle:e.forcePathStyle??!1,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):J(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 R({namespace:e.namespace,bucket:e.bucket,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):J(t,"[storage] Hugging Face credentials missing. Using local fallback.")}return new P(t)}function qt(r){return new Promise(t=>setTimeout(t,r))}async function Pe(r,t={}){let e=Math.max(1,t.attempts??3),n=t.baseDelayMs??150,o=t.maxDelayMs??2e3,s;for(let a=1;a<=e;a++)try{return await r()}catch(p){if(s=p,a>=e)break;let i=Math.min(o,n*2**(a-1)),l=Math.floor(Math.random()*Math.max(1,i*.2));await qt(i+l)}throw s}var Xt=new Set(["put","get","getText","delete","list"]);function Ae(r,t={}){return new Proxy(r,{get(e,n){let o=Reflect.get(e,n,e);return typeof o!="function"?o:Xt.has(n)?(...s)=>Pe(()=>Promise.resolve(o.apply(e,s)),t):o.bind(e)}})}function Ce(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 Te(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=Ce(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 a;try{a=JSON.parse(o)}catch(p){throw new Error(`Invalid remote ToolNet project manifest at ${t}: ${p instanceof Error?p.message:String(p)}`)}if(typeof a.id=="string"&&a.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${a.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof a.createdAt=="string"&&(n=a.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)+`
198
- `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(t){if(t=Te(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 Zt(){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 er(){let{command:r,options:t}=Zt();try{switch(r){case"print":await tr(t);break;case"sync":await rr(t);break;case"refresh":await nr(t);break;case"profile-show":await or(t);break;case"profile-sync":await sr(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 tr(r){let t=r.mode||"minimal";if(t==="minimal"){let e=F({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 rr(r){let t=F({projectPath:r.project});t||(console.error("No ToolNet project found."),process.exit(1));let e=ae(t);console.log(`Context hash: ${e}`),console.log(`Context size: ${t.length} chars`)}async function nr(r){console.log("Refreshing deep startup brief cache...");let t=r.project||process.cwd(),e=j(t);e||(console.error("No ToolNet project found."),process.exit(1));let n=new M().detect(e),o=he(),s=Ae(be({provider:o.storage.provider,huggingface:o.storage.huggingface,localRoot:o.storage.localRoot}),{attempts:2}),a=new N(s,n.id,n.name,n.remote??n.name);await ge(n,a),console.log("Deep startup brief cache refreshed.")}async function or(r){let t=r.project||process.cwd(),e=j(t);e||(console.error("No ToolNet project found."),process.exit(1));let n=Oe.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 sr(r){let t=ie({projectPath:r.project});console.log("Created/updated:");for(let e of t)console.log(`- ${e}`)}er();
98
+ `);let c={version:1,projectId:r.id,projectName:r.name,text:n,digest:S(n),estimatedTokens:j(n),generatedAt:new Date().toISOString()};return We(r,c),await t.put(`projects/${r.id}/context/startup.md`,c.text+`
99
+ `,"text/markdown"),await t.put(`projects/${r.id}/context/startup.json`,JSON.stringify(c,null,2)+`
100
+ `,"application/json"),c}import{existsSync as Fe,readFileSync as Le}from"node:fs";import{homedir as Ve}from"node:os";import{join as Je}from"node:path";function ze(r){let t=r.trim();return t.length>=2&&t.startsWith('"')&&t.endsWith('"')?(t=t.slice(1,-1),t.replace(/\\n/g,`
101
+ `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):t.length>=2&&t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1):t}function Ue(){let r=process.env.TOOLNET_GLOBAL_ENV??Je(Ve(),".config","toolnet-memory",".env");if(!Fe(r))return;let t=Le(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]=ze(o.slice(n+1)))}}Ue();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 oe(){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 He}from"node:crypto";import{existsSync as W,mkdirSync as Ye,readFileSync as Ge,renameSync as Qe,writeFileSync as qe}from"node:fs";import{basename as Ze,dirname as O,join as E,parse as ie,resolve as b}from"node:path";var ae=".toolnet",Xe="project.json";function et(r){return He("sha256").update(r).digest("hex").slice(0,16)}function F(r){return E(r,ae,Xe)}function tt(r){return W(F(r))}function rt(r,t){let e=b(r),o=ie(e).root;for(;;){if(tt(e))return e;if(e===o||t&&e===b(t))break;let n=O(e);if(n===e)break;e=n}return null}function ot(r){let t=b(r),e=ie(t).root,o=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(o.some(s=>W(E(t,s))))return t;if(t===e)break;let n=O(t);if(n===t)break;t=n}return b(r)}function nt(r){let t;try{t=JSON.parse(Ge(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:O(O(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 ne(r,t){let e=E(r,ae);Ye(e,{recursive:!0});let o=F(r),n=`${o}.tmp-${process.pid}`;qe(n,JSON.stringify(t,null,2)+`
102
+ `,{encoding:"utf8",mode:384}),Qe(n,o)}function se(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=ot(e),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(m=>W(E(o,m))),c=rt(e,s?o:void 0);if(c){let m=F(c),a=nt(m);return a.rootPath!==c&&(a.rootPath=c,a.updatedAt=new Date().toISOString(),ne(c,a)),se(a,c)}let l=new Date().toISOString(),i=Ze(o),u={version:1,id:et(o),name:i,remote:i,rootPath:o,createdAt:l,updatedAt:l,graphVersion:0,memoryVersion:0};return ne(o,u),se(u,o)}};import{homedir as Ct}from"node:os";import{join as Tt}from"node:path";import{DeleteObjectCommand as st,GetObjectCommand as it,HeadObjectCommand as at,ListObjectsV2Command as ct,PutObjectCommand as pt,S3Client as ut}from"@aws-sdk/client-s3";import{getSignedUrl as lt}from"@aws-sdk/s3-request-presigner";var I=class{name="huggingface";client;bucket;constructor(t){this.bucket=t.bucket,this.client=new ut({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 pt({Bucket:this.bucket,Key:t,Body:n,ContentType:o}))}async get(t){let e=await lt(this.client,new it({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 at({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 st({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],o;do{let n=await this.client.send(new ct({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 ce,mkdir as dt,readFile as mt,readdir as ft,rm as gt,stat as pe,writeFile as ht}from"node:fs/promises";import{dirname as yt,join as St,relative as ue,resolve as jt}from"node:path";var P=class{constructor(t){this.root=t}root;name="local";path(t){let e=t.replace(/^\/+/,"");return jt(this.root,e)}async put(t,e){let o=this.path(t);await dt(yt(o),{recursive:!0}),await ht(o,e)}async get(t){try{return await mt(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 ce(this.path(t)),!0}catch{return!1}}async delete(t){await gt(this.path(t),{force:!0})}async list(t=""){let e=this.path(t),o=[];try{await ce(e)}catch{return o}let n=async c=>{let l=await ft(c,{withFileTypes:!0});for(let i of l){let u=St(c,i.name);if(i.isDirectory()){await n(u);continue}let m=await pe(u);o.push({key:ue(this.root,u),size:m.size,updatedAt:m.mtime.toISOString()})}},s=await pe(e);return s.isDirectory()?await n(e):o.push({key:ue(this.root,e),size:s.size,updatedAt:s.mtime.toISOString()}),o}};import{DeleteObjectCommand as kt,GetObjectCommand as vt,HeadObjectCommand as xt,ListObjectsV2Command as wt,PutObjectCommand as bt,S3Client as Pt}from"@aws-sdk/client-s3";import{getSignedUrl as At}from"@aws-sdk/s3-request-presigner";var A=class{name;client;bucket;constructor(t){this.name=t.name??"s3",this.bucket=t.bucket,this.client=new Pt({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 bt({Bucket:this.bucket,Key:t,Body:n,ContentType:o}))}async get(t){let e=await At(this.client,new vt({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 xt({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 kt({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],o;do{let n=await this.client.send(new wt({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 L(r,t){return console.warn(t),new P(r)}function le(r){let t=r.localRoot??Tt(Ct(),".toolnet-memory","storage");if(r.provider==="r2"){let e=r.r2;return e?.accountId&&e.bucket&&e.accessKeyId&&e.secretAccessKey?new A({name:"r2",endpoint:`https://${e.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:e.bucket,forcePathStyle:!0,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):L(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 A({name:"s3",endpoint:e.endpoint,region:e.region??"us-east-1",bucket:e.bucket,forcePathStyle:e.forcePathStyle??!1,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):L(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 I({namespace:e.namespace,bucket:e.bucket,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):L(t,"[storage] Hugging Face credentials missing. Using local fallback.")}return new P(t)}function Ot(r){return new Promise(t=>setTimeout(t,r))}async function de(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)),u=Math.floor(Math.random()*Math.max(1,i*.2));await Ot(i+u)}throw s}var Rt=new Set(["put","get","getText","delete","list"]);function me(r,t={}){return new Proxy(r,{get(e,o){let n=Reflect.get(e,o,e);return typeof n!="function"?n:Rt.has(o)?(...s)=>de(()=>Promise.resolve(n.apply(e,s)),t):n.bind(e)}})}function fe(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 ge(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 M=class{constructor(t,e,o,n){this.provider=t;this.name=t.name,this.projectId=e,this.projectName=o,this.folder=fe(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)+`
103
+ `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(t){if(t=ge(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 Et(){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 It(){let{command:r,options:t}=Et();try{switch(r){case"print":await Mt(t);break;case"sync":await Nt(t);break;case"refresh":await $t(t);break;case"profile-show":await _t(t);break;case"profile-sync":await Kt(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 Mt(r){let t=r.mode||"minimal";if(t==="minimal"){let e=_({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 Nt(r){let t=_({projectPath:r.project});t||(console.error("No ToolNet project found."),process.exit(1));let e=Y(t);console.log(`Context hash: ${e}`),console.log(`Context size: ${t.length} chars`)}async function $t(r){console.log("Refreshing deep startup brief cache...");let t=r.project||process.cwd(),e=k(t);e||(console.error("No ToolNet project found."),process.exit(1));let o=new R().detect(e),n=oe(),s=me(le({provider:n.storage.provider,huggingface:n.storage.huggingface,localRoot:n.storage.localRoot}),{attempts:2}),c=new M(s,o.id,o.name,o.remote??o.name);await re(o,c),console.log("Deep startup brief cache refreshed.")}async function _t(r){let t=r.project||process.cwd(),e=k(t);e||(console.error("No ToolNet project found."),process.exit(1));let o=he.join(e,".toolnet","profile.md");N.existsSync(o)||(console.error("No profile.md found in .toolnet directory."),process.exit(1));let n=N.readFileSync(o,"utf-8");process.stdout.write(n)}async function Kt(r){let t=H({projectPath:r.project});console.log("Created/updated:");for(let e of t)console.log(`- ${e}`)}It();
package/bundle/context.js CHANGED
@@ -1,8 +1,8 @@
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
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
6
6
 
7
7
  This file contains persistent instructions for AI agents working on this project.
8
8
 
@@ -45,19 +45,18 @@ Things an AI agent should always remember.
45
45
  <!--
46
46
  - [advisory] Prefer small focused files.
47
47
  -->
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
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
57
56
 
58
57
  Commands:
59
58
  brief [--project PATH] [--tokens N]
60
59
  brief-json [--project PATH] [--tokens N]
61
60
  handoff-latest [--project PATH]
62
61
  handoff-create --agent <opencode|agy|codex> --session <id>
63
- `)}Vt().catch(r=>{console.error(r instanceof Error?r.message:r),process.exit(1)});
62
+ `)}Kt().catch(r=>{console.error(r instanceof Error?r.message:r),process.exit(1)});