toolnet-memory 0.3.11 → 0.3.13
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/.env.example +0 -4
- package/README.md +428 -202
- package/bin/toolnet-memory +15 -42
- package/bundle/agy-hook.js +47 -47
- package/bundle/agy.js +31 -31
- package/bundle/api.js +15 -52
- package/bundle/auto-integrate.js +34 -33
- package/bundle/claude-hook.js +9 -9
- package/bundle/codex.js +28 -28
- package/bundle/config.js +17 -12
- package/bundle/context-runtime.js +9 -9
- package/bundle/context.js +12 -12
- package/bundle/continuity-certify.js +13 -50
- package/bundle/copilot-hook.js +13 -13
- package/bundle/cursor-hook.js +13 -13
- package/bundle/doctor.js +4 -6
- package/bundle/full-index.js +21 -22
- package/bundle/graph-index.js +3 -3
- package/bundle/graph-ui.js +3 -3
- package/bundle/grok-hook.js +13 -13
- package/bundle/help-cli.js +4 -4
- package/bundle/impact.js +3 -3
- package/bundle/incremental.js +3 -3
- package/bundle/init.js +37 -36
- package/bundle/kilo.js +2 -0
- package/bundle/kiro-hook.js +14 -14
- package/bundle/learner.js +4 -4
- package/bundle/mcp.js +18 -56
- package/bundle/memory-agent.js +7 -47
- package/bundle/opencode.js +27 -27
- package/bundle/project-manual.js +2 -2
- package/bundle/recovery-certify.js +13 -53
- package/bundle/runtime.js +11 -11
- package/bundle/semantic.js +5 -6
- package/bundle/snapshot.js +3 -3
- package/bundle/status-cli.js +4 -4
- package/bundle/toolnet-cli.js +2 -0
- package/bundle/work-continuity.js +5 -5
- package/package.json +1 -3
- package/bundle/model-cli.js +0 -2
- package/bundle/provider.js +0 -6
- package/bundle/setup.js +0 -1159
package/bundle/config.js
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
|
-
import
|
|
2
|
-
`)
|
|
1
|
+
import{spawnSync as v}from"node:child_process";import s from"node:fs";import E from"node:os";import m from"node:path";var f=m.join(E.homedir(),".config","toolnet-memory"),i=m.join(f,".env"),y=/(SECRET|TOKEN|PASSWORD|ACCESS_KEY|API_KEY|PRIVATE_KEY)/i;function c(){s.mkdirSync(f,{recursive:!0,mode:448}),s.existsSync(i)||s.writeFileSync(i,"",{encoding:"utf8",mode:384}),s.chmodSync(f,448),s.chmodSync(i,384)}function u(){return c(),s.readFileSync(i,"utf8").split(/\r?\n/)}function l(){let o=new Map;for(let n of u()){let e=n.trim();if(!e||e.startsWith("#"))continue;let t=e.indexOf("=");t<1||o.set(e.slice(0,t).trim(),e.slice(t+1).trim())}return o}function d(o){return/^[A-Za-z_][A-Za-z0-9_]*$/.test(o)}function a(o,n){return y.test(o)?n?n.length<=8?"********":`${n.slice(0,4)}\u2026${n.slice(-4)}`:"":n}function p(o){c(),s.writeFileSync(i,`${o.join(`
|
|
2
|
+
`).replace(/\n+$/,"")}
|
|
3
|
+
`,{encoding:"utf8",mode:384}),s.chmodSync(i,384)}function h(o,n){if(!d(o))throw new Error(`Invalid config key: ${o}`);let e=u(),t=!1,r=e.map(g=>g.trim().startsWith(`${o}=`)?(t=!0,`${o}=${n}`):g);t||(r.length&&r.at(-1)!==""&&r.push(""),r.push(`${o}=${n}`)),p(r)}function S(o){if(!d(o))throw new Error(`Invalid config key: ${o}`);let n=u(),e=n.filter(t=>!t.trim().startsWith(`${o}=`));return e.length===n.length?!1:(p(e),!0)}function w(){let n=l().get("MEMORY_STORAGE_PROVIDER")||"not configured";console.log(""),console.log("\u25C7 ToolNet Memory Config"),console.log(""),console.log(`Storage ${n}`),console.log(`File ${i}`),console.log("Secrets hidden"),console.log("")}function $(){let o=l(),n=[];if(o.get("MEMORY_STORAGE_PROVIDER")||n.push("Storage provider is missing"),n.length){console.log("Config needs attention:");for(let e of n)console.log(` \u2717 ${e}`);return!1}return console.log("\u2713 Config structure looks valid"),!0}function R(){console.log("Guided AI/LLM setup was removed."),console.log("Configure storage and runtime via environment variables (see .env.example)"),console.log("or use `toolnet-memory config set KEY VALUE`.")}function C(){console.log(`ToolNet Memory Config
|
|
3
4
|
|
|
4
5
|
Commands:
|
|
5
|
-
toolnet-memory config
|
|
6
|
-
toolnet-memory config
|
|
7
|
-
toolnet-memory config
|
|
8
|
-
toolnet-memory config
|
|
9
|
-
toolnet-memory config
|
|
10
|
-
toolnet-memory config
|
|
6
|
+
toolnet-memory config Guided configuration
|
|
7
|
+
toolnet-memory config show Friendly summary
|
|
8
|
+
toolnet-memory config file Print config path
|
|
9
|
+
toolnet-memory config list List active keys (secrets masked)
|
|
10
|
+
toolnet-memory config get KEY Read one value (masked if secret)
|
|
11
|
+
toolnet-memory config get KEY --reveal Read one value including secret
|
|
12
|
+
toolnet-memory config set KEY VALUE Set one value
|
|
13
|
+
toolnet-memory config unset KEY Remove one value
|
|
14
|
+
toolnet-memory config validate Validate required fields
|
|
15
|
+
toolnet-memory config open Open config in editor
|
|
11
16
|
|
|
12
|
-
|
|
13
|
-
toolnet-memory
|
|
14
|
-
toolnet-memory
|
|
15
|
-
toolnet-memory
|
|
17
|
+
Targeted setup:
|
|
18
|
+
toolnet-memory setup --section storage
|
|
19
|
+
toolnet-memory setup --section integrations
|
|
20
|
+
toolnet-memory setup --section health`)}function A(){let[o,...n]=process.argv.slice(2);if(!o||o==="configure"){R();return}if(o==="show"){w();return}if(o==="path"||o==="file"){c(),console.log(i);return}if(o==="list"){let e=l();for(let[t,r]of e)console.log(`${t}=${a(t,r)}`);return}if(o==="get"){let e=n[0];if(!e)throw new Error("Usage: toolnet-memory config get KEY");let t=l();if(!t.has(e))throw new Error(`Config key not found: ${e}`);let r=t.get(e)??"";console.log(n.includes("--reveal")?r:a(e,r));return}if(o==="set"){let e=n[0],t=n[1];if(!e||t===void 0)throw new Error("Usage: toolnet-memory config set KEY VALUE");h(e,t),console.log(`\u2713 ${e} updated`);return}if(o==="unset"){let e=n[0];if(!e)throw new Error("Usage: toolnet-memory config unset KEY");console.log(S(e)?`\u2713 ${e} removed`:`\xB7 ${e} was not set`);return}if(o==="validate"){$()||(process.exitCode=1);return}if(o==="open"){if(c(),!process.stdin.isTTY){console.log(i);return}let e=process.env.VISUAL||process.env.EDITOR||"vi",t=v(e,[i],{stdio:"inherit",shell:!0});process.exitCode=t.status??0;return}C()}try{A()}catch(o){console.error(o instanceof Error?o.message:String(o)),process.exit(1)}
|
|
@@ -10,9 +10,9 @@ ${n}
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
`)}function G(r,t){let e=[{title:"Profile",content:J(r,10),priority:100},{title:"Current Work",content:J(t,15),priority:90}];return Me(e,{maxTokens:800})}import{chmodSync as cr,existsSync as
|
|
13
|
+
`)}function G(r,t){let e=[{title:"Profile",content:J(r,10),priority:100},{title:"Current Work",content:J(t,15),priority:90}];return Me(e,{maxTokens:800})}import{chmodSync as cr,existsSync as Re,mkdirSync as ur,readFileSync as Ee,renameSync as lr,writeFileSync as dr}from"node:fs";import{dirname as fr,join as q}from"node:path";var Y="toolnet.context-offload.v1";function Ne(r){return q(r,".toolnet","offload")}function $e(r){return q(Ne(r),"graph.json")}function W(){return{schema:Y,version:1,updatedAt:new Date(0).toISOString(),nodes:[]}}function _e(r){let t=$e(r);if(!Re(t))return W();try{let e=JSON.parse(Ee(t,"utf8"));return e.schema!==Y||e.version!==1||!Array.isArray(e.nodes)?W():e}catch{return W()}}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)),s=Math.max(320,Math.min(2400,t.maxChars??900)),o=["[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 d=U(a.sourceRefs.at(-1)??"unknown",72),i=a.files.length>0?` files=${U(a.files.join(","),120)}`:"",l=`event:${d} --offloads--> asset:${a.id.slice(0,12)} kind=${a.kind} bytes=${a.bytes}${i}`;if([...o,l].join(`
|
|
14
14
|
`).length>s)break;o.push(l)}return o.join(`
|
|
15
|
-
`)}import{existsSync as X,mkdirSync as
|
|
15
|
+
`)}import{existsSync as X,mkdirSync as vr,readFileSync as Z,renameSync as wr,writeFileSync as br}from"node:fs";import{dirname as Ar,join as ee}from"node:path";import{createHash as Fe}from"node:crypto";import{dirname as We}from"node:path";import{mkdirSync as De,readFileSync as kr,renameSync as Ke,writeFileSync as Le}from"node:fs";function y(r){return Fe("sha256").update(r).digest("hex")}function x(r,t){De(We(r),{recursive:!0});let e=`${r}.${process.pid}.tmp`;Le(e,JSON.stringify(t,null,2)+`
|
|
16
16
|
`,{encoding:"utf8",mode:384}),Ke(e,r)}function Be(r){return ee(r.rootPath,".toolnet","context","handoff.md")}function Ve(r){return ee(r.rootPath,".toolnet","context","handoff.json")}function He(r){let t=Be(r);if(!X(t))return null;try{let e=Z(t,"utf8").trim();if(!e)return null;let n=new Date(0).toISOString(),s=Ve(r);if(X(s))try{let o=JSON.parse(Z(s,"utf8"));typeof o.generatedAt=="string"&&(n=o.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)}
|
|
17
17
|
|
|
18
18
|
[Fast handoff truncated]`),["[TOOLNET FAST HANDOFF]","",`Project: ${r.name}`,`Updated: ${e.generatedAt}`,"",n].join(`
|
|
@@ -92,9 +92,9 @@ Use:
|
|
|
92
92
|
- mode="ai" for ambiguous or combined continuity questions.
|
|
93
93
|
|
|
94
94
|
Current repository evidence overrides stale memory.
|
|
95
|
-
`.trim()}function
|
|
95
|
+
`.trim()}function v(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 se(r){return r.split(`
|
|
96
96
|
`).filter(n=>{let s=n.toUpperCase();return!(s.includes("SECRET")||s.includes("TOKEN")||s.includes("API_KEY")||s.includes("APIKEY")||s.includes("PASSWORD")||s.includes("PASS="))}).join(`
|
|
97
|
-
`)}function D(r={}){let t=r.projectPath||process.cwd(),e=
|
|
97
|
+
`)}function D(r={}){let t=r.projectPath||process.cwd(),e=v(t);if(!e)return null;let n=m.join(e,".toolnet","project.json"),s="Unknown";try{if(h.existsSync(n)){let _=JSON.parse(h.readFileSync(n,"utf-8"));s=_.name||_.projectName||"Unknown"}}catch{}let o=m.join(e,".toolnet","profile.md"),a=m.join(e,".toolnet","current.md"),d=T(o)||"",i=T(a)||"";d=se(d),i=se(i);let l=`[TOOLNET PROJECT CONTEXT]
|
|
98
98
|
|
|
99
99
|
Project: ${s}
|
|
100
100
|
Root: ${e}
|
|
@@ -114,7 +114,7 @@ Forbidden At Startup:
|
|
|
114
114
|
- Do not perform deep recovery merely because an agent starts.
|
|
115
115
|
- If the user asks to resume previous work and fast context is insufficient,
|
|
116
116
|
use memory_agent_ask before guessing.
|
|
117
|
-
`;return l+f+k+g+Oe}function ie(r={}){let t=r.projectPath||process.cwd(),e=
|
|
117
|
+
`;return l+f+k+g+Oe}function ie(r={}){let t=r.projectPath||process.cwd(),e=v(t);if(!e)throw new Error("Not in a ToolNet project (no .toolnet directory found)");let n=m.join(e,".toolnet","profile.md"),s=m.join(e,".toolnet","current.md"),o=T(n)||"",a=T(s)||"",d=`# AI Startup Instructions
|
|
118
118
|
|
|
119
119
|
Read and follow:
|
|
120
120
|
- .toolnet/profile.md
|
|
@@ -187,12 +187,12 @@ Things an AI agent should always remember.
|
|
|
187
187
|
`)),a=Math.max(0,t-o);if(a>=16){let d=Math.floor(a*3.2),i=p(n,d);i&&e.push(i)}break}return e.join(`
|
|
188
188
|
`).trim()}async function fe(r){let t=Math.max(256,Math.min(2e3,r.maxTokens??1e3)),e=K(r.project,!1),n=e?.content??"";n||(n=await r.storage.getText(`projects/${r.project.id}/project/manual.md`)??"");let s=tt(n),o=e?e.rules.filter(u=>u.mode==="enforce").map(u=>u.text):s.enforce,a=e?e.rules.filter(u=>u.mode==="advisory").map(u=>u.text):s.advisory,d=n?et(n):[],i=await L(r.project,r.storage),l=await pe(r.project,r.storage),f=await de(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: ${I(r.project)}`),o.length){c.push("","PROJECT RULES \u2014 MUST FOLLOW");for(let u of o.slice(0,24))c.push(`- [ENFORCE] ${p(u,240)}`)}if(a.length){c.push("","PROJECT PREFERENCES");for(let u of a.slice(0,10))c.push(`- ${p(u,220)}`)}if(l&&(l.mission&&c.push("","MISSION",p(l.mission.value,420)),l.activeObjective&&c.push("","CURRENT OBJECTIVE",p(l.activeObjective.value,420)),l.why&&c.push("","WHY THIS WORK MATTERS",p(l.why.value,420)),l.desiredOutcome&&c.push("","DESIRED OUTCOME",p(l.desiredOutcome.value,420)),l.planRationale&&c.push("","WHY THIS APPROACH",p(l.planRationale.value,420))),i){if(c.push("","ACTIVE WORK"),i.goal&&c.push(`Goal: ${p(i.goal,320)}`),i.plan&&c.push(`Plan: ${p(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: ${p(u.objective.value,340)}`),u.why?c.push(`Why this phase: ${p(u.why.value,340)}`):c.push("Why this phase: not explicitly recorded. Inspect existing implementation before assuming intent."),u.deliverable&&c.push(`Deliverable: ${p(u.deliverable.value,340)}`),u.dependencies.length&&c.push(`Depends on: ${u.dependencies.slice(0,4).map(g=>p(g.value,180)).join("; ")}`),u.acceptanceCriteria.length&&(c.push("","DEFINITION OF DONE"),u.acceptanceCriteria.slice(0,6).forEach(g=>{c.push(`- ${p(g.value,260)}`)})),u.openQuestions.length&&(c.push("","OPEN QUESTIONS FOR CURRENT PHASE"),u.openQuestions.slice(0,4).forEach(g=>{c.push(`- ${p(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}. ${p(u,260)}`)})),i.blockers.length&&(c.push("","BLOCKERS"),i.blockers.slice(0,5).forEach(u=>{c.push(`- ${p(u,260)}`)})),i.warnings.length&&(c.push("","ATTENTION"),i.warnings.slice(-5).forEach(u=>{c.push(`- ${p(u,260)}`)})),i.decisions.length&&(c.push("","RECENT DECISIONS"),i.decisions.slice(-5).forEach(u=>{c.push(`- ${p(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(`- ${p(u.value,260)}`)})),f&&c.push(`Latest handoff: ${f.reason} / ${f.sourceSession.agent}`),d.length){c.push("","OPERATING NOTES");for(let u of d)c.push(`- ${u}`)}c.push("","Before changing anything: verify the current repository state and continue from the active phase/task above.");let k=rt(c,t);return{version:1,projectId:r.project.id,projectName:r.project.name,text:k,estimatedTokens:B(k),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(ot(e),{recursive:!0}),st(e,t.text.endsWith(`
|
|
189
189
|
`)?t.text:t.text+`
|
|
190
|
-
`,{encoding:"utf8",mode:384}),
|
|
190
|
+
`,{encoding:"utf8",mode:384}),x(at(r),t)}async function ge(r,t,e=800){let s=(await fe({project:r,storage:t,maxTokens:e})).text;S(s)>e&&(s=F(s,e),s+=`
|
|
191
191
|
|
|
192
192
|
[Context trimmed by ToolNet Memory token budget]
|
|
193
193
|
`);let a={version:1,projectId:r.id,projectName:r.name,text:s,digest:y(s),estimatedTokens:S(s),generatedAt:new Date().toISOString()};return ct(r,a),await t.put(`projects/${r.id}/context/startup.md`,a.text+`
|
|
194
194
|
`,"text/markdown"),await t.put(`projects/${r.id}/context/startup.json`,JSON.stringify(a,null,2)+`
|
|
195
195
|
`,"application/json"),a}import{existsSync as ut,readFileSync as lt}from"node:fs";import{homedir as dt}from"node:os";import{join as pt}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??pt(dt(),".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 s=n.indexOf("=");if(s<=0)continue;let o=n.slice(0,s).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(o)&&process.env[o]===void 0&&(process.env[o]=ft(n.slice(s+1)))}}mt();function w(r,t){return r===void 0?t:["1","true","yes","on"].includes(r.toLowerCase())}function
|
|
197
|
-
`,{encoding:"utf8",mode:384}),kt(s,n)}function ke(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=
|
|
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 sr(t);break;case"profile-sync":await or(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=D({projectPath:r.project});e||(console.error("No ToolNet project found. Run toolnet-memory init first."),process.exit(1)),process.stdout.write(e)}else(t==="focused"||t==="deep")&&(console.error("Focused and deep modes require storage access."),console.error("Use: toolnet-memory brief --deep for deep context"),process.exit(1))}async function rr(r){let t=D({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=
|
|
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??pt(dt(),".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 s=n.indexOf("=");if(s<=0)continue;let o=n.slice(0,s).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(o)&&process.env[o]===void 0&&(process.env[o]=ft(n.slice(s+1)))}}mt();function w(r,t){return r===void 0?t:["1","true","yes","on"].includes(r.toLowerCase())}function b(r,t){if(!r)return t;let e=Number(r);return Number.isFinite(e)?e:t}function he(){return{memory:{autoCapture:w(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:w(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:w(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:w(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:b(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:b(process.env.MEMORY_RERANK_TOP,10),finalContext:b(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:b(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",r2:{accountId:process.env.R2_ACCOUNT_ID,bucket:process.env.R2_BUCKET,accessKeyId:process.env.R2_ACCESS_KEY_ID,secretAccessKey:process.env.R2_SECRET_ACCESS_KEY},s3:{endpoint:process.env.S3_ENDPOINT,region:process.env.S3_REGION,bucket:process.env.S3_BUCKET,accessKeyId:process.env.S3_ACCESS_KEY_ID,secretAccessKey:process.env.S3_SECRET_ACCESS_KEY,forcePathStyle:w(process.env.S3_FORCE_PATH_STYLE,!1)},huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:b(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as gt}from"node:crypto";import{existsSync as V,mkdirSync as ht,readFileSync as yt,renameSync as kt,writeFileSync as St}from"node:fs";import{basename as xt,dirname as O,join as R,parse as Se,resolve as j}from"node:path";var xe=".toolnet",vt="project.json";function wt(r){return gt("sha256").update(r).digest("hex").slice(0,16)}function H(r){return R(r,xe,vt)}function bt(r){return V(H(r))}function jt(r,t){let e=j(r),n=Se(e).root;for(;;){if(bt(e))return e;if(e===n||t&&e===j(t))break;let s=O(e);if(s===e)break;e=s}return null}function At(r){let t=j(r),e=Se(t).root,n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(n.some(o=>V(R(t,o))))return t;if(t===e)break;let s=O(t);if(s===t)break;t=s}return j(r)}function Pt(r){let t;try{t=JSON.parse(yt(r,"utf8"))}catch(s){throw new Error(`Invalid ToolNet project manifest: ${r}: ${s instanceof Error?s.message:String(s)}`)}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:O(O(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=R(r,xe);ht(e,{recursive:!0});let n=H(r),s=`${n}.tmp-${process.pid}`;St(s,JSON.stringify(t,null,2)+`
|
|
197
|
+
`,{encoding:"utf8",mode:384}),kt(s,n)}function ke(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=j(t),n=At(e),o=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(f=>V(R(n,f))),a=jt(e,o?n:void 0);if(a){let f=H(a),c=Pt(f);return c.rootPath!==a&&(c.rootPath=a,c.updatedAt=new Date().toISOString(),ye(a,c)),ke(c,a)}let d=new Date().toISOString(),i=xt(n),l={version:1,id:wt(n),name:i,remote:i,rootPath:n,createdAt:d,updatedAt:d,graphVersion:0,memoryVersion:0};return ye(n,l),ke(l,n)}};import{homedir as qt}from"node:os";import{join as Yt}from"node:path";import{DeleteObjectCommand as Ct,GetObjectCommand as Tt,HeadObjectCommand as It,ListObjectsV2Command as Ot,PutObjectCommand as Mt,S3Client as Rt}from"@aws-sdk/client-s3";import{getSignedUrl as Et}from"@aws-sdk/s3-request-presigner";var E=class{name="huggingface";client;bucket;constructor(t){this.bucket=t.bucket,this.client=new Rt({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 s=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new Mt({Bucket:this.bucket,Key:t,Body:s,ContentType:n}))}async get(t){let e=await Et(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 It({Bucket:this.bucket,Key:t})),!0}catch(e){if(typeof e=="object"&&e!==null&&"$metadata"in e&&e.$metadata?.httpStatusCode===404)return!1;throw e}}async delete(t){await this.client.send(new Ct({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],n;do{let s=await this.client.send(new Ot({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:n}));for(let o of s.Contents??[])o.Key&&e.push({key:o.Key,size:o.Size,updatedAt:o.LastModified?.toISOString()});n=s.IsTruncated?s.NextContinuationToken:void 0}while(n);return e}};import{access as ve,mkdir as Nt,readFile as $t,readdir as _t,rm as Ft,stat as we,writeFile as Wt}from"node:fs/promises";import{dirname as Dt,join as Kt,relative as be,resolve as Lt}from"node:path";var A=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(Dt(n),{recursive:!0}),await Wt(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 ve(this.path(t)),!0}catch{return!1}}async delete(t){await Ft(this.path(t),{force:!0})}async list(t=""){let e=this.path(t),n=[];try{await ve(e)}catch{return n}let s=async a=>{let d=await _t(a,{withFileTypes:!0});for(let i of d){let l=Kt(a,i.name);if(i.isDirectory()){await s(l);continue}let f=await we(l);n.push({key:be(this.root,l),size:f.size,updatedAt:f.mtime.toISOString()})}},o=await we(e);return o.isDirectory()?await s(e):n.push({key:be(this.root,e),size:o.size,updatedAt:o.mtime.toISOString()}),n}};import{DeleteObjectCommand as Bt,GetObjectCommand as Vt,HeadObjectCommand as Ht,ListObjectsV2Command as zt,PutObjectCommand as Jt,S3Client as Gt}from"@aws-sdk/client-s3";import{getSignedUrl as Ut}from"@aws-sdk/s3-request-presigner";var P=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 s=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new Jt({Bucket:this.bucket,Key:t,Body:s,ContentType:n}))}async get(t){let e=await Ut(this.client,new Vt({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 Bt({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],n;do{let s=await this.client.send(new zt({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:n}));for(let o of s.Contents??[])o.Key&&e.push({key:o.Key,size:o.Size,updatedAt:o.LastModified?.toISOString()});n=s.IsTruncated?s.NextContinuationToken:void 0}while(n);return e}};function z(r,t){return console.warn(t),new A(r)}function je(r){let t=r.localRoot??Yt(qt(),".toolnet-memory","storage");if(r.provider==="r2"){let e=r.r2;return e?.accountId&&e.bucket&&e.accessKeyId&&e.secretAccessKey?new P({name:"r2",endpoint:`https://${e.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:e.bucket,forcePathStyle:!0,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):z(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 P({name:"s3",endpoint:e.endpoint,region:e.region??"us-east-1",bucket:e.bucket,forcePathStyle:e.forcePathStyle??!1,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):z(t,"[storage] S3 credentials missing. Using local fallback.")}if(r.provider==="huggingface"){let e=r.huggingface;return e?.namespace&&e.bucket&&e.accessKeyId&&e.secretAccessKey?new E({namespace:e.namespace,bucket:e.bucket,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):z(t,"[storage] Hugging Face credentials missing. Using local fallback.")}return new A(t)}function Qt(r){return new Promise(t=>setTimeout(t,r))}async function Ae(r,t={}){let e=Math.max(1,t.attempts??3),n=t.baseDelayMs??150,s=t.maxDelayMs??2e3,o;for(let a=1;a<=e;a++)try{return await r()}catch(d){if(o=d,a>=e)break;let i=Math.min(s,n*2**(a-1)),l=Math.floor(Math.random()*Math.max(1,i*.2));await Qt(i+l)}throw o}var Xt=new Set(["put","get","getText","delete","list"]);function Pe(r,t={}){return new Proxy(r,{get(e,n){let s=Reflect.get(e,n,e);return typeof s!="function"?s:Xt.has(n)?(...o)=>Ae(()=>Promise.resolve(s.apply(e,o)),t):s.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 s=t.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return s?`${s[1]}memory/records/${s[2]}`:(s=t.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),s?`${s[1]}memory/vectors/${s[2]}`:(s=t.match(/^(projects\/[^/]+\/)graph\/(.+)$/),s?`${s[1]}code/graph/${s[2]}`:t))}var N=class{constructor(t,e,n,s){this.provider=t;this.name=t.name,this.projectId=e,this.projectName=n,this.folder=Ce(s??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,s=await this.provider.getText(t);if(s){let a;try{a=JSON.parse(s)}catch(d){throw new Error(`Invalid remote ToolNet project manifest at ${t}: ${d instanceof Error?d.message:String(d)}`)}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 o={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:n,updatedAt:e};await this.provider.put(t,JSON.stringify(o,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 sr(t);break;case"profile-sync":await or(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=D({projectPath:r.project});e||(console.error("No ToolNet project found. Run toolnet-memory init first."),process.exit(1)),process.stdout.write(e)}else(t==="focused"||t==="deep")&&(console.error("Focused and deep modes require storage access."),console.error("Use: toolnet-memory brief --deep for deep context"),process.exit(1))}async function rr(r){let t=D({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=v(t);e||(console.error("No ToolNet project found."),process.exit(1));let n=new M().detect(e),s=he(),o=Pe(je({provider:s.storage.provider,huggingface:s.storage.huggingface,localRoot:s.storage.localRoot}),{attempts:2}),a=new N(o,n.id,n.name,n.remote??n.name);await ge(n,a),console.log("Deep startup brief cache refreshed.")}async function sr(r){let t=r.project||process.cwd(),e=v(t);e||(console.error("No ToolNet project found."),process.exit(1));let n=Ie.join(e,".toolnet","profile.md");$.existsSync(n)||(console.error("No profile.md found in .toolnet directory."),process.exit(1));let s=$.readFileSync(n,"utf-8");process.stdout.write(s)}async function or(r){let t=ie({projectPath:r.project});console.log("Created/updated:");for(let e of t)console.log(`- ${e}`)}er();
|
package/bundle/context.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import{existsSync as Ne,readFileSync as $e}from"node:fs";import{homedir as _e}from"node:os";import{join as We}from"node:path";function Ke(r){let t=r.trim();return t.length>=2&&t.startsWith('"')&&t.endsWith('"')?(t=t.slice(1,-1),t.replace(/\\n/g,`
|
|
2
|
-
`).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):t.length>=2&&t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1):t}function Fe(){let r=process.env.TOOLNET_GLOBAL_ENV??We(_e(),".config","toolnet-memory",".env");if(!Ne(r))return;let t=$e(r,"utf8");for(let e of t.split(/\r?\n/)){let s=e.trim();if(!s||s.startsWith("#"))continue;s.startsWith("export ")&&(s=s.slice(7));let n=s.indexOf("=");if(n<=0)continue;let i=s.slice(0,n).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(i)&&process.env[i]===void 0&&(process.env[i]=Ke(s.slice(n+1)))}}Fe();function
|
|
3
|
-
`,{encoding:"utf8",mode:384}),Be(n,s)}function ce(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 $=class{detect(t=process.cwd()){let e=I(t),s=Ye(e),i=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(g=>U(_(s,g))),u=Ge(e,i?s:void 0);if(u){let g=q(u),a=Qe(g);return a.rootPath!==u&&(a.rootPath=u,a.updatedAt=new Date().toISOString(),ae(u,a)),ce(a,u)}let
|
|
4
|
-
`,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(t){if(t=ke(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,s){return await this.ensureRegistered(),this.provider.put(this.key(t),e,s)}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))}};import{join as
|
|
5
|
-
`,{encoding:"utf8",mode:384}),It(e,r)}function Se(r,t){let e=r.trim(),s=e.replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,""),n=S(e).slice(0,12);if(!s||s==="."||s==="..")return`${t}--${n}`;let i=s.slice(0,100);return i===e&&e.length<=100?i:`${i.slice(0,85)}--${n}`}function ve(r,t,e){let s=t.trim(),n=e.trim();if(!s)throw new Error("Session agent is required");if(!n)throw new Error("Native session ID is required");let i=Se(s.toLowerCase(),"agent"),u=Se(n,"session");return{projectId:r.id,projectName:r.name,projectRoot:r.rootPath,agent:s,nativeSessionId:n,sessionKey:`${s}:${n}`,remotePrefix:["projects",r.id,"sessions",i,u].join("/"),localDirectory:
|
|
2
|
+
`).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):t.length>=2&&t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1):t}function Fe(){let r=process.env.TOOLNET_GLOBAL_ENV??We(_e(),".config","toolnet-memory",".env");if(!Ne(r))return;let t=$e(r,"utf8");for(let e of t.split(/\r?\n/)){let s=e.trim();if(!s||s.startsWith("#"))continue;s.startsWith("export ")&&(s=s.slice(7));let n=s.indexOf("=");if(n<=0)continue;let i=s.slice(0,n).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(i)&&process.env[i]===void 0&&(process.env[i]=Ke(s.slice(n+1)))}}Fe();function A(r,t){return r===void 0?t:["1","true","yes","on"].includes(r.toLowerCase())}function P(r,t){if(!r)return t;let e=Number(r);return Number.isFinite(e)?e:t}function ie(){return{memory:{autoCapture:A(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:A(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:A(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:A(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:P(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:P(process.env.MEMORY_RERANK_TOP,10),finalContext:P(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:P(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:A(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:P(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as De}from"node:crypto";import{existsSync as U,mkdirSync as Le,readFileSync as Ve,renameSync as Be,writeFileSync as ze}from"node:fs";import{basename as He,dirname as N,join as _,parse as ue,resolve as I}from"node:path";var de=".toolnet",Je="project.json";function Ue(r){return De("sha256").update(r).digest("hex").slice(0,16)}function q(r){return _(r,de,Je)}function qe(r){return U(q(r))}function Ge(r,t){let e=I(r),s=ue(e).root;for(;;){if(qe(e))return e;if(e===s||t&&e===I(t))break;let n=N(e);if(n===e)break;e=n}return null}function Ye(r){let t=I(r),e=ue(t).root,s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(s.some(i=>U(_(t,i))))return t;if(t===e)break;let n=N(t);if(n===t)break;t=n}return I(r)}function Qe(r){let t;try{t=JSON.parse(Ve(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 s=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:N(N(r)),createdAt:typeof e.createdAt=="string"?e.createdAt:s,updatedAt:typeof e.updatedAt=="string"?e.updatedAt:s,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 ae(r,t){let e=_(r,de);Le(e,{recursive:!0});let s=q(r),n=`${s}.tmp-${process.pid}`;ze(n,JSON.stringify(t,null,2)+`
|
|
3
|
+
`,{encoding:"utf8",mode:384}),Be(n,s)}function ce(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 $=class{detect(t=process.cwd()){let e=I(t),s=Ye(e),i=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(g=>U(_(s,g))),u=Ge(e,i?s:void 0);if(u){let g=q(u),a=Qe(g);return a.rootPath!==u&&(a.rootPath=u,a.updatedAt=new Date().toISOString(),ae(u,a)),ce(a,u)}let l=new Date().toISOString(),c=He(s),p={version:1,id:Ue(s),name:c,remote:c,rootPath:s,createdAt:l,updatedAt:l,graphVersion:0,memoryVersion:0};return ae(s,p),ce(p,s)}};import{homedir as vt}from"node:os";import{join as wt}from"node:path";import{DeleteObjectCommand as Ze,GetObjectCommand as Xe,HeadObjectCommand as et,ListObjectsV2Command as tt,PutObjectCommand as rt,S3Client as st}from"@aws-sdk/client-s3";import{getSignedUrl as nt}from"@aws-sdk/s3-request-presigner";var W=class{name="huggingface";client;bucket;constructor(t){this.bucket=t.bucket,this.client=new st({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,s="application/octet-stream"){let n=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new rt({Bucket:this.bucket,Key:t,Body:n,ContentType:s}))}async get(t){let e=await nt(this.client,new Xe({Bucket:this.bucket,Key:t}),{expiresIn:60}),s=await fetch(e,{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(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 et({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 Ze({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],s;do{let n=await this.client.send(new tt({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:s}));for(let i of n.Contents??[])i.Key&&e.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});s=n.IsTruncated?n.NextContinuationToken:void 0}while(s);return e}};import{access as le,mkdir as ot,readFile as it,readdir as at,rm as ct,stat as pe,writeFile as ut}from"node:fs/promises";import{dirname as dt,join as lt,relative as me,resolve as pt}from"node:path";var T=class{constructor(t){this.root=t}root;name="local";path(t){let e=t.replace(/^\/+/,"");return pt(this.root,e)}async put(t,e){let s=this.path(t);await ot(dt(s),{recursive:!0}),await ut(s,e)}async get(t){try{return await it(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 le(this.path(t)),!0}catch{return!1}}async delete(t){await ct(this.path(t),{force:!0})}async list(t=""){let e=this.path(t),s=[];try{await le(e)}catch{return s}let n=async u=>{let l=await at(u,{withFileTypes:!0});for(let c of l){let p=lt(u,c.name);if(c.isDirectory()){await n(p);continue}let g=await pe(p);s.push({key:me(this.root,p),size:g.size,updatedAt:g.mtime.toISOString()})}},i=await pe(e);return i.isDirectory()?await n(e):s.push({key:me(this.root,e),size:i.size,updatedAt:i.mtime.toISOString()}),s}};import{DeleteObjectCommand as mt,GetObjectCommand as ft,HeadObjectCommand as gt,ListObjectsV2Command as ht,PutObjectCommand as yt,S3Client as kt}from"@aws-sdk/client-s3";import{getSignedUrl as St}from"@aws-sdk/s3-request-presigner";var M=class{name;client;bucket;constructor(t){this.name=t.name??"s3",this.bucket=t.bucket,this.client=new kt({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,s="application/octet-stream"){let n=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new yt({Bucket:this.bucket,Key:t,Body:n,ContentType:s}))}async get(t){let e=await St(this.client,new ft({Bucket:this.bucket,Key:t}),{expiresIn:60}),s=await fetch(e,{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(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 gt({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 mt({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],s;do{let n=await this.client.send(new ht({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:s}));for(let i of n.Contents??[])i.Key&&e.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});s=n.IsTruncated?n.NextContinuationToken:void 0}while(s);return e}};function G(r,t){return console.warn(t),new T(r)}function fe(r){let t=r.localRoot??wt(vt(),".toolnet-memory","storage");if(r.provider==="r2"){let e=r.r2;return e?.accountId&&e.bucket&&e.accessKeyId&&e.secretAccessKey?new M({name:"r2",endpoint:`https://${e.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:e.bucket,forcePathStyle:!0,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):G(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 M({name:"s3",endpoint:e.endpoint,region:e.region??"us-east-1",bucket:e.bucket,forcePathStyle:e.forcePathStyle??!1,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):G(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 W({namespace:e.namespace,bucket:e.bucket,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):G(t,"[storage] Hugging Face credentials missing. Using local fallback.")}return new T(t)}function bt(r){return new Promise(t=>setTimeout(t,r))}async function ge(r,t={}){let e=Math.max(1,t.attempts??3),s=t.baseDelayMs??150,n=t.maxDelayMs??2e3,i;for(let u=1;u<=e;u++)try{return await r()}catch(l){if(i=l,u>=e)break;let c=Math.min(n,s*2**(u-1)),p=Math.floor(Math.random()*Math.max(1,c*.2));await bt(c+p)}throw i}var jt=new Set(["put","get","getText","delete","list"]);function he(r,t={}){return new Proxy(r,{get(e,s){let n=Reflect.get(e,s,e);return typeof n!="function"?n:jt.has(s)?(...i)=>ge(()=>Promise.resolve(n.apply(e,i)),t):n.bind(e)}})}function ye(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 ke(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 s=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(s)return`${s[1]}memory/records/${s[2]}`;if(s=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),s)return`${s[1]}memory/vectors/${s[2]}`;if(s=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),s)return`${s[1]}code/graph/${s[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 K=class{constructor(t,e,s,n){this.provider=t;this.name=t.name,this.projectId=e,this.projectName=s,this.folder=ye(n??s),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(),s=e,n=await this.provider.getText(t);if(n){let u;try{u=JSON.parse(n)}catch(l){throw new Error(`Invalid remote ToolNet project manifest at ${t}: ${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 i={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:s,updatedAt:e};await this.provider.put(t,JSON.stringify(i,null,2)+`
|
|
4
|
+
`,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(t){if(t=ke(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,s){return await this.ensureRegistered(),this.provider.put(this.key(t),e,s)}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))}};import{join as Mt}from"node:path";import{createHash as xt}from"node:crypto";import{dirname as At}from"node:path";import{mkdirSync as Pt,readFileSync as nn,renameSync as It,writeFileSync as Tt}from"node:fs";function S(r){return xt("sha256").update(r).digest("hex")}function x(r,t){Pt(At(r),{recursive:!0});let e=`${r}.${process.pid}.tmp`;Tt(e,JSON.stringify(t,null,2)+`
|
|
5
|
+
`,{encoding:"utf8",mode:384}),It(e,r)}function Se(r,t){let e=r.trim(),s=e.replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,""),n=S(e).slice(0,12);if(!s||s==="."||s==="..")return`${t}--${n}`;let i=s.slice(0,100);return i===e&&e.length<=100?i:`${i.slice(0,85)}--${n}`}function ve(r,t,e){let s=t.trim(),n=e.trim();if(!s)throw new Error("Session agent is required");if(!n)throw new Error("Native session ID is required");let i=Se(s.toLowerCase(),"agent"),u=Se(n,"session");return{projectId:r.id,projectName:r.name,projectRoot:r.rootPath,agent:s,nativeSessionId:n,sessionKey:`${s}:${n}`,remotePrefix:["projects",r.id,"sessions",i,u].join("/"),localDirectory:Mt(r.rootPath,".toolnet","sessions",i,u)}}import{existsSync as je,mkdirSync as Rt,readFileSync as Ct,statSync as we,writeFileSync as Ot}from"node:fs";import{dirname as Et,join as Nt}from"node:path";var be=64*1024,$t=`# 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,19 @@ Things an AI agent should always remember.
|
|
|
45
45
|
<!--
|
|
46
46
|
- [advisory] Prefer small focused files.
|
|
47
47
|
-->
|
|
48
|
-
`;function F(r){return Nt(r.rootPath,".toolnet","PROJECT.md")}function _t(r){return r.normalize("NFKC").replace(/\s+/g," ").trim()}function Wt(r){let t=[],e=new Set,s=/^\s*[-*]\s+\[(enforce|advisory)\]\s+(.+?)\s*$/gimu,n;for(;n=s.exec(r);){let i=n[1].toLowerCase(),u=_t(n[2]);if(!u)continue;let
|
|
49
|
-
`,"application/json"),J}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}}import{existsSync as Bt,mkdirSync as
|
|
50
|
-
`).toLowerCase();return/(?:failed|failing|failure|error|✗|❌)/u.test(s)?"failing":/(?:passed|passing|green|success|✓|✅)/u.test(s)?"passing":"unknown"}function qt(r){return S(JSON.stringify(r))}function
|
|
48
|
+
`;function F(r){return Nt(r.rootPath,".toolnet","PROJECT.md")}function _t(r){return r.normalize("NFKC").replace(/\s+/g," ").trim()}function Wt(r){let t=[],e=new Set,s=/^\s*[-*]\s+\[(enforce|advisory)\]\s+(.+?)\s*$/gimu,n;for(;n=s.exec(r);){let i=n[1].toLowerCase(),u=_t(n[2]);if(!u)continue;let l=`${i}:${u.toLowerCase()}`;e.has(l)||(e.add(l),t.push({id:S(l).slice(0,24),mode:i,text:u,source:"manual"}))}return t}function Kt(r){let t=F(r);return je(t)||(Rt(Et(t),{recursive:!0}),Ot(t,$t,{encoding:"utf8",mode:384})),t}function D(r,t=!1){let e=t?Kt(r):F(r);if(!je(e))return null;if(we(e).size>be)throw new Error(`PROJECT.md exceeds ${be} bytes`);let n=Ct(e,"utf8");return{path:e,content:n,digest:S(n),rules:Wt(n),bytes:Buffer.byteLength(n,"utf8"),updatedAt:new Date(we(e).mtimeMs).toISOString()}}import{existsSync as Yt,mkdirSync as Qt}from"node:fs";import{join as Y}from"node:path";import{join as xe}from"node:path";import{mkdirSync as Ft}from"node:fs";function Pe(r){return r.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}]+/gu," ").replace(/\s+/g," ").trim()}function v(r,t=20){let e=[],s=new Set;for(let n of r.slice().reverse()){let i=Pe(n);if(!(!i||s.has(i))&&(s.add(i),e.push(n),e.length>=t))break}return e.reverse()}function Dt(r,t=20){let e=new Map;for(let s of r){let n=`${s.kind}|${Pe(s.command)}`;e.delete(n),e.set(n,s)}return Array.from(e.values()).slice(-t)}function Lt(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 Ae(r,t){let e=t.status??r?.status??"pending",s=e;r&&(r.status==="completed"&&e!=="completed"?s="completed":e==="pending"&&(r.status==="in_progress"||r.status==="blocked")&&(s=r.status));let n=r&&Lt(t)?r.title:t.text;return{id:r?.id??S(t.key).slice(0,24),title:n,status:s,order:t.order??r?.order,confidence:Math.max(t.confidence,r?.confidence??0),updatedAt:t.occurredAt,updatedBy:{agent:t.agent,nativeSessionId:t.nativeSessionId,eventId:t.eventId}}}async function Vt(r,t){let e=`projects/${r.id}/work/observations/`,s=await t.list(e),n=[];for(let i of s.filter(u=>u.key.endsWith(".json")).sort((u,l)=>u.key.localeCompare(l.key))){let u=await t.getText(i.key);if(u)try{let l=JSON.parse(u);l.version===1&&Array.isArray(l.observations)&&n.push(l)}catch{}}return n}async function Ie(r,t){let s=(await Vt(r,t)).flatMap(o=>o.observations).sort((o,k)=>{let ne=o.occurredAt.localeCompare(k.occurredAt);if(ne!==0)return ne;let oe=o.sequence-k.sequence;return oe!==0?oe:o.id.localeCompare(k.id)}),n=new Map,i=new Map,u,l,c,p,g,a=[],j=[],d=[],y=[],z=[],C=new Map,O=[],m=[],X=[],ee=[],te=[],re=[];for(let o of s)switch(o.kind){case"request":u=o.text;break;case"activity":l=o.text;break;case"goal":c=o.text;break;case"plan":p=o.text;break;case"phase":n.set(o.key,Ae(n.get(o.key),o));break;case"task":i.set(o.key,Ae(i.get(o.key),o));break;case"decision":a.push(o.text);break;case"blocker":j.push(o.text);break;case"warning":d.push(o.text);break;case"next_action":y.push(o.text);break;case"file":{z.push(o.text);let k=o.fileAction??"active";C.delete(o.text),C.set(o.text,k),k==="modified"?O.push(o.text):k==="created"?m.push(o.text):k==="deleted"&&X.push(o.text);break}case"command":ee.push(o.text);break;case"test":te.push(o.text),o.checkKind&&re.push({kind:o.checkKind,command:o.text,status:o.checkStatus??"unknown",updatedAt:o.occurredAt,agent:o.agent,nativeSessionId:o.nativeSessionId});break;case"session":g={agent:o.agent,nativeSessionId:o.nativeSessionId,sessionKey:o.sessionKey,updatedAt:o.occurredAt};break}let b=Array.from(n.values()).sort((o,k)=>(o.order??Number.MAX_SAFE_INTEGER)-(k.order??Number.MAX_SAFE_INTEGER)),w=Array.from(i.values()).sort((o,k)=>(o.order??Number.MAX_SAFE_INTEGER)-(k.order??Number.MAX_SAFE_INTEGER)),H=b.find(o=>o.status==="in_progress")??b.find(o=>o.status==="blocked")??b.find(o=>o.status==="pending"),E=w.find(o=>o.status==="in_progress")??w.find(o=>o.status==="blocked")??w.find(o=>o.status==="pending"),Oe=v([...y,...E?[E.title]:[],...!E&&H?[H.title]:[],...w.filter(o=>o.status==="pending").slice(0,5).map(o=>o.title)],8),Ee=v([...j,...b.filter(o=>o.status==="blocked").map(o=>o.title),...w.filter(o=>o.status==="blocked").map(o=>o.title)],20),J={version:1,projectId:r.id,projectName:r.name,currentRequest:u,currentActivity:l,goal:c,plan:p,phases:b,tasks:w,decisions:v(a,20),blockers:Ee,warnings:v(d,20),nextActions:Oe,filesTouched:v(z,30),activeFiles:Array.from(C.entries()).filter(([,o])=>o!=="deleted").map(([o])=>o).slice(-5),modifiedFiles:v(O,30),createdFiles:v(m,30),deletedFiles:v(X,30),commands:v(ee,20),tests:v(te,20),checks:Dt(re,20),currentPhase:H,currentTask:E,progress:{phasesTotal:b.length,phasesCompleted:b.filter(o=>o.status==="completed").length,tasksTotal:w.length,tasksCompleted:w.filter(o=>o.status==="completed").length,blocked:b.filter(o=>o.status==="blocked").length+w.filter(o=>o.status==="blocked").length},lastSession:g,updatedAt:s.length?s[s.length-1].occurredAt:new Date().toISOString()},se=xe(r.rootPath,".toolnet","work");return Ft(se,{recursive:!0}),x(xe(se,"current.json"),J),await t.put(`projects/${r.id}/work/current.json`,JSON.stringify(J,null,2)+`
|
|
49
|
+
`,"application/json"),J}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}}import{existsSync as Bt,mkdirSync as Sn,readFileSync as zt,renameSync as vn,writeFileSync as wn}from"node:fs";import{dirname as jn,join as Ht}from"node:path";function Jt(r){return Ht(r.rootPath,".toolnet","work","current.json")}function Te(r){let t=Jt(r);if(!Bt(t))return null;try{let e=JSON.parse(zt(t,"utf8"));return e.version!==1||e.projectId!==r.id?null:e}catch{return null}}function h(r,t){let e=new Set,s=[];for(let n of r){let i=n.replace(/\s+/g," ").trim();if(!i)continue;let u=i.normalize("NFKC").toLowerCase();if(!e.has(u)&&(e.add(u),s.push(i),s.length>=t))break}return s}function Me(r){if(r)return{id:r.id,title:r.title,status:r.status}}function Ut(r,t=[]){let e=t.slice(-10);if(e.some(n=>n.status==="failed"))return"failing";if(e.some(n=>n.status==="passed"))return"passing";let s=r.slice(-10).join(`
|
|
50
|
+
`).toLowerCase();return/(?:failed|failing|failure|error|✗|❌)/u.test(s)?"failing":/(?:passed|passing|green|success|✓|✅)/u.test(s)?"passing":"unknown"}function qt(r){return S(JSON.stringify(r))}function Gt(r){let t=[];for(let e of r){if(!e)continue;let s=e.match(/https?:\/\/[^\s<>"'`)\]}]+/giu)??[];for(let n of s){let i=n.replace(/[.,;:!?]+$/gu,"").trim();i&&t.push(i)}}return h(t,30)}function Re(r){let{project:t,identity:e,state:s}=r,n=s.activeFiles?.at(-1)??s.filesTouched.at(-1),i=s.phases.filter(m=>m.status==="completed").map(m=>m.title),u=s.tasks.filter(m=>m.status==="completed").map(m=>m.title),l=s.phases.filter(m=>m.status!=="completed"&&m.status!=="cancelled").map(m=>m.title),c=s.tasks.filter(m=>m.status!=="completed"&&m.status!=="cancelled").map(m=>m.title),p=new Set(s.tasks.filter(m=>m.status==="completed").map(m=>m.title.normalize("NFKC").toLowerCase().replace(/\s+/gu," ").trim())),g=h(s.nextActions.filter(m=>!p.has(m.normalize("NFKC").toLowerCase().replace(/\s+/gu," ").trim())),10),a=h([...c,...g],15),j=h(s.tests.slice().reverse(),10),d=h([...(s.activeFiles??[]).slice().reverse(),...s.filesTouched.slice().reverse()],20),y={schema:"toolnet.handoff.v2",version:2,project:{id:t.id,name:t.name},source:{agent:e.agent,nativeSessionId:e.nativeSessionId,sessionKey:e.sessionKey,sequence:r.sequence,reason:r.reason},capturedAt:r.capturedAt??new Date().toISOString(),goal:s.goal,request:s.currentRequest,activity:s.currentActivity,current:{phase:Me(s.currentPhase),task:Me(s.currentTask),file:n},completed:{phases:h(i,20),tasks:h(u,30)},remaining:{phases:h(l,20),tasks:h(c,30),todos:a},nextAction:g[0],blockers:h(s.blockers.slice().reverse(),10),decisions:h(s.decisions.slice().reverse(),10),files:{current:n,recent:d,active:h(s.activeFiles??[],10),modified:h(s.modifiedFiles??[],20),created:h(s.createdFiles??[],20),deleted:h(s.deletedFiles??[],20)},tests:{status:Ut(s.tests,s.checks),recent:j,checks:(s.checks??[]).slice(-10).map(m=>({kind:m.kind,status:m.status,command:m.command}))},evidence:{commands:h((s.commands??[]).slice().reverse(),20),references:Gt([s.currentRequest,s.currentActivity,s.goal,s.plan,...s.decisions,...s.blockers,...s.warnings,...s.nextActions,...s.filesTouched,...s.commands??[],...s.tests,...(s.checks??[]).map(m=>m.command)])},attention:h(r.attention??[],20),progress:s.progress},{capturedAt:z,source:C,...O}=y;return{...y,stateDigest:qt(O)}}function Zt(r){return!!(r.currentRequest||r.currentActivity||r.goal||r.plan||r.phases.length>0||r.tasks.length>0||r.nextActions.length>0||r.blockers.length>0||r.decisions.length>0||r.filesTouched.length>0)}function Xt(r,t,e,s,n){if(!Zt(e))return null;let i=D(r,!1),l=[...i?i.rules.filter(a=>a.mode==="enforce").map(a=>a.text):[],...e.warnings].slice(0,20),c=Re({project:r,identity:t,state:e,reason:s,sequence:n,attention:l}),p=c.stateDigest;return{version:1,id:S([r.id,t.sessionKey,p].join("|")).slice(0,24),projectId:r.id,projectName:r.name,createdAt:new Date().toISOString(),reason:s,sourceSession:{agent:t.agent,nativeSessionId:t.nativeSessionId,sessionKey:t.sessionKey,sequence:n},currentRequest:e.currentRequest,currentActivity:e.currentActivity,goal:e.goal,plan:e.plan,progress:e.progress,currentPhase:e.currentPhase,currentTask:e.currentTask,incompletePhases:e.phases.filter(a=>a.status!=="completed"&&a.status!=="cancelled"),incompleteTasks:e.tasks.filter(a=>a.status!=="completed"&&a.status!=="cancelled"),nextActions:c.remaining.todos.slice(0,10),blockers:e.blockers.slice(-10),decisions:e.decisions.slice(-10),warnings:e.warnings.slice(-10),attention:l,filesTouched:e.filesTouched.slice(-20),activeFiles:e.activeFiles?.slice(-10),modifiedFiles:e.modifiedFiles?.slice(-20),createdFiles:e.createdFiles?.slice(-20),deletedFiles:e.deletedFiles?.slice(-20),tests:e.tests.slice(-15),checks:e.checks?.slice(-10),stateDigest:p,continuity:c}}function er(r,t){let e=Y(r.rootPath,".toolnet","work","handoffs");Qt(e,{recursive:!0});let s=Y(e,`${t.id}.json`);Yt(s)||x(s,t),x(Y(r.rootPath,".toolnet","work","handoff-latest.json"),t)}var V=class{constructor(t){this.options=t}options;async capture(t,e){let s=Te(this.options.project);s||(s=await L(this.options.project,this.options.storage)),s||(s=await Ie(this.options.project,this.options.storage));let n=Xt(this.options.project,this.options.identity,s,t,e);if(!n)return null;er(this.options.project,n);let i=`projects/${this.options.project.id}/work/handoffs/${n.id}.json`;return await this.options.storage.exists(i)||await this.options.storage.put(i,JSON.stringify(n,null,2)+`
|
|
51
51
|
`,"application/json"),await this.options.storage.put(`projects/${this.options.project.id}/work/handoff-latest.json`,JSON.stringify(n,null,2)+`
|
|
52
|
-
`,"application/json"),n}};async function B(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
|
|
52
|
+
`,"application/json"),n}};async function B(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 Ce(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 Q(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 f(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 tr(r){let t=[],e=!1;for(let s of r.split(/\r?\n/u)){let n=s.trim();if(n.includes("<!--")&&(e=!0),e){n.includes("-->")&&(e=!1);continue}let i=n.toLowerCase();if(!(!n||n.startsWith("#")||n==="```"||i.startsWith("- [enforce]")||i.startsWith("* [enforce]")||i.startsWith("- [advisory]")||i.startsWith("* [advisory]"))&&(n=n.replace(/^[-*]\s+/u,""),n&&t.push(f(n,280)),t.length>=16))break}return t}function rr(r){let t=[],e=[];for(let s of r.split(/\\r?\\n/u)){let n=s.trim(),i=n.toLowerCase(),l=["- [enforce]","* [enforce]","- [advisory]","* [advisory]"].find(p=>i.startsWith(p));if(!l)continue;let c=n.slice(l.length).trim();c&&(l.includes("enforce")?t.push(c):e.push(c))}return{enforce:t,advisory:e}}function sr(r,t){let e=[];for(let s of r){let n=[...e,s].join(`
|
|
53
53
|
`);if(Q(n)<=t){e.push(s);continue}let i=Q(e.join(`
|
|
54
|
-
`)),u=Math.max(0,t-i);if(u>=16){let
|
|
55
|
-
`).trim()}async function Z(r){let t=Math.max(256,Math.min(2e3,r.maxTokens??1e3)),e=D(r.project,!1),s=e?.content??"";s||(s=await r.storage.getText(`projects/${r.project.id}/project/manual.md`)??"");let n=
|
|
56
|
-
`);return}if(r==="brief-json"){let n=await Z({project:e,storage:s,maxTokens:Number(
|
|
54
|
+
`)),u=Math.max(0,t-i);if(u>=16){let l=Math.floor(u*3.2),c=f(s,l);c&&e.push(c)}break}return e.join(`
|
|
55
|
+
`).trim()}async function Z(r){let t=Math.max(256,Math.min(2e3,r.maxTokens??1e3)),e=D(r.project,!1),s=e?.content??"";s||(s=await r.storage.getText(`projects/${r.project.id}/project/manual.md`)??"");let n=rr(s),i=e?e.rules.filter(d=>d.mode==="enforce").map(d=>d.text):n.enforce,u=e?e.rules.filter(d=>d.mode==="advisory").map(d=>d.text):n.advisory,l=s?tr(s):[],c=await L(r.project,r.storage),p=await Ce(r.project,r.storage),g=await B(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: ${F(r.project)}`),i.length){a.push("","PROJECT RULES \u2014 MUST FOLLOW");for(let d of i.slice(0,24))a.push(`- [ENFORCE] ${f(d,240)}`)}if(u.length){a.push("","PROJECT PREFERENCES");for(let d of u.slice(0,10))a.push(`- ${f(d,220)}`)}if(p&&(p.mission&&a.push("","MISSION",f(p.mission.value,420)),p.activeObjective&&a.push("","CURRENT OBJECTIVE",f(p.activeObjective.value,420)),p.why&&a.push("","WHY THIS WORK MATTERS",f(p.why.value,420)),p.desiredOutcome&&a.push("","DESIRED OUTCOME",f(p.desiredOutcome.value,420)),p.planRationale&&a.push("","WHY THIS APPROACH",f(p.planRationale.value,420))),c){if(a.push("","ACTIVE WORK"),c.goal&&a.push(`Goal: ${f(c.goal,320)}`),c.plan&&a.push(`Plan: ${f(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&&p){let d=p.phases.find(y=>y.order===c.currentPhase?.order);d&&(d.objective&&a.push(`Phase objective: ${f(d.objective.value,340)}`),d.why?a.push(`Why this phase: ${f(d.why.value,340)}`):a.push("Why this phase: not explicitly recorded. Inspect existing implementation before assuming intent."),d.deliverable&&a.push(`Deliverable: ${f(d.deliverable.value,340)}`),d.dependencies.length&&a.push(`Depends on: ${d.dependencies.slice(0,4).map(y=>f(y.value,180)).join("; ")}`),d.acceptanceCriteria.length&&(a.push("","DEFINITION OF DONE"),d.acceptanceCriteria.slice(0,6).forEach(y=>{a.push(`- ${f(y.value,260)}`)})),d.openQuestions.length&&(a.push("","OPEN QUESTIONS FOR CURRENT PHASE"),d.openQuestions.slice(0,4).forEach(y=>{a.push(`- ${f(y.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((d,y)=>{a.push(`${y+1}. ${f(d,260)}`)})),c.blockers.length&&(a.push("","BLOCKERS"),c.blockers.slice(0,5).forEach(d=>{a.push(`- ${f(d,260)}`)})),c.warnings.length&&(a.push("","ATTENTION"),c.warnings.slice(-5).forEach(d=>{a.push(`- ${f(d,260)}`)})),c.decisions.length&&(a.push("","RECENT DECISIONS"),c.decisions.slice(-5).forEach(d=>{a.push(`- ${f(d,260)}`)})),c.lastSession&&a.push("",`Last work session: ${c.lastSession.agent} / ${c.lastSession.nativeSessionId}`)}if(p&&p.openQuestions.length&&(a.push("","UNRESOLVED QUESTIONS"),p.openQuestions.slice(0,5).forEach(d=>{a.push(`- ${f(d.value,260)}`)})),g&&a.push(`Latest handoff: ${g.reason} / ${g.sourceSession.agent}`),l.length){a.push("","OPERATING NOTES");for(let d of l)a.push(`- ${d}`)}a.push("","Before changing anything: verify the current repository state and continue from the active phase/task above.");let j=sr(a,t);return{version:1,projectId:r.project.id,projectName:r.project.name,text:j,estimatedTokens:Q(j),maxTokens:t,hasManual:!!s,hasWorkState:!!c,hasHandoff:!!g,generatedAt:new Date().toISOString()}}function R(r,t){let e=r.indexOf(t);return e>=0?r[e+1]:void 0}function nr(r){let t=ie(),e=he(fe({provider:t.storage.provider,huggingface:t.storage.huggingface,localRoot:t.storage.localRoot}),{attempts:3});return new K(e,r.id,r.name,r.remote??r.name)}async function or(){let[r="help",...t]=process.argv.slice(2),e=new $().detect(R(t,"--project")??process.cwd()),s=nr(e);if(r==="brief"){let n=Number(R(t,"--tokens")??1e3),i=await Z({project:e,storage:s,maxTokens:Number.isFinite(n)?n:1e3});process.stdout.write(i.text+`
|
|
56
|
+
`);return}if(r==="brief-json"){let n=await Z({project:e,storage:s,maxTokens:Number(R(t,"--tokens")??1e3)});console.log(JSON.stringify(n,null,2));return}if(r==="handoff-latest"){let n=await B(e,s);console.log(JSON.stringify(n,null,2));return}if(r==="handoff-create"){let n=R(t,"--agent")??"manual";if(n==="manual")throw new Error("--agent must be opencode, agy, or codex");let i=R(t,"--session");if(!i)throw new Error("--session is required");let u=ve(e,n,i),c=await new V({project:e,storage:s,identity:u}).capture("manual",0);console.log(JSON.stringify(c,null,2));return}console.log(`Work Context
|
|
57
57
|
|
|
58
58
|
Commands:
|
|
59
59
|
brief [--project PATH] [--tokens N]
|
|
60
60
|
brief-json [--project PATH] [--tokens N]
|
|
61
61
|
handoff-latest [--project PATH]
|
|
62
62
|
handoff-create --agent <opencode|agy|codex> --session <id>
|
|
63
|
-
`)}
|
|
63
|
+
`)}or().catch(r=>{console.error(r instanceof Error?r.message:r),process.exit(1)});
|