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