toolnet-memory 0.2.20 → 0.2.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bundle/init.js ADDED
@@ -0,0 +1,763 @@
1
+ import{existsSync as ze,statSync as En}from"node:fs";import{resolve as Tn,join as jn}from"node:path";import{existsSync as Ke,readFileSync as Ue}from"node:fs";import{homedir as Ve}from"node:os";import{join as Ge}from"node:path";function We(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(Ve(),".config","toolnet-memory",".env");if(!Ke(e))return;let t=Ue(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]=We(o.slice(s+1)))}}qe();import{createHash as Xe}from"node:crypto";import{existsSync as T,mkdirSync as Qe,readFileSync as Ze,renameSync as et,writeFileSync as tt}from"node:fs";import{basename as nt,dirname as w,join as O,parse as V,resolve as h}from"node:path";var G=".toolnet",ot="project.json";function rt(e){return Xe("sha256").update(e).digest("hex").slice(0,16)}function j(e){return O(e,G,ot)}function st(e){return T(j(e))}function it(e,t){let n=h(e),o=V(n).root;for(;;){if(st(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=>T(O(t,i))))return t;if(t===n)break;let s=w(t);if(s===t)break;t=s}return h(e)}function at(e){let t;try{t=JSON.parse(Ze(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 K(e,t){let n=O(e,G);Qe(n,{recursive:!0});let o=j(e),s=`${o}.tmp-${process.pid}`;tt(s,JSON.stringify(t,null,2)+`
3
+ `,{encoding:"utf8",mode:384}),et(s,o)}function U(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 C=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=>T(O(o,u))),r=it(n,i?o:void 0);if(r){let u=j(r),p=at(u);return p.rootPath!==r&&(p.rootPath=r,p.updatedAt=new Date().toISOString(),K(r,p)),U(p,r)}let c=new Date().toISOString(),l=nt(o),a={version:1,id:rt(o),name:l,remote:l,rootPath:o,createdAt:c,updatedAt:c,graphVersion:0,memoryVersion:0};return K(o,a),U(a,o)}};var W=["\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",dim:"\x1B[2m",reset:"\x1B[0m"};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 R=class{stream;enabled;interactive;color;intervalMs;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)}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)%W.length,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=W[this.frame],n=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
+ `):this.stream.write(`${i} ${n} (${s})
6
+ `),this.active=!1}};async function N(e,t,n={}){let o=new R(e,n).start();try{let s=await t();return o.succeed(),s}catch(s){throw o.fail(),s}}import{existsSync as dt}from"node:fs";import{homedir as mt}from"node:os";import{join as pt}from"node:path";import{spawnSync as gt}from"node:child_process";import{homedir as lt}from"node:os";import{join as f}from"node:path";function X(e={}){return f(e.home??lt(),".gemini")}function _(e={}){return f(X(e),"config")}function M(e={}){return f(_(e),"mcp_config.json")}function A(e={}){return f(_(e),"hooks.json")}function Q(e={}){return f(X(e),"antigravity-cli")}function Z(e="toolnet-memory",t={}){return f(Q(t),"plugins",e)}function ee(e={}){return[Q(e),_(e)]}import{homedir as ut}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??ut(),".config","opencode")}function te(e={}){return v(g(e),"opencode.json")}function ne(e={}){return v(g(e),"plugins")}function oe(e={}){return v(g(e),"AGENTS.md")}import{homedir as re}from"node:os";import{join as P}from"node:path";function F(e={}){return P(e.home??re(),".claude")}function se(e={}){return P(F(e),"settings.json")}function ie(e={}){return P(e.home??re(),".claude.json")}function ft(e){return gt("sh",["-lc",`command -v ${JSON.stringify(e)} >/dev/null 2>&1`],{stdio:"ignore"}).status===0}function k(e){let t=e.commandExists(e.command),n=e.configPaths.filter(i=>dt(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??mt(),n=e.commandExists??ft,o=e.codexHome??process.env.CODEX_HOME??pt(t,".codex");return[k({agent:"agy",command:"agy",commandExists:n,configPaths:ee({home:t})}),k({agent:"opencode",command:"opencode",commandExists:n,configPaths:[g({home:t,xdgConfigHome:e.xdgConfigHome})]}),k({agent:"claude",command:"claude",commandExists:n,configPaths:[F({home:t})]}),k({agent:"codex",command:"codex",commandExists:n,configPaths:[o]})]}import{existsSync as E,mkdirSync as pe,readFileSync as ge,renameSync as jt,writeFileSync as Rt}from"node:fs";import{dirname as Nt,join as I}from"node:path";import{existsSync as yt,mkdirSync as ht,readFileSync as vt,renameSync as bt,rmSync as xt,writeFileSync as St}from"node:fs";import{dirname as wt}from"node:path";function Ct(e){return`'${e.replace(/'/g,"'\\''")}'`}function ae(e={}){let t=e.hooksFile??A();ht(wt(t),{recursive:!0,mode:448});let n={};if(yt(t)){let r;try{r=JSON.parse(vt(t,"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.");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{St(i,JSON.stringify(n,null,2)+`
7
+ `,{encoding:"utf8",mode:384}),bt(i,t)}finally{xt(i,{force:!0})}return t}import{existsSync as Ot,mkdirSync as Mt,readFileSync as At,renameSync as kt,writeFileSync as It}from"node:fs";import{dirname as Et}from"node:path";function b(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Tt(e,t){Mt(Et(e),{recursive:!0,mode:448});let n=`${e}.tmp-${process.pid}-${Date.now()}`;It(n,`${JSON.stringify(t,null,2)}
8
+ `,{encoding:"utf8",mode:384}),kt(n,e)}function le(e){if(!Ot(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 ue(e,t){return b(e)?e.command===t&&Array.isArray(e.args)&&e.args.length===1&&e.args[0]==="mcp":!1}function de(e={}){let t=e.configFile??M(),n=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=e.serverName??"toolnet-memory",s=le(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}:{},c=r[o];if(ue(c,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};Tt(t,l);let u=le(t).mcpServers;if(!b(u)||!ue(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 _t=`# ToolNet Memory Continuity
9
+
10
+ ToolNet Memory is the authoritative continuity layer for previous project work.
11
+
12
+ ## Resume / continue behavior
13
+
14
+ Whenever the user asks to continue, resume, finish, pick up, return to, or complete previous work:
15
+
16
+ 1. FIRST call the ToolNet Memory MCP tool \`memory_agent_ask\`.
17
+ 2. Use ToolNet's compact continuity result to determine:
18
+ - current task
19
+ - completed work
20
+ - current or last file
21
+ - TODOs
22
+ - blockers
23
+ - next action
24
+ 3. Only AFTER continuity is known may you inspect current source or git to verify repository truth.
25
+
26
+ ## Forbidden continuity recovery
27
+
28
+ Do NOT reconstruct previous work by reading, listing, searching, or shelling into:
29
+
30
+ - \`.toolnet/sessions/**\`
31
+ - \`state.json\`
32
+ - \`events.jsonl\`
33
+ - raw transcripts
34
+ - \`~/.gemini/antigravity-cli/brain/**\`
35
+ - Antigravity \`transcript.jsonl\`
36
+ - another coding agent's internal session history
37
+
38
+ Do NOT run Bash/cat/tail/grep against those locations to discover previous work.
39
+
40
+ Do NOT search the filesystem for the implementation or schema of \`memory_agent_ask\`.
41
+ Invoke the MCP tool directly.
42
+
43
+ For direct continuity facts, prefer \`mode="local"\`.
44
+ Use \`mode="ai"\` only when continuity is ambiguous or requires synthesis.
45
+
46
+ Current repository evidence overrides stale memory after ToolNet has restored the working context.
47
+
48
+ Do not ask the user to repeat context already available through ToolNet Memory.
49
+ `;function fe(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function D(e,t){pe(Nt(e),{recursive:!0});let n=`${e}.tmp-${process.pid}-${Date.now()}`;Rt(n,t,{encoding:"utf8",mode:384}),jt(n,e)}function me(e,t){E(e)&&ge(e,"utf8")===t||D(e,t)}function ye(e){if(!E(e))return{};let t=ge(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(!fe(n))throw new Error(`Invalid legacy Antigravity config ${e}: root must be object`);return n}function Pt(e,t){if(!E(e))return!1;let n=ye(e);if(!fe(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 Ft(e){if(!E(e))return!1;let t=ye(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 he(e={}){let t=e.pluginName??"toolnet-memory",n=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=e.pluginRoot??Z(t),s=I(o,"plugin.json"),i=I(o,"mcp_config.json"),r=I(o,"hooks.json"),c=I(o,"rules","toolnet-memory-continuity.md");pe(o,{recursive:!0,mode:448}),me(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
+ `),de({configFile:i,binary:n,serverName:"toolnet-memory"}),ae({hooksFile:r,binary:n}),me(c,`${_t.trim()}
53
+ `);let l=e.legacyMcpFile??M(),a=e.legacyHooksFile??A(),u=[];return l!==i&&Pt(l,"toolnet-memory")&&u.push(l),a!==r&&Ft(a)&&u.push(a),{installed:!0,pluginRoot:o,files:[s,i,r,c],migratedLegacy:u}}import{existsSync as $t,mkdirSync as xe,readFileSync as Lt,writeFileSync as Se}from"node:fs";import{join as Jt}from"node:path";var Dt="memory_agent_ask";function ve(){return`
54
+ [TOOLNET MEMORY AGENT]
55
+
56
+ Tool available:
57
+ - ${Dt}
58
+
59
+ Use it automatically BEFORE guessing when:
60
+
61
+ - The user asks to continue, resume, or pick up previous work.
62
+ - The user says things like:
63
+ - "ti\u1EBFp t\u1EE5c task l\xFAc n\xE3y"
64
+ - "l\xE0m ti\u1EBFp ph\u1EA7n \u0111ang d\u1EDF"
65
+ - "agent tr\u01B0\u1EDBc \u0111ang l\xE0m g\xEC?"
66
+ - "d\u1EEBng \u1EDF \u0111\xE2u?"
67
+ - "todo n\xE0o ch\u01B0a xong?"
68
+ - "continue the previous task"
69
+ - "resume the last session"
70
+ - Previous-agent state, unfinished work, blockers,
71
+ decisions, touched files, or next actions are unclear.
72
+ - Fast startup context is not enough to safely continue.
73
+
74
+ Preferred mode:
75
+
76
+ - mode="local"
77
+ for direct factual questions such as:
78
+ current task, last file, blocker, completed TODOs.
79
+
80
+ - mode="ai"
81
+ for composite or ambiguous continuity questions
82
+ that benefit from ToolNet Memory Agent reasoning.
83
+
84
+ Do NOT call it automatically when:
85
+
86
+ - Normal startup context already gives enough information.
87
+ - The question is unrelated to previous project work.
88
+ - The answer is obvious from current repository evidence.
89
+
90
+ Rules:
91
+
92
+ - Never invent previous work.
93
+ - Current repository evidence overrides stale memory.
94
+ - NEVER reconstruct previous work by reading ToolNet internal session files.
95
+ - NEVER read/list/search .toolnet/sessions/**, session state.json,
96
+ events.jsonl, or raw transcripts to discover previous-agent state.
97
+ - Do not search the filesystem for the implementation/schema of
98
+ memory_agent_ask. Invoke the MCP tool directly when deeper
99
+ continuity is required.
100
+ - Do not dump raw transcripts or full memory.
101
+ - After receiving the ToolNet answer, continue the task
102
+ instead of asking the user to repeat known context.
103
+ - If ToolNet says information is not recorded, say so.
104
+ `.trim()}var be="<!-- TOOLNET_MEMORY_BOOTSTRAP_START -->",$="<!-- TOOLNET_MEMORY_BOOTSTRAP_END -->";function Yt(){let e=oe();xe(g(),{recursive:!0});let t=`${be}
105
+ ## ToolNet Memory
106
+
107
+ ToolNet Memory is available for projects that already contain a valid \`.toolnet/project.json\`.
108
+
109
+ Rules:
110
+
111
+ 1. Do not create a ToolNet project automatically.
112
+ 2. If no valid \`.toolnet/project.json\` exists in the current project or an ancestor, ignore ToolNet Memory.
113
+ 3. Normal startup context must stay small and selective.
114
+ 4. Do not automatically run deep recovery commands.
115
+ 5. Use ToolNet MCP or selective retrieval when older project knowledge is actually needed.
116
+ 6. Raw transcripts must not be injected into prompts.
117
+ 7. Current repository evidence has priority over stale memory when they conflict.
118
+
119
+
120
+ ${ve()}
121
+
122
+ ${$}`,n=$t(e)?Lt(e,"utf8"):"",o=n.indexOf(be),s=n.indexOf($);return o>=0&&s>=o?n=n.slice(0,o)+t+n.slice(s+$.length):(n=n.trimEnd(),n&&(n+=`
123
+
124
+ `),n+=t),Se(e,n.trimEnd()+`
125
+ `,{encoding:"utf8",mode:384}),e}function we(e={}){let t=e.directory??ne();xe(t,{recursive:!0}),Yt();let n=Jt(t,"toolnet-memory.js"),o=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",s=`
126
+ // Generated by ToolNet Memory.
127
+ // OpenCode integration:
128
+ // - project gated
129
+ // - selective context injection
130
+ // - incremental session checkpoints
131
+ // - periodic durability sync
132
+ // - best-effort shutdown flush
133
+
134
+ import fs from "node:fs"
135
+ import path from "node:path"
136
+ import os from "node:os"
137
+
138
+ const TOOLNET_BINARY =
139
+ ${JSON.stringify(o)}
140
+
141
+ const CONTEXT_MAX_TOKENS = 700
142
+
143
+ const CONTEXT_CACHE_MS = 5000
144
+
145
+ const PERIODIC_SYNC_MS = 30000
146
+
147
+ const SYNC_TIMEOUT_MS = 15000
148
+
149
+ const STATUS_FILE = path.join(
150
+ os.homedir(),
151
+ ".config",
152
+ "toolnet-memory",
153
+ "opencode-sync-status.json"
154
+ )
155
+
156
+ function writeStatus(data) {
157
+ try {
158
+ fs.mkdirSync(
159
+ path.dirname(
160
+ STATUS_FILE
161
+ ),
162
+ {
163
+ recursive: true,
164
+ }
165
+ )
166
+
167
+ fs.writeFileSync(
168
+ STATUS_FILE,
169
+ JSON.stringify(
170
+ {
171
+ timestamp:
172
+ new Date()
173
+ .toISOString(),
174
+ ...data,
175
+ },
176
+ null,
177
+ 2
178
+ ) + "\\n"
179
+ )
180
+ } catch {
181
+ // Status reporting must
182
+ // never break OpenCode.
183
+ }
184
+ }
185
+
186
+ function validProjectManifest(
187
+ file
188
+ ) {
189
+ try {
190
+ const parsed =
191
+ JSON.parse(
192
+ fs.readFileSync(
193
+ file,
194
+ "utf8"
195
+ )
196
+ )
197
+
198
+ return Boolean(
199
+ parsed &&
200
+ typeof parsed ===
201
+ "object" &&
202
+ typeof parsed.id ===
203
+ "string" &&
204
+ parsed.id.length > 0
205
+ )
206
+ } catch {
207
+ return false
208
+ }
209
+ }
210
+
211
+ function findProjectRoot(
212
+ inputDirectory
213
+ ) {
214
+ let current =
215
+ path.resolve(
216
+ inputDirectory
217
+ )
218
+
219
+ const filesystemRoot =
220
+ path.parse(
221
+ current
222
+ ).root
223
+
224
+ while (true) {
225
+ const manifest =
226
+ path.join(
227
+ current,
228
+ ".toolnet",
229
+ "project.json"
230
+ )
231
+
232
+ if (
233
+ fs.existsSync(
234
+ manifest
235
+ ) &&
236
+ validProjectManifest(
237
+ manifest
238
+ )
239
+ ) {
240
+ return current
241
+ }
242
+
243
+ if (
244
+ current ===
245
+ filesystemRoot
246
+ ) {
247
+ break
248
+ }
249
+
250
+ const parent =
251
+ path.dirname(
252
+ current
253
+ )
254
+
255
+ if (
256
+ parent === current
257
+ ) {
258
+ break
259
+ }
260
+
261
+ current = parent
262
+ }
263
+
264
+ return null
265
+ }
266
+
267
+ function getSessionId(
268
+ event
269
+ ) {
270
+ const p =
271
+ event?.properties ?? {}
272
+
273
+ const candidates = [
274
+ p.sessionID,
275
+ p.sessionId,
276
+ p.info?.id,
277
+ p.session?.id,
278
+ ]
279
+
280
+ return (
281
+ candidates.find(
282
+ value =>
283
+ typeof value ===
284
+ "string" &&
285
+ value.length > 0
286
+ ) ?? null
287
+ )
288
+ }
289
+
290
+ async function runWithTimeout(
291
+ args,
292
+ {
293
+ stdout = "ignore",
294
+ timeout =
295
+ SYNC_TIMEOUT_MS,
296
+ } = {}
297
+ ) {
298
+ const child =
299
+ Bun.spawn(
300
+ args,
301
+ {
302
+ stdin:
303
+ "ignore",
304
+ stdout,
305
+ stderr:
306
+ "ignore",
307
+ }
308
+ )
309
+
310
+ let timer
311
+
312
+ const timeoutPromise =
313
+ new Promise(
314
+ (_, reject) => {
315
+ timer =
316
+ setTimeout(
317
+ () => {
318
+ try {
319
+ child.kill()
320
+ } catch {}
321
+
322
+ reject(
323
+ new Error(
324
+ "ToolNet command timeout"
325
+ )
326
+ )
327
+ },
328
+ timeout
329
+ )
330
+ }
331
+ )
332
+
333
+ try {
334
+ const exitCode =
335
+ await Promise.race([
336
+ child.exited,
337
+ timeoutPromise,
338
+ ])
339
+
340
+ if (
341
+ typeof exitCode ===
342
+ "number" &&
343
+ exitCode !== 0
344
+ ) {
345
+ throw new Error(
346
+ \`ToolNet exited with code \${exitCode}\`
347
+ )
348
+ }
349
+
350
+ return child
351
+ } finally {
352
+ if (timer) {
353
+ clearTimeout(
354
+ timer
355
+ )
356
+ }
357
+ }
358
+ }
359
+
360
+ export const ToolNetMemoryPlugin =
361
+ async ({
362
+ directory,
363
+ }) => {
364
+ /*
365
+ * CRITICAL:
366
+ * Never auto-create a ToolNet
367
+ * project just because OpenCode
368
+ * happens to start in /root,
369
+ * /tmp, $HOME, etc.
370
+ */
371
+ const projectRoot =
372
+ findProjectRoot(
373
+ directory
374
+ )
375
+
376
+ if (!projectRoot) {
377
+ writeStatus({
378
+ active: false,
379
+ reason:
380
+ "no-toolnet-project",
381
+ directory,
382
+ })
383
+
384
+ return {}
385
+ }
386
+
387
+ writeStatus({
388
+ active: true,
389
+ projectRoot,
390
+ state:
391
+ "plugin-loaded",
392
+ })
393
+
394
+ let lastSessionId =
395
+ null
396
+
397
+ let syncChain =
398
+ Promise.resolve()
399
+
400
+ let contextCache = {
401
+ value: "",
402
+ expiresAt: 0,
403
+ }
404
+
405
+ async function readContext() {
406
+ const now =
407
+ Date.now()
408
+
409
+ if (
410
+ contextCache.value &&
411
+ now <
412
+ contextCache.expiresAt
413
+ ) {
414
+ return (
415
+ contextCache.value
416
+ )
417
+ }
418
+
419
+ try {
420
+ const child =
421
+ Bun.spawn(
422
+ [
423
+ TOOLNET_BINARY,
424
+ "context:print",
425
+ "--project",
426
+ projectRoot,
427
+ "--tokens",
428
+ String(
429
+ CONTEXT_MAX_TOKENS
430
+ ),
431
+ ],
432
+ {
433
+ stdin:
434
+ "ignore",
435
+ stdout:
436
+ "pipe",
437
+ stderr:
438
+ "ignore",
439
+ }
440
+ )
441
+
442
+ const textPromise =
443
+ new Response(
444
+ child.stdout
445
+ ).text()
446
+
447
+ const exitCode =
448
+ await child.exited
449
+
450
+ if (
451
+ exitCode !== 0
452
+ ) {
453
+ return ""
454
+ }
455
+
456
+ const text =
457
+ (
458
+ await textPromise
459
+ ).trim()
460
+
461
+ contextCache = {
462
+ value: text,
463
+ expiresAt:
464
+ now +
465
+ CONTEXT_CACHE_MS,
466
+ }
467
+
468
+ return text
469
+ } catch {
470
+ return ""
471
+ }
472
+ }
473
+
474
+ async function syncNow(
475
+ sessionId,
476
+ flag,
477
+ reason
478
+ ) {
479
+ if (!sessionId) {
480
+ return
481
+ }
482
+
483
+ const args = [
484
+ TOOLNET_BINARY,
485
+ "session:opencode-sync",
486
+ sessionId,
487
+ "--project",
488
+ projectRoot,
489
+ ]
490
+
491
+ if (flag) {
492
+ args.push(
493
+ flag
494
+ )
495
+ }
496
+
497
+ try {
498
+ await runWithTimeout(
499
+ args
500
+ )
501
+
502
+ writeStatus({
503
+ active: true,
504
+ projectRoot,
505
+ sessionId,
506
+ reason,
507
+ state:
508
+ "sync-success",
509
+ })
510
+ } catch (error) {
511
+ writeStatus({
512
+ active: true,
513
+ projectRoot,
514
+ sessionId,
515
+ reason,
516
+ state:
517
+ "sync-failed",
518
+ error:
519
+ error instanceof
520
+ Error
521
+ ? error.message
522
+ : String(
523
+ error
524
+ ),
525
+ })
526
+
527
+ throw error
528
+ }
529
+ }
530
+
531
+ function queueSync(
532
+ sessionId,
533
+ flag,
534
+ reason
535
+ ) {
536
+ if (!sessionId) {
537
+ return syncChain
538
+ }
539
+
540
+ lastSessionId =
541
+ sessionId
542
+
543
+ syncChain =
544
+ syncChain
545
+ .catch(
546
+ () =>
547
+ undefined
548
+ )
549
+ .then(
550
+ () =>
551
+ syncNow(
552
+ sessionId,
553
+ flag,
554
+ reason
555
+ )
556
+ )
557
+
558
+ return syncChain
559
+ }
560
+
561
+ /*
562
+ * Periodic durability checkpoint.
563
+ *
564
+ * This is intentionally independent
565
+ * from session.idle/dispose because
566
+ * abrupt SSH/process termination may
567
+ * skip shutdown hooks.
568
+ *
569
+ * unref() prevents the timer itself
570
+ * from keeping OpenCode alive.
571
+ */
572
+ const periodic =
573
+ setInterval(
574
+ () => {
575
+ if (
576
+ lastSessionId
577
+ ) {
578
+ void queueSync(
579
+ lastSessionId,
580
+ null,
581
+ "periodic"
582
+ )
583
+ }
584
+ },
585
+ PERIODIC_SYNC_MS
586
+ )
587
+
588
+ if (
589
+ typeof periodic.unref ===
590
+ "function"
591
+ ) {
592
+ periodic.unref()
593
+ }
594
+
595
+ return {
596
+ event: async ({
597
+ event,
598
+ }) => {
599
+ const sessionId =
600
+ getSessionId(
601
+ event
602
+ )
603
+
604
+ if (sessionId) {
605
+ lastSessionId =
606
+ sessionId
607
+ }
608
+
609
+ if (
610
+ event.type ===
611
+ "session.idle"
612
+ ) {
613
+ await queueSync(
614
+ sessionId,
615
+ "--idle",
616
+ "session.idle"
617
+ )
618
+
619
+ return
620
+ }
621
+
622
+ if (
623
+ event.type ===
624
+ "session.compacted"
625
+ ) {
626
+ await queueSync(
627
+ sessionId,
628
+ "--compacted",
629
+ "session.compacted"
630
+ )
631
+
632
+ contextCache = {
633
+ value: "",
634
+ expiresAt: 0,
635
+ }
636
+
637
+ return
638
+ }
639
+
640
+ if (
641
+ event.type ===
642
+ "session.error"
643
+ ) {
644
+ await queueSync(
645
+ sessionId,
646
+ "--error",
647
+ "session.error"
648
+ )
649
+
650
+ return
651
+ }
652
+ },
653
+
654
+ /*
655
+ * VERIFIED on OpenCode 1.18.14:
656
+ * this hook fires on real model turns.
657
+ *
658
+ * It remains experimental, therefore
659
+ * AGENTS.md + MCP stay as fallbacks.
660
+ */
661
+ "experimental.chat.system.transform":
662
+ async (
663
+ _input,
664
+ output
665
+ ) => {
666
+ const context =
667
+ await readContext()
668
+
669
+ if (!context) {
670
+ return
671
+ }
672
+
673
+ if (
674
+ Array.isArray(
675
+ output?.system
676
+ )
677
+ ) {
678
+ output.system.push(
679
+ context
680
+ )
681
+
682
+ return
683
+ }
684
+
685
+ if (
686
+ typeof output?.system ===
687
+ "string"
688
+ ) {
689
+ output.system =
690
+ output.system
691
+ ? \`\${output.system}\\n\\n\${context}\`
692
+ : context
693
+ }
694
+ },
695
+
696
+ /*
697
+ * Optional compaction survival hook.
698
+ * We keep it as a supplementary path,
699
+ * never as the only continuity path.
700
+ */
701
+ "experimental.session.compacting":
702
+ async (
703
+ _input,
704
+ output
705
+ ) => {
706
+ const context =
707
+ await readContext()
708
+
709
+ if (
710
+ context &&
711
+ Array.isArray(
712
+ output?.context
713
+ )
714
+ ) {
715
+ output.context.push(
716
+ context
717
+ )
718
+ }
719
+ },
720
+
721
+ /*
722
+ * VERIFIED for clean OpenCode exits.
723
+ * Best effort only: kill -9 / crash /
724
+ * machine loss cannot guarantee this.
725
+ */
726
+ dispose: async () => {
727
+ clearInterval(
728
+ periodic
729
+ )
730
+
731
+ if (
732
+ lastSessionId
733
+ ) {
734
+ try {
735
+ await queueSync(
736
+ lastSessionId,
737
+ "--idle",
738
+ "dispose"
739
+ )
740
+ } catch {
741
+ // Status already recorded.
742
+ }
743
+ }
744
+
745
+ try {
746
+ await syncChain
747
+ } catch {
748
+ // Do not block OpenCode shutdown.
749
+ }
750
+ },
751
+ }
752
+ }
753
+ `;return Se(n,s.trimStart(),{encoding:"utf8",mode:384}),n}import{existsSync as Me,mkdirSync as Ht,readFileSync as Bt,renameSync as zt,writeFileSync as Kt}from"node:fs";import{dirname as Ae,join as Ut}from"node:path";function y(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Vt(e,t){Ht(Ae(e),{recursive:!0});let n=`${e}.tmp-${process.pid}-${Date.now()}`;Kt(n,`${JSON.stringify(t,null,2)}
754
+ `,{encoding:"utf8",mode:384}),zt(n,e)}function Ce(e){if(!Me(e))return{};let t=Bt(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 Oe(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 ke(e={}){let t=e.configFile??te(),n=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=e.serverName??"toolnet-memory",s=Ut(Ae(t),"opencode.jsonc"),i=Me(s)?s:void 0,r=Ce(t),c=Gt(r,o),l=c.root,a=l.mcp;if(a!==void 0&&!y(a))throw new Error("Invalid existing OpenCode config: mcp must be an object.");let u=y(a)?{...a}:{},p=u[o];if(Oe(p,n)&&!c.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};Vt(t,d);let z=Ce(t);if(!y(z.mcp)||!Oe(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 Wt,mkdirSync as Ie,readFileSync as qt,writeFileSync as Ee}from"node:fs";import{homedir as Te}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 Re(e={}){let t=e.configFile??L(process.env.CODEX_HOME??L(Te(),".codex"),"config.toml"),n=e.previousFile??L(Te(),".config","toolnet-memory","codex-notify-previous.json");Ie(je(t),{recursive:!0}),Ie(je(n),{recursive:!0});let o=Wt(t)?qt(t,"utf8"):"",s=e.binary??"toolnet-memory",i=`notify = [${JSON.stringify(s)}, "session:codex-notify"]`,r=o.split(`
755
+ `),c=r.findIndex(d=>/^\s*\[/.test(d));c<0&&(c=r.length);let l=-1,a=-1;for(let d=0;d<c;d+=1)if(/^\s*notify\s*=/.test(r[d])){if(l=d,a=d,r[d].includes("[")&&!r[d].includes("]"))for(;a+1<c&&(a+=1,!r[a].includes("]")););break}let u=[];if(l>=0){let d=r.slice(l,a+1).join(`
756
+ `);u=Xt(d),r.splice(l,a-l+1,i)}else c=r.findIndex(d=>/^\s*\[/.test(d)),c<0&&(c=r.length),r.splice(c,0,i);let p=u.length>=2&&u[u.length-1]==="session:codex-notify";return u.length>0&&!p&&Ee(n,JSON.stringify(u,null,2)+`
757
+ `,{encoding:"utf8",mode:384}),o=r.join(`
758
+ `),o.endsWith(`
759
+ `)||(o+=`
760
+ `),Ee(t,o,{encoding:"utf8",mode:384}),{configFile:t,previousFile:n,preservedPrevious:u.length>0&&!p}}import{existsSync as Qt,mkdirSync as Zt,readFileSync as en,writeFileSync as tn}from"node:fs";import{homedir as nn}from"node:os";import{dirname as on,join as Ne}from"node:path";function rn(e){return`'${e.replace(/'/g,"'\\''")}'`}function _e(e={}){let t=e.hooksFile??Ne(process.env.CODEX_HOME??Ne(nn(),".codex"),"hooks.json");Zt(on(t),{recursive:!0});let n={};if(Qt(t))try{n=JSON.parse(en(t,"utf8"))}catch(c){throw new Error(`Invalid existing Codex hooks.json: ${c instanceof Error?c.message:String(c)}`)}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(c=>{try{return!JSON.stringify(c).includes("session:codex-context")}catch{return!0}}),r=e.binary??"toolnet-memory";return i.push({matcher:"startup|resume|clear|compact",hooks:[{type:"command",command:`${rn(r)} session:codex-context`,timeout:15,additionalContextLimit:1e3,statusMessage:"Loading ToolNet project continuity"}]}),o.SessionStart=i,tn(t,JSON.stringify(n,null,2)+`
761
+ `,{encoding:"utf8",mode:384}),t}import{spawnSync as sn}from"node:child_process";function J(e,t){return sn(e,t,{encoding:"utf8",stdio:["ignore","pipe","pipe"]})}function Pe(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 Fe(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 De(e={}){let t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",n=e.codexBinary??"codex",o=e.serverName??"toolnet-memory",s=Pe(n,o);if(s&&Fe(s,t))return{installed:!0,changed:!1,serverName:o,command:t,args:["mcp"]};if(s){let c=J(n,["mcp","remove",o]);if(c.status!==0)return{installed:!1,changed:!1,serverName:o,command:t,args:["mcp"],error:(c.stderr||c.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=Pe(n,o);return!r||!Fe(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 an,readFileSync as ln,renameSync as un,rmSync as dn,writeFileSync as mn}from"node:fs";import{dirname as pn}from"node:path";function x(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function gn(e){return/^[A-Za-z0-9_./:-]+$/u.test(e)?e:`'${e.replace(/'/gu,"'\\''")}'`}function fn(e){if(!cn(e))return{};let t;try{t=JSON.parse(ln(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 Y(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 yn(e,t){an(pn(e),{recursive:!0,mode:448});let n=`${e}.toolnet-${process.pid}-${Date.now()}.tmp`;try{mn(n,JSON.stringify(t,null,2)+`
762
+ `,{encoding:"utf8",mode:384}),un(n,e)}finally{dn(n,{force:!0})}}function $e(e={}){let t=e.settingsFile??se(),n=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=fn(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=`${gn(n)} session:claude-hook`,c=Y(i.SessionStart);c.push({matcher:"startup|resume|clear|compact",hooks:[H(r)]}),i.SessionStart=c;let l=Y(i.PostToolUse);l.push({matcher:"Edit|Write",hooks:[H(r)]}),i.PostToolUse=l;let a=Y(i.Stop);a.push({hooks:[H(r)]}),i.Stop=a;let u={...o,hooks:i},p=JSON.stringify(o),d=JSON.stringify(u);return p===d?{settingsFile:t,changed:!1}:(yn(t,u),{settingsFile:t,changed:!0})}import{existsSync as hn,mkdirSync as vn,readFileSync as bn,renameSync as xn,rmSync as Sn,writeFileSync as wn}from"node:fs";import{dirname as Cn}from"node:path";function S(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Le(e){if(!hn(e))return{};let t;try{t=JSON.parse(bn(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 Je(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 On(e,t){vn(Cn(e),{recursive:!0});let n=`${e}.toolnet-${process.pid}-${Date.now()}.tmp`;try{wn(n,JSON.stringify(t,null,2)+`
763
+ `,{encoding:"utf8",mode:384}),xn(n,e)}finally{Sn(n,{force:!0})}}function Ye(e={}){let t=e.stateFile??ie(),n=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=e.serverName??"toolnet-memory",s=Le(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}:{},c=r[o];if(Je(c,n))return{installed:!0,changed:!1,configFile:t,serverName:o,command:[n,"mcp"],repaired:!1};let l=c!==void 0;r[o]={type:"stdio",command:n,args:["mcp"]},On(t,{...s,mcpServers:r});let u=Le(t).mcpServers;if(!S(u)||!Je(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 He(e={}){let t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",n=$e({binary:t,settingsFile:e.settingsFile}),o=Ye({binary:t,stateFile:e.stateFile});return{hooks:n,mcp:o,files:[n.settingsFile,o.configFile]}}function Be(){return ce()}function B(e={}){let t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",n=[],o=Be(),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=he({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=we({binary:t}),c=ke({binary:t});n.push({agent:"opencode",detected:!0,installed:!0,targets:[r,c.configFile,`mcp:${c.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=He({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=Re({binary:t}),c=_e({binary:t}),l=De({binary:t});if(!l.installed)throw new Error(l.error??"Codex MCP registration failed");let a=[r.configFile,c,`mcp:${l.serverName}`];r.preservedPrevious&&a.push(r.previousFile),n.push({agent:"codex",detected:!0,installed:!0,targets:a})}catch(r){n.push({agent:"codex",detected:!0,installed:!1,targets:[],error:r instanceof Error?r.message:String(r)})}return n}function Mn(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 kn(){let e=process.argv.slice(2),t=e.includes("--all"),n=e.includes("--json");if(e.includes("--detect-only")){let i=Be();if(n){console.log(JSON.stringify(i,null,2));return}Mn(i);return}let s=B({force:t});if(n){console.log(JSON.stringify(s,null,2));return}An(s)}var In=process.argv[1]&&(process.argv[1].endsWith("auto-integrate.js")||process.argv[1].endsWith("auto-integrate.ts"));In&&kn().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exitCode=1});function Rn(e=process.cwd()){let t=Tn(e);if(!ze(t))throw new Error(`Project path does not exist: ${t}`);if(!En(t).isDirectory())throw new Error(`Project path is not a directory: ${t}`);let n=new C().detect(t),o=jn(n.rootPath,".toolnet","project.json");if(!ze(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 Nn(e,t){let n=e.indexOf(t);return n>=0?e[n+1]:void 0}async function _n(){let e=process.argv.slice(2),t=e.includes("--json"),n=!e.includes("--no-integrate"),o=Nn(e,"--project"),s=e.find((l,a)=>!l.startsWith("-")&&(a===0||e[a-1]!=="--project")),i=o??s??process.cwd(),r=await N("Initializing ToolNet project",()=>Rn(i),{enabled:!t}),c=[];if(n&&(c=await N("Detecting AI coding agents",()=>B(),{enabled:!t})),t){console.log(JSON.stringify({...r,integrations:c},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=c.filter(a=>a.detected&&a.installed);if(!l.length)console.log(" \u25CB No supported coding agent detected");else for(let a of l){let u=a.agent==="agy"?"Agy / Antigravity":a.agent==="opencode"?"OpenCode":"Codex";console.log(` \u2713 ${u}`)}console.log("")}console.log("Next: toolnet-memory doctor"),console.log("")}var Pn=process.argv[1]?.endsWith("/init.js")||process.argv[1]?.endsWith("/init.ts");Pn&&_n().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exitCode=1});export{Rn as initializeToolNetProject};