toolnet-memory 0.2.20 → 0.2.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/toolnet-memory +71 -0
- package/bundle/agy-hook.js +40 -39
- package/bundle/agy.js +82 -34
- package/bundle/auto-integrate.js +66 -19
- package/bundle/claude-hook.js +73 -0
- package/bundle/claude.js +3 -0
- package/bundle/codex-context.js +19 -19
- package/bundle/codex.js +61 -60
- package/bundle/context-runtime.js +30 -30
- package/bundle/context.js +15 -14
- package/bundle/continuity-certify.js +49 -0
- package/bundle/doctor.js +4 -4
- package/bundle/full-index.js +18 -8
- package/bundle/graph-index.js +4 -4
- package/bundle/handoff-refresh.js +8 -8
- package/bundle/impact.js +3 -3
- package/bundle/incremental.js +4 -4
- package/bundle/init.js +763 -0
- package/bundle/learner.js +5 -5
- package/bundle/mcp.js +17 -15
- package/bundle/memory-agent.js +10 -11
- package/bundle/memory-query.js +7 -7
- package/bundle/opencode.js +54 -53
- package/bundle/production-certify.js +5 -0
- package/bundle/project-manual.js +6 -6
- package/bundle/recovery-certify.js +55 -0
- package/bundle/runtime.js +10 -10
- package/bundle/semantic.js +6 -6
- package/bundle/setup.js +69 -22
- package/bundle/snapshot.js +4 -4
- package/bundle/update.js +4 -1
- package/bundle/work-continuity.js +6 -6
- package/package.json +1 -1
package/bundle/auto-integrate.js
CHANGED
|
@@ -1,10 +1,55 @@
|
|
|
1
|
-
import{existsSync as
|
|
2
|
-
`,{encoding:"utf8",mode:384}),
|
|
3
|
-
`,{encoding:"utf8",mode:384}),
|
|
1
|
+
import{existsSync as Ae}from"node:fs";import{homedir as Te}from"node:os";import{join as Me}from"node:path";import{spawnSync as Fe}from"node:child_process";import{homedir as je}from"node:os";import{join as p}from"node:path";function P(e={}){return p(e.home??je(),".gemini")}function k(e={}){return p(P(e),"config")}function v(e={}){return p(k(e),"mcp_config.json")}function C(e={}){return p(k(e),"hooks.json")}function _(e={}){return p(P(e),"antigravity-cli")}function $(e="toolnet-memory",n={}){return p(_(n),"plugins",e)}function J(e={}){return[_(e),k(e)]}import{homedir as Ee}from"node:os";import{join as y}from"node:path";function g(e={}){let n=e.xdgConfigHome??process.env.XDG_CONFIG_HOME?.trim();return n?y(n,"opencode"):y(e.home??Ee(),".config","opencode")}function D(e={}){return y(g(e),"opencode.json")}function H(e={}){return y(g(e),"plugins")}function L(e={}){return y(g(e),"AGENTS.md")}import{homedir as B}from"node:os";import{join as N}from"node:path";function I(e={}){return N(e.home??B(),".claude")}function Y(e={}){return N(I(e),"settings.json")}function U(e={}){return N(e.home??B(),".claude.json")}function Re(e){return Fe("sh",["-lc",`command -v ${JSON.stringify(e)} >/dev/null 2>&1`],{stdio:"ignore"}).status===0}function S(e){let n=e.commandExists(e.command),t=e.configPaths.filter(s=>Ae(s)),o=t.length>0,i=[];n&&i.push(`command:${e.command}`);for(let s of t)i.push(`config:${s}`);return{agent:e.agent,detected:n||o,commandDetected:n,configDetected:o,evidence:i}}function q(e={}){let n=e.home??Te(),t=e.commandExists??Re,o=e.codexHome??process.env.CODEX_HOME??Me(n,".codex");return[S({agent:"agy",command:"agy",commandExists:t,configPaths:J({home:n})}),S({agent:"opencode",command:"opencode",commandExists:t,configPaths:[g({home:n,xdgConfigHome:e.xdgConfigHome})]}),S({agent:"claude",command:"claude",commandExists:t,configPaths:[I({home:n})]}),S({agent:"codex",command:"codex",commandExists:t,configPaths:[o]})]}import{existsSync as w,mkdirSync as z,readFileSync as Q,renameSync as Ke,writeFileSync as ze}from"node:fs";import{dirname as Qe,join as b}from"node:path";import{existsSync as Pe,mkdirSync as _e,readFileSync as $e,renameSync as Je,rmSync as De,writeFileSync as He}from"node:fs";import{dirname as Le}from"node:path";function Be(e){return`'${e.replace(/'/g,"'\\''")}'`}function G(e={}){let n=e.hooksFile??C();_e(Le(n),{recursive:!0,mode:448});let t={};if(Pe(n)){let r;try{r=JSON.parse($e(n,"utf8"))}catch(c){throw new Error(`Invalid existing Agy hooks.json: ${c instanceof Error?c.message:String(c)}`)}if(typeof r!="object"||r===null||Array.isArray(r))throw new Error("Invalid existing Agy hooks.json: root must be a JSON object.");t=r}let o=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",i=`${Be(o)} session:agy-hook`;t["toolnet-memory"]={enabled:!0,PreToolUse:[{matcher:"view_file|list_dir|find_by_name|grep_search|run_command",hooks:[{type:"command",command:`${i} pre-tool`,timeout:5}]}],PreInvocation:[{type:"command",command:`${i} pre`,timeout:15}],PostInvocation:[{type:"command",command:`${i} post`,timeout:15}],Stop:[{type:"command",command:`${i} stop`,timeout:30}]};let s=`${n}.tmp-${process.pid}-${Date.now()}`;try{He(s,JSON.stringify(t,null,2)+`
|
|
2
|
+
`,{encoding:"utf8",mode:384}),Je(s,n)}finally{De(s,{force:!0})}return n}import{existsSync as Ye,mkdirSync as Ue,readFileSync as qe,renameSync as Ge,writeFileSync as We}from"node:fs";import{dirname as Xe}from"node:path";function h(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Ve(e,n){Ue(Xe(e),{recursive:!0,mode:448});let t=`${e}.tmp-${process.pid}-${Date.now()}`;We(t,`${JSON.stringify(n,null,2)}
|
|
3
|
+
`,{encoding:"utf8",mode:384}),Ge(t,e)}function W(e){if(!Ye(e))return{};let n=qe(e,"utf8").trim();if(!n)return{};let t;try{t=JSON.parse(n)}catch(o){throw new Error(`Invalid existing Agy MCP config: ${o instanceof Error?o.message:String(o)}`)}if(!h(t))throw new Error("Invalid existing Agy MCP config: root must be a JSON object.");return t}function X(e,n){return h(e)?e.command===n&&Array.isArray(e.args)&&e.args.length===1&&e.args[0]==="mcp":!1}function V(e={}){let n=e.configFile??v(),t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=e.serverName??"toolnet-memory",i=W(n),s=i.mcpServers;if(s!==void 0&&!h(s))throw new Error("Invalid existing Agy MCP config: mcpServers must be an object.");let r=h(s)?{...s}:{},c=r[o];if(X(c,t))return{installed:!0,changed:!1,configFile:n,serverName:o,command:t,args:["mcp"]};r[o]={command:t,args:["mcp"]};let l={...i,mcpServers:r};Ve(n,l);let a=W(n).mcpServers;if(!h(a)||!X(a[o],t))throw new Error("Agy MCP configuration was written but verification failed.");return{installed:!0,changed:!0,configFile:n,serverName:o,command:t,args:["mcp"]}}var Ze=`# ToolNet Memory Continuity
|
|
4
|
+
|
|
5
|
+
ToolNet Memory is the authoritative continuity layer for previous project work.
|
|
6
|
+
|
|
7
|
+
## Resume / continue behavior
|
|
8
|
+
|
|
9
|
+
Whenever the user asks to continue, resume, finish, pick up, return to, or complete previous work:
|
|
10
|
+
|
|
11
|
+
1. FIRST call the ToolNet Memory MCP tool \`memory_agent_ask\`.
|
|
12
|
+
2. Use ToolNet's compact continuity result to determine:
|
|
13
|
+
- current task
|
|
14
|
+
- completed work
|
|
15
|
+
- current or last file
|
|
16
|
+
- TODOs
|
|
17
|
+
- blockers
|
|
18
|
+
- next action
|
|
19
|
+
3. Only AFTER continuity is known may you inspect current source or git to verify repository truth.
|
|
20
|
+
|
|
21
|
+
## Forbidden continuity recovery
|
|
22
|
+
|
|
23
|
+
Do NOT reconstruct previous work by reading, listing, searching, or shelling into:
|
|
24
|
+
|
|
25
|
+
- \`.toolnet/sessions/**\`
|
|
26
|
+
- \`state.json\`
|
|
27
|
+
- \`events.jsonl\`
|
|
28
|
+
- raw transcripts
|
|
29
|
+
- \`~/.gemini/antigravity-cli/brain/**\`
|
|
30
|
+
- Antigravity \`transcript.jsonl\`
|
|
31
|
+
- another coding agent's internal session history
|
|
32
|
+
|
|
33
|
+
Do NOT run Bash/cat/tail/grep against those locations to discover previous work.
|
|
34
|
+
|
|
35
|
+
Do NOT search the filesystem for the implementation or schema of \`memory_agent_ask\`.
|
|
36
|
+
Invoke the MCP tool directly.
|
|
37
|
+
|
|
38
|
+
For direct continuity facts, prefer \`mode="local"\`.
|
|
39
|
+
Use \`mode="ai"\` only when continuity is ambiguous or requires synthesis.
|
|
40
|
+
|
|
41
|
+
Current repository evidence overrides stale memory after ToolNet has restored the working context.
|
|
42
|
+
|
|
43
|
+
Do not ask the user to repeat context already available through ToolNet Memory.
|
|
44
|
+
`;function Z(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function j(e,n){z(Qe(e),{recursive:!0});let t=`${e}.tmp-${process.pid}-${Date.now()}`;ze(t,n,{encoding:"utf8",mode:384}),Ke(t,e)}function K(e,n){w(e)&&Q(e,"utf8")===n||j(e,n)}function ee(e){if(!w(e))return{};let n=Q(e,"utf8").trim();if(!n)return{};let t;try{t=JSON.parse(n)}catch(o){throw new Error(`Invalid legacy Antigravity config ${e}: ${o instanceof Error?o.message:String(o)}`)}if(!Z(t))throw new Error(`Invalid legacy Antigravity config ${e}: root must be object`);return t}function et(e,n){if(!w(e))return!1;let t=ee(e);if(!Z(t.mcpServers)||!Object.prototype.hasOwnProperty.call(t.mcpServers,n))return!1;let o={...t.mcpServers};return delete o[n],j(e,`${JSON.stringify({...t,mcpServers:o},null,2)}
|
|
45
|
+
`),!0}function tt(e){if(!w(e))return!1;let n=ee(e);if(!Object.prototype.hasOwnProperty.call(n,"toolnet-memory"))return!1;let t={...n};return delete t["toolnet-memory"],j(e,`${JSON.stringify(t,null,2)}
|
|
46
|
+
`),!0}function te(e={}){let n=e.pluginName??"toolnet-memory",t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=e.pluginRoot??$(n),i=b(o,"plugin.json"),s=b(o,"mcp_config.json"),r=b(o,"hooks.json"),c=b(o,"rules","toolnet-memory-continuity.md");z(o,{recursive:!0,mode:448}),K(i,`${JSON.stringify({$schema:"https://antigravity.google/schemas/v1/plugin.json",name:n,description:"Persistent project continuity and memory for Antigravity coding sessions."},null,2)}
|
|
47
|
+
`),V({configFile:s,binary:t,serverName:"toolnet-memory"}),G({hooksFile:r,binary:t}),K(c,`${Ze.trim()}
|
|
48
|
+
`);let l=e.legacyMcpFile??v(),u=e.legacyHooksFile??C(),a=[];return l!==s&&et(l,"toolnet-memory")&&a.push(l),u!==r&&tt(u)&&a.push(u),{installed:!0,pluginRoot:o,files:[i,s,r,c],migratedLegacy:a}}import{existsSync as ot,mkdirSync as re,readFileSync as rt,writeFileSync as se}from"node:fs";import{join as st}from"node:path";var nt="memory_agent_ask";function ne(){return`
|
|
4
49
|
[TOOLNET MEMORY AGENT]
|
|
5
50
|
|
|
6
51
|
Tool available:
|
|
7
|
-
- ${
|
|
52
|
+
- ${nt}
|
|
8
53
|
|
|
9
54
|
Use it automatically BEFORE guessing when:
|
|
10
55
|
|
|
@@ -51,7 +96,7 @@ Rules:
|
|
|
51
96
|
- After receiving the ToolNet answer, continue the task
|
|
52
97
|
instead of asking the user to repeat known context.
|
|
53
98
|
- If ToolNet says information is not recorded, say so.
|
|
54
|
-
`.trim()}var
|
|
99
|
+
`.trim()}var oe="<!-- TOOLNET_MEMORY_BOOTSTRAP_START -->",E="<!-- TOOLNET_MEMORY_BOOTSTRAP_END -->";function it(){let e=L();re(g(),{recursive:!0});let n=`${oe}
|
|
55
100
|
## ToolNet Memory
|
|
56
101
|
|
|
57
102
|
ToolNet Memory is available for projects that already contain a valid \`.toolnet/project.json\`.
|
|
@@ -67,12 +112,12 @@ Rules:
|
|
|
67
112
|
7. Current repository evidence has priority over stale memory when they conflict.
|
|
68
113
|
|
|
69
114
|
|
|
70
|
-
${
|
|
115
|
+
${ne()}
|
|
71
116
|
|
|
72
|
-
${
|
|
117
|
+
${E}`,t=ot(e)?rt(e,"utf8"):"",o=t.indexOf(oe),i=t.indexOf(E);return o>=0&&i>=o?t=t.slice(0,o)+n+t.slice(i+E.length):(t=t.trimEnd(),t&&(t+=`
|
|
73
118
|
|
|
74
|
-
`),t+=
|
|
75
|
-
`,{encoding:"utf8",mode:384}),e}function
|
|
119
|
+
`),t+=n),se(e,t.trimEnd()+`
|
|
120
|
+
`,{encoding:"utf8",mode:384}),e}function ie(e={}){let n=e.directory??H();re(n,{recursive:!0}),it();let t=st(n,"toolnet-memory.js"),o=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",i=`
|
|
76
121
|
// Generated by ToolNet Memory.
|
|
77
122
|
// OpenCode integration:
|
|
78
123
|
// - project gated
|
|
@@ -86,7 +131,7 @@ import path from "node:path"
|
|
|
86
131
|
import os from "node:os"
|
|
87
132
|
|
|
88
133
|
const TOOLNET_BINARY =
|
|
89
|
-
${JSON.stringify(
|
|
134
|
+
${JSON.stringify(o)}
|
|
90
135
|
|
|
91
136
|
const CONTEXT_MAX_TOKENS = 700
|
|
92
137
|
|
|
@@ -700,12 +745,14 @@ export const ToolNetMemoryPlugin =
|
|
|
700
745
|
},
|
|
701
746
|
}
|
|
702
747
|
}
|
|
703
|
-
`;return
|
|
704
|
-
`,{encoding:"utf8",mode:384}),
|
|
705
|
-
`),c=
|
|
706
|
-
`);
|
|
707
|
-
`,{encoding:"utf8",mode:384}),
|
|
708
|
-
`),
|
|
709
|
-
`)||(
|
|
710
|
-
`),
|
|
711
|
-
`,{encoding:"utf8",mode:384}),
|
|
748
|
+
`;return se(t,i.trimStart(),{encoding:"utf8",mode:384}),t}import{existsSync as le,mkdirSync as ct,readFileSync as at,renameSync as lt,writeFileSync as ut}from"node:fs";import{dirname as ue,join as dt}from"node:path";function f(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function gt(e,n){ct(ue(e),{recursive:!0});let t=`${e}.tmp-${process.pid}-${Date.now()}`;ut(t,`${JSON.stringify(n,null,2)}
|
|
749
|
+
`,{encoding:"utf8",mode:384}),lt(t,e)}function ce(e){if(!le(e))return{};let n=at(e,"utf8").trim();if(!n)return{};let t;try{t=JSON.parse(n)}catch(o){throw new Error(`Invalid existing OpenCode opencode.json: ${o instanceof Error?o.message:String(o)}`)}if(!f(t))throw new Error("Invalid existing OpenCode opencode.json: root must be a JSON object.");return t}function ae(e,n){if(!f(e))return!1;let t=e.command;return e.type==="local"&&e.enabled!==!1&&Array.isArray(t)&&t.length===2&&t[0]===n&&t[1]==="mcp"}function mt(e,n){let t=e.mcpServers;if(!f(t)||!Object.prototype.hasOwnProperty.call(t,n))return{root:e,changed:!1};let o={...t};return delete o[n],{root:{...e,mcpServers:o},changed:!0}}function de(e={}){let n=e.configFile??D(),t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=e.serverName??"toolnet-memory",i=dt(ue(n),"opencode.jsonc"),s=le(i)?i:void 0,r=ce(n),c=mt(r,o),l=c.root,u=l.mcp;if(u!==void 0&&!f(u))throw new Error("Invalid existing OpenCode config: mcp must be an object.");let a=f(u)?{...u}:{},m=a[o];if(ae(m,t)&&!c.changed)return{installed:!0,changed:!1,configFile:n,serverName:o,command:[t,"mcp"],preservedJsonc:s};a[o]={type:"local",command:[t,"mcp"],enabled:!0};let d={...l,mcp:a};gt(n,d);let R=ce(n);if(!f(R.mcp)||!ae(R.mcp[o],t))throw new Error("OpenCode MCP configuration was written but verification failed.");return{installed:!0,changed:!0,configFile:n,serverName:o,command:[t,"mcp"],preservedJsonc:s}}import{existsSync as pt,mkdirSync as ge,readFileSync as ft,writeFileSync as me}from"node:fs";import{homedir as pe}from"node:os";import{dirname as fe,join as A}from"node:path";function yt(e){let n=[],t=/"((?:\\.|[^"\\])*)"|'([^']*)'/g,o;for(;o=t.exec(e);){let i=o[1]??o[2]??"";try{n.push(o[1]!==void 0?JSON.parse(`"${i}"`):i)}catch{n.push(i)}}return n}function ye(e={}){let n=e.configFile??A(process.env.CODEX_HOME??A(pe(),".codex"),"config.toml"),t=e.previousFile??A(pe(),".config","toolnet-memory","codex-notify-previous.json");ge(fe(n),{recursive:!0}),ge(fe(t),{recursive:!0});let o=pt(n)?ft(n,"utf8"):"",i=e.binary??"toolnet-memory",s=`notify = [${JSON.stringify(i)}, "session:codex-notify"]`,r=o.split(`
|
|
750
|
+
`),c=r.findIndex(d=>/^\s*\[/.test(d));c<0&&(c=r.length);let l=-1,u=-1;for(let d=0;d<c;d+=1)if(/^\s*notify\s*=/.test(r[d])){if(l=d,u=d,r[d].includes("[")&&!r[d].includes("]"))for(;u+1<c&&(u+=1,!r[u].includes("]")););break}let a=[];if(l>=0){let d=r.slice(l,u+1).join(`
|
|
751
|
+
`);a=yt(d),r.splice(l,u-l+1,s)}else c=r.findIndex(d=>/^\s*\[/.test(d)),c<0&&(c=r.length),r.splice(c,0,s);let m=a.length>=2&&a[a.length-1]==="session:codex-notify";return a.length>0&&!m&&me(t,JSON.stringify(a,null,2)+`
|
|
752
|
+
`,{encoding:"utf8",mode:384}),o=r.join(`
|
|
753
|
+
`),o.endsWith(`
|
|
754
|
+
`)||(o+=`
|
|
755
|
+
`),me(n,o,{encoding:"utf8",mode:384}),{configFile:n,previousFile:t,preservedPrevious:a.length>0&&!m}}import{existsSync as ht,mkdirSync as Ot,readFileSync as xt,writeFileSync as vt}from"node:fs";import{homedir as Ct}from"node:os";import{dirname as St,join as he}from"node:path";function bt(e){return`'${e.replace(/'/g,"'\\''")}'`}function Oe(e={}){let n=e.hooksFile??he(process.env.CODEX_HOME??he(Ct(),".codex"),"hooks.json");Ot(St(n),{recursive:!0});let t={};if(ht(n))try{t=JSON.parse(xt(n,"utf8"))}catch(c){throw new Error(`Invalid existing Codex hooks.json: ${c instanceof Error?c.message:String(c)}`)}let o=t.hooks&&typeof t.hooks=="object"&&!Array.isArray(t.hooks)?t.hooks:{};t.hooks=o;let s=(Array.isArray(o.SessionStart)?o.SessionStart:[]).filter(c=>{try{return!JSON.stringify(c).includes("session:codex-context")}catch{return!0}}),r=e.binary??"toolnet-memory";return s.push({matcher:"startup|resume|clear|compact",hooks:[{type:"command",command:`${bt(r)} session:codex-context`,timeout:15,additionalContextLimit:1e3,statusMessage:"Loading ToolNet project continuity"}]}),o.SessionStart=s,vt(n,JSON.stringify(t,null,2)+`
|
|
756
|
+
`,{encoding:"utf8",mode:384}),n}import{spawnSync as wt}from"node:child_process";function T(e,n){return wt(e,n,{encoding:"utf8",stdio:["ignore","pipe","pipe"]})}function xe(e,n){let t=T(e,["mcp","get",n,"--json"]);if(t.status!==0||!t.stdout)return null;try{return JSON.parse(t.stdout)}catch{return null}}function ve(e,n){return e.enabled!==!1&&e.transport?.type==="stdio"&&e.transport?.command===n&&Array.isArray(e.transport?.args)&&e.transport?.args.length===1&&e.transport.args[0]==="mcp"}function Ce(e={}){let n=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",t=e.codexBinary??"codex",o=e.serverName??"toolnet-memory",i=xe(t,o);if(i&&ve(i,n))return{installed:!0,changed:!1,serverName:o,command:n,args:["mcp"]};if(i){let c=T(t,["mcp","remove",o]);if(c.status!==0)return{installed:!1,changed:!1,serverName:o,command:n,args:["mcp"],error:(c.stderr||c.stdout||"Unable to remove old ToolNet MCP configuration.").trim()}}let s=T(t,["mcp","add",o,"--",n,"mcp"]);if(s.status!==0)return{installed:!1,changed:!1,serverName:o,command:n,args:["mcp"],error:(s.stderr||s.stdout||"Unable to register ToolNet MCP.").trim()};let r=xe(t,o);return!r||!ve(r,n)?{installed:!1,changed:!0,serverName:o,command:n,args:["mcp"],error:"Codex accepted MCP registration but verification did not match expected ToolNet command."}:{installed:!0,changed:!0,serverName:o,command:n,args:["mcp"]}}import{existsSync as kt,mkdirSync as Nt,readFileSync as It,renameSync as jt,rmSync as Et,writeFileSync as At}from"node:fs";import{dirname as Tt}from"node:path";function O(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Mt(e){return/^[A-Za-z0-9_./:-]+$/u.test(e)?e:`'${e.replace(/'/gu,"'\\''")}'`}function Ft(e){if(!kt(e))return{};let n;try{n=JSON.parse(It(e,"utf8"))}catch(t){throw new Error(`Invalid existing Claude settings.json: ${t instanceof Error?t.message:String(t)}`)}if(!O(n))throw new Error("Invalid existing Claude settings.json: root must be a JSON object.");return n}function M(e){if(e===void 0)return[];if(!Array.isArray(e))throw new Error("Invalid existing Claude settings.json: hook event must be an array.");let n=[];for(let t of e){if(!O(t)){n.push(t);continue}let o=t.hooks;if(!Array.isArray(o)){n.push(t);continue}let i=o.filter(s=>{if(!O(s))return!0;let r=s.command;return!(typeof r=="string"&&r.includes("session:claude-hook"))});i.length!==0&&n.push({...t,hooks:i})}return n}function F(e){return{type:"command",command:e,timeout:10}}function Rt(e,n){Nt(Tt(e),{recursive:!0,mode:448});let t=`${e}.toolnet-${process.pid}-${Date.now()}.tmp`;try{At(t,JSON.stringify(n,null,2)+`
|
|
757
|
+
`,{encoding:"utf8",mode:384}),jt(t,e)}finally{Et(t,{force:!0})}}function Se(e={}){let n=e.settingsFile??Y(),t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=Ft(n),i=o.hooks;if(i!==void 0&&!O(i))throw new Error("Invalid existing Claude settings.json: hooks must be an object.");let s=O(i)?{...i}:{},r=`${Mt(t)} session:claude-hook`,c=M(s.SessionStart);c.push({matcher:"startup|resume|clear|compact",hooks:[F(r)]}),s.SessionStart=c;let l=M(s.PostToolUse);l.push({matcher:"Edit|Write",hooks:[F(r)]}),s.PostToolUse=l;let u=M(s.Stop);u.push({hooks:[F(r)]}),s.Stop=u;let a={...o,hooks:s},m=JSON.stringify(o),d=JSON.stringify(a);return m===d?{settingsFile:n,changed:!1}:(Rt(n,a),{settingsFile:n,changed:!0})}import{existsSync as Pt,mkdirSync as _t,readFileSync as $t,renameSync as Jt,rmSync as Dt,writeFileSync as Ht}from"node:fs";import{dirname as Lt}from"node:path";function x(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function be(e){if(!Pt(e))return{};let n;try{n=JSON.parse($t(e,"utf8"))}catch(t){throw new Error(`Invalid existing Claude Code config: ${t instanceof Error?t.message:String(t)}`)}if(!x(n))throw new Error("Invalid existing Claude Code config: root must be a JSON object.");return n}function we(e,n){if(!x(e))return!1;let t=e.args;return e.type==="stdio"&&e.command===n&&Array.isArray(t)&&t.length===1&&t[0]==="mcp"}function Bt(e,n){_t(Lt(e),{recursive:!0});let t=`${e}.toolnet-${process.pid}-${Date.now()}.tmp`;try{Ht(t,JSON.stringify(n,null,2)+`
|
|
758
|
+
`,{encoding:"utf8",mode:384}),Jt(t,e)}finally{Dt(t,{force:!0})}}function ke(e={}){let n=e.stateFile??U(),t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=e.serverName??"toolnet-memory",i=be(n),s=i.mcpServers;if(s!==void 0&&!x(s))throw new Error("Invalid existing Claude Code config: mcpServers must be an object.");let r=x(s)?{...s}:{},c=r[o];if(we(c,t))return{installed:!0,changed:!1,configFile:n,serverName:o,command:[t,"mcp"],repaired:!1};let l=c!==void 0;r[o]={type:"stdio",command:t,args:["mcp"]},Bt(n,{...i,mcpServers:r});let a=be(n).mcpServers;if(!x(a)||!we(a[o],t))throw new Error("Claude Code MCP configuration was written but verification failed.");return{installed:!0,changed:!0,configFile:n,serverName:o,command:[t,"mcp"],repaired:l}}function Ne(e={}){let n=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",t=Se({binary:n,settingsFile:e.settingsFile}),o=ke({binary:n,stateFile:e.stateFile});return{hooks:t,mcp:o,files:[t.settingsFile,o.configFile]}}function Ie(){return q()}function Yt(e={}){let n=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",t=[],o=Ie(),i=new Map(o.map(s=>[s.agent,s.detected]));if(!(e.force===!0||i.get("agy")===!0))t.push({agent:"agy",detected:!1,installed:!1,targets:[]});else try{let r=te({binary:n});t.push({agent:"agy",detected:!0,installed:!0,targets:r.files})}catch(r){t.push({agent:"agy",detected:!0,installed:!1,targets:[],error:r instanceof Error?r.message:String(r)})}if(!(e.force===!0||i.get("opencode")===!0))t.push({agent:"opencode",detected:!1,installed:!1,targets:[]});else try{let r=ie({binary:n}),c=de({binary:n});t.push({agent:"opencode",detected:!0,installed:!0,targets:[r,c.configFile,`mcp:${c.serverName}`]})}catch(r){t.push({agent:"opencode",detected:!0,installed:!1,targets:[],error:r instanceof Error?r.message:String(r)})}if(!(e.force===!0||i.get("claude")===!0))t.push({agent:"claude",detected:!1,installed:!1,targets:[]});else try{let r=Ne({binary:n});t.push({agent:"claude",detected:!0,installed:!0,targets:[r.hooks.settingsFile,r.mcp.configFile,`mcp:${r.mcp.serverName}`]})}catch(r){t.push({agent:"claude",detected:!0,installed:!1,targets:[],error:r instanceof Error?r.message:String(r)})}if(!(e.force===!0||i.get("codex")===!0))t.push({agent:"codex",detected:!1,installed:!1,targets:[]});else try{let r=ye({binary:n}),c=Oe({binary:n}),l=Ce({binary:n});if(!l.installed)throw new Error(l.error??"Codex MCP registration failed");let u=[r.configFile,c,`mcp:${l.serverName}`];r.preservedPrevious&&u.push(r.previousFile),t.push({agent:"codex",detected:!0,installed:!0,targets:u})}catch(r){t.push({agent:"codex",detected:!0,installed:!1,targets:[],error:r instanceof Error?r.message:String(r)})}return t}function Ut(e){console.log(""),console.log("ToolNet Memory Integration Detection"),console.log("===================================="),console.log("");for(let n of e){let t=n.agent==="agy"?"Agy / Antigravity":n.agent==="opencode"?"OpenCode":n.agent==="claude"?"Claude Code":"Codex";if(!n.detected){console.log(`\u25CB ${t}: not detected`);continue}console.log(`\u2713 ${t}: detected`);for(let o of n.evidence)console.log(` ${o}`)}console.log("")}function qt(e){console.log(""),console.log("ToolNet Memory AI Integrations"),console.log("=============================="),console.log("");for(let n of e){let t=n.agent==="agy"?"Agy / Antigravity":n.agent==="opencode"?"OpenCode":"Codex";if(!n.detected){console.log(`- ${t}: not detected`);continue}if(n.installed){console.log(`\u2713 ${t}: automatic memory enabled`);continue}console.log(`\u2717 ${t}: integration failed`),n.error&&console.log(` ${n.error}`)}console.log("")}async function Gt(){let e=process.argv.slice(2),n=e.includes("--all"),t=e.includes("--json");if(e.includes("--detect-only")){let s=Ie();if(t){console.log(JSON.stringify(s,null,2));return}Ut(s);return}let i=Yt({force:n});if(t){console.log(JSON.stringify(i,null,2));return}qt(i)}var Wt=process.argv[1]&&(process.argv[1].endsWith("auto-integrate.js")||process.argv[1].endsWith("auto-integrate.ts"));Wt&&Gt().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exitCode=1});export{Ie as detectAutoIntegrations,Yt as installAutoIntegrations};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import{existsSync as Ne,mkdirSync as Fe,readFileSync as De,renameSync as $e,rmSync as Le,writeFileSync as He}from"node:fs";import{isAbsolute as ze,join as Z,relative as Be}from"node:path";import{existsSync as X,readFileSync as ee}from"node:fs";import{homedir as te}from"node:os";import{join as re}from"node:path";function ne(e){let t=e.trim();return t.length>=2&&t.startsWith('"')&&t.endsWith('"')?(t=t.slice(1,-1),t.replace(/\\n/g,`
|
|
2
|
+
`).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):t.length>=2&&t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1):t}function oe(){let e=process.env.TOOLNET_GLOBAL_ENV??re(te(),".config","toolnet-memory",".env");if(!X(e))return;let t=ee(e,"utf8");for(let r of t.split(/\r?\n/)){let n=r.trim();if(!n||n.startsWith("#"))continue;n.startsWith("export ")&&(n=n.slice(7));let o=n.indexOf("=");if(o<=0)continue;let s=n.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(s)&&process.env[s]===void 0&&(process.env[s]=ne(n.slice(o+1)))}}oe();import{createHash as se}from"node:crypto";import{existsSync as b,mkdirSync as ie,readFileSync as ce,renameSync as ae,writeFileSync as ue}from"node:fs";import{basename as de,dirname as S,join as x,parse as O,resolve as g}from"node:path";var I=".toolnet",le="project.json";function fe(e){return se("sha256").update(e).digest("hex").slice(0,16)}function w(e){return x(e,I,le)}function pe(e){return b(w(e))}function me(e,t){let r=g(e),n=O(r).root;for(;;){if(pe(r))return r;if(r===n||t&&r===g(t))break;let o=S(r);if(o===r)break;r=o}return null}function ge(e){let t=g(e),r=O(t).root,n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(n.some(s=>b(x(t,s))))return t;if(t===r)break;let o=S(t);if(o===t)break;t=o}return g(e)}function ye(e){let t;try{t=JSON.parse(ce(e,"utf8"))}catch(o){throw new Error(`Invalid ToolNet project manifest: ${e}: ${o instanceof Error?o.message:String(o)}`)}if(!t||typeof t!="object")throw new Error(`Invalid ToolNet project manifest: ${e}`);let r=t;if(typeof r.id!="string"||!r.id.trim())throw new Error(`ToolNet project manifest is missing id: ${e}`);if(typeof r.name!="string"||!r.name.trim())throw new Error(`ToolNet project manifest is missing name: ${e}`);let n=new Date().toISOString();return{version:1,id:r.id,name:r.name,remote:typeof r.remote=="string"&&r.remote.trim()?r.remote:r.name,rootPath:typeof r.rootPath=="string"?r.rootPath:S(S(e)),createdAt:typeof r.createdAt=="string"?r.createdAt:n,updatedAt:typeof r.updatedAt=="string"?r.updatedAt:n,graphVersion:typeof r.graphVersion=="number"?r.graphVersion:0,memoryVersion:typeof r.memoryVersion=="number"?r.memoryVersion:0,metadata:r.metadata&&typeof r.metadata=="object"?r.metadata:void 0}}function E(e,t){let r=x(e,I);ie(r,{recursive:!0});let n=w(e),o=`${n}.tmp-${process.pid}`;ue(o,JSON.stringify(t,null,2)+`
|
|
3
|
+
`,{encoding:"utf8",mode:384}),ae(o,n)}function j(e,t){return{id:e.id,name:e.name,remote:e.remote,rootPath:t,createdAt:e.createdAt,updatedAt:e.updatedAt,graphVersion:e.graphVersion,memoryVersion:e.memoryVersion,metadata:e.metadata}}var k=class{detect(t=process.cwd()){let r=g(t),n=ge(r),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(p=>b(x(n,p))),i=me(r,s?n:void 0);if(i){let p=w(i),l=ye(p);return l.rootPath!==i&&(l.rootPath=i,l.updatedAt=new Date().toISOString(),E(i,l)),j(l,i)}let a=new Date().toISOString(),c=de(n),u={version:1,id:fe(n),name:c,remote:c,rootPath:n,createdAt:a,updatedAt:a,graphVersion:0,memoryVersion:0};return E(n,u),j(u,n)}};import*as f from"fs";import*as d from"path";function M(e){return e?Math.ceil(e.length/3.5):0}function he(e,t){if(!e)return"";if(M(e)<=t)return e;let n=Math.floor(t*3.5),o=e.slice(0,n),s=o.lastIndexOf("."),i=o.lastIndexOf(`
|
|
4
|
+
`),a=Math.max(s,i);return a>n*.7?o.slice(0,a+1):o}function _(e,t){if(!e)return"";let r=e.split(`
|
|
5
|
+
`).filter(s=>s.trim());if(r.length<=t)return e;let n=r.slice(0,t),o=r.length-t;return[...n,`... (${o} more items omitted)`].join(`
|
|
6
|
+
`)}function Se(e,t){let{maxTokens:r,trimMarker:n="[Context trimmed by ToolNet Memory token budget]"}=t;if(e.length===0)return"";let o=[...e].sort((c,u)=>u.priority-c.priority),s=[],i=0,a=!1;for(let c of o){let u=`# ${c.title}
|
|
7
|
+
|
|
8
|
+
`,p=M(u),l=M(c.content),y=p+l;if(i+y<=r)s.push(u+c.content),i+=y;else{let h=r-i-p;if(h>50){let m=he(c.content,h);s.push(u+m),i=r,a=!0}else a=!0;break}}return a&&s.push(`
|
|
9
|
+
${n}
|
|
10
|
+
`),s.join(`
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
`)}function P(e,t){let r=[{title:"Profile",content:_(e,10),priority:100},{title:"Current Work",content:_(t,15),priority:90}];return Se(r,{maxTokens:800})}import{existsSync as N,mkdirSync as xe,readFileSync as F,renameSync as be,writeFileSync as we}from"node:fs";import{dirname as Me,join as D}from"node:path";import{createHash as ke}from"node:crypto";function v(e){return ke("sha256").update(e).digest("hex")}function $(e){return D(e.rootPath,".toolnet","context","handoff.md")}function L(e){return D(e.rootPath,".toolnet","context","handoff.json")}function H(e,t){xe(Me(e),{recursive:!0});let r=`${e}.tmp-${process.pid}-${Date.now()}`;we(r,t.endsWith(`
|
|
15
|
+
`)?t:`${t}
|
|
16
|
+
`,{encoding:"utf8",mode:384}),be(r,e)}function ve(e,t){H(e,`${JSON.stringify(t,null,2)}
|
|
17
|
+
`)}function z(e,t){let r=t.replace(/\r\n/g,`
|
|
18
|
+
`).trim();if(!r)return null;let n={version:1,projectId:e.id,projectName:e.name,text:r,digest:v(r),generatedAt:new Date().toISOString()};return H($(e),r),ve(L(e),n),n}function A(e){let t=$(e);if(!N(t))return null;try{let r=F(t,"utf8").trim();if(!r)return null;let n=new Date(0).toISOString(),o=L(e);if(N(o))try{let s=JSON.parse(F(o,"utf8"));typeof s.generatedAt=="string"&&(n=s.generatedAt)}catch{}return{version:1,projectId:e.id,projectName:e.name,text:r,digest:v(r),generatedAt:n}}catch{return null}}function B(e,t=1800){let r=A(e);if(!r?.text)return null;let n=r.text;return n.length>t&&(n=`${n.slice(0,t)}
|
|
19
|
+
|
|
20
|
+
[Fast handoff truncated]`),["[TOOLNET FAST HANDOFF]","",`Project: ${e.name}`,`Updated: ${r.generatedAt}`,"",n].join(`
|
|
21
|
+
`)}var T="memory_agent_ask";function K(){return`
|
|
22
|
+
[TOOLNET MEMORY AGENT]
|
|
23
|
+
|
|
24
|
+
Tool:
|
|
25
|
+
- ${T}
|
|
26
|
+
|
|
27
|
+
For resume/continue requests:
|
|
28
|
+
|
|
29
|
+
1. Use the injected ToolNet continuity handoff FIRST.
|
|
30
|
+
2. If the handoff is missing or ambiguous, invoke
|
|
31
|
+
${T} directly BEFORE repository/history exploration.
|
|
32
|
+
3. NEVER reconstruct prior work from:
|
|
33
|
+
- .toolnet/sessions/**
|
|
34
|
+
- state.json
|
|
35
|
+
- events.jsonl
|
|
36
|
+
- raw transcripts
|
|
37
|
+
4. NEVER search for the implementation/schema of
|
|
38
|
+
${T}; invoke the MCP tool directly.
|
|
39
|
+
5. Inspect git/source only AFTER continuity context is known.
|
|
40
|
+
|
|
41
|
+
Use:
|
|
42
|
+
- mode="local" for current task, last file, blocker or next action.
|
|
43
|
+
- mode="ai" for ambiguous or combined continuity questions.
|
|
44
|
+
|
|
45
|
+
Current repository evidence overrides stale memory.
|
|
46
|
+
`.trim()}function R(e){let t=d.resolve(e),r=d.parse(t).root;for(;t!==r;){let n=d.join(t,".toolnet");if(f.existsSync(n)&&f.statSync(n).isDirectory())return t;t=d.dirname(t)}return null}function Y(e){try{return f.existsSync(e)?f.readFileSync(e,"utf-8").trim():null}catch{return null}}function J(e){return e.split(`
|
|
47
|
+
`).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(`
|
|
48
|
+
`)}function V(e={}){let t=e.projectPath||process.cwd(),r=R(t);if(!r)return null;let n=d.join(r,".toolnet","project.json"),o="Unknown";try{if(f.existsSync(n)){let m=JSON.parse(f.readFileSync(n,"utf-8"));o=m.name||m.projectName||"Unknown"}}catch{}let s=d.join(r,".toolnet","profile.md"),i=d.join(r,".toolnet","current.md"),a=Y(s)||"",c=Y(i)||"";a=J(a),c=J(c);let u=`[TOOLNET PROJECT CONTEXT]
|
|
49
|
+
|
|
50
|
+
Project: ${o}
|
|
51
|
+
Root: ${r}
|
|
52
|
+
|
|
53
|
+
`,p=P(a,c),l=B({id:"",name:o,rootPath:r},1600),y=l?`
|
|
54
|
+
|
|
55
|
+
${l}
|
|
56
|
+
`:"",h=`
|
|
57
|
+
|
|
58
|
+
${K()}
|
|
59
|
+
|
|
60
|
+
Forbidden At Startup:
|
|
61
|
+
- Do not run session:agy-recover, handoff:latest, or brief automatically.
|
|
62
|
+
- Do not perform deep recovery merely because an agent starts.
|
|
63
|
+
- If the user asks to resume previous work and fast context is insufficient,
|
|
64
|
+
use memory_agent_ask before guessing.
|
|
65
|
+
`;return u+p+y+h}import{existsSync as je,readFileSync as Oe}from"node:fs";import{join as Ie}from"node:path";import{existsSync as Ae,mkdirSync as Zt,readFileSync as Te,renameSync as qt,writeFileSync as Xt}from"node:fs";import{dirname as tr,join as Re}from"node:path";function Ce(e){return Re(e.rootPath,".toolnet","context","session-origin.json")}function Ee(e){let t=Ce(e);if(!Ae(t))return null;try{let r=JSON.parse(Te(t,"utf8"));return r.version!==1||r.projectId!==e.id?null:r}catch{return null}}function W(e){let t=Ee(e);if(!t)return null;let r=["Previous session:",`- Agent: ${t.agent}`,`- Session: ${t.nativeSessionId}`];return t.currentPhase&&r.push(`- Current phase: ${t.currentPhase}`),t.currentTask&&r.push(`- Current task: ${t.currentTask}`),t.lastTouchedFile&&r.push(`- Last touched file: ${t.lastTouchedFile}`),t.latestBlocker&&r.push(`- Blocker: ${t.latestBlocker}`),t.latestNextAction&&r.push(`- Next action: ${t.latestNextAction}`),t.latestDecision&&r.push(`- Important decision: ${t.latestDecision}`),r.join(`
|
|
66
|
+
`)}function _e(e){let t=Ie(e.rootPath,".toolnet","current.md");if(!je(t))return null;try{return Oe(t,"utf8").trim()}catch{return null}}function Pe(e,t){let r=t.replace(/\r\n/g,`
|
|
67
|
+
`).trim(),n=2400,o=r.length>n?`${r.slice(0,n)}
|
|
68
|
+
|
|
69
|
+
[Current work truncated for fast handoff]`:r,s=W(e);return["# ToolNet Fast Handoff","",`Project: ${e.name}`,"",...s?[s,""]:[],o,"","---","","Continuation rule:","- Continue unfinished work; do not redo completed steps.","- Query ToolNet Memory only when deeper history is needed."].join(`
|
|
70
|
+
`)}function C(e){let t=_e(e);if(t===null)return{updated:!1,reason:"missing-current",chars:0};if(!t.trim())return{updated:!1,reason:"empty-current",chars:0};let r=Pe(e,t);return A(e)?.text.trim()===r.trim()?{updated:!1,reason:"unchanged",chars:r.length}:(z(e,r),{updated:!0,reason:"updated",chars:r.length})}var U=3200;function G(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Ke(e){return e.length<=U?e:`${e.slice(0,U)}
|
|
71
|
+
|
|
72
|
+
[ToolNet startup context truncated]`}function Ye(e){let t=R(e);return t?new k().detect(t)??null:null}function Je(e){return Z(e.rootPath,".toolnet","context","session-origin.json")}function Ve(e,t){if(!ze(t))return t;let r=Be(e.rootPath,t);return r.startsWith("..")?t:r}function Q(e,t){let r=Je(e),n={};if(Ne(r))try{let u=JSON.parse(De(r,"utf8"));G(u)&&u.projectId===e.id&&(n=u)}catch{}let o,s=t.tool_input;G(s)&&typeof s.file_path=="string"&&(o=Ve(e,s.file_path));let i=typeof t.session_id=="string"?t.session_id:typeof n.nativeSessionId=="string"?n.nativeSessionId:"claude",a={...n,version:1,projectId:e.id,agent:"claude",nativeSessionId:i,updatedAt:new Date().toISOString()};o&&(a.lastTouchedFile=o),Fe(Z(e.rootPath,".toolnet","context"),{recursive:!0,mode:448});let c=`${r}.tmp-${process.pid}-${Date.now()}`;try{He(c,JSON.stringify(a,null,2)+`
|
|
73
|
+
`,{encoding:"utf8",mode:384}),$e(c,r)}finally{Le(c,{force:!0})}}function q(e){let t=typeof e.hook_event_name=="string"?e.hook_event_name:"",r=typeof e.cwd=="string"?e.cwd:"";if(!r)return{};let n=Ye(r);if(!n)return{};try{if(t==="SessionStart"){let o=V({projectPath:r});return o?.trim()?{hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:Ke(o)}}:{}}if(t==="PostToolUse"){Q(n,e);try{C(n)}catch{}return{}}if(t==="Stop"){Q(n,e);try{C(n)}catch{}return{}}return{}}catch{return{}}}async function We(){let e="";for await(let t of process.stdin)e+=t.toString();if(!e.trim())return{};try{return JSON.parse(e)}catch{return{}}}async function Ue(){let e=await We(),t=q(e);process.stdout.write(JSON.stringify(t))}Ue().catch(()=>{process.stdout.write("{}"),process.exitCode=0});
|
package/bundle/claude.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{existsSync as I,mkdirSync as v,readFileSync as J,renameSync as E,rmSync as N,writeFileSync as M}from"node:fs";import{dirname as $}from"node:path";import{homedir as y}from"node:os";import{join as g}from"node:path";function x(n={}){return g(n.home??y(),".claude")}function h(n={}){return g(x(n),"settings.json")}function C(n={}){return g(n.home??y(),".claude.json")}function l(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)}function A(n){return/^[A-Za-z0-9_./:-]+$/u.test(n)?n:`'${n.replace(/'/gu,"'\\''")}'`}function R(n){if(!I(n))return{};let t;try{t=JSON.parse(J(n,"utf8"))}catch(e){throw new Error(`Invalid existing Claude settings.json: ${e instanceof Error?e.message:String(e)}`)}if(!l(t))throw new Error("Invalid existing Claude settings.json: root must be a JSON object.");return t}function f(n){if(n===void 0)return[];if(!Array.isArray(n))throw new Error("Invalid existing Claude settings.json: hook event must be an array.");let t=[];for(let e of n){if(!l(e)){t.push(e);continue}let o=e.hooks;if(!Array.isArray(o)){t.push(e);continue}let s=o.filter(r=>{if(!l(r))return!0;let i=r.command;return!(typeof i=="string"&&i.includes("session:claude-hook"))});s.length!==0&&t.push({...e,hooks:s})}return t}function p(n){return{type:"command",command:n,timeout:10}}function T(n,t){v($(n),{recursive:!0,mode:448});let e=`${n}.toolnet-${process.pid}-${Date.now()}.tmp`;try{M(e,JSON.stringify(t,null,2)+`
|
|
2
|
+
`,{encoding:"utf8",mode:384}),E(e,n)}finally{N(e,{force:!0})}}function b(n={}){let t=n.settingsFile??h(),e=n.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=R(t),s=o.hooks;if(s!==void 0&&!l(s))throw new Error("Invalid existing Claude settings.json: hooks must be an object.");let r=l(s)?{...s}:{},i=`${A(e)} session:claude-hook`,a=f(r.SessionStart);a.push({matcher:"startup|resume|clear|compact",hooks:[p(i)]}),r.SessionStart=a;let d=f(r.PostToolUse);d.push({matcher:"Edit|Write",hooks:[p(i)]}),r.PostToolUse=d;let m=f(r.Stop);m.push({hooks:[p(i)]}),r.Stop=m;let c={...o,hooks:r},j=JSON.stringify(o),F=JSON.stringify(c);return j===F?{settingsFile:t,changed:!1}:(T(t,c),{settingsFile:t,changed:!0})}import{existsSync as H,mkdirSync as P,readFileSync as _,renameSync as U,rmSync as B,writeFileSync as D}from"node:fs";import{dirname as L}from"node:path";function u(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)}function O(n){if(!H(n))return{};let t;try{t=JSON.parse(_(n,"utf8"))}catch(e){throw new Error(`Invalid existing Claude Code config: ${e instanceof Error?e.message:String(e)}`)}if(!u(t))throw new Error("Invalid existing Claude Code config: root must be a JSON object.");return t}function S(n,t){if(!u(n))return!1;let e=n.args;return n.type==="stdio"&&n.command===t&&Array.isArray(e)&&e.length===1&&e[0]==="mcp"}function W(n,t){P(L(n),{recursive:!0});let e=`${n}.toolnet-${process.pid}-${Date.now()}.tmp`;try{D(e,JSON.stringify(t,null,2)+`
|
|
3
|
+
`,{encoding:"utf8",mode:384}),U(e,n)}finally{B(e,{force:!0})}}function w(n={}){let t=n.stateFile??C(),e=n.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=n.serverName??"toolnet-memory",s=O(t),r=s.mcpServers;if(r!==void 0&&!u(r))throw new Error("Invalid existing Claude Code config: mcpServers must be an object.");let i=u(r)?{...r}:{},a=i[o];if(S(a,e))return{installed:!0,changed:!1,configFile:t,serverName:o,command:[e,"mcp"],repaired:!1};let d=a!==void 0;i[o]={type:"stdio",command:e,args:["mcp"]},W(t,{...s,mcpServers:i});let c=O(t).mcpServers;if(!u(c)||!S(c[o],e))throw new Error("Claude Code MCP configuration was written but verification failed.");return{installed:!0,changed:!0,configFile:t,serverName:o,command:[e,"mcp"],repaired:d}}function k(n={}){let t=n.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",e=b({binary:t,settingsFile:n.settingsFile}),o=w({binary:t,stateFile:n.stateFile});return{hooks:e,mcp:o,files:[e.settingsFile,o.configFile]}}function Y(){if((process.argv[2]??"install")!=="install")throw new Error("Usage: toolnet-memory integrate:claude");let t=k();console.log("\u2705 Claude Code integration installed"),console.log(`Hooks: ${t.hooks.settingsFile}`),console.log(`MCP: ${t.mcp.configFile}`),console.log("Server: toolnet-memory")}try{Y()}catch(n){console.error(n instanceof Error?n.message:String(n)),process.exitCode=1}
|
package/bundle/codex-context.js
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
import*as
|
|
2
|
-
`),c=Math.max(s,i);return c>n*.7?o.slice(0,c+1):o}function
|
|
1
|
+
import*as p from"fs";import*as u from"path";function k(e){return e?Math.ceil(e.length/3.5):0}function Y(e,t){if(!e)return"";if(k(e)<=t)return e;let n=Math.floor(t*3.5),o=e.slice(0,n),s=o.lastIndexOf("."),i=o.lastIndexOf(`
|
|
2
|
+
`),c=Math.max(s,i);return c>n*.7?o.slice(0,c+1):o}function A(e,t){if(!e)return"";let r=e.split(`
|
|
3
3
|
`).filter(s=>s.trim());if(r.length<=t)return e;let n=r.slice(0,t),o=r.length-t;return[...n,`... (${o} more items omitted)`].join(`
|
|
4
|
-
`)}function
|
|
4
|
+
`)}function H(e,t){let{maxTokens:r,trimMarker:n="[Context trimmed by ToolNet Memory token budget]"}=t;if(e.length===0)return"";let o=[...e].sort((a,d)=>d.priority-a.priority),s=[],i=0,c=!1;for(let a of o){let d=`# ${a.title}
|
|
5
5
|
|
|
6
|
-
`,
|
|
6
|
+
`,m=k(d),l=k(a.content),h=m+l;if(i+h<=r)s.push(d+a.content),i+=h;else{let S=r-i-m;if(S>50){let g=Y(a.content,S);s.push(d+g),i=r,c=!0}else c=!0;break}}return c&&s.push(`
|
|
7
7
|
${n}
|
|
8
8
|
`),s.join(`
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
-
`)}function C(e,t){let r=[{title:"Profile",content:
|
|
12
|
+
`)}function C(e,t){let r=[{title:"Profile",content:A(e,10),priority:100},{title:"Current Work",content:A(t,15),priority:90}];return H(r,{maxTokens:800})}import{existsSync as v,mkdirSync as Ce,readFileSync as _,renameSync as Ee,writeFileSync as ve}from"node:fs";import{dirname as je,join as j}from"node:path";import{createHash as V}from"node:crypto";function E(e){return V("sha256").update(e).digest("hex")}function U(e){return j(e.rootPath,".toolnet","context","handoff.md")}function G(e){return j(e.rootPath,".toolnet","context","handoff.json")}function J(e){let t=U(e);if(!v(t))return null;try{let r=_(t,"utf8").trim();if(!r)return null;let n=new Date(0).toISOString(),o=G(e);if(v(o))try{let s=JSON.parse(_(o,"utf8"));typeof s.generatedAt=="string"&&(n=s.generatedAt)}catch{}return{version:1,projectId:e.id,projectName:e.name,text:r,digest:E(r),generatedAt:n}}catch{return null}}function I(e,t=1800){let r=J(e);if(!r?.text)return null;let n=r.text;return n.length>t&&(n=`${n.slice(0,t)}
|
|
13
13
|
|
|
14
14
|
[Fast handoff truncated]`),["[TOOLNET FAST HANDOFF]","",`Project: ${e.name}`,`Updated: ${r.generatedAt}`,"",n].join(`
|
|
15
|
-
`)}var
|
|
15
|
+
`)}var b="memory_agent_ask";function O(){return`
|
|
16
16
|
[TOOLNET MEMORY AGENT]
|
|
17
17
|
|
|
18
18
|
Tool:
|
|
19
|
-
- ${
|
|
19
|
+
- ${b}
|
|
20
20
|
|
|
21
21
|
For resume/continue requests:
|
|
22
22
|
|
|
23
23
|
1. Use the injected ToolNet continuity handoff FIRST.
|
|
24
24
|
2. If the handoff is missing or ambiguous, invoke
|
|
25
|
-
${
|
|
25
|
+
${b} directly BEFORE repository/history exploration.
|
|
26
26
|
3. NEVER reconstruct prior work from:
|
|
27
27
|
- .toolnet/sessions/**
|
|
28
28
|
- state.json
|
|
29
29
|
- events.jsonl
|
|
30
30
|
- raw transcripts
|
|
31
31
|
4. NEVER search for the implementation/schema of
|
|
32
|
-
${
|
|
32
|
+
${b}; invoke the MCP tool directly.
|
|
33
33
|
5. Inspect git/source only AFTER continuity context is known.
|
|
34
34
|
|
|
35
35
|
Use:
|
|
@@ -37,28 +37,28 @@ Use:
|
|
|
37
37
|
- mode="ai" for ambiguous or combined continuity questions.
|
|
38
38
|
|
|
39
39
|
Current repository evidence overrides stale memory.
|
|
40
|
-
`.trim()}function W(e){let t=u.resolve(e),r=u.parse(t).root;for(;t!==r;){let n=u.join(t,".toolnet");if(
|
|
40
|
+
`.trim()}function W(e){let t=u.resolve(e),r=u.parse(t).root;for(;t!==r;){let n=u.join(t,".toolnet");if(p.existsSync(n)&&p.statSync(n).isDirectory())return t;t=u.dirname(t)}return null}function N(e){try{return p.existsSync(e)?p.readFileSync(e,"utf-8").trim():null}catch{return null}}function P(e){return e.split(`
|
|
41
41
|
`).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(`
|
|
42
|
-
`)}function
|
|
42
|
+
`)}function D(e={}){let t=e.projectPath||process.cwd(),r=W(t);if(!r)return null;let n=u.join(r,".toolnet","project.json"),o="Unknown";try{if(p.existsSync(n)){let g=JSON.parse(p.readFileSync(n,"utf-8"));o=g.name||g.projectName||"Unknown"}}catch{}let s=u.join(r,".toolnet","profile.md"),i=u.join(r,".toolnet","current.md"),c=N(s)||"",a=N(i)||"";c=P(c),a=P(a);let d=`[TOOLNET PROJECT CONTEXT]
|
|
43
43
|
|
|
44
44
|
Project: ${o}
|
|
45
45
|
Root: ${r}
|
|
46
46
|
|
|
47
|
-
`,
|
|
47
|
+
`,m=C(c,a),l=I({id:"",name:o,rootPath:r},1600),h=l?`
|
|
48
48
|
|
|
49
|
-
${
|
|
49
|
+
${l}
|
|
50
50
|
`:"",S=`
|
|
51
51
|
|
|
52
|
-
${
|
|
52
|
+
${O()}
|
|
53
53
|
|
|
54
54
|
Forbidden At Startup:
|
|
55
55
|
- Do not run session:agy-recover, handoff:latest, or brief automatically.
|
|
56
56
|
- Do not perform deep recovery merely because an agent starts.
|
|
57
57
|
- If the user asks to resume previous work and fast context is insufficient,
|
|
58
58
|
use memory_agent_ask before guessing.
|
|
59
|
-
`;return m+
|
|
60
|
-
`).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):t.length>=2&&t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1):t}function te(){let e=process.env.TOOLNET_GLOBAL_ENV??
|
|
61
|
-
`,{encoding:"utf8",mode:384}),se(o,n)}function L(e,t){return{id:e.id,name:e.name,remote:e.remote,rootPath:t,createdAt:e.createdAt,updatedAt:e.updatedAt,graphVersion:e.graphVersion,memoryVersion:e.memoryVersion,metadata:e.metadata}}var
|
|
59
|
+
`;return d+m+h+S}import{existsSync as fe}from"node:fs";import{dirname as ge,join as ye,parse as he,resolve as Se}from"node:path";import{existsSync as Q,readFileSync as Z}from"node:fs";import{homedir as X}from"node:os";import{join as q}from"node:path";function ee(e){let t=e.trim();return t.length>=2&&t.startsWith('"')&&t.endsWith('"')?(t=t.slice(1,-1),t.replace(/\\n/g,`
|
|
60
|
+
`).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):t.length>=2&&t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1):t}function te(){let e=process.env.TOOLNET_GLOBAL_ENV??q(X(),".config","toolnet-memory",".env");if(!Q(e))return;let t=Z(e,"utf8");for(let r of t.split(/\r?\n/)){let n=r.trim();if(!n||n.startsWith("#"))continue;n.startsWith("export ")&&(n=n.slice(7));let o=n.indexOf("=");if(o<=0)continue;let s=n.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(s)&&process.env[s]===void 0&&(process.env[s]=ee(n.slice(o+1)))}}te();import{createHash as re}from"node:crypto";import{existsSync as R,mkdirSync as ne,readFileSync as oe,renameSync as se,writeFileSync as ie}from"node:fs";import{basename as ce,dirname as w,join as M,parse as $,resolve as y}from"node:path";var z=".toolnet",ae="project.json";function ue(e){return re("sha256").update(e).digest("hex").slice(0,16)}function T(e){return M(e,z,ae)}function de(e){return R(T(e))}function le(e,t){let r=y(e),n=$(r).root;for(;;){if(de(r))return r;if(r===n||t&&r===y(t))break;let o=w(r);if(o===r)break;r=o}return null}function pe(e){let t=y(e),r=$(t).root,n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(n.some(s=>R(M(t,s))))return t;if(t===r)break;let o=w(t);if(o===t)break;t=o}return y(e)}function me(e){let t;try{t=JSON.parse(oe(e,"utf8"))}catch(o){throw new Error(`Invalid ToolNet project manifest: ${e}: ${o instanceof Error?o.message:String(o)}`)}if(!t||typeof t!="object")throw new Error(`Invalid ToolNet project manifest: ${e}`);let r=t;if(typeof r.id!="string"||!r.id.trim())throw new Error(`ToolNet project manifest is missing id: ${e}`);if(typeof r.name!="string"||!r.name.trim())throw new Error(`ToolNet project manifest is missing name: ${e}`);let n=new Date().toISOString();return{version:1,id:r.id,name:r.name,remote:typeof r.remote=="string"&&r.remote.trim()?r.remote:r.name,rootPath:typeof r.rootPath=="string"?r.rootPath:w(w(e)),createdAt:typeof r.createdAt=="string"?r.createdAt:n,updatedAt:typeof r.updatedAt=="string"?r.updatedAt:n,graphVersion:typeof r.graphVersion=="number"?r.graphVersion:0,memoryVersion:typeof r.memoryVersion=="number"?r.memoryVersion:0,metadata:r.metadata&&typeof r.metadata=="object"?r.metadata:void 0}}function F(e,t){let r=M(e,z);ne(r,{recursive:!0});let n=T(e),o=`${n}.tmp-${process.pid}`;ie(o,JSON.stringify(t,null,2)+`
|
|
61
|
+
`,{encoding:"utf8",mode:384}),se(o,n)}function L(e,t){return{id:e.id,name:e.name,remote:e.remote,rootPath:t,createdAt:e.createdAt,updatedAt:e.updatedAt,graphVersion:e.graphVersion,memoryVersion:e.memoryVersion,metadata:e.metadata}}var x=class{detect(t=process.cwd()){let r=y(t),n=pe(r),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(m=>R(M(n,m))),i=le(r,s?n:void 0);if(i){let m=T(i),l=me(m);return l.rootPath!==i&&(l.rootPath=i,l.updatedAt=new Date().toISOString(),F(i,l)),L(l,i)}let c=new Date().toISOString(),a=ce(n),d={version:1,id:ue(n),name:a,remote:a,rootPath:n,createdAt:c,updatedAt:c,graphVersion:0,memoryVersion:0};return F(n,d),L(d,n)}};function K(e){let t=Se(e),r=he(t).root;for(;;){if(fe(ye(t,".toolnet","project.json")))return new x().detect(t);if(t===r)break;let n=ge(t);if(n===t)break;t=n}return null}var B=3200;async function we(){let e="";for await(let t of process.stdin)e+=t.toString();if(!e.trim())return{};try{return JSON.parse(e)}catch{return{}}}function f(){process.stdout.write("{}")}function xe(e){return e.length<=B?e:`${e.slice(0,B)}
|
|
62
62
|
|
|
63
|
-
[ToolNet startup context truncated]`}function
|
|
64
|
-
`)}async function
|
|
63
|
+
[ToolNet startup context truncated]`}function Me(e,t,r){if(process.env.TOOLNET_CODEX_STARTUP_DEBUG!=="1")return;let n=Date.now()-e;process.stderr.write(`[toolnet-memory] codex SessionStart ${n}ms cwd=${t} chars=${r}
|
|
64
|
+
`)}async function ke(){let e=Date.now(),t=await we();if(t.hook_event_name!=="SessionStart"){f();return}let r=typeof t.cwd=="string"?t.cwd:"";if(!r){f();return}if(!K(r)){f();return}try{let o=D({projectPath:r});if(!o?.trim()){f();return}let s=xe(o),i={hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:s}};Me(e,r,s.length),process.stdout.write(JSON.stringify(i))}catch{f()}}ke().catch(()=>{f(),process.exitCode=0});
|