toolnet-memory 0.3.2 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/toolnet-memory +44 -0
- package/bundle/agy-hook.js +39 -88
- package/bundle/agy.js +38 -104
- package/bundle/auto-integrate.js +131 -707
- package/bundle/codex-context.js +44 -54
- package/bundle/codex.js +47 -83
- package/bundle/context-runtime.js +29 -124
- package/bundle/context.js +14 -15
- package/bundle/doctor.js +3 -6
- package/bundle/full-index.js +8 -18
- package/bundle/graph-index.js +4 -4
- package/bundle/impact.js +3 -3
- package/bundle/incremental.js +4 -4
- package/bundle/learner.js +5 -5
- package/bundle/mcp.js +7 -54
- package/bundle/opencode.js +168 -717
- package/bundle/project-manual.js +6 -6
- package/bundle/runtime.js +11 -11
- package/bundle/semantic.js +6 -6
- package/bundle/setup.js +187 -716
- package/bundle/snapshot.js +4 -4
- package/bundle/update.js +1 -4
- package/bundle/work-continuity.js +6 -6
- package/package.json +1 -1
- package/bundle/api.js +0 -52
- package/bundle/claude-hook.js +0 -78
- package/bundle/claude.js +0 -3
- package/bundle/continuity-certify.js +0 -49
- package/bundle/handoff-refresh.js +0 -12
- package/bundle/init.js +0 -763
- package/bundle/memory-agent.js +0 -48
- package/bundle/memory-query.js +0 -7
- package/bundle/production-certify.js +0 -5
- package/bundle/provider.js +0 -3
- package/bundle/recovery-certify.js +0 -55
|
@@ -1,120 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import*as
|
|
3
|
-
`),
|
|
4
|
-
`).filter(s=>s.trim());if(e.length<=t)return r;let
|
|
5
|
-
`)}function
|
|
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
|
-
`,
|
|
8
|
-
${
|
|
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
|
|
14
|
-
`).
|
|
15
|
-
`)}
|
|
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
|
-
|
|
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
|
-
`,
|
|
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
|
-
-
|
|
115
|
-
|
|
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
|
-
-
|
|
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
|
-
${
|
|
142
|
-
`,i=["GEMINI.md","AGENTS.md","CLAUDE.md"],
|
|
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
|
|
186
|
-
`);if(
|
|
187
|
-
`)),
|
|
188
|
-
`).trim()}async function
|
|
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(
|
|
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
|
|
194
|
-
`,"text/markdown"),await t.put(`projects/${r.id}/context/startup.json`,JSON.stringify(
|
|
195
|
-
`,"application/json"),
|
|
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
|
|
197
|
-
`,{encoding:"utf8",mode:384}),
|
|
198
|
-
`,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(t){if(t=
|
|
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
|
|
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
|
|
3
|
-
`,{encoding:"utf8",mode:384}),
|
|
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
|
|
5
|
-
`,{encoding:"utf8",mode:384}),
|
|
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
|
|
49
|
-
`,"application/json"),
|
|
50
|
-
|
|
51
|
-
`,"application/json")
|
|
52
|
-
|
|
53
|
-
`);if(
|
|
54
|
-
`))
|
|
55
|
-
`)
|
|
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
|
-
`)}
|
|
62
|
+
`)}Kt().catch(r=>{console.error(r instanceof Error?r.message:r),process.exit(1)});
|