toolnet-memory 0.3.3 → 0.3.5
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/bundle/agy-hook.js +79 -69
- package/bundle/agy.js +71 -61
- package/bundle/api.js +16 -16
- package/bundle/auto-integrate.js +377 -71
- package/bundle/claude-hook.js +102 -36
- package/bundle/codex-context.js +10 -10
- package/bundle/codex.js +67 -65
- package/bundle/context-runtime.js +30 -30
- package/bundle/context.js +15 -15
- package/bundle/continuity-certify.js +14 -13
- package/bundle/doctor.js +5 -5
- package/bundle/full-index.js +22 -18
- package/bundle/graph-index.js +2 -2
- package/bundle/handoff-refresh.js +7 -7
- package/bundle/init.js +386 -80
- package/bundle/learner.js +1 -1
- package/bundle/mcp.js +19 -19
- package/bundle/memory-agent.js +11 -11
- package/bundle/memory-query.js +5 -5
- package/bundle/opencode.js +442 -121
- package/bundle/project-manual.js +3 -3
- package/bundle/provider.js +5 -2
- package/bundle/recovery-certify.js +21 -20
- package/bundle/runtime.js +4 -4
- package/bundle/semantic.js +5 -5
- package/bundle/service-cli.js +2 -2
- package/bundle/setup.js +376 -70
- package/bundle/snapshot.js +4 -4
- package/bundle/update.js +4 -4
- package/bundle/work-continuity.js +7 -7
- package/package.json +1 -1
package/bundle/init.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import{existsSync as
|
|
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
|
|
3
|
-
`,{encoding:"utf8",mode:384}),
|
|
4
|
-
`),this))}update(t){return this.label=t,this.enabled&&this.active&&this.interactive&&this.render(),this}succeed(t){this.finish("\u2713",t??this.label,m.green)}fail(t){this.finish("\u2717",t??this.label,m.red)}warn(t){this.finish("!",t??this.label,m.yellow)}stop(){this.active&&(this.timer&&(clearInterval(this.timer),this.timer=void 0),this.enabled&&this.interactive&&this.stream.write(m.clear),this.active=!1)}render(){if(!this.enabled||!this.active||!this.interactive)return;let t=
|
|
1
|
+
import{existsSync as Ue,statSync as kn}from"node:fs";import{resolve as Rn,join as jn}from"node:path";import{existsSync as Ke,readFileSync as Ve}from"node:fs";import{homedir as We}from"node:os";import{join as Ge}from"node:path";function qe(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 Qe(){let e=process.env.TOOLNET_GLOBAL_ENV??Ge(We(),".config","toolnet-memory",".env");if(!Ke(e))return;let t=Ve(e,"utf8");for(let n of t.split(/\r?\n/)){let o=n.trim();if(!o||o.startsWith("#"))continue;o.startsWith("export ")&&(o=o.slice(7));let s=o.indexOf("=");if(s<=0)continue;let i=o.slice(0,s).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(i)&&process.env[i]===void 0&&(process.env[i]=qe(o.slice(s+1)))}}Qe();import{createHash as Xe}from"node:crypto";import{existsSync as k,mkdirSync as Ze,readFileSync as et,renameSync as tt,writeFileSync as nt}from"node:fs";import{basename as ot,dirname as w,join as C,parse as V,resolve as h}from"node:path";var W=".toolnet",rt="project.json";function st(e){return Xe("sha256").update(e).digest("hex").slice(0,16)}function R(e){return C(e,W,rt)}function it(e){return k(R(e))}function at(e,t){let n=h(e),o=V(n).root;for(;;){if(it(n))return n;if(n===o||t&&n===h(t))break;let s=w(n);if(s===n)break;n=s}return null}function ct(e){let t=h(e),n=V(t).root,o=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(o.some(i=>k(C(t,i))))return t;if(t===n)break;let s=w(t);if(s===t)break;t=s}return h(e)}function lt(e){let t;try{t=JSON.parse(et(e,"utf8"))}catch(s){throw new Error(`Invalid ToolNet project manifest: ${e}: ${s instanceof Error?s.message:String(s)}`)}if(!t||typeof t!="object")throw new Error(`Invalid ToolNet project manifest: ${e}`);let n=t;if(typeof n.id!="string"||!n.id.trim())throw new Error(`ToolNet project manifest is missing id: ${e}`);if(typeof n.name!="string"||!n.name.trim())throw new Error(`ToolNet project manifest is missing name: ${e}`);let o=new Date().toISOString();return{version:1,id:n.id,name:n.name,remote:typeof n.remote=="string"&&n.remote.trim()?n.remote:n.name,rootPath:typeof n.rootPath=="string"?n.rootPath:w(w(e)),createdAt:typeof n.createdAt=="string"?n.createdAt:o,updatedAt:typeof n.updatedAt=="string"?n.updatedAt:o,graphVersion:typeof n.graphVersion=="number"?n.graphVersion:0,memoryVersion:typeof n.memoryVersion=="number"?n.memoryVersion:0,metadata:n.metadata&&typeof n.metadata=="object"?n.metadata:void 0}}function U(e,t){let n=C(e,W);Ze(n,{recursive:!0});let o=R(e),s=`${o}.tmp-${process.pid}`;nt(s,JSON.stringify(t,null,2)+`
|
|
3
|
+
`,{encoding:"utf8",mode:384}),tt(s,o)}function K(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 O=class{detect(t=process.cwd()){let n=h(t),o=ct(n),i=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(u=>k(C(o,u))),r=at(n,i?o:void 0);if(r){let u=R(r),p=lt(u);return p.rootPath!==r&&(p.rootPath=r,p.updatedAt=new Date().toISOString(),U(r,p)),K(p,r)}let a=new Date().toISOString(),l=ot(o),c={version:1,id:st(o),name:l,remote:l,rootPath:o,createdAt:a,updatedAt:a,graphVersion:0,memoryVersion:0};return U(o,c),K(c,o)}};var G=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],m={clear:"\r\x1B[2K",cyan:"\x1B[36m",green:"\x1B[32m",red:"\x1B[31m",yellow:"\x1B[33m",amber:"\x1B[38;5;214m",dim:"\x1B[2m",reset:"\x1B[0m"};function q(e,t=16){let o=Math.max(1,t-4+1),s=e%o;return"\u2500".repeat(s)+"\u2501".repeat(4)+"\u2500".repeat(Math.max(0,t-s-4))}function Q(e){let t=Date.now()-e;return t<1e3?`${t}ms`:t<1e4?`${(t/1e3).toFixed(1)}s`:`${Math.round(t/1e3)}s`}var j=class{stream;enabled;interactive;color;intervalMs;display;label;frame=0;startedAt=0;timer;active=!1;constructor(t,n={}){this.label=t,this.stream=n.stream??process.stderr,this.enabled=n.enabled??!0,this.interactive=n.interactive??this.stream.isTTY===!0,this.color=n.color??(this.interactive&&process.env.NO_COLOR===void 0),this.intervalMs=Math.max(40,n.intervalMs??80),this.display=n.display??"spinner"}start(){return!this.enabled||this.active?this:(this.active=!0,this.startedAt=Date.now(),this.interactive?(this.render(),this.timer=setInterval(()=>{this.frame=(this.frame+1)%1e4,this.render()},this.intervalMs),this.timer.unref?.(),this):(this.stream.write(`\u2192 ${this.label}
|
|
4
|
+
`),this))}update(t){return this.label=t,this.enabled&&this.active&&this.interactive&&this.render(),this}succeed(t){this.finish("\u2713",t??this.label,m.green)}fail(t){this.finish("\u2717",t??this.label,m.red)}warn(t){this.finish("!",t??this.label,m.yellow)}stop(){this.active&&(this.timer&&(clearInterval(this.timer),this.timer=void 0),this.enabled&&this.interactive&&this.stream.write(m.clear),this.active=!1)}render(){if(!this.enabled||!this.active||!this.interactive)return;let t=G[this.frame%G.length],n=this.display==="bar"?this.color?`${m.amber}${q(this.frame)}${m.reset}`:q(this.frame):this.color?`${m.cyan}${t}${m.reset}`:t,o=Q(this.startedAt),s=this.color?`${m.dim}${o}${m.reset}`:o;this.stream.write(`${m.clear}${n} ${this.label} ${s}`)}finish(t,n,o){if(!this.enabled){this.active=!1;return}this.startedAt||(this.startedAt=Date.now()),this.timer&&(clearInterval(this.timer),this.timer=void 0);let s=Q(this.startedAt),i=this.color?`${o}${t}${m.reset}`:t,r=this.color?`${m.dim}${s}${m.reset}`:s;this.interactive?this.stream.write(`${m.clear}${i} ${n} ${r}
|
|
5
5
|
`):this.stream.write(`${i} ${n} (${s})
|
|
6
|
-
`),this.active=!1}};async function N(e,t,n={}){let o=new
|
|
7
|
-
`,{encoding:"utf8",mode:384}),
|
|
8
|
-
`,{encoding:"utf8",mode:384}),
|
|
6
|
+
`),this.active=!1}};async function N(e,t,n={}){let o=new j(e,n).start();try{let s=await t();return o.succeed(),s}catch(s){throw o.fail(),s}}import{existsSync as mt}from"node:fs";import{homedir as pt}from"node:os";import{join as gt}from"node:path";import{spawnSync as ft}from"node:child_process";import{homedir as ut}from"node:os";import{join as f}from"node:path";function X(e={}){return f(e.home??ut(),".gemini")}function _(e={}){return f(X(e),"config")}function M(e={}){return f(_(e),"mcp_config.json")}function I(e={}){return f(_(e),"hooks.json")}function Z(e={}){return f(X(e),"antigravity-cli")}function ee(e="toolnet-memory",t={}){return f(Z(t),"plugins",e)}function te(e={}){return[Z(e),_(e)]}import{homedir as dt}from"node:os";import{join as v}from"node:path";function g(e={}){let t=e.xdgConfigHome??process.env.XDG_CONFIG_HOME?.trim();return t?v(t,"opencode"):v(e.home??dt(),".config","opencode")}function ne(e={}){return v(g(e),"opencode.json")}function oe(e={}){return v(g(e),"plugins")}function re(e={}){return v(g(e),"AGENTS.md")}import{homedir as se}from"node:os";import{join as P}from"node:path";function F(e={}){return P(e.home??se(),".claude")}function ie(e={}){return P(F(e),"settings.json")}function ae(e={}){return P(e.home??se(),".claude.json")}function yt(e){return ft("sh",["-lc",`command -v ${JSON.stringify(e)} >/dev/null 2>&1`],{stdio:"ignore"}).status===0}function A(e){let t=e.commandExists(e.command),n=e.configPaths.filter(i=>mt(i)),o=n.length>0,s=[];t&&s.push(`command:${e.command}`);for(let i of n)s.push(`config:${i}`);return{agent:e.agent,detected:t||o,commandDetected:t,configDetected:o,evidence:s}}function ce(e={}){let t=e.home??pt(),n=e.commandExists??yt,o=e.codexHome??process.env.CODEX_HOME??gt(t,".codex");return[A({agent:"agy",command:"agy",commandExists:n,configPaths:te({home:t})}),A({agent:"opencode",command:"opencode",commandExists:n,configPaths:[g({home:t,xdgConfigHome:e.xdgConfigHome})]}),A({agent:"claude",command:"claude",commandExists:n,configPaths:[F({home:t})]}),A({agent:"codex",command:"codex",commandExists:n,configPaths:[o]})]}import{existsSync as T,mkdirSync as ge,readFileSync as fe,renameSync as jt,writeFileSync as Nt}from"node:fs";import{dirname as _t,join as E}from"node:path";import{existsSync as ht,mkdirSync as vt,readFileSync as bt,renameSync as xt,rmSync as St,writeFileSync as wt}from"node:fs";import{dirname as Ot}from"node:path";function Ct(e){return`'${e.replace(/'/g,"'\\''")}'`}function le(e={}){let t=e.hooksFile??I();vt(Ot(t),{recursive:!0,mode:448});let n={};if(ht(t)){let r;try{r=JSON.parse(bt(t,"utf8"))}catch(a){throw new Error(`Invalid existing Agy hooks.json: ${a instanceof Error?a.message:String(a)}`)}if(typeof r!="object"||r===null||Array.isArray(r))throw new Error("Invalid existing Agy hooks.json: root must be a JSON object.");n=r}let o=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",s=`${Ct(o)} session:agy-hook`;n["toolnet-memory"]={enabled:!0,PreToolUse:[{matcher:"view_file|list_dir|find_by_name|grep_search|run_command",hooks:[{type:"command",command:`${s} pre-tool`,timeout:5}]}],PreInvocation:[{type:"command",command:`${s} pre`,timeout:15}],PostInvocation:[{type:"command",command:`${s} post`,timeout:15}],Stop:[{type:"command",command:`${s} stop`,timeout:30}]};let i=`${t}.tmp-${process.pid}-${Date.now()}`;try{wt(i,JSON.stringify(n,null,2)+`
|
|
7
|
+
`,{encoding:"utf8",mode:384}),xt(i,t)}finally{St(i,{force:!0})}return t}import{existsSync as Mt,mkdirSync as It,readFileSync as At,renameSync as Et,writeFileSync as Tt}from"node:fs";import{dirname as kt}from"node:path";function b(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Rt(e,t){It(kt(e),{recursive:!0,mode:448});let n=`${e}.tmp-${process.pid}-${Date.now()}`;Tt(n,`${JSON.stringify(t,null,2)}
|
|
8
|
+
`,{encoding:"utf8",mode:384}),Et(n,e)}function ue(e){if(!Mt(e))return{};let t=At(e,"utf8").trim();if(!t)return{};let n;try{n=JSON.parse(t)}catch(o){throw new Error(`Invalid existing Agy MCP config: ${o instanceof Error?o.message:String(o)}`)}if(!b(n))throw new Error("Invalid existing Agy MCP config: root must be a JSON object.");return n}function de(e,t){return b(e)?e.command===t&&Array.isArray(e.args)&&e.args.length===1&&e.args[0]==="mcp":!1}function me(e={}){let t=e.configFile??M(),n=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=e.serverName??"toolnet-memory",s=ue(t),i=s.mcpServers;if(i!==void 0&&!b(i))throw new Error("Invalid existing Agy MCP config: mcpServers must be an object.");let r=b(i)?{...i}:{},a=r[o];if(de(a,n))return{installed:!0,changed:!1,configFile:t,serverName:o,command:n,args:["mcp"]};r[o]={command:n,args:["mcp"]};let l={...s,mcpServers:r};Rt(t,l);let u=ue(t).mcpServers;if(!b(u)||!de(u[o],n))throw new Error("Agy MCP configuration was written but verification failed.");return{installed:!0,changed:!0,configFile:t,serverName:o,command:n,args:["mcp"]}}var Pt=`# ToolNet Memory Continuity
|
|
9
9
|
|
|
10
10
|
ToolNet Memory is the authoritative continuity layer for previous project work.
|
|
11
11
|
|
|
@@ -46,15 +46,15 @@ Use \`mode="ai"\` only when continuity is ambiguous or requires synthesis.
|
|
|
46
46
|
Current repository evidence overrides stale memory after ToolNet has restored the working context.
|
|
47
47
|
|
|
48
48
|
Do not ask the user to repeat context already available through ToolNet Memory.
|
|
49
|
-
`;function
|
|
50
|
-
`),!0}function
|
|
51
|
-
`),!0}function
|
|
52
|
-
`),
|
|
53
|
-
`);let l=e.legacyMcpFile??M(),
|
|
49
|
+
`;function ye(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function D(e,t){ge(_t(e),{recursive:!0});let n=`${e}.tmp-${process.pid}-${Date.now()}`;Nt(n,t,{encoding:"utf8",mode:384}),jt(n,e)}function pe(e,t){T(e)&&fe(e,"utf8")===t||D(e,t)}function he(e){if(!T(e))return{};let t=fe(e,"utf8").trim();if(!t)return{};let n;try{n=JSON.parse(t)}catch(o){throw new Error(`Invalid legacy Antigravity config ${e}: ${o instanceof Error?o.message:String(o)}`)}if(!ye(n))throw new Error(`Invalid legacy Antigravity config ${e}: root must be object`);return n}function Ft(e,t){if(!T(e))return!1;let n=he(e);if(!ye(n.mcpServers)||!Object.prototype.hasOwnProperty.call(n.mcpServers,t))return!1;let o={...n.mcpServers};return delete o[t],D(e,`${JSON.stringify({...n,mcpServers:o},null,2)}
|
|
50
|
+
`),!0}function Dt(e){if(!T(e))return!1;let t=he(e);if(!Object.prototype.hasOwnProperty.call(t,"toolnet-memory"))return!1;let n={...t};return delete n["toolnet-memory"],D(e,`${JSON.stringify(n,null,2)}
|
|
51
|
+
`),!0}function ve(e={}){let t=e.pluginName??"toolnet-memory",n=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=e.pluginRoot??ee(t),s=E(o,"plugin.json"),i=E(o,"mcp_config.json"),r=E(o,"hooks.json"),a=E(o,"rules","toolnet-memory-continuity.md");ge(o,{recursive:!0,mode:448}),pe(s,`${JSON.stringify({$schema:"https://antigravity.google/schemas/v1/plugin.json",name:t,description:"Persistent project continuity and memory for Antigravity coding sessions."},null,2)}
|
|
52
|
+
`),me({configFile:i,binary:n,serverName:"toolnet-memory"}),le({hooksFile:r,binary:n}),pe(a,`${Pt.trim()}
|
|
53
|
+
`);let l=e.legacyMcpFile??M(),c=e.legacyHooksFile??I(),u=[];return l!==i&&Ft(l,"toolnet-memory")&&u.push(l),c!==r&&Dt(c)&&u.push(c),{installed:!0,pluginRoot:o,files:[s,i,r,a],migratedLegacy:u}}import{existsSync as Lt,mkdirSync as Se,readFileSync as Jt,writeFileSync as we}from"node:fs";import{join as Bt}from"node:path";var $t="memory_agent_ask";function be(){return`
|
|
54
54
|
[TOOLNET MEMORY AGENT]
|
|
55
55
|
|
|
56
56
|
Tool available:
|
|
57
|
-
- ${
|
|
57
|
+
- ${$t}
|
|
58
58
|
|
|
59
59
|
Use it automatically BEFORE guessing when:
|
|
60
60
|
|
|
@@ -101,7 +101,7 @@ Rules:
|
|
|
101
101
|
- After receiving the ToolNet answer, continue the task
|
|
102
102
|
instead of asking the user to repeat known context.
|
|
103
103
|
- If ToolNet says information is not recorded, say so.
|
|
104
|
-
`.trim()}var
|
|
104
|
+
`.trim()}var xe="<!-- TOOLNET_MEMORY_BOOTSTRAP_START -->",$="<!-- TOOLNET_MEMORY_BOOTSTRAP_END -->";function Ht(){let e=re();Se(g(),{recursive:!0});let t=`${xe}
|
|
105
105
|
## ToolNet Memory
|
|
106
106
|
|
|
107
107
|
ToolNet Memory is available for projects that already contain a valid \`.toolnet/project.json\`.
|
|
@@ -117,12 +117,12 @@ Rules:
|
|
|
117
117
|
7. Current repository evidence has priority over stale memory when they conflict.
|
|
118
118
|
|
|
119
119
|
|
|
120
|
-
${
|
|
120
|
+
${be()}
|
|
121
121
|
|
|
122
|
-
${$}`,n
|
|
122
|
+
${$}`,n=Lt(e)?Jt(e,"utf8"):"",o=n.indexOf(xe),s=n.indexOf($);return o>=0&&s>=o?n=n.slice(0,o)+t+n.slice(s+$.length):(n=n.trimEnd(),n&&(n+=`
|
|
123
123
|
|
|
124
|
-
`),n+=t),
|
|
125
|
-
`,{encoding:"utf8",mode:384}),e}function
|
|
124
|
+
`),n+=t),we(e,n.trimEnd()+`
|
|
125
|
+
`,{encoding:"utf8",mode:384}),e}function Oe(e={}){let t=e.directory??oe();Se(t,{recursive:!0}),Ht();let n=Bt(t,"toolnet-memory.js"),o=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",s=`
|
|
126
126
|
// Generated by ToolNet Memory.
|
|
127
127
|
// OpenCode integration:
|
|
128
128
|
// - project gated
|
|
@@ -142,22 +142,52 @@ const CONTEXT_MAX_TOKENS = 700
|
|
|
142
142
|
|
|
143
143
|
const CONTEXT_CACHE_MS = 5000
|
|
144
144
|
|
|
145
|
-
const
|
|
145
|
+
const LOCAL_CAPTURE_MS = 15000
|
|
146
146
|
|
|
147
|
-
const
|
|
147
|
+
const REMOTE_SYNC_MS = 60000
|
|
148
148
|
|
|
149
|
-
const
|
|
149
|
+
const EVENT_CAPTURE_DEBOUNCE_MS = 1200
|
|
150
|
+
|
|
151
|
+
const CAPTURE_TIMEOUT_MS = 20000
|
|
152
|
+
|
|
153
|
+
const REMOTE_TIMEOUT_MS = 120000
|
|
154
|
+
|
|
155
|
+
const GLOBAL_STATUS_FILE = path.join(
|
|
150
156
|
os.homedir(),
|
|
151
157
|
".config",
|
|
152
158
|
"toolnet-memory",
|
|
153
159
|
"opencode-sync-status.json"
|
|
154
160
|
)
|
|
155
161
|
|
|
162
|
+
function projectStatusFile(
|
|
163
|
+
data
|
|
164
|
+
) {
|
|
165
|
+
if (
|
|
166
|
+
typeof data?.projectRoot ===
|
|
167
|
+
"string" &&
|
|
168
|
+
data.projectRoot.length > 0
|
|
169
|
+
) {
|
|
170
|
+
return path.join(
|
|
171
|
+
data.projectRoot,
|
|
172
|
+
".toolnet",
|
|
173
|
+
"runtime",
|
|
174
|
+
"opencode-status.json"
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return GLOBAL_STATUS_FILE
|
|
179
|
+
}
|
|
180
|
+
|
|
156
181
|
function writeStatus(data) {
|
|
157
182
|
try {
|
|
183
|
+
const statusFile =
|
|
184
|
+
projectStatusFile(
|
|
185
|
+
data
|
|
186
|
+
)
|
|
187
|
+
|
|
158
188
|
fs.mkdirSync(
|
|
159
189
|
path.dirname(
|
|
160
|
-
|
|
190
|
+
statusFile
|
|
161
191
|
),
|
|
162
192
|
{
|
|
163
193
|
recursive: true,
|
|
@@ -165,7 +195,7 @@ function writeStatus(data) {
|
|
|
165
195
|
)
|
|
166
196
|
|
|
167
197
|
fs.writeFileSync(
|
|
168
|
-
|
|
198
|
+
statusFile,
|
|
169
199
|
JSON.stringify(
|
|
170
200
|
{
|
|
171
201
|
timestamp:
|
|
@@ -292,7 +322,7 @@ async function runWithTimeout(
|
|
|
292
322
|
{
|
|
293
323
|
stdout = "ignore",
|
|
294
324
|
timeout =
|
|
295
|
-
|
|
325
|
+
REMOTE_TIMEOUT_MS,
|
|
296
326
|
} = {}
|
|
297
327
|
) {
|
|
298
328
|
const child =
|
|
@@ -394,14 +424,67 @@ export const ToolNetMemoryPlugin =
|
|
|
394
424
|
let lastSessionId =
|
|
395
425
|
null
|
|
396
426
|
|
|
397
|
-
|
|
427
|
+
/*
|
|
428
|
+
* Two independent lanes:
|
|
429
|
+
*
|
|
430
|
+
* captureChain:
|
|
431
|
+
* OpenCode DB -> local fsync WAL -> current work
|
|
432
|
+
*
|
|
433
|
+
* remoteInFlight:
|
|
434
|
+
* WAL -> Hugging Face / S3
|
|
435
|
+
*
|
|
436
|
+
* A slow remote backend must never block local capture.
|
|
437
|
+
*/
|
|
438
|
+
let captureChain =
|
|
398
439
|
Promise.resolve()
|
|
399
440
|
|
|
441
|
+
let remoteInFlight =
|
|
442
|
+
null
|
|
443
|
+
|
|
444
|
+
let debounceTimer =
|
|
445
|
+
null
|
|
446
|
+
|
|
400
447
|
let contextCache = {
|
|
401
448
|
value: "",
|
|
402
449
|
expiresAt: 0,
|
|
403
450
|
}
|
|
404
451
|
|
|
452
|
+
/*
|
|
453
|
+
* Compact startup context is injected once
|
|
454
|
+
* per native OpenCode session.
|
|
455
|
+
*
|
|
456
|
+
* Deep history is retrieved through
|
|
457
|
+
* memory_agent_ask / MCP on demand.
|
|
458
|
+
*/
|
|
459
|
+
const injectedSessions =
|
|
460
|
+
new Set()
|
|
461
|
+
|
|
462
|
+
function contextSessionKey(
|
|
463
|
+
input
|
|
464
|
+
) {
|
|
465
|
+
const candidates = [
|
|
466
|
+
input?.sessionID,
|
|
467
|
+
input?.sessionId,
|
|
468
|
+
input?.session?.id,
|
|
469
|
+
input?.info?.id,
|
|
470
|
+
lastSessionId,
|
|
471
|
+
]
|
|
472
|
+
|
|
473
|
+
const sessionId =
|
|
474
|
+
candidates.find(
|
|
475
|
+
value =>
|
|
476
|
+
typeof value ===
|
|
477
|
+
"string" &&
|
|
478
|
+
value.length > 0
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
return sessionId
|
|
482
|
+
? "session:" +
|
|
483
|
+
sessionId
|
|
484
|
+
: "project:" +
|
|
485
|
+
projectRoot
|
|
486
|
+
}
|
|
487
|
+
|
|
405
488
|
async function readContext() {
|
|
406
489
|
const now =
|
|
407
490
|
Date.now()
|
|
@@ -473,8 +556,13 @@ export const ToolNetMemoryPlugin =
|
|
|
473
556
|
|
|
474
557
|
async function syncNow(
|
|
475
558
|
sessionId,
|
|
476
|
-
|
|
477
|
-
|
|
559
|
+
{
|
|
560
|
+
flag = null,
|
|
561
|
+
reason = "unknown",
|
|
562
|
+
localOnly = false,
|
|
563
|
+
timeout =
|
|
564
|
+
REMOTE_TIMEOUT_MS,
|
|
565
|
+
} = {}
|
|
478
566
|
) {
|
|
479
567
|
if (!sessionId) {
|
|
480
568
|
return
|
|
@@ -494,9 +582,18 @@ export const ToolNetMemoryPlugin =
|
|
|
494
582
|
)
|
|
495
583
|
}
|
|
496
584
|
|
|
585
|
+
if (localOnly) {
|
|
586
|
+
args.push(
|
|
587
|
+
"--local-only"
|
|
588
|
+
)
|
|
589
|
+
}
|
|
590
|
+
|
|
497
591
|
try {
|
|
498
592
|
await runWithTimeout(
|
|
499
|
-
args
|
|
593
|
+
args,
|
|
594
|
+
{
|
|
595
|
+
timeout,
|
|
596
|
+
}
|
|
500
597
|
)
|
|
501
598
|
|
|
502
599
|
writeStatus({
|
|
@@ -504,8 +601,14 @@ export const ToolNetMemoryPlugin =
|
|
|
504
601
|
projectRoot,
|
|
505
602
|
sessionId,
|
|
506
603
|
reason,
|
|
604
|
+
mode:
|
|
605
|
+
localOnly
|
|
606
|
+
? "local"
|
|
607
|
+
: "remote",
|
|
507
608
|
state:
|
|
508
|
-
|
|
609
|
+
localOnly
|
|
610
|
+
? "capture-success"
|
|
611
|
+
: "sync-success",
|
|
509
612
|
})
|
|
510
613
|
} catch (error) {
|
|
511
614
|
writeStatus({
|
|
@@ -513,8 +616,14 @@ export const ToolNetMemoryPlugin =
|
|
|
513
616
|
projectRoot,
|
|
514
617
|
sessionId,
|
|
515
618
|
reason,
|
|
619
|
+
mode:
|
|
620
|
+
localOnly
|
|
621
|
+
? "local"
|
|
622
|
+
: "remote",
|
|
516
623
|
state:
|
|
517
|
-
|
|
624
|
+
localOnly
|
|
625
|
+
? "capture-failed"
|
|
626
|
+
: "sync-failed",
|
|
518
627
|
error:
|
|
519
628
|
error instanceof
|
|
520
629
|
Error
|
|
@@ -528,20 +637,24 @@ export const ToolNetMemoryPlugin =
|
|
|
528
637
|
}
|
|
529
638
|
}
|
|
530
639
|
|
|
531
|
-
function
|
|
640
|
+
function queueCapture(
|
|
532
641
|
sessionId,
|
|
533
642
|
flag,
|
|
534
643
|
reason
|
|
535
644
|
) {
|
|
536
645
|
if (!sessionId) {
|
|
537
|
-
return
|
|
646
|
+
return captureChain
|
|
538
647
|
}
|
|
539
648
|
|
|
540
649
|
lastSessionId =
|
|
541
650
|
sessionId
|
|
542
651
|
|
|
543
|
-
|
|
544
|
-
|
|
652
|
+
/*
|
|
653
|
+
* Local captures are serialized with each other,
|
|
654
|
+
* but completely independent from remote sync.
|
|
655
|
+
*/
|
|
656
|
+
captureChain =
|
|
657
|
+
captureChain
|
|
545
658
|
.catch(
|
|
546
659
|
() =>
|
|
547
660
|
undefined
|
|
@@ -550,46 +663,162 @@ export const ToolNetMemoryPlugin =
|
|
|
550
663
|
() =>
|
|
551
664
|
syncNow(
|
|
552
665
|
sessionId,
|
|
553
|
-
|
|
554
|
-
|
|
666
|
+
{
|
|
667
|
+
flag,
|
|
668
|
+
reason,
|
|
669
|
+
localOnly: true,
|
|
670
|
+
timeout:
|
|
671
|
+
CAPTURE_TIMEOUT_MS,
|
|
672
|
+
}
|
|
555
673
|
)
|
|
556
674
|
)
|
|
675
|
+
.catch(
|
|
676
|
+
() =>
|
|
677
|
+
undefined
|
|
678
|
+
)
|
|
679
|
+
|
|
680
|
+
return captureChain
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
function queueRemote(
|
|
684
|
+
sessionId,
|
|
685
|
+
flag,
|
|
686
|
+
reason
|
|
687
|
+
) {
|
|
688
|
+
if (!sessionId) {
|
|
689
|
+
return Promise.resolve()
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
lastSessionId =
|
|
693
|
+
sessionId
|
|
694
|
+
|
|
695
|
+
/*
|
|
696
|
+
* Never build an endless remote backlog.
|
|
697
|
+
* One remote flush is enough because WAL keeps
|
|
698
|
+
* every pending local event until acknowledged.
|
|
699
|
+
*/
|
|
700
|
+
if (remoteInFlight) {
|
|
701
|
+
return remoteInFlight
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
remoteInFlight =
|
|
705
|
+
syncNow(
|
|
706
|
+
sessionId,
|
|
707
|
+
{
|
|
708
|
+
flag,
|
|
709
|
+
reason,
|
|
710
|
+
localOnly: false,
|
|
711
|
+
timeout:
|
|
712
|
+
REMOTE_TIMEOUT_MS,
|
|
713
|
+
}
|
|
714
|
+
)
|
|
715
|
+
.catch(
|
|
716
|
+
() =>
|
|
717
|
+
undefined
|
|
718
|
+
)
|
|
719
|
+
.finally(
|
|
720
|
+
() => {
|
|
721
|
+
remoteInFlight =
|
|
722
|
+
null
|
|
723
|
+
}
|
|
724
|
+
)
|
|
725
|
+
|
|
726
|
+
return remoteInFlight
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
function scheduleCapture(
|
|
730
|
+
sessionId,
|
|
731
|
+
reason
|
|
732
|
+
) {
|
|
733
|
+
if (!sessionId) {
|
|
734
|
+
return
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
if (debounceTimer) {
|
|
738
|
+
clearTimeout(
|
|
739
|
+
debounceTimer
|
|
740
|
+
)
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
debounceTimer =
|
|
744
|
+
setTimeout(
|
|
745
|
+
() => {
|
|
746
|
+
debounceTimer =
|
|
747
|
+
null
|
|
748
|
+
|
|
749
|
+
void queueCapture(
|
|
750
|
+
sessionId,
|
|
751
|
+
null,
|
|
752
|
+
reason
|
|
753
|
+
)
|
|
754
|
+
},
|
|
755
|
+
EVENT_CAPTURE_DEBOUNCE_MS
|
|
756
|
+
)
|
|
557
757
|
|
|
558
|
-
|
|
758
|
+
if (
|
|
759
|
+
typeof debounceTimer.unref ===
|
|
760
|
+
"function"
|
|
761
|
+
) {
|
|
762
|
+
debounceTimer.unref()
|
|
763
|
+
}
|
|
559
764
|
}
|
|
560
765
|
|
|
561
766
|
/*
|
|
562
|
-
*
|
|
767
|
+
* Crash-safe LOCAL checkpoint.
|
|
563
768
|
*
|
|
564
|
-
*
|
|
565
|
-
*
|
|
566
|
-
* abrupt SSH/process termination may
|
|
567
|
-
* skip shutdown hooks.
|
|
769
|
+
* Fast lane:
|
|
770
|
+
* OpenCode DB -> fsync WAL -> current work.
|
|
568
771
|
*
|
|
569
|
-
*
|
|
570
|
-
* from keeping OpenCode alive.
|
|
772
|
+
* No network dependency.
|
|
571
773
|
*/
|
|
572
|
-
const
|
|
774
|
+
const localPeriodic =
|
|
573
775
|
setInterval(
|
|
574
776
|
() => {
|
|
575
777
|
if (
|
|
576
778
|
lastSessionId
|
|
577
779
|
) {
|
|
578
|
-
void
|
|
780
|
+
void queueCapture(
|
|
579
781
|
lastSessionId,
|
|
580
782
|
null,
|
|
581
|
-
"periodic"
|
|
783
|
+
"periodic-local"
|
|
582
784
|
)
|
|
583
785
|
}
|
|
584
786
|
},
|
|
585
|
-
|
|
787
|
+
LOCAL_CAPTURE_MS
|
|
586
788
|
)
|
|
587
789
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
790
|
+
/*
|
|
791
|
+
* Remote durability is deliberately slower
|
|
792
|
+
* and independent from the local lane.
|
|
793
|
+
*/
|
|
794
|
+
const remotePeriodic =
|
|
795
|
+
setInterval(
|
|
796
|
+
() => {
|
|
797
|
+
if (
|
|
798
|
+
lastSessionId
|
|
799
|
+
) {
|
|
800
|
+
void queueRemote(
|
|
801
|
+
lastSessionId,
|
|
802
|
+
null,
|
|
803
|
+
"periodic-remote"
|
|
804
|
+
)
|
|
805
|
+
}
|
|
806
|
+
},
|
|
807
|
+
REMOTE_SYNC_MS
|
|
808
|
+
)
|
|
809
|
+
|
|
810
|
+
for (
|
|
811
|
+
const timer of [
|
|
812
|
+
localPeriodic,
|
|
813
|
+
remotePeriodic,
|
|
814
|
+
]
|
|
591
815
|
) {
|
|
592
|
-
|
|
816
|
+
if (
|
|
817
|
+
typeof timer.unref ===
|
|
818
|
+
"function"
|
|
819
|
+
) {
|
|
820
|
+
timer.unref()
|
|
821
|
+
}
|
|
593
822
|
}
|
|
594
823
|
|
|
595
824
|
return {
|
|
@@ -606,14 +835,39 @@ export const ToolNetMemoryPlugin =
|
|
|
606
835
|
sessionId
|
|
607
836
|
}
|
|
608
837
|
|
|
838
|
+
const terminalEvent =
|
|
839
|
+
event.type ===
|
|
840
|
+
"session.idle" ||
|
|
841
|
+
event.type ===
|
|
842
|
+
"session.compacted" ||
|
|
843
|
+
event.type ===
|
|
844
|
+
"session.error"
|
|
845
|
+
|
|
846
|
+
if (
|
|
847
|
+
sessionId &&
|
|
848
|
+
!terminalEvent
|
|
849
|
+
) {
|
|
850
|
+
scheduleCapture(
|
|
851
|
+
sessionId,
|
|
852
|
+
"event:" +
|
|
853
|
+
event.type
|
|
854
|
+
)
|
|
855
|
+
}
|
|
856
|
+
|
|
609
857
|
if (
|
|
610
858
|
event.type ===
|
|
611
859
|
"session.idle"
|
|
612
860
|
) {
|
|
613
|
-
await
|
|
861
|
+
await queueCapture(
|
|
614
862
|
sessionId,
|
|
615
863
|
"--idle",
|
|
616
|
-
"session.idle"
|
|
864
|
+
"session.idle:capture"
|
|
865
|
+
)
|
|
866
|
+
|
|
867
|
+
void queueRemote(
|
|
868
|
+
sessionId,
|
|
869
|
+
null,
|
|
870
|
+
"session.idle:remote"
|
|
617
871
|
)
|
|
618
872
|
|
|
619
873
|
return
|
|
@@ -623,10 +877,16 @@ export const ToolNetMemoryPlugin =
|
|
|
623
877
|
event.type ===
|
|
624
878
|
"session.compacted"
|
|
625
879
|
) {
|
|
626
|
-
await
|
|
880
|
+
await queueCapture(
|
|
627
881
|
sessionId,
|
|
628
882
|
"--compacted",
|
|
629
|
-
"session.compacted"
|
|
883
|
+
"session.compacted:capture"
|
|
884
|
+
)
|
|
885
|
+
|
|
886
|
+
void queueRemote(
|
|
887
|
+
sessionId,
|
|
888
|
+
null,
|
|
889
|
+
"session.compacted:remote"
|
|
630
890
|
)
|
|
631
891
|
|
|
632
892
|
contextCache = {
|
|
@@ -641,10 +901,16 @@ export const ToolNetMemoryPlugin =
|
|
|
641
901
|
event.type ===
|
|
642
902
|
"session.error"
|
|
643
903
|
) {
|
|
644
|
-
await
|
|
904
|
+
await queueCapture(
|
|
645
905
|
sessionId,
|
|
646
906
|
"--error",
|
|
647
|
-
"session.error"
|
|
907
|
+
"session.error:capture"
|
|
908
|
+
)
|
|
909
|
+
|
|
910
|
+
void queueRemote(
|
|
911
|
+
sessionId,
|
|
912
|
+
null,
|
|
913
|
+
"session.error:remote"
|
|
648
914
|
)
|
|
649
915
|
|
|
650
916
|
return
|
|
@@ -660,9 +926,26 @@ export const ToolNetMemoryPlugin =
|
|
|
660
926
|
*/
|
|
661
927
|
"experimental.chat.system.transform":
|
|
662
928
|
async (
|
|
663
|
-
|
|
929
|
+
input,
|
|
664
930
|
output
|
|
665
931
|
) => {
|
|
932
|
+
const injectionKey =
|
|
933
|
+
contextSessionKey(
|
|
934
|
+
input
|
|
935
|
+
)
|
|
936
|
+
|
|
937
|
+
/*
|
|
938
|
+
* Never inject ToolNet context on
|
|
939
|
+
* every model turn.
|
|
940
|
+
*/
|
|
941
|
+
if (
|
|
942
|
+
injectedSessions.has(
|
|
943
|
+
injectionKey
|
|
944
|
+
)
|
|
945
|
+
) {
|
|
946
|
+
return
|
|
947
|
+
}
|
|
948
|
+
|
|
666
949
|
const context =
|
|
667
950
|
await readContext()
|
|
668
951
|
|
|
@@ -679,6 +962,10 @@ export const ToolNetMemoryPlugin =
|
|
|
679
962
|
context
|
|
680
963
|
)
|
|
681
964
|
|
|
965
|
+
injectedSessions.add(
|
|
966
|
+
injectionKey
|
|
967
|
+
)
|
|
968
|
+
|
|
682
969
|
return
|
|
683
970
|
}
|
|
684
971
|
|
|
@@ -688,8 +975,14 @@ export const ToolNetMemoryPlugin =
|
|
|
688
975
|
) {
|
|
689
976
|
output.system =
|
|
690
977
|
output.system
|
|
691
|
-
?
|
|
978
|
+
? output.system +
|
|
979
|
+
"\\n\\n" +
|
|
980
|
+
context
|
|
692
981
|
: context
|
|
982
|
+
|
|
983
|
+
injectedSessions.add(
|
|
984
|
+
injectionKey
|
|
985
|
+
)
|
|
693
986
|
}
|
|
694
987
|
},
|
|
695
988
|
|
|
@@ -725,39 +1018,52 @@ export const ToolNetMemoryPlugin =
|
|
|
725
1018
|
*/
|
|
726
1019
|
dispose: async () => {
|
|
727
1020
|
clearInterval(
|
|
728
|
-
|
|
1021
|
+
localPeriodic
|
|
1022
|
+
)
|
|
1023
|
+
|
|
1024
|
+
clearInterval(
|
|
1025
|
+
remotePeriodic
|
|
729
1026
|
)
|
|
730
1027
|
|
|
1028
|
+
if (debounceTimer) {
|
|
1029
|
+
clearTimeout(
|
|
1030
|
+
debounceTimer
|
|
1031
|
+
)
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
/*
|
|
1035
|
+
* On a clean exit we only REQUIRE the
|
|
1036
|
+
* fast local fsync checkpoint.
|
|
1037
|
+
*
|
|
1038
|
+
* Never hold OpenCode shutdown hostage
|
|
1039
|
+
* to a slow remote backend.
|
|
1040
|
+
*/
|
|
731
1041
|
if (
|
|
732
1042
|
lastSessionId
|
|
733
1043
|
) {
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
)
|
|
740
|
-
} catch {
|
|
741
|
-
// Status already recorded.
|
|
742
|
-
}
|
|
1044
|
+
await queueCapture(
|
|
1045
|
+
lastSessionId,
|
|
1046
|
+
"--idle",
|
|
1047
|
+
"dispose:capture"
|
|
1048
|
+
)
|
|
743
1049
|
}
|
|
744
1050
|
|
|
745
1051
|
try {
|
|
746
|
-
await
|
|
1052
|
+
await captureChain
|
|
747
1053
|
} catch {
|
|
748
|
-
//
|
|
1054
|
+
// Fail open.
|
|
749
1055
|
}
|
|
750
1056
|
},
|
|
751
1057
|
}
|
|
752
1058
|
}
|
|
753
|
-
`;return
|
|
754
|
-
`,{encoding:"utf8",mode:384}),
|
|
755
|
-
`),
|
|
756
|
-
`);u=Xt(d),r.splice(l,
|
|
1059
|
+
`;return we(n,s.trimStart(),{encoding:"utf8",mode:384}),n}import{existsSync as Ie,mkdirSync as Yt,readFileSync as zt,renameSync as Ut,writeFileSync as Kt}from"node:fs";import{dirname as Ae,join as Vt}from"node:path";function y(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Wt(e,t){Yt(Ae(e),{recursive:!0});let n=`${e}.tmp-${process.pid}-${Date.now()}`;Kt(n,`${JSON.stringify(t,null,2)}
|
|
1060
|
+
`,{encoding:"utf8",mode:384}),Ut(n,e)}function Ce(e){if(!Ie(e))return{};let t=zt(e,"utf8").trim();if(!t)return{};let n;try{n=JSON.parse(t)}catch(o){throw new Error(`Invalid existing OpenCode opencode.json: ${o instanceof Error?o.message:String(o)}`)}if(!y(n))throw new Error("Invalid existing OpenCode opencode.json: root must be a JSON object.");return n}function Me(e,t){if(!y(e))return!1;let n=e.command;return e.type==="local"&&e.enabled!==!1&&Array.isArray(n)&&n.length===2&&n[0]===t&&n[1]==="mcp"}function Gt(e,t){let n=e.mcpServers;if(!y(n)||!Object.prototype.hasOwnProperty.call(n,t))return{root:e,changed:!1};let o={...n};return delete o[t],{root:{...e,mcpServers:o},changed:!0}}function Ee(e={}){let t=e.configFile??ne(),n=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=e.serverName??"toolnet-memory",s=Vt(Ae(t),"opencode.jsonc"),i=Ie(s)?s:void 0,r=Ce(t),a=Gt(r,o),l=a.root,c=l.mcp;if(c!==void 0&&!y(c))throw new Error("Invalid existing OpenCode config: mcp must be an object.");let u=y(c)?{...c}:{},p=u[o];if(Me(p,n)&&!a.changed)return{installed:!0,changed:!1,configFile:t,serverName:o,command:[n,"mcp"],preservedJsonc:i};u[o]={type:"local",command:[n,"mcp"],enabled:!0};let d={...l,mcp:u};Wt(t,d);let z=Ce(t);if(!y(z.mcp)||!Me(z.mcp[o],n))throw new Error("OpenCode MCP configuration was written but verification failed.");return{installed:!0,changed:!0,configFile:t,serverName:o,command:[n,"mcp"],preservedJsonc:i}}import{existsSync as qt,mkdirSync as Te,readFileSync as Qt,writeFileSync as ke}from"node:fs";import{homedir as Re}from"node:os";import{dirname as je,join as L}from"node:path";function Xt(e){let t=[],n=/"((?:\\.|[^"\\])*)"|'([^']*)'/g,o;for(;o=n.exec(e);){let s=o[1]??o[2]??"";try{t.push(o[1]!==void 0?JSON.parse(`"${s}"`):s)}catch{t.push(s)}}return t}function Ne(e={}){let t=e.configFile??L(process.env.CODEX_HOME??L(Re(),".codex"),"config.toml"),n=e.previousFile??L(Re(),".config","toolnet-memory","codex-notify-previous.json");Te(je(t),{recursive:!0}),Te(je(n),{recursive:!0});let o=qt(t)?Qt(t,"utf8"):"",s=e.binary??"toolnet-memory",i=`notify = [${JSON.stringify(s)}, "session:codex-notify"]`,r=o.split(`
|
|
1061
|
+
`),a=r.findIndex(d=>/^\s*\[/.test(d));a<0&&(a=r.length);let l=-1,c=-1;for(let d=0;d<a;d+=1)if(/^\s*notify\s*=/.test(r[d])){if(l=d,c=d,r[d].includes("[")&&!r[d].includes("]"))for(;c+1<a&&(c+=1,!r[c].includes("]")););break}let u=[];if(l>=0){let d=r.slice(l,c+1).join(`
|
|
1062
|
+
`);u=Xt(d),r.splice(l,c-l+1,i)}else a=r.findIndex(d=>/^\s*\[/.test(d)),a<0&&(a=r.length),r.splice(a,0,i);let p=u.length>=2&&u[u.length-1]==="session:codex-notify";return u.length>0&&!p&&ke(n,JSON.stringify(u,null,2)+`
|
|
757
1063
|
`,{encoding:"utf8",mode:384}),o=r.join(`
|
|
758
1064
|
`),o.endsWith(`
|
|
759
1065
|
`)||(o+=`
|
|
760
|
-
`),
|
|
761
|
-
`,{encoding:"utf8",mode:384}),t}import{spawnSync as
|
|
762
|
-
`,{encoding:"utf8",mode:384}),
|
|
763
|
-
`,{encoding:"utf8",mode:384}),
|
|
1066
|
+
`),ke(t,o,{encoding:"utf8",mode:384}),{configFile:t,previousFile:n,preservedPrevious:u.length>0&&!p}}import{existsSync as Zt,mkdirSync as en,readFileSync as tn,writeFileSync as nn}from"node:fs";import{homedir as on}from"node:os";import{dirname as rn,join as _e}from"node:path";function sn(e){return`'${e.replace(/'/g,"'\\''")}'`}function Pe(e={}){let t=e.hooksFile??_e(process.env.CODEX_HOME??_e(on(),".codex"),"hooks.json");en(rn(t),{recursive:!0});let n={};if(Zt(t))try{n=JSON.parse(tn(t,"utf8"))}catch(a){throw new Error(`Invalid existing Codex hooks.json: ${a instanceof Error?a.message:String(a)}`)}let o=n.hooks&&typeof n.hooks=="object"&&!Array.isArray(n.hooks)?n.hooks:{};n.hooks=o;let i=(Array.isArray(o.SessionStart)?o.SessionStart:[]).filter(a=>{try{return!JSON.stringify(a).includes("session:codex-context")}catch{return!0}}),r=e.binary??"toolnet-memory";return i.push({matcher:"startup|resume|clear|compact",hooks:[{type:"command",command:`${sn(r)} session:codex-context`,timeout:15,additionalContextLimit:1e3,statusMessage:"Loading ToolNet project continuity"}]}),o.SessionStart=i,nn(t,JSON.stringify(n,null,2)+`
|
|
1067
|
+
`,{encoding:"utf8",mode:384}),t}import{spawnSync as an}from"node:child_process";function J(e,t){return an(e,t,{encoding:"utf8",stdio:["ignore","pipe","pipe"]})}function Fe(e,t){let n=J(e,["mcp","get",t,"--json"]);if(n.status!==0||!n.stdout)return null;try{return JSON.parse(n.stdout)}catch{return null}}function De(e,t){return e.enabled!==!1&&e.transport?.type==="stdio"&&e.transport?.command===t&&Array.isArray(e.transport?.args)&&e.transport?.args.length===1&&e.transport.args[0]==="mcp"}function $e(e={}){let t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",n=e.codexBinary??"codex",o=e.serverName??"toolnet-memory",s=Fe(n,o);if(s&&De(s,t))return{installed:!0,changed:!1,serverName:o,command:t,args:["mcp"]};if(s){let a=J(n,["mcp","remove",o]);if(a.status!==0)return{installed:!1,changed:!1,serverName:o,command:t,args:["mcp"],error:(a.stderr||a.stdout||"Unable to remove old ToolNet MCP configuration.").trim()}}let i=J(n,["mcp","add",o,"--",t,"mcp"]);if(i.status!==0)return{installed:!1,changed:!1,serverName:o,command:t,args:["mcp"],error:(i.stderr||i.stdout||"Unable to register ToolNet MCP.").trim()};let r=Fe(n,o);return!r||!De(r,t)?{installed:!1,changed:!0,serverName:o,command:t,args:["mcp"],error:"Codex accepted MCP registration but verification did not match expected ToolNet command."}:{installed:!0,changed:!0,serverName:o,command:t,args:["mcp"]}}import{existsSync as cn,mkdirSync as ln,readFileSync as un,renameSync as dn,rmSync as mn,writeFileSync as pn}from"node:fs";import{dirname as gn}from"node:path";function x(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function fn(e){return/^[A-Za-z0-9_./:-]+$/u.test(e)?e:`'${e.replace(/'/gu,"'\\''")}'`}function yn(e){if(!cn(e))return{};let t;try{t=JSON.parse(un(e,"utf8"))}catch(n){throw new Error(`Invalid existing Claude settings.json: ${n instanceof Error?n.message:String(n)}`)}if(!x(t))throw new Error("Invalid existing Claude settings.json: root must be a JSON object.");return t}function B(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 t=[];for(let n of e){if(!x(n)){t.push(n);continue}let o=n.hooks;if(!Array.isArray(o)){t.push(n);continue}let s=o.filter(i=>{if(!x(i))return!0;let r=i.command;return!(typeof r=="string"&&r.includes("session:claude-hook"))});s.length!==0&&t.push({...n,hooks:s})}return t}function H(e){return{type:"command",command:e,timeout:10}}function hn(e,t){ln(gn(e),{recursive:!0,mode:448});let n=`${e}.toolnet-${process.pid}-${Date.now()}.tmp`;try{pn(n,JSON.stringify(t,null,2)+`
|
|
1068
|
+
`,{encoding:"utf8",mode:384}),dn(n,e)}finally{mn(n,{force:!0})}}function Le(e={}){let t=e.settingsFile??ie(),n=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=yn(t),s=o.hooks;if(s!==void 0&&!x(s))throw new Error("Invalid existing Claude settings.json: hooks must be an object.");let i=x(s)?{...s}:{},r=`${fn(n)} session:claude-hook`,a=B(i.SessionStart);a.push({matcher:"startup|resume|clear|compact",hooks:[H(r)]}),i.SessionStart=a;let l=B(i.PostToolUse);l.push({matcher:"Edit|Write",hooks:[H(r)]}),i.PostToolUse=l;let c=B(i.Stop);c.push({hooks:[H(r)]}),i.Stop=c;let u={...o,hooks:i},p=JSON.stringify(o),d=JSON.stringify(u);return p===d?{settingsFile:t,changed:!1}:(hn(t,u),{settingsFile:t,changed:!0})}import{existsSync as vn,mkdirSync as bn,readFileSync as xn,renameSync as Sn,rmSync as wn,writeFileSync as On}from"node:fs";import{dirname as Cn}from"node:path";function S(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Je(e){if(!vn(e))return{};let t;try{t=JSON.parse(xn(e,"utf8"))}catch(n){throw new Error(`Invalid existing Claude Code config: ${n instanceof Error?n.message:String(n)}`)}if(!S(t))throw new Error("Invalid existing Claude Code config: root must be a JSON object.");return t}function Be(e,t){if(!S(e))return!1;let n=e.args;return e.type==="stdio"&&e.command===t&&Array.isArray(n)&&n.length===1&&n[0]==="mcp"}function Mn(e,t){bn(Cn(e),{recursive:!0});let n=`${e}.toolnet-${process.pid}-${Date.now()}.tmp`;try{On(n,JSON.stringify(t,null,2)+`
|
|
1069
|
+
`,{encoding:"utf8",mode:384}),Sn(n,e)}finally{wn(n,{force:!0})}}function He(e={}){let t=e.stateFile??ae(),n=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=e.serverName??"toolnet-memory",s=Je(t),i=s.mcpServers;if(i!==void 0&&!S(i))throw new Error("Invalid existing Claude Code config: mcpServers must be an object.");let r=S(i)?{...i}:{},a=r[o];if(Be(a,n))return{installed:!0,changed:!1,configFile:t,serverName:o,command:[n,"mcp"],repaired:!1};let l=a!==void 0;r[o]={type:"stdio",command:n,args:["mcp"]},Mn(t,{...s,mcpServers:r});let u=Je(t).mcpServers;if(!S(u)||!Be(u[o],n))throw new Error("Claude Code MCP configuration was written but verification failed.");return{installed:!0,changed:!0,configFile:t,serverName:o,command:[n,"mcp"],repaired:l}}function Ye(e={}){let t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",n=Le({binary:t,settingsFile:e.settingsFile}),o=He({binary:t,stateFile:e.stateFile});return{hooks:n,mcp:o,files:[n.settingsFile,o.configFile]}}function ze(){return ce()}function Y(e={}){let t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",n=[],o=ze(),s=new Map(o.map(i=>[i.agent,i.detected]));if(!(e.force===!0||s.get("agy")===!0))n.push({agent:"agy",detected:!1,installed:!1,targets:[]});else try{let r=ve({binary:t});n.push({agent:"agy",detected:!0,installed:!0,targets:r.files})}catch(r){n.push({agent:"agy",detected:!0,installed:!1,targets:[],error:r instanceof Error?r.message:String(r)})}if(!(e.force===!0||s.get("opencode")===!0))n.push({agent:"opencode",detected:!1,installed:!1,targets:[]});else try{let r=Oe({binary:t}),a=Ee({binary:t});n.push({agent:"opencode",detected:!0,installed:!0,targets:[r,a.configFile,`mcp:${a.serverName}`]})}catch(r){n.push({agent:"opencode",detected:!0,installed:!1,targets:[],error:r instanceof Error?r.message:String(r)})}if(!(e.force===!0||s.get("claude")===!0))n.push({agent:"claude",detected:!1,installed:!1,targets:[]});else try{let r=Ye({binary:t});n.push({agent:"claude",detected:!0,installed:!0,targets:[r.hooks.settingsFile,r.mcp.configFile,`mcp:${r.mcp.serverName}`]})}catch(r){n.push({agent:"claude",detected:!0,installed:!1,targets:[],error:r instanceof Error?r.message:String(r)})}if(!(e.force===!0||s.get("codex")===!0))n.push({agent:"codex",detected:!1,installed:!1,targets:[]});else try{let r=Ne({binary:t}),a=Pe({binary:t}),l=$e({binary:t});if(!l.installed)throw new Error(l.error??"Codex MCP registration failed");let c=[r.configFile,a,`mcp:${l.serverName}`];r.preservedPrevious&&c.push(r.previousFile),n.push({agent:"codex",detected:!0,installed:!0,targets:c})}catch(r){n.push({agent:"codex",detected:!0,installed:!1,targets:[],error:r instanceof Error?r.message:String(r)})}return n}function In(e){console.log(""),console.log("ToolNet Memory Integration Detection"),console.log("===================================="),console.log("");for(let t of e){let n=t.agent==="agy"?"Agy / Antigravity":t.agent==="opencode"?"OpenCode":t.agent==="claude"?"Claude Code":"Codex";if(!t.detected){console.log(`\u25CB ${n}: not detected`);continue}console.log(`\u2713 ${n}: detected`);for(let o of t.evidence)console.log(` ${o}`)}console.log("")}function An(e){console.log(""),console.log("ToolNet Memory AI Integrations"),console.log("=============================="),console.log("");for(let t of e){let n=t.agent==="agy"?"Agy / Antigravity":t.agent==="opencode"?"OpenCode":"Codex";if(!t.detected){console.log(`- ${n}: not detected`);continue}if(t.installed){console.log(`\u2713 ${n}: automatic memory enabled`);continue}console.log(`\u2717 ${n}: integration failed`),t.error&&console.log(` ${t.error}`)}console.log("")}async function En(){let e=process.argv.slice(2),t=e.includes("--all"),n=e.includes("--json");if(e.includes("--detect-only")){let i=ze();if(n){console.log(JSON.stringify(i,null,2));return}In(i);return}let s=Y({force:t});if(n){console.log(JSON.stringify(s,null,2));return}An(s)}var Tn=process.argv[1]&&(process.argv[1].endsWith("auto-integrate.js")||process.argv[1].endsWith("auto-integrate.ts"));Tn&&En().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exitCode=1});function Nn(e=process.cwd()){let t=Rn(e);if(!Ue(t))throw new Error(`Project path does not exist: ${t}`);if(!kn(t).isDirectory())throw new Error(`Project path is not a directory: ${t}`);let n=new O().detect(t),o=jn(n.rootPath,".toolnet","project.json");if(!Ue(o))throw new Error(`ToolNet project initialization failed: ${o} was not created`);return{initialized:!0,project:{id:n.id,name:n.name,remote:n.remote,rootPath:n.rootPath},manifestFile:o}}function _n(e,t){let n=e.indexOf(t);return n>=0?e[n+1]:void 0}async function Pn(){let e=process.argv.slice(2),t=e.includes("--json"),n=!e.includes("--no-integrate"),o=_n(e,"--project"),s=e.find((l,c)=>!l.startsWith("-")&&(c===0||e[c-1]!=="--project")),i=o??s??process.cwd(),r=await N("Initializing ToolNet project",()=>Nn(i),{enabled:!t}),a=[];if(n&&(a=await N("Detecting AI coding agents",()=>Y(),{enabled:!t})),t){console.log(JSON.stringify({...r,integrations:a},null,2));return}if(console.log(""),console.log("ToolNet Memory"),console.log("=============="),console.log(""),console.log("\u2713 Project initialized"),console.log(""),console.log(`Project: ${r.project.name}`),console.log(`ID: ${r.project.id}`),console.log(`Root: ${r.project.rootPath}`),console.log(`Manifest: ${r.manifestFile}`),console.log(""),n){console.log("AI integrations:");let l=a.filter(c=>c.detected&&c.installed);if(!l.length)console.log(" \u25CB No supported coding agent detected");else for(let c of l){let u=c.agent==="agy"?"Agy / Antigravity":c.agent==="opencode"?"OpenCode":"Codex";console.log(` \u2713 ${u}`)}console.log("")}console.log("Next: toolnet-memory doctor"),console.log("")}var Fn=process.argv[1]?.endsWith("/init.js")||process.argv[1]?.endsWith("/init.ts");Fn&&Pn().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exitCode=1});export{Nn as initializeToolNetProject};
|