toolnet-memory 0.3.8 → 0.3.10
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/README.md +12 -4
- package/bin/toolnet-memory +48 -0
- package/bundle/auto-integrate.js +60 -28
- package/bundle/continuity-certify.js +1 -1
- package/bundle/copilot-hook.js +208 -0
- package/bundle/copilot.js +33 -0
- package/bundle/cursor-hook.js +206 -0
- package/bundle/cursor.js +33 -0
- package/bundle/grok-hook.js +114 -0
- package/bundle/grok.js +33 -0
- package/bundle/help-cli.js +4 -4
- package/bundle/init.js +65 -33
- package/bundle/kiro-hook.js +1 -1
- package/bundle/mcp.js +18 -18
- package/bundle/production-certify.js +5 -5
- package/bundle/setup.js +61 -29
- package/bundle/status-cli.js +4 -4
- package/bundle/update.js +5 -4
- package/package.json +16 -3
package/README.md
CHANGED
|
@@ -145,7 +145,7 @@ The default startup context is intentionally small and local. Deep recovery is r
|
|
|
145
145
|
ToolNet Memory is designed for workflows such as:
|
|
146
146
|
|
|
147
147
|
```text
|
|
148
|
-
OpenCode → Agy / Antigravity → Codex → Claude Code → Kiro CLI
|
|
148
|
+
OpenCode → Agy / Antigravity → Codex → Claude Code → Kiro CLI → Cursor CLI → GitHub Copilot CLI → Grok Build
|
|
149
149
|
```
|
|
150
150
|
|
|
151
151
|
The next agent should receive the same project continuity instead of starting from zero.
|
|
@@ -174,18 +174,26 @@ toolnet-memory integrate:opencode
|
|
|
174
174
|
toolnet-memory integrate:codex
|
|
175
175
|
toolnet-memory integrate:claude
|
|
176
176
|
toolnet-memory integrate:kiro
|
|
177
|
+
toolnet-memory integrate:cursor
|
|
178
|
+
toolnet-memory integrate:copilot
|
|
179
|
+
toolnet-memory integrate:grok
|
|
177
180
|
```
|
|
178
181
|
|
|
182
|
+
Cursor CLI, GitHub Copilot CLI, and Grok Build use the same shared ToolNet
|
|
183
|
+
continuity layer. Their integrations register ToolNet MCP plus lifecycle capture/
|
|
184
|
+
continuity guards; Grok also installs the ToolNet continuity skill.
|
|
185
|
+
|
|
179
186
|
Detect integrations without modifying configuration:
|
|
180
187
|
|
|
181
188
|
```bash
|
|
182
189
|
toolnet-memory integrate:detect
|
|
183
190
|
```
|
|
184
191
|
|
|
185
|
-
|
|
186
192
|
### Kiro CLI
|
|
193
|
+
|
|
187
194
|
ToolNet Memory integrates with Kiro through MCP and lifecycle hooks.
|
|
188
|
-
|
|
195
|
+
|
|
196
|
+
````bash
|
|
189
197
|
toolnet-memory integrate:kiro
|
|
190
198
|
toolnet-memory integrate:kiro --status
|
|
191
199
|
|
|
@@ -197,7 +205,7 @@ ToolNet Memory also exposes an MCP server:
|
|
|
197
205
|
|
|
198
206
|
```bash
|
|
199
207
|
toolnet-memory mcp
|
|
200
|
-
|
|
208
|
+
````
|
|
201
209
|
|
|
202
210
|
---
|
|
203
211
|
|
package/bin/toolnet-memory
CHANGED
|
@@ -336,6 +336,39 @@ case "$COMMAND" in
|
|
|
336
336
|
exit 0
|
|
337
337
|
;;
|
|
338
338
|
|
|
339
|
+
session:cursor-hook)
|
|
340
|
+
shift
|
|
341
|
+
if [ -f "$ROOT/bundle/cursor-hook.js" ]; then
|
|
342
|
+
exec node "$ROOT/bundle/cursor-hook.js" "$@"
|
|
343
|
+
fi
|
|
344
|
+
if [ -x "${TSX:-}" ] && [ -f "$ROOT/src/session/cursor/hook.ts" ]; then
|
|
345
|
+
exec "$TSX" "$ROOT/src/session/cursor/hook.ts" "$@"
|
|
346
|
+
fi
|
|
347
|
+
exit 0
|
|
348
|
+
;;
|
|
349
|
+
|
|
350
|
+
session:copilot-hook)
|
|
351
|
+
shift
|
|
352
|
+
if [ -f "$ROOT/bundle/copilot-hook.js" ]; then
|
|
353
|
+
exec node "$ROOT/bundle/copilot-hook.js" "$@"
|
|
354
|
+
fi
|
|
355
|
+
if [ -x "${TSX:-}" ] && [ -f "$ROOT/src/session/copilot/hook.ts" ]; then
|
|
356
|
+
exec "$TSX" "$ROOT/src/session/copilot/hook.ts" "$@"
|
|
357
|
+
fi
|
|
358
|
+
exit 0
|
|
359
|
+
;;
|
|
360
|
+
|
|
361
|
+
session:grok-hook)
|
|
362
|
+
shift
|
|
363
|
+
if [ -f "$ROOT/bundle/grok-hook.js" ]; then
|
|
364
|
+
exec node "$ROOT/bundle/grok-hook.js" "$@"
|
|
365
|
+
fi
|
|
366
|
+
if [ -x "${TSX:-}" ] && [ -f "$ROOT/src/session/grok/hook.ts" ]; then
|
|
367
|
+
exec "$TSX" "$ROOT/src/session/grok/hook.ts" "$@"
|
|
368
|
+
fi
|
|
369
|
+
exit 0
|
|
370
|
+
;;
|
|
371
|
+
|
|
339
372
|
integrate:claude)
|
|
340
373
|
shift
|
|
341
374
|
|
|
@@ -366,6 +399,21 @@ case "$COMMAND" in
|
|
|
366
399
|
exit 1
|
|
367
400
|
;;
|
|
368
401
|
|
|
402
|
+
integrate:cursor)
|
|
403
|
+
shift
|
|
404
|
+
run bundle/cursor.js src/session/cursor/cli.ts "$@"
|
|
405
|
+
;;
|
|
406
|
+
|
|
407
|
+
integrate:copilot)
|
|
408
|
+
shift
|
|
409
|
+
run bundle/copilot.js src/session/copilot/cli.ts "$@"
|
|
410
|
+
;;
|
|
411
|
+
|
|
412
|
+
integrate:grok)
|
|
413
|
+
shift
|
|
414
|
+
run bundle/grok.js src/session/grok/cli.ts "$@"
|
|
415
|
+
;;
|
|
416
|
+
|
|
369
417
|
session:codex-notify)
|
|
370
418
|
shift
|
|
371
419
|
|
package/bundle/auto-integrate.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import{existsSync as
|
|
2
|
-
`,{encoding:"utf8",mode:384}),
|
|
3
|
-
`,{encoding:"utf8",mode:384}),
|
|
1
|
+
import{existsSync as Qt}from"node:fs";import{homedir as Zt}from"node:os";import{join as eo}from"node:path";import{spawnSync as to}from"node:child_process";import{homedir as $t}from"node:os";import{join as C}from"node:path";function ie(e={}){return C(e.home??$t(),".gemini")}function U(e={}){return C(ie(e),"config")}function R(e={}){return C(U(e),"mcp_config.json")}function _(e={}){return C(U(e),"hooks.json")}function se(e={}){return C(ie(e),"antigravity-cli")}function ce(e="toolnet-memory",o={}){return C(se(o),"plugins",e)}function ae(e={}){return[se(e),U(e)]}import{homedir as Dt}from"node:os";import{join as S}from"node:path";function k(e={}){let o=e.xdgConfigHome??process.env.XDG_CONFIG_HOME?.trim();return o?S(o,"opencode"):S(e.home??Dt(),".config","opencode")}function le(e={}){return S(k(e),"opencode.json")}function ue(e={}){return S(k(e),"plugins")}function ge(e={}){return S(k(e),"AGENTS.md")}import{homedir as me}from"node:os";import{join as Y}from"node:path";function W(e={}){return Y(e.home??me(),".claude")}function de(e={}){return Y(W(e),"settings.json")}function fe(e={}){return Y(e.home??me(),".claude.json")}import{homedir as Gt}from"node:os";import{join as I}from"node:path";function q(e={}){return e.kiroHome??process.env.KIRO_HOME??I(e.home??Gt(),".kiro")}function Lt(e={}){return I(q(e),"settings")}function pe(e={}){return I(Lt(e),"mcp.json")}function Bt(e={}){return I(q(e),"hooks")}function ye(e={}){return I(Bt(e),"toolnet-memory.json")}function he(e={}){return[q(e)]}import{homedir as Kt}from"node:os";import{join as H}from"node:path";function J(e={}){return e.cursorHome??H(e.home??Kt(),".cursor")}function Ut(e={}){return e.cursorConfigDir??process.env.CURSOR_CONFIG_DIR??(e.xdgConfigHome??process.env.XDG_CONFIG_HOME?H(e.xdgConfigHome??process.env.XDG_CONFIG_HOME,"cursor"):void 0)??J(e)}function ke(e={}){return H(J(e),"mcp.json")}function Oe(e={}){return H(J(e),"hooks.json")}function Ce(e={}){return Array.from(new Set([J(e),Ut(e)]))}import{homedir as Yt}from"node:os";import{join as $}from"node:path";function V(e={}){return e.copilotHome??process.env.COPILOT_HOME??$(e.home??Yt(),".copilot")}function be(e={}){return $(V(e),"mcp-config.json")}function Wt(e={}){return $(V(e),"hooks")}function ve(e={}){return $(Wt(e),"toolnet-memory.json")}function xe(e={}){return[V(e)]}import{homedir as qt}from"node:os";import{join as O}from"node:path";function D(e={}){return e.grokHome??process.env.GROK_HOME??O(e.home??qt(),".grok")}function we(e={}){return O(D(e),"config.toml")}function Vt(e={}){return O(D(e),"hooks")}function Se(e={}){return O(Vt(e),"toolnet-memory.json")}function Ie(e={}){return[D(e)]}function Xt(e={}){return O(D(e),"skills")}function zt(e={}){return O(Xt(e),"toolnet-continuity")}function Ee(e={}){return O(zt(e),"SKILL.md")}function oo(e){return to("sh",["-lc",`command -v ${JSON.stringify(e)} >/dev/null 2>&1`],{stdio:"ignore"}).status===0}function h(e){let o=e.commandExists(e.command),t=e.configPaths.filter(s=>Qt(s)),n=t.length>0,i=[];o&&i.push(`command:${e.command}`);for(let s of t)i.push(`config:${s}`);return{agent:e.agent,detected:o||n,commandDetected:o,configDetected:n,evidence:i}}function Ne(e={}){let o=e.home??Zt(),t=e.commandExists??oo,n=e.codexHome??process.env.CODEX_HOME??eo(o,".codex");return[h({agent:"agy",command:"agy",commandExists:t,configPaths:ae({home:o})}),h({agent:"opencode",command:"opencode",commandExists:t,configPaths:[k({home:o,xdgConfigHome:e.xdgConfigHome})]}),h({agent:"claude",command:"claude",commandExists:t,configPaths:[W({home:o})]}),h({agent:"kiro",command:"kiro-cli",commandExists:t,configPaths:he({home:o,kiroHome:e.kiroHome})}),h({agent:"cursor",command:"agent",commandExists:t,configPaths:Ce({home:o,cursorHome:e.cursorHome,cursorConfigDir:e.cursorConfigDir,xdgConfigHome:e.xdgConfigHome})}),h({agent:"copilot",command:"copilot",commandExists:t,configPaths:xe({home:o,copilotHome:e.copilotHome})}),h({agent:"grok",command:"grok",commandExists:t,configPaths:Ie({home:o,grokHome:e.grokHome})}),h({agent:"codex",command:"codex",commandExists:t,configPaths:[n]})]}import{existsSync as L,mkdirSync as Pe,readFileSync as Re,renameSync as Oo,writeFileSync as Co}from"node:fs";import{dirname as bo,join as G}from"node:path";import{existsSync as no,mkdirSync as ro,readFileSync as io,renameSync as so,rmSync as co,writeFileSync as ao}from"node:fs";import{dirname as lo}from"node:path";function uo(e){return`'${e.replace(/'/g,"'\\''")}'`}function Fe(e={}){let o=e.hooksFile??_();ro(lo(o),{recursive:!0,mode:448});let t={};if(no(o)){let r;try{r=JSON.parse(io(o,"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 n=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",i=`${uo(n)} 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=`${o}.tmp-${process.pid}-${Date.now()}`;try{ao(s,JSON.stringify(t,null,2)+`
|
|
2
|
+
`,{encoding:"utf8",mode:384}),so(s,o)}finally{co(s,{force:!0})}return o}import{existsSync as go,mkdirSync as mo,readFileSync as fo,renameSync as po,writeFileSync as yo}from"node:fs";import{dirname as ho}from"node:path";function E(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function ko(e,o){mo(ho(e),{recursive:!0,mode:448});let t=`${e}.tmp-${process.pid}-${Date.now()}`;yo(t,`${JSON.stringify(o,null,2)}
|
|
3
|
+
`,{encoding:"utf8",mode:384}),po(t,e)}function je(e){if(!go(e))return{};let o=fo(e,"utf8").trim();if(!o)return{};let t;try{t=JSON.parse(o)}catch(n){throw new Error(`Invalid existing Agy MCP config: ${n instanceof Error?n.message:String(n)}`)}if(!E(t))throw new Error("Invalid existing Agy MCP config: root must be a JSON object.");return t}function Te(e,o){return E(e)?e.command===o&&Array.isArray(e.args)&&e.args.length===1&&e.args[0]==="mcp":!1}function Me(e={}){let o=e.configFile??R(),t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",n=e.serverName??"toolnet-memory",i=je(o),s=i.mcpServers;if(s!==void 0&&!E(s))throw new Error("Invalid existing Agy MCP config: mcpServers must be an object.");let r=E(s)?{...s}:{},c=r[n];if(Te(c,t))return{installed:!0,changed:!1,configFile:o,serverName:n,command:t,args:["mcp"]};r[n]={command:t,args:["mcp"]};let u={...i,mcpServers:r};ko(o,u);let a=je(o).mcpServers;if(!E(a)||!Te(a[n],t))throw new Error("Agy MCP configuration was written but verification failed.");return{installed:!0,changed:!0,configFile:o,serverName:n,command:t,args:["mcp"]}}var vo=`# ToolNet Memory Continuity
|
|
4
4
|
|
|
5
5
|
ToolNet Memory is the authoritative continuity layer for previous project work.
|
|
6
6
|
|
|
@@ -41,15 +41,15 @@ Use \`mode="ai"\` only when continuity is ambiguous or requires synthesis.
|
|
|
41
41
|
Current repository evidence overrides stale memory after ToolNet has restored the working context.
|
|
42
42
|
|
|
43
43
|
Do not ask the user to repeat context already available through ToolNet Memory.
|
|
44
|
-
`;function
|
|
45
|
-
`),!0}function
|
|
46
|
-
`),!0}function
|
|
47
|
-
`),
|
|
48
|
-
`);let
|
|
44
|
+
`;function _e(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function X(e,o){Pe(bo(e),{recursive:!0});let t=`${e}.tmp-${process.pid}-${Date.now()}`;Co(t,o,{encoding:"utf8",mode:384}),Oo(t,e)}function Ae(e,o){L(e)&&Re(e,"utf8")===o||X(e,o)}function He(e){if(!L(e))return{};let o=Re(e,"utf8").trim();if(!o)return{};let t;try{t=JSON.parse(o)}catch(n){throw new Error(`Invalid legacy Antigravity config ${e}: ${n instanceof Error?n.message:String(n)}`)}if(!_e(t))throw new Error(`Invalid legacy Antigravity config ${e}: root must be object`);return t}function xo(e,o){if(!L(e))return!1;let t=He(e);if(!_e(t.mcpServers)||!Object.prototype.hasOwnProperty.call(t.mcpServers,o))return!1;let n={...t.mcpServers};return delete n[o],X(e,`${JSON.stringify({...t,mcpServers:n},null,2)}
|
|
45
|
+
`),!0}function wo(e){if(!L(e))return!1;let o=He(e);if(!Object.prototype.hasOwnProperty.call(o,"toolnet-memory"))return!1;let t={...o};return delete t["toolnet-memory"],X(e,`${JSON.stringify(t,null,2)}
|
|
46
|
+
`),!0}function Je(e={}){let o=e.pluginName??"toolnet-memory",t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",n=e.pluginRoot??ce(o),i=G(n,"plugin.json"),s=G(n,"mcp_config.json"),r=G(n,"hooks.json"),c=G(n,"rules","toolnet-memory-continuity.md");Pe(n,{recursive:!0,mode:448}),Ae(i,`${JSON.stringify({$schema:"https://antigravity.google/schemas/v1/plugin.json",name:o,description:"Persistent project continuity and memory for Antigravity coding sessions."},null,2)}
|
|
47
|
+
`),Me({configFile:s,binary:t,serverName:"toolnet-memory"}),Fe({hooksFile:r,binary:t}),Ae(c,`${vo.trim()}
|
|
48
|
+
`);let u=e.legacyMcpFile??R(),l=e.legacyHooksFile??_(),a=[];return u!==s&&xo(u,"toolnet-memory")&&a.push(u),l!==r&&wo(l)&&a.push(l),{installed:!0,pluginRoot:n,files:[i,s,r,c],migratedLegacy:a}}import{existsSync as Io,mkdirSync as Ge,readFileSync as Eo,writeFileSync as Le}from"node:fs";import{join as No}from"node:path";var So="memory_agent_ask";function $e(){return`
|
|
49
49
|
[TOOLNET MEMORY AGENT]
|
|
50
50
|
|
|
51
51
|
Tool available:
|
|
52
|
-
- ${
|
|
52
|
+
- ${So}
|
|
53
53
|
|
|
54
54
|
Use it automatically BEFORE guessing when:
|
|
55
55
|
|
|
@@ -96,7 +96,7 @@ Rules:
|
|
|
96
96
|
- After receiving the ToolNet answer, continue the task
|
|
97
97
|
instead of asking the user to repeat known context.
|
|
98
98
|
- If ToolNet says information is not recorded, say so.
|
|
99
|
-
`.trim()}var
|
|
99
|
+
`.trim()}var De="<!-- TOOLNET_MEMORY_BOOTSTRAP_START -->",z="<!-- TOOLNET_MEMORY_BOOTSTRAP_END -->";function Fo(){let e=ge();Ge(k(),{recursive:!0});let o=`${De}
|
|
100
100
|
## ToolNet Memory
|
|
101
101
|
|
|
102
102
|
ToolNet Memory is available for projects that already contain a valid \`.toolnet/project.json\`.
|
|
@@ -112,12 +112,12 @@ Rules:
|
|
|
112
112
|
7. Current repository evidence has priority over stale memory when they conflict.
|
|
113
113
|
|
|
114
114
|
|
|
115
|
-
${
|
|
115
|
+
${$e()}
|
|
116
116
|
|
|
117
|
-
${
|
|
117
|
+
${z}`,t=Io(e)?Eo(e,"utf8"):"",n=t.indexOf(De),i=t.indexOf(z);return n>=0&&i>=n?t=t.slice(0,n)+o+t.slice(i+z.length):(t=t.trimEnd(),t&&(t+=`
|
|
118
118
|
|
|
119
|
-
`),t+=
|
|
120
|
-
`,{encoding:"utf8",mode:384}),e}function
|
|
119
|
+
`),t+=o),Le(e,t.trimEnd()+`
|
|
120
|
+
`,{encoding:"utf8",mode:384}),e}function Be(e={}){let o=e.directory??ue();Ge(o,{recursive:!0}),Fo();let t=No(o,"toolnet-memory.js"),n=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",i=`
|
|
121
121
|
// Generated by ToolNet Memory.
|
|
122
122
|
// OpenCode integration:
|
|
123
123
|
// - project gated
|
|
@@ -131,7 +131,7 @@ import path from "node:path"
|
|
|
131
131
|
import os from "node:os"
|
|
132
132
|
|
|
133
133
|
const TOOLNET_BINARY =
|
|
134
|
-
${JSON.stringify(
|
|
134
|
+
${JSON.stringify(n)}
|
|
135
135
|
|
|
136
136
|
const CONTEXT_MAX_TOKENS = 700
|
|
137
137
|
|
|
@@ -1051,16 +1051,48 @@ export const ToolNetMemoryPlugin =
|
|
|
1051
1051
|
},
|
|
1052
1052
|
}
|
|
1053
1053
|
}
|
|
1054
|
-
`;return
|
|
1055
|
-
`,{encoding:"utf8",mode:384}),
|
|
1056
|
-
`),c=r.findIndex(
|
|
1057
|
-
`);a
|
|
1058
|
-
`,{encoding:"utf8",mode:384}),
|
|
1059
|
-
`),
|
|
1060
|
-
`)||(
|
|
1061
|
-
`),
|
|
1062
|
-
`,{encoding:"utf8",mode:384}),
|
|
1063
|
-
`,{encoding:"utf8",mode:384}),
|
|
1064
|
-
`,{encoding:"utf8",mode:384}),
|
|
1065
|
-
`,{encoding:"utf8",mode:384}),
|
|
1066
|
-
`,{encoding:"utf8",mode:384}),
|
|
1054
|
+
`;return Le(t,i.trimStart(),{encoding:"utf8",mode:384}),t}import{existsSync as Ye,mkdirSync as jo,readFileSync as To,renameSync as Mo,writeFileSync as Ao}from"node:fs";import{dirname as We,join as Po}from"node:path";function b(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Ro(e,o){jo(We(e),{recursive:!0});let t=`${e}.tmp-${process.pid}-${Date.now()}`;Ao(t,`${JSON.stringify(o,null,2)}
|
|
1055
|
+
`,{encoding:"utf8",mode:384}),Mo(t,e)}function Ke(e){if(!Ye(e))return{};let o=To(e,"utf8").trim();if(!o)return{};let t;try{t=JSON.parse(o)}catch(n){throw new Error(`Invalid existing OpenCode opencode.json: ${n instanceof Error?n.message:String(n)}`)}if(!b(t))throw new Error("Invalid existing OpenCode opencode.json: root must be a JSON object.");return t}function Ue(e,o){if(!b(e))return!1;let t=e.command;return e.type==="local"&&e.enabled!==!1&&Array.isArray(t)&&t.length===2&&t[0]===o&&t[1]==="mcp"}function _o(e,o){let t=e.mcpServers;if(!b(t)||!Object.prototype.hasOwnProperty.call(t,o))return{root:e,changed:!1};let n={...t};return delete n[o],{root:{...e,mcpServers:n},changed:!0}}function qe(e={}){let o=e.configFile??le(),t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",n=e.serverName??"toolnet-memory",i=Po(We(o),"opencode.jsonc"),s=Ye(i)?i:void 0,r=Ke(o),c=_o(r,n),u=c.root,l=u.mcp;if(l!==void 0&&!b(l))throw new Error("Invalid existing OpenCode config: mcp must be an object.");let a=b(l)?{...l}:{},g=a[n];if(Ue(g,t)&&!c.changed)return{installed:!0,changed:!1,configFile:o,serverName:n,command:[t,"mcp"],preservedJsonc:s};a[n]={type:"local",command:[t,"mcp"],enabled:!0};let m={...u,mcp:a};Ro(o,m);let y=Ke(o);if(!b(y.mcp)||!Ue(y.mcp[n],t))throw new Error("OpenCode MCP configuration was written but verification failed.");return{installed:!0,changed:!0,configFile:o,serverName:n,command:[t,"mcp"],preservedJsonc:s}}import{existsSync as Ho,mkdirSync as Ve,readFileSync as Jo,writeFileSync as Xe}from"node:fs";import{homedir as ze}from"node:os";import{dirname as Qe,join as Q}from"node:path";function $o(e){let o=[],t=/"((?:\\.|[^"\\])*)"|'([^']*)'/g,n;for(;n=t.exec(e);){let i=n[1]??n[2]??"";try{o.push(n[1]!==void 0?JSON.parse(`"${i}"`):i)}catch{o.push(i)}}return o}function Ze(e={}){let o=e.configFile??Q(process.env.CODEX_HOME??Q(ze(),".codex"),"config.toml"),t=e.previousFile??Q(ze(),".config","toolnet-memory","codex-notify-previous.json");Ve(Qe(o),{recursive:!0}),Ve(Qe(t),{recursive:!0});let n=Ho(o)?Jo(o,"utf8"):"",i=e.binary??"toolnet-memory",s=`notify = [${JSON.stringify(i)}, "session:codex-notify"]`,r=n.split(`
|
|
1056
|
+
`),c=r.findIndex(m=>/^\s*\[/.test(m));c<0&&(c=r.length);let u=-1,l=-1;for(let m=0;m<c;m+=1)if(/^\s*notify\s*=/.test(r[m])){if(u=m,l=m,r[m].includes("[")&&!r[m].includes("]"))for(;l+1<c&&(l+=1,!r[l].includes("]")););break}let a=[];if(u>=0){let m=r.slice(u,l+1).join(`
|
|
1057
|
+
`);a=$o(m),r.splice(u,l-u+1,s)}else c=r.findIndex(m=>/^\s*\[/.test(m)),c<0&&(c=r.length),r.splice(c,0,s);let g=a.length>=2&&a[a.length-1]==="session:codex-notify";return a.length>0&&!g&&Xe(t,JSON.stringify(a,null,2)+`
|
|
1058
|
+
`,{encoding:"utf8",mode:384}),n=r.join(`
|
|
1059
|
+
`),n.endsWith(`
|
|
1060
|
+
`)||(n+=`
|
|
1061
|
+
`),Xe(o,n,{encoding:"utf8",mode:384}),{configFile:o,previousFile:t,preservedPrevious:a.length>0&&!g}}import{existsSync as Do,mkdirSync as Go,readFileSync as Lo,writeFileSync as Bo}from"node:fs";import{homedir as Ko}from"node:os";import{dirname as Uo,join as et}from"node:path";function Yo(e){return`'${e.replace(/'/g,"'\\''")}'`}function tt(e={}){let o=e.hooksFile??et(process.env.CODEX_HOME??et(Ko(),".codex"),"hooks.json");Go(Uo(o),{recursive:!0});let t={};if(Do(o))try{t=JSON.parse(Lo(o,"utf8"))}catch(c){throw new Error(`Invalid existing Codex hooks.json: ${c instanceof Error?c.message:String(c)}`)}let n=t.hooks&&typeof t.hooks=="object"&&!Array.isArray(t.hooks)?t.hooks:{};t.hooks=n;let s=(Array.isArray(n.SessionStart)?n.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:`${Yo(r)} session:codex-context`,timeout:15,additionalContextLimit:1e3,statusMessage:"Loading ToolNet project continuity"}]}),n.SessionStart=s,Bo(o,JSON.stringify(t,null,2)+`
|
|
1062
|
+
`,{encoding:"utf8",mode:384}),o}import{spawnSync as Wo}from"node:child_process";function Z(e,o){return Wo(e,o,{encoding:"utf8",stdio:["ignore","pipe","pipe"]})}function ot(e,o){let t=Z(e,["mcp","get",o,"--json"]);if(t.status!==0||!t.stdout)return null;try{return JSON.parse(t.stdout)}catch{return null}}function nt(e,o){return e.enabled!==!1&&e.transport?.type==="stdio"&&e.transport?.command===o&&Array.isArray(e.transport?.args)&&e.transport?.args.length===1&&e.transport.args[0]==="mcp"}function rt(e={}){let o=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",t=e.codexBinary??"codex",n=e.serverName??"toolnet-memory",i=ot(t,n);if(i&&nt(i,o))return{installed:!0,changed:!1,serverName:n,command:o,args:["mcp"]};if(i){let c=Z(t,["mcp","remove",n]);if(c.status!==0)return{installed:!1,changed:!1,serverName:n,command:o,args:["mcp"],error:(c.stderr||c.stdout||"Unable to remove old ToolNet MCP configuration.").trim()}}let s=Z(t,["mcp","add",n,"--",o,"mcp"]);if(s.status!==0)return{installed:!1,changed:!1,serverName:n,command:o,args:["mcp"],error:(s.stderr||s.stdout||"Unable to register ToolNet MCP.").trim()};let r=ot(t,n);return!r||!nt(r,o)?{installed:!1,changed:!0,serverName:n,command:o,args:["mcp"],error:"Codex accepted MCP registration but verification did not match expected ToolNet command."}:{installed:!0,changed:!0,serverName:n,command:o,args:["mcp"]}}import{existsSync as qo,mkdirSync as Vo,readFileSync as Xo,renameSync as zo,rmSync as Qo,writeFileSync as Zo}from"node:fs";import{dirname as en}from"node:path";function N(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function tn(e){return/^[A-Za-z0-9_./:-]+$/u.test(e)?e:`'${e.replace(/'/gu,"'\\''")}'`}function on(e){if(!qo(e))return{};let o;try{o=JSON.parse(Xo(e,"utf8"))}catch(t){throw new Error(`Invalid existing Claude settings.json: ${t instanceof Error?t.message:String(t)}`)}if(!N(o))throw new Error("Invalid existing Claude settings.json: root must be a JSON object.");return o}function ee(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 o=[];for(let t of e){if(!N(t)){o.push(t);continue}let n=t.hooks;if(!Array.isArray(n)){o.push(t);continue}let i=n.filter(s=>{if(!N(s))return!0;let r=s.command;return!(typeof r=="string"&&r.includes("session:claude-hook"))});i.length!==0&&o.push({...t,hooks:i})}return o}function te(e){return{type:"command",command:e,timeout:10}}function nn(e,o){Vo(en(e),{recursive:!0,mode:448});let t=`${e}.toolnet-${process.pid}-${Date.now()}.tmp`;try{Zo(t,JSON.stringify(o,null,2)+`
|
|
1063
|
+
`,{encoding:"utf8",mode:384}),zo(t,e)}finally{Qo(t,{force:!0})}}function it(e={}){let o=e.settingsFile??de(),t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",n=on(o),i=n.hooks;if(i!==void 0&&!N(i))throw new Error("Invalid existing Claude settings.json: hooks must be an object.");let s=N(i)?{...i}:{},r=`${tn(t)} session:claude-hook`,c=ee(s.SessionStart);c.push({matcher:"startup|resume|clear|compact",hooks:[te(r)]}),s.SessionStart=c;let u=ee(s.PostToolUse);u.push({matcher:"Edit|Write",hooks:[te(r)]}),s.PostToolUse=u;let l=ee(s.Stop);l.push({hooks:[te(r)]}),s.Stop=l;let a={...n,hooks:s},g=JSON.stringify(n),m=JSON.stringify(a);return g===m?{settingsFile:o,changed:!1}:(nn(o,a),{settingsFile:o,changed:!0})}import{existsSync as rn,mkdirSync as sn,readFileSync as cn,renameSync as an,rmSync as ln,writeFileSync as un}from"node:fs";import{dirname as gn}from"node:path";function F(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function st(e){if(!rn(e))return{};let o;try{o=JSON.parse(cn(e,"utf8"))}catch(t){throw new Error(`Invalid existing Claude Code config: ${t instanceof Error?t.message:String(t)}`)}if(!F(o))throw new Error("Invalid existing Claude Code config: root must be a JSON object.");return o}function ct(e,o){if(!F(e))return!1;let t=e.args;return e.type==="stdio"&&e.command===o&&Array.isArray(t)&&t.length===1&&t[0]==="mcp"}function mn(e,o){sn(gn(e),{recursive:!0});let t=`${e}.toolnet-${process.pid}-${Date.now()}.tmp`;try{un(t,JSON.stringify(o,null,2)+`
|
|
1064
|
+
`,{encoding:"utf8",mode:384}),an(t,e)}finally{ln(t,{force:!0})}}function at(e={}){let o=e.stateFile??fe(),t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",n=e.serverName??"toolnet-memory",i=st(o),s=i.mcpServers;if(s!==void 0&&!F(s))throw new Error("Invalid existing Claude Code config: mcpServers must be an object.");let r=F(s)?{...s}:{},c=r[n];if(ct(c,t))return{installed:!0,changed:!1,configFile:o,serverName:n,command:[t,"mcp"],repaired:!1};let u=c!==void 0;r[n]={type:"stdio",command:t,args:["mcp"]},mn(o,{...i,mcpServers:r});let a=st(o).mcpServers;if(!F(a)||!ct(a[n],t))throw new Error("Claude Code MCP configuration was written but verification failed.");return{installed:!0,changed:!0,configFile:o,serverName:n,command:[t,"mcp"],repaired:u}}function lt(e={}){let o=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",t=it({binary:o,settingsFile:e.settingsFile}),n=at({binary:o,stateFile:e.stateFile});return{hooks:t,mcp:n,files:[t.settingsFile,n.configFile]}}import{existsSync as dn,mkdirSync as fn,readFileSync as pn,renameSync as yn,rmSync as hn,writeFileSync as kn}from"node:fs";import{dirname as On}from"node:path";var v="ToolNet Memory - ";function mt(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Cn(e){return/^[A-Za-z0-9_./:-]+$/u.test(e)?e:`'${e.replace(/'/gu,"'\\''")}'`}function ut(e){if(!dn(e))return{};let o=pn(e,"utf8").trim();if(!o)return{};let t;try{t=JSON.parse(o)}catch(n){throw new Error(`Invalid existing Kiro hooks file: ${n instanceof Error?n.message:String(n)}`)}if(!mt(t))throw new Error("Invalid existing Kiro hooks file: root must be a JSON object.");return t}function gt(e){return mt(e)?typeof e.name=="string"&&e.name.startsWith(v):!1}function j(e){return{type:"command",command:e}}function bn(e){return[{name:`${v}Session Start`,description:"Inject compact local ToolNet continuity and capture Kiro session activation.",trigger:"SessionStart",action:j(e),timeout:10,enabled:!0},{name:`${v}Prompt Continuity`,description:"Capture prompts and refresh ToolNet guidance only for resume/continue requests.",trigger:"UserPromptSubmit",action:j(e),timeout:10,enabled:!0},{name:`${v}Raw History Guard`,description:"Prevent Kiro from reconstructing continuity from raw ToolNet/agent session history.",trigger:"PreToolUse",matcher:"*",action:j(e),timeout:10,enabled:!0},{name:`${v}Tool Capture`,description:"Capture durable tool activity while filtering noisy read-only events.",trigger:"PostToolUse",matcher:"*",action:j(e),timeout:15,enabled:!0},{name:`${v}Final Flush`,description:"Flush pending Kiro WAL events when the assistant finishes a turn.",trigger:"Stop",action:j(e),timeout:30,enabled:!0}]}function vn(e,o){fn(On(e),{recursive:!0,mode:448});let t=`${e}.toolnet-${process.pid}-${Date.now()}.tmp`;try{kn(t,JSON.stringify(o,null,2)+`
|
|
1065
|
+
`,{encoding:"utf8",mode:384}),yn(t,e)}finally{hn(t,{force:!0})}}function dt(e={}){let o=e.hooksFile??ye(),t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",n=ut(o);if(n.version!==void 0&&n.version!=="v1")throw new Error(`Unsupported existing Kiro hooks version: ${String(n.version)}`);let i=n.hooks;if(i!==void 0&&!Array.isArray(i))throw new Error("Invalid existing Kiro hooks file: hooks must be an array.");let s=Array.isArray(i)?i.filter(a=>!gt(a)):[],r=`${Cn(t)} session:kiro-hook`,c=bn(r),u={...n,version:"v1",hooks:[...s,...c]};if(JSON.stringify(n)===JSON.stringify(u))return{hooksFile:o,changed:!1,hookCount:c.length};vn(o,u);let l=ut(o);if(l.version!=="v1"||!Array.isArray(l.hooks)||l.hooks.filter(gt).length!==c.length)throw new Error("Kiro hooks were written but verification failed.");return{hooksFile:o,changed:!0,hookCount:c.length}}import{existsSync as xn,mkdirSync as wn,readFileSync as Sn,renameSync as In,rmSync as En,writeFileSync as Nn}from"node:fs";import{dirname as Fn}from"node:path";function T(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function ft(e){if(!xn(e))return{};let o=Sn(e,"utf8").trim();if(!o)return{};let t;try{t=JSON.parse(o)}catch(n){throw new Error(`Invalid existing Kiro MCP config: ${n instanceof Error?n.message:String(n)}`)}if(!T(t))throw new Error("Invalid existing Kiro MCP config: root must be a JSON object.");return t}function pt(e,o){return T(e)?e.command===o&&Array.isArray(e.args)&&e.args.length===1&&e.args[0]==="mcp"&&e.disabled===!1:!1}function jn(e,o){wn(Fn(e),{recursive:!0,mode:448});let t=`${e}.tmp-${process.pid}-${Date.now()}`;try{Nn(t,`${JSON.stringify(o,null,2)}
|
|
1066
|
+
`,{encoding:"utf8",mode:384}),In(t,e)}finally{En(t,{force:!0})}}function yt(e={}){let o=e.configFile??pe(),t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",n=e.serverName??"toolnet-memory",i=ft(o),s=i.mcpServers;if(s!==void 0&&!T(s))throw new Error("Invalid existing Kiro MCP config: mcpServers must be an object.");let r=T(s)?{...s}:{},c=r[n];if(pt(c,t))return{installed:!0,changed:!1,configFile:o,serverName:n,command:t,args:["mcp"]};r[n]={command:t,args:["mcp"],disabled:!1};let u={...i,mcpServers:r};jn(o,u);let a=ft(o).mcpServers;if(!T(a)||!pt(a[n],t))throw new Error("Kiro MCP configuration was written but verification failed.");return{installed:!0,changed:!0,configFile:o,serverName:n,command:t,args:["mcp"]}}function ht(e={}){let o=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",t=yt({binary:o,configFile:e.configFile}),n=dt({binary:o,hooksFile:e.hooksFile});return{installed:t.installed,changed:t.changed||n.changed,mcp:t,hooks:n,files:[t.configFile,n.hooksFile]}}import{existsSync as Tn,mkdirSync as Mn,readFileSync as An,renameSync as Pn,rmSync as Rn,writeFileSync as _n}from"node:fs";import{dirname as Hn}from"node:path";function d(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function p(e,o){if(!Tn(e))return{};let t=An(e,"utf8").trim();if(!t)return{};let n;try{n=JSON.parse(t)}catch(i){throw new Error(`Invalid existing ${o} hooks file: ${i instanceof Error?i.message:String(i)}`)}if(!d(n))throw new Error(`Invalid existing ${o} hooks file: root must be a JSON object.`);return n}function x(e,o){Mn(Hn(e),{recursive:!0,mode:448});let t=`${e}.toolnet-${process.pid}-${Date.now()}.tmp`;try{_n(t,`${JSON.stringify(o,null,2)}
|
|
1067
|
+
`,{encoding:"utf8",mode:384}),Pn(t,e)}finally{Rn(t,{force:!0})}}function oe(e){return/^[A-Za-z0-9_./:-]+$/u.test(e)?e:`'${e.replace(/'/gu,"'\\''")}'`}var M=[["sessionStart",10],["beforeSubmitPrompt",10],["preToolUse",10],["postToolUse",15],["afterAgentResponse",15],["stop",30]];function kt(e){return d(e)&&typeof e.command=="string"&&e.command.includes("session:cursor-hook")}function Jn(e,o,t){let i={type:"command",command:`TOOLNET_HOOK_EVENT=${oe(e)} ${oe(o)} session:cursor-hook`,timeout:t};return e==="preToolUse"&&(i.matcher=".*"),i}function Ot(e={}){let o=e.hooksFile??Oe(),t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",n=p(o,"Cursor");if(n.version!==void 0&&n.version!==1)throw new Error(`Unsupported existing Cursor hooks version: ${String(n.version)}`);let i=n.hooks;if(i!==void 0&&!d(i))throw new Error("Invalid existing Cursor hooks file: hooks must be an object.");let s=d(i)?{...i}:{};for(let[l,a]of M){let g=s[l];if(g!==void 0&&!Array.isArray(g))throw new Error(`Invalid existing Cursor hooks file: hooks.${l} must be an array.`);let m=Array.isArray(g)?g.filter(y=>!kt(y)):[];s[l]=[...m,Jn(l,t,a)]}let r={...n,version:1,hooks:s};if(JSON.stringify(n)===JSON.stringify(r))return{hooksFile:o,changed:!1,hookCount:M.length};x(o,r);let c=p(o,"Cursor");if(c.version!==1||!d(c.hooks))throw new Error("Cursor hooks were written but verification failed.");let u=0;for(let[l]of M){let a=c.hooks[l];if(!Array.isArray(a))throw new Error("Cursor hooks were written but verification failed.");u+=a.filter(kt).length}if(u!==M.length)throw new Error("Cursor hooks were written but verification failed.");return{hooksFile:o,changed:!0,hookCount:M.length}}import{existsSync as $n,mkdirSync as Dn,readFileSync as Gn,renameSync as Ln,rmSync as Bn,writeFileSync as Kn}from"node:fs";import{dirname as Un}from"node:path";function f(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function w(e,o){if(!$n(e))return{};let t=Gn(e,"utf8").trim();if(!t)return{};let n;try{n=JSON.parse(t)}catch(i){throw new Error(`Invalid existing ${o} MCP config: ${i instanceof Error?i.message:String(i)}`)}if(!f(n))throw new Error(`Invalid existing ${o} MCP config: root must be a JSON object.`);return n}function B(e,o){Dn(Un(e),{recursive:!0,mode:448});let t=`${e}.tmp-${process.pid}-${Date.now()}`;try{Kn(t,`${JSON.stringify(o,null,2)}
|
|
1068
|
+
`,{encoding:"utf8",mode:384}),Ln(t,e)}finally{Bn(t,{force:!0})}}function Ct(e,o){return f(e)?(e.type===void 0||e.type==="stdio")&&e.command===o&&Array.isArray(e.args)&&e.args.length===1&&e.args[0]==="mcp":!1}function bt(e={}){let o=e.configFile??ke(),t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",n=e.serverName??"toolnet-memory",i=w(o,"Cursor"),s=i.mcpServers;if(s!==void 0&&!f(s))throw new Error("Invalid existing Cursor MCP config: mcpServers must be an object.");let r=f(s)?{...s}:{};if(Ct(r[n],t))return{installed:!0,changed:!1,configFile:o,serverName:n,command:t,args:["mcp"]};r[n]={type:"stdio",command:t,args:["mcp"]},B(o,{...i,mcpServers:r});let u=w(o,"Cursor").mcpServers;if(!f(u)||!Ct(u[n],t))throw new Error("Cursor MCP configuration was written but verification failed.");return{installed:!0,changed:!0,configFile:o,serverName:n,command:t,args:["mcp"]}}function vt(e={}){let o=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",t=bt({binary:o,configFile:e.configFile}),n=Ot({binary:o,hooksFile:e.hooksFile});return{installed:t.installed,changed:t.changed||n.changed,mcp:t,hooks:n,files:[t.configFile,n.hooksFile]}}var A=[["sessionStart",10],["userPromptSubmitted",10],["userPromptTransformed",10],["preToolUse",10],["postToolUse",15],["agentStop",30]];function Yn(e){if(typeof e.command=="string")return e.command;if(typeof e.bash=="string")return e.bash}function xt(e){return d(e)&&Yn(e)?.includes("session:copilot-hook")===!0}function Wn(e,o,t){let n={type:"command",command:`${o} session:copilot-hook`,env:{TOOLNET_HOOK_EVENT:e},timeoutSec:t};return e==="preToolUse"&&(n.matcher=".*"),n}function wt(e={}){let o=e.hooksFile??ve(),t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",n=p(o,"GitHub Copilot CLI");if(n.version!==void 0&&n.version!==1)throw new Error(`Unsupported existing GitHub Copilot CLI hooks version: ${String(n.version)}`);let i=n.hooks;if(i!==void 0&&!d(i))throw new Error("Invalid existing GitHub Copilot CLI hooks file: hooks must be an object.");let s=d(i)?{...i}:{};for(let[l,a]of A){let g=s[l];if(g!==void 0&&!Array.isArray(g))throw new Error(`Invalid existing GitHub Copilot CLI hooks file: hooks.${l} must be an array.`);let m=Array.isArray(g)?g.filter(y=>!xt(y)):[];s[l]=[...m,Wn(l,t,a)]}let r={...n,version:1,hooks:s};if(JSON.stringify(n)===JSON.stringify(r))return{hooksFile:o,changed:!1,hookCount:A.length};x(o,r);let c=p(o,"GitHub Copilot CLI");if(c.version!==1||!d(c.hooks))throw new Error("GitHub Copilot CLI hooks were written but verification failed.");let u=0;for(let[l]of A){let a=c.hooks[l];if(!Array.isArray(a))throw new Error("GitHub Copilot CLI hooks were written but verification failed.");u+=a.filter(xt).length}if(u!==A.length)throw new Error("GitHub Copilot CLI hooks were written but verification failed.");return{hooksFile:o,changed:!0,hookCount:A.length}}function St(e,o){return f(e)?(e.type===void 0||e.type==="local"||e.type==="stdio")&&e.command===o&&Array.isArray(e.args)&&e.args.length===1&&e.args[0]==="mcp"&&Array.isArray(e.tools)&&e.tools.length===1&&e.tools[0]==="*":!1}function It(e={}){let o=e.configFile??be(),t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",n=e.serverName??"toolnet-memory",i=w(o,"GitHub Copilot CLI"),s=i.mcpServers;if(s!==void 0&&!f(s))throw new Error("Invalid existing GitHub Copilot CLI MCP config: mcpServers must be an object.");let r=f(s)?{...s}:{};if(St(r[n],t))return{installed:!0,changed:!1,configFile:o,serverName:n,command:t,args:["mcp"]};r[n]={type:"stdio",command:t,args:["mcp"],tools:["*"]},B(o,{...i,mcpServers:r});let u=w(o,"GitHub Copilot CLI").mcpServers;if(!f(u)||!St(u[n],t))throw new Error("GitHub Copilot CLI MCP configuration was written but verification failed.");return{installed:!0,changed:!0,configFile:o,serverName:n,command:t,args:["mcp"]}}function Et(e={}){let o=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",t=It({binary:o,configFile:e.configFile}),n=wt({binary:o,hooksFile:e.hooksFile});return{installed:t.installed,changed:t.changed||n.changed,mcp:t,hooks:n,files:[t.configFile,n.hooksFile]}}import{existsSync as qn,mkdirSync as Vn,readFileSync as Nt,renameSync as Xn,rmSync as zn,writeFileSync as Qn}from"node:fs";import{dirname as Zn}from"node:path";var ne=`---
|
|
1069
|
+
name: toolnet-continuity
|
|
1070
|
+
description: Restore previous ToolNet project work when the user asks to continue, resume, pick up, finish unfinished work, or asks where work stopped.
|
|
1071
|
+
when-to-use: continue, resume, pick up, carry on, ti\u1EBFp t\u1EE5c, l\xE0m ti\u1EBFp, l\xE0m n\u1ED1t, \u0111ang l\xE0m \u0111\u1EBFn \u0111\xE2u, d\u1EEBng \u1EDF \u0111\xE2u
|
|
1072
|
+
---
|
|
1073
|
+
|
|
1074
|
+
# ToolNet Continuity
|
|
1075
|
+
|
|
1076
|
+
When the user asks to continue or resume previous work:
|
|
1077
|
+
|
|
1078
|
+
1. Use the ToolNet Memory MCP server as the continuity source.
|
|
1079
|
+
2. Invoke \`memory_agent_ask\` before exploring old history if the current
|
|
1080
|
+
ToolNet handoff is missing, stale, or ambiguous.
|
|
1081
|
+
3. Prefer \`mode="local"\` for current task, last file, blocker, completed
|
|
1082
|
+
work, TODOs, and next action.
|
|
1083
|
+
4. Use \`mode="ai"\` only when continuity requires synthesis.
|
|
1084
|
+
5. Do not reconstruct previous work from:
|
|
1085
|
+
- \`.toolnet/sessions/**\`
|
|
1086
|
+
- ToolNet \`events.jsonl\` or \`state.json\`
|
|
1087
|
+
- raw transcripts
|
|
1088
|
+
- another coding agent's private session/history files
|
|
1089
|
+
6. After ToolNet continuity is known, verify current git and repository
|
|
1090
|
+
source truth before changing code.
|
|
1091
|
+
7. Do not ask the user to repeat context ToolNet already provides.
|
|
1092
|
+
|
|
1093
|
+
Current repository evidence overrides stale memory.
|
|
1094
|
+
`;function er(e,o){Vn(Zn(e),{recursive:!0,mode:448});let t=`${e}.toolnet-${process.pid}-${Date.now()}.tmp`;try{Qn(t,o,{encoding:"utf8",mode:384}),Xn(t,e)}finally{zn(t,{force:!0})}}function Ft(e={}){let o=e.skillFile??Ee();if(qn(o)&&Nt(o,"utf8")===ne)return{skillFile:o,changed:!1};if(er(o,ne),Nt(o,"utf8")!==ne)throw new Error("Grok ToolNet continuity skill was written but verification failed.");return{skillFile:o,changed:!0}}var P=[["SessionStart",10],["UserPromptSubmit",10],["PreToolUse",10],["PostToolUse",15],["Stop",30]];function jt(e){return!d(e)||!Array.isArray(e.hooks)?!1:e.hooks.some(o=>d(o)&&typeof o.command=="string"&&o.command.includes("session:grok-hook"))}function tr(e,o,t){let n={hooks:[{type:"command",command:`${o} session:grok-hook`,timeout:t,env:{TOOLNET_HOOK_EVENT:e}}]};return e==="PreToolUse"&&(n.matcher=".*"),n}function Tt(e={}){let o=e.hooksFile??Se(),t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",n=p(o,"Grok Build"),i=n.hooks;if(i!==void 0&&!d(i))throw new Error("Invalid existing Grok Build hooks file: hooks must be an object.");let s=d(i)?{...i}:{};for(let[l,a]of P){let g=s[l];if(g!==void 0&&!Array.isArray(g))throw new Error(`Invalid existing Grok Build hooks file: hooks.${l} must be an array.`);let m=Array.isArray(g)?g.filter(y=>!jt(y)):[];s[l]=[...m,tr(l,t,a)]}let r={...n,hooks:s};if(JSON.stringify(n)===JSON.stringify(r))return{hooksFile:o,changed:!1,hookCount:P.length};x(o,r);let c=p(o,"Grok Build");if(!d(c.hooks))throw new Error("Grok Build hooks were written but verification failed.");let u=0;for(let[l]of P){let a=c.hooks[l];if(!Array.isArray(a))throw new Error("Grok Build hooks were written but verification failed.");u+=a.filter(jt).length}if(u!==P.length)throw new Error("Grok Build hooks were written but verification failed.");return{hooksFile:o,changed:!0,hookCount:P.length}}import{existsSync as or,mkdirSync as nr,readFileSync as rr,renameSync as ir,rmSync as sr,writeFileSync as cr}from"node:fs";import{dirname as ar}from"node:path";function Mt(e){return or(e)?rr(e,"utf8"):""}function lr(e,o){nr(ar(e),{recursive:!0,mode:448});let t=`${e}.tmp-${process.pid}-${Date.now()}`;try{cr(t,o,{encoding:"utf8",mode:384}),ir(t,e)}finally{sr(t,{force:!0})}}function re(e){return e.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/\t/g,"\\t")}function ur(e){return`[mcp_servers."${re(e)}"]`}function gr(e,o){return[ur(e),`command = "${re(o)}"`,'args = ["mcp"]',"enabled = true"].join(`
|
|
1095
|
+
`)}function mr(e){let o=e.trim();return o.startsWith("[")&&o.includes("]")}function K(e){return e.trim().replace(/\s+/g,"")}function dr(e){return new Set([K(`[mcp_servers.${e}]`),K(`[mcp_servers."${e}"]`),K(`[mcp_servers.'${e}']`)])}function Pt(e,o){let t=e.split(/\r?\n/),n=dr(o),i=-1;for(let a=0;a<t.length;a+=1){let g=K(t[a].replace(/\s+#.*$/,""));if(n.has(g)){i=a;break}}if(i<0)return null;let s=t.length;for(let a=i+1;a<t.length;a+=1)if(mr(t[a])){s=a;break}let r=[],c=0;for(let a of t)r.push(c),c+=a.length+1;let u=r[i]??0,l=s>=t.length?e.length:r[s]??e.length;return{start:u,end:l}}function fr(e,o,t){let n=`${gr(o,t)}
|
|
1096
|
+
`,i=Pt(e,o);if(i){let s=e.slice(0,i.start),r=e.slice(i.end);return`${s}${n}${r.replace(/^\n+/,"")}`}return e.trim()?`${e.replace(/\s*$/,"")}
|
|
1097
|
+
|
|
1098
|
+
${n}`:n}function At(e,o,t){let n=Pt(e,o);if(!n)return!1;let i=e.slice(n.start,n.end);return i.includes(`command = "${re(t)}"`)&&/args\s*=\s*\[\s*"mcp"\s*\]/.test(i)&&/enabled\s*=\s*true/.test(i)}function Rt(e={}){let o=e.configFile??we(),t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",n=e.serverName??"toolnet-memory",i=Mt(o);if(At(i,n,t))return{installed:!0,changed:!1,configFile:o,serverName:n,command:t,args:["mcp"]};let s=fr(i,n,t);lr(o,s);let r=Mt(o);if(!At(r,n,t))throw new Error("Grok Build MCP configuration was written but verification failed.");return{installed:!0,changed:!0,configFile:o,serverName:n,command:t,args:["mcp"]}}function _t(e={}){let o=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",t=Rt({binary:o,configFile:e.configFile}),n=Tt({binary:o,hooksFile:e.hooksFile}),i=Ft({skillFile:e.skillFile});return{installed:t.installed,changed:t.changed||n.changed||i.changed,mcp:t,hooks:n,skill:i,files:[t.configFile,n.hooksFile,i.skillFile]}}function Ht(){return Ne()}function pr(e={}){let o=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",t=[],n=e.detections??Ht(),i=new Map(n.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=Je({binary:o});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=Be({binary:o}),c=qe({binary:o});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=lt({binary:o});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("kiro")===!0))t.push({agent:"kiro",detected:!1,installed:!1,targets:[]});else try{let r=ht({...e.kiro??{},binary:o});t.push({agent:"kiro",detected:!0,installed:!0,targets:[r.mcp.configFile,`mcp:${r.mcp.serverName}`,r.hooks.hooksFile]})}catch(r){t.push({agent:"kiro",detected:!0,installed:!1,targets:[],error:r instanceof Error?r.message:String(r)})}if(!(e.force===!0||i.get("cursor")===!0))t.push({agent:"cursor",detected:!1,installed:!1,targets:[]});else try{let r=vt({...e.cursor??{},binary:o});t.push({agent:"cursor",detected:!0,installed:!0,targets:[r.mcp.configFile,`mcp:${r.mcp.serverName}`,r.hooks.hooksFile]})}catch(r){t.push({agent:"cursor",detected:!0,installed:!1,targets:[],error:r instanceof Error?r.message:String(r)})}if(!(e.force===!0||i.get("copilot")===!0))t.push({agent:"copilot",detected:!1,installed:!1,targets:[]});else try{let r=Et({...e.copilot??{},binary:o});t.push({agent:"copilot",detected:!0,installed:!0,targets:[r.mcp.configFile,`mcp:${r.mcp.serverName}`,r.hooks.hooksFile]})}catch(r){t.push({agent:"copilot",detected:!0,installed:!1,targets:[],error:r instanceof Error?r.message:String(r)})}if(!(e.force===!0||i.get("grok")===!0))t.push({agent:"grok",detected:!1,installed:!1,targets:[]});else try{let r=_t({...e.grok??{},binary:o});t.push({agent:"grok",detected:!0,installed:!0,targets:[r.mcp.configFile,`mcp:${r.mcp.serverName}`,r.hooks.hooksFile,r.skill.skillFile]})}catch(r){t.push({agent:"grok",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=Ze({binary:o}),c=tt({binary:o}),u=rt({binary:o});if(!u.installed)throw new Error(u.error??"Codex MCP registration failed");let l=[r.configFile,c,`mcp:${u.serverName}`];r.preservedPrevious&&l.push(r.previousFile),t.push({agent:"codex",detected:!0,installed:!0,targets:l})}catch(r){t.push({agent:"codex",detected:!0,installed:!1,targets:[],error:r instanceof Error?r.message:String(r)})}return t}function Jt(e){switch(e){case"agy":return"Agy / Antigravity";case"opencode":return"OpenCode";case"claude":return"Claude Code";case"kiro":return"Kiro CLI";case"cursor":return"Cursor CLI";case"copilot":return"GitHub Copilot CLI";case"grok":return"Grok Build";case"codex":return"Codex"}}function yr(e){console.log(""),console.log("ToolNet Memory Integration Detection"),console.log("===================================="),console.log("");for(let o of e){let t=Jt(o.agent);if(!o.detected){console.log(`\u25CB ${t}: not detected`);continue}console.log(`\u2713 ${t}: detected`);for(let n of o.evidence)console.log(` ${n}`)}console.log("")}function hr(e){console.log(""),console.log("ToolNet Memory AI Integrations"),console.log("=============================="),console.log("");for(let o of e){let t=Jt(o.agent);if(!o.detected){console.log(`- ${t}: not detected`);continue}if(o.installed){console.log(`\u2713 ${t}: automatic memory enabled`);continue}console.log(`\u2717 ${t}: integration failed`),o.error&&console.log(` ${o.error}`)}console.log("")}async function kr(){let e=process.argv.slice(2),o=e.includes("--all"),t=e.includes("--json");if(e.includes("--detect-only")){let s=Ht();if(t){console.log(JSON.stringify(s,null,2));return}yr(s);return}let i=pr({force:o});if(t){console.log(JSON.stringify(i,null,2));return}hr(i)}var Or=process.argv[1]&&(process.argv[1].endsWith("auto-integrate.js")||process.argv[1].endsWith("auto-integrate.ts"));Or&&kr().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exitCode=1});export{Ht as detectAutoIntegrations,pr as installAutoIntegrations};
|
|
@@ -47,4 +47,4 @@ Answer the coding agent directly.
|
|
|
47
47
|
`):e.trim(),l=C(e),d=y(n,p,{maxFacts:8,maxChars:1800}).facts.map(b=>({kind:b.kind,value:b.value,source:b.source})),g=St(a&&i?[...d,...i.focus]:d);return Et(n,{schema:"toolnet.memory-conversation.v1",version:1,projectId:n.id,updatedAt:new Date(r).toISOString(),lastIntent:l,focus:g}),{question:p,originalQuestion:e,usedPriorFocus:a,previousIntent:i?.lastIntent,currentIntent:l,priorFocus:i?.focus??[],focusCount:g.length}}var Dr={question:pe.string().min(2).max(4e3).describe("Question about previous project work, unfinished tasks, handoff, blockers, decisions, files or next actions."),mode:pe.enum(["ai","local"]).optional().describe("ai uses ToolNet Memory Agent with configured LLM fallback chain. local returns deterministic local memory only.")};async function fe(n,e){let t=me(n.project,e.question);if(e.mode==="local"){let o=H(t);if(o)return{answer:o.answer,mode:"local",usedAi:!1,source:o.source,intent:o.intent};let i=M(n.project,t.originalQuestion);return{answer:i.answer,mode:"local",usedAi:!1,source:i.source,intent:i.intent}}let r=await le(n.project,t.question);if(!r.usedAi){let o=H(t);if(o)return{answer:o.answer,mode:"ai",usedAi:!1,provider:r.provider,model:r.model}}return{answer:r.answer,mode:"ai",usedAi:r.usedAi,provider:r.provider,model:r.model}}var ge="RAW_TRANSCRIPT_MUST_NEVER_ENTER_CONTINUITY_CONTEXT_X1";function ye(n,e){he(n,JSON.stringify(e,null,2)+`
|
|
48
48
|
`,"utf8")}function Rt(n,e,t,r,o,i,a){return{continuity:{schema:"toolnet.handoff.v2",version:2,project:{id:n,name:e},source:{agent:t,nativeSessionId:`${t}-x1-source`,sessionKey:`${t}:${t}-x1-source`,sequence:42,reason:"checkpoint"},capturedAt:"2026-08-13T05:30:00.000Z",current:{phase:{id:"x1",title:"Cross-Agent Continuity E2E",status:"in_progress"},task:{id:"x1-cross-agent",title:r,status:"in_progress"},file:o},completed:{phases:["A1 OpenCode Adapter","A2 Agy Adapter","A3 Claude Code Adapter"],tasks:["Build canonical handoff schema","Build intelligent memory retrieval"]},remaining:{phases:["X1 Cross-Agent Continuity E2E"],tasks:[r],todos:[i]},nextAction:a,blockers:[],decisions:["Canonical handoff must override stale session state","Never replay raw transcripts during continuity recovery"],files:{current:o,recent:[o]},tests:{status:"passing",recent:["A1-A3 adapter tests passed"]},attention:[],progress:{phasesTotal:3,phasesCompleted:2,tasksTotal:4,tasksCompleted:3,blocked:0},stateDigest:"a".repeat(64)}}}async function L(n,e){return fe({project:n},{question:e,mode:"local"})}async function Ft(n,e){let t=Tt(k(Ot(),`toolnet-x1-${n}-${e}-`));try{let r=`x1-${n}-${e}`,o=`x1-${n}-to-${e}`,i={id:r,name:o,rootPath:t,createdAt:"2026-08-13T05:30:00.000Z",updatedAt:"2026-08-13T05:30:00.000Z",graphVersion:1,memoryVersion:1},a=k(t,".toolnet","work"),p=k(t,".toolnet","context"),l=k(t,".toolnet","sessions",n,"raw-session-x1");G(a,{recursive:!0}),G(p,{recursive:!0}),G(l,{recursive:!0});let f=`X1 ${n} to ${e} canonical continuity`,d=`src/x1/${n}-to-${e}.ts`,g=`Implement ${e} continuation step`,b=`Continue ${f} in ${d}`;ye(k(a,"handoff-latest.json"),Rt(r,o,n,f,d,g,b)),ye(k(p,"session-origin.json"),{version:1,projectId:r,agent:"stale-agent",nativeSessionId:"stale-session",updatedAt:"2026-01-01T00:00:00.000Z",currentTask:"STALE TASK MUST NOT WIN",currentPhase:"STALE PHASE",lastTouchedFile:"src/stale/old.ts",latestNextAction:"STALE NEXT ACTION"}),he(k(l,"events.jsonl"),JSON.stringify({text:ge})+`
|
|
49
49
|
`,"utf8");let be=y(i,"T\xF4i ph\u1EA3i l\xE0m g\xEC ti\u1EBFp?",{maxFacts:12,maxChars:3200}),D=await L(i,"Task hi\u1EC7n t\u1EA1i \u0111ang l\xE0m l\xE0 g\xEC?"),K=await L(i,"File hi\u1EC7n t\u1EA1i l\xE0 file n\xE0o?"),$=await L(i,"TODO c\xF2n l\u1EA1i l\xE0 g\xEC?"),U=await L(i,"T\xF4i ph\u1EA3i l\xE0m g\xEC ti\u1EBFp?"),ve=[D.answer,K.answer,$.answer,U.answer].join(`
|
|
50
|
-
`),j=[D,K,$,U],Y={canonicalHandoffPreferred:be.facts[0]?.source==="handoff",taskRecovered:D.answer.includes(f),fileRecovered:K.answer.includes(d),todoRecovered:$.answer.includes(g),nextActionRecovered:U.answer.includes(b),localModeOnly:j.every(A=>A.mode==="local"),noAiRequired:j.every(A=>A.usedAi===!1),rawTranscriptIsolated:!ve.includes(ge),compact:j.every(A=>A.answer.length<1600)};return{from:n,to:e,passed:Object.values(Y).every(Boolean),checks:Y}}finally{Pt(t,{recursive:!0,force:!0})}}async function ke(){let n=[["agy","codex"],["codex","opencode"],["opencode","claude"],["claude","kiro"],["kiro","agy"]],e=[];for(let[r,o]of n)e.push(await Ft(r,o));let t=e.filter(r=>r.passed).length;return{passed:t===e.length,total:e.length,passedCount:t,cases:e}}var m={reset:"\x1B[0m",green:"\x1B[32m",red:"\x1B[31m",cyan:"\x1B[36m",gray:"\x1B[90m",white:"\x1B[97m"};function Nt(n){return n?`${m.green}\u2713${m.reset}`:`${m.red}\u2717${m.reset}`}function Lt(n){let e=`${n.from.padEnd(8)} \u2192 ${n.to.padEnd(8)}`;if(console.log(`\u2502 ${Nt(n.passed)} ${e}`),!n.passed)for(let[t,r]of Object.entries(n.checks))r||console.log(`\u2502 ${m.red}\u2717${m.reset} ${t}`)}async function Dt(){console.log(""),console.log(`${m.cyan}\u25C7${m.reset} ${m.white}ToolNet Cross-Agent Continuity Certification${m.reset}`),console.log(`${m.gray}\u2502${m.reset}`);let n=await ke();for(let e of n.cases)Lt(e);if(console.log(`${m.gray}\u2502${m.reset}`),n.passed){console.log(`${m.green}\u25C6 PASS${m.reset} ${n.passedCount}/${n.total} cross-agent transitions`),console.log(`${m.green}\u2713${m.reset} canonical handoff`),console.log(`${m.green}\u2713${m.reset} memory_agent_ask`),console.log(`${m.green}\u2713${m.reset} task / file / TODO / next action`),console.log(`${m.green}\u2713${m.reset} raw transcripts isolated`),console.log(`${m.green}\u2713${m.reset} AI not required for direct continuity`);return}console.log(`${m.red}\u25C6 FAIL${m.reset} ${n.passedCount}/${n.total} cross-agent transitions`),process.exitCode=1}Dt().catch(n=>{console.error(n instanceof Error?n.message:String(n)),process.exitCode=1});
|
|
50
|
+
`),j=[D,K,$,U],Y={canonicalHandoffPreferred:be.facts[0]?.source==="handoff",taskRecovered:D.answer.includes(f),fileRecovered:K.answer.includes(d),todoRecovered:$.answer.includes(g),nextActionRecovered:U.answer.includes(b),localModeOnly:j.every(A=>A.mode==="local"),noAiRequired:j.every(A=>A.usedAi===!1),rawTranscriptIsolated:!ve.includes(ge),compact:j.every(A=>A.answer.length<1600)};return{from:n,to:e,passed:Object.values(Y).every(Boolean),checks:Y}}finally{Pt(t,{recursive:!0,force:!0})}}async function ke(){let n=[["agy","codex"],["codex","opencode"],["opencode","claude"],["claude","kiro"],["kiro","cursor"],["cursor","copilot"],["copilot","grok"],["grok","agy"]],e=[];for(let[r,o]of n)e.push(await Ft(r,o));let t=e.filter(r=>r.passed).length;return{passed:t===e.length,total:e.length,passedCount:t,cases:e}}var m={reset:"\x1B[0m",green:"\x1B[32m",red:"\x1B[31m",cyan:"\x1B[36m",gray:"\x1B[90m",white:"\x1B[97m"};function Nt(n){return n?`${m.green}\u2713${m.reset}`:`${m.red}\u2717${m.reset}`}function Lt(n){let e=`${n.from.padEnd(8)} \u2192 ${n.to.padEnd(8)}`;if(console.log(`\u2502 ${Nt(n.passed)} ${e}`),!n.passed)for(let[t,r]of Object.entries(n.checks))r||console.log(`\u2502 ${m.red}\u2717${m.reset} ${t}`)}async function Dt(){console.log(""),console.log(`${m.cyan}\u25C7${m.reset} ${m.white}ToolNet Cross-Agent Continuity Certification${m.reset}`),console.log(`${m.gray}\u2502${m.reset}`);let n=await ke();for(let e of n.cases)Lt(e);if(console.log(`${m.gray}\u2502${m.reset}`),n.passed){console.log(`${m.green}\u25C6 PASS${m.reset} ${n.passedCount}/${n.total} cross-agent transitions`),console.log(`${m.green}\u2713${m.reset} canonical handoff`),console.log(`${m.green}\u2713${m.reset} memory_agent_ask`),console.log(`${m.green}\u2713${m.reset} task / file / TODO / next action`),console.log(`${m.green}\u2713${m.reset} raw transcripts isolated`),console.log(`${m.green}\u2713${m.reset} AI not required for direct continuity`);return}console.log(`${m.red}\u25C6 FAIL${m.reset} ${n.passedCount}/${n.total} cross-agent transitions`),process.exitCode=1}Dt().catch(n=>{console.error(n instanceof Error?n.message:String(n)),process.exitCode=1});
|