toolnet-memory 0.2.13 → 0.2.14

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/setup.js CHANGED
@@ -1,25 +1,169 @@
1
- import m from"node:fs";import Te from"node:os";import q from"node:path";import Ae from"node:readline/promises";import{stdin as d,stdout as S}from"node:process";import{existsSync as A}from"node:fs";import{homedir as R}from"node:os";import{join as N}from"node:path";import{spawnSync as me}from"node:child_process";import{existsSync as V,mkdirSync as W,readFileSync as X,writeFileSync as Z}from"node:fs";import{homedir as Q}from"node:os";import{dirname as z,join as ee}from"node:path";function te(e){return`'${e.replace(/'/g,"'\\''")}'`}function Y(e={}){let t=e.hooksFile??ee(Q(),".gemini","config","hooks.json");W(z(t),{recursive:!0});let o={};if(V(t))try{o=JSON.parse(X(t,"utf8"))}catch(s){throw new Error(`Invalid existing Agy hooks.json: ${s instanceof Error?s.message:String(s)}`)}let r=e.binary??"toolnet-memory",n=`${te(r)} session:agy-hook`;return o["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}]},Z(t,JSON.stringify(o,null,2)+`
2
- `,{encoding:"utf8",mode:384}),t}import{existsSync as oe,mkdirSync as K,readFileSync as ne,writeFileSync as D}from"node:fs";import{homedir as O}from"node:os";import{join as p}from"node:path";var $="<!-- TOOLNET_MEMORY_BOOTSTRAP_START -->",C="<!-- TOOLNET_MEMORY_BOOTSTRAP_END -->";function re(){let e=p(O(),".config","opencode","AGENTS.md");K(p(O(),".config","opencode"),{recursive:!0});let t=`${$}
3
- ## ToolNet Memory \u2014 Project Continuity
1
+ import S from"node:fs";import Oe from"node:os";import G from"node:path";import Ae from"node:readline/promises";import{stdin as g,stdout as E}from"node:process";import{existsSync as A}from"node:fs";import{homedir as h}from"node:os";import{join as R}from"node:path";import{spawnSync as Se}from"node:child_process";import{existsSync as X,mkdirSync as W,readFileSync as V,writeFileSync as Z}from"node:fs";import{homedir as Q}from"node:os";import{dirname as z,join as ee}from"node:path";function te(e){return`'${e.replace(/'/g,"'\\''")}'`}function F(e={}){let t=e.hooksFile??ee(Q(),".gemini","config","hooks.json");W(z(t),{recursive:!0});let o={};if(X(t))try{o=JSON.parse(V(t,"utf8"))}catch(i){throw new Error(`Invalid existing Agy hooks.json: ${i instanceof Error?i.message:String(i)}`)}let r=e.binary??"toolnet-memory",n=`${te(r)} session:agy-hook`;return o["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}]},Z(t,JSON.stringify(o,null,2)+`
2
+ `,{encoding:"utf8",mode:384}),t}import{existsSync as oe,mkdirSync as D,readFileSync as ne,writeFileSync as $}from"node:fs";import{homedir as T}from"node:os";import{join as _}from"node:path";var Y="<!-- TOOLNET_MEMORY_BOOTSTRAP_START -->",C="<!-- TOOLNET_MEMORY_BOOTSTRAP_END -->";function re(){let e=_(T(),".config","opencode","AGENTS.md");D(_(T(),".config","opencode"),{recursive:!0});let t=`${Y}
3
+ ## ToolNet Memory
4
+
5
+ ToolNet Memory is available for projects that already contain a valid \`.toolnet/project.json\`.
6
+
7
+ Rules:
8
+
9
+ 1. Do not create a ToolNet project automatically.
10
+ 2. If no valid \`.toolnet/project.json\` exists in the current project or an ancestor, ignore ToolNet Memory.
11
+ 3. Normal startup context must stay small and selective.
12
+ 4. Do not automatically run deep recovery commands.
13
+ 5. Use ToolNet MCP or selective retrieval when older project knowledge is actually needed.
14
+ 6. Raw transcripts must not be injected into prompts.
15
+ 7. Current repository evidence has priority over stale memory when they conflict.
16
+
17
+ ${C}`,o=oe(e)?ne(e,"utf8"):"",r=o.indexOf(Y),n=o.indexOf(C);return r>=0&&n>=r?o=o.slice(0,r)+t+o.slice(n+C.length):(o=o.trimEnd(),o&&(o+=`
18
+
19
+ `),o+=t),$(e,o.trimEnd()+`
20
+ `,{encoding:"utf8",mode:384}),e}function v(e={}){let t=e.directory??_(T(),".config","opencode","plugins");D(t,{recursive:!0}),re();let o=_(t,"toolnet-memory.js"),r=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",n=`
21
+ // Generated by ToolNet Memory.
22
+ // OpenCode integration:
23
+ // - project gated
24
+ // - selective context injection
25
+ // - incremental session checkpoints
26
+ // - periodic durability sync
27
+ // - best-effort shutdown flush
28
+
29
+ import fs from "node:fs"
30
+ import path from "node:path"
31
+ import os from "node:os"
32
+
33
+ const TOOLNET_BINARY =
34
+ ${JSON.stringify(r)}
35
+
36
+ const CONTEXT_MAX_TOKENS = 700
37
+
38
+ const CONTEXT_CACHE_MS = 5000
39
+
40
+ const PERIODIC_SYNC_MS = 30000
41
+
42
+ const SYNC_TIMEOUT_MS = 15000
43
+
44
+ const STATUS_FILE = path.join(
45
+ os.homedir(),
46
+ ".config",
47
+ "toolnet-memory",
48
+ "opencode-sync-status.json"
49
+ )
50
+
51
+ function writeStatus(data) {
52
+ try {
53
+ fs.mkdirSync(
54
+ path.dirname(
55
+ STATUS_FILE
56
+ ),
57
+ {
58
+ recursive: true,
59
+ }
60
+ )
61
+
62
+ fs.writeFileSync(
63
+ STATUS_FILE,
64
+ JSON.stringify(
65
+ {
66
+ timestamp:
67
+ new Date()
68
+ .toISOString(),
69
+ ...data,
70
+ },
71
+ null,
72
+ 2
73
+ ) + "\\n"
74
+ )
75
+ } catch {
76
+ // Status reporting must
77
+ // never break OpenCode.
78
+ }
79
+ }
80
+
81
+ function validProjectManifest(
82
+ file
83
+ ) {
84
+ try {
85
+ const parsed =
86
+ JSON.parse(
87
+ fs.readFileSync(
88
+ file,
89
+ "utf8"
90
+ )
91
+ )
92
+
93
+ return Boolean(
94
+ parsed &&
95
+ typeof parsed ===
96
+ "object" &&
97
+ typeof parsed.id ===
98
+ "string" &&
99
+ parsed.id.length > 0
100
+ )
101
+ } catch {
102
+ return false
103
+ }
104
+ }
105
+
106
+ function findProjectRoot(
107
+ inputDirectory
108
+ ) {
109
+ let current =
110
+ path.resolve(
111
+ inputDirectory
112
+ )
113
+
114
+ const filesystemRoot =
115
+ path.parse(
116
+ current
117
+ ).root
118
+
119
+ while (true) {
120
+ const manifest =
121
+ path.join(
122
+ current,
123
+ ".toolnet",
124
+ "project.json"
125
+ )
126
+
127
+ if (
128
+ fs.existsSync(
129
+ manifest
130
+ ) &&
131
+ validProjectManifest(
132
+ manifest
133
+ )
134
+ ) {
135
+ return current
136
+ }
137
+
138
+ if (
139
+ current ===
140
+ filesystemRoot
141
+ ) {
142
+ break
143
+ }
4
144
 
5
- When the current workspace belongs to a ToolNet project, identified by a nearest ancestor containing \`.toolnet/project.json\`:
145
+ const parent =
146
+ path.dirname(
147
+ current
148
+ )
6
149
 
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.
150
+ if (
151
+ parent === current
152
+ ) {
153
+ break
154
+ }
13
155
 
14
- This applies automatically to every ToolNet-managed project.
15
- ${C}`,o=oe(e)?ne(e,"utf8"):"",r=o.indexOf($),n=o.indexOf(C);return r>=0&&n>=r?o=o.slice(0,r)+t+o.slice(n+C.length):(o=o.trimEnd(),o&&(o+=`
156
+ current = parent
157
+ }
16
158
 
17
- `),o+=t),D(e,o.trimEnd()+`
18
- `,{encoding:"utf8",mode:384}),e}function w(e={}){let t=e.directory??p(O(),".config","opencode","plugins");K(t,{recursive:!0}),re();let o=p(t,"toolnet-memory.js"),r=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",n=`// Generated by ToolNet Memory.
19
- // OpenCode session capture + continuity bootstrap.
159
+ return null
160
+ }
20
161
 
21
- function getSessionId(event) {
22
- const p = event?.properties ?? {}
162
+ function getSessionId(
163
+ event
164
+ ) {
165
+ const p =
166
+ event?.properties ?? {}
23
167
 
24
168
  const candidates = [
25
169
  p.sessionID,
@@ -28,158 +172,487 @@ function getSessionId(event) {
28
172
  p.session?.id,
29
173
  ]
30
174
 
31
- return candidates.find(
32
- value => typeof value === "string" && value.length > 0
33
- ) ?? null
175
+ return (
176
+ candidates.find(
177
+ value =>
178
+ typeof value ===
179
+ "string" &&
180
+ value.length > 0
181
+ ) ?? null
182
+ )
34
183
  }
35
184
 
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
- }
185
+ async function runWithTimeout(
186
+ args,
187
+ {
188
+ stdout = "ignore",
189
+ timeout =
190
+ SYNC_TIMEOUT_MS,
191
+ } = {}
192
+ ) {
193
+ const child =
194
+ Bun.spawn(
195
+ args,
196
+ {
197
+ stdin:
198
+ "ignore",
199
+ stdout,
200
+ stderr:
201
+ "ignore",
202
+ }
203
+ )
204
+
205
+ let timer
206
+
207
+ const timeoutPromise =
208
+ new Promise(
209
+ (_, reject) => {
210
+ timer =
211
+ setTimeout(
212
+ () => {
213
+ try {
214
+ child.kill()
215
+ } catch {}
216
+
217
+ reject(
218
+ new Error(
219
+ "ToolNet command timeout"
220
+ )
221
+ )
222
+ },
223
+ timeout
224
+ )
225
+ }
226
+ )
227
+
228
+ try {
229
+ const exitCode =
230
+ await Promise.race([
231
+ child.exited,
232
+ timeoutPromise,
233
+ ])
234
+
235
+ if (
236
+ typeof exitCode ===
237
+ "number" &&
238
+ exitCode !== 0
239
+ ) {
240
+ throw new Error(
241
+ \`ToolNet exited with code \${exitCode}\`
53
242
  )
243
+ }
54
244
 
55
- await child.exited
56
- } catch {
57
- // Context sync must never break OpenCode startup.
245
+ return child
246
+ } finally {
247
+ if (timer) {
248
+ clearTimeout(
249
+ timer
250
+ )
58
251
  }
59
252
  }
253
+ }
60
254
 
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",
76
- }
255
+ export const ToolNetMemoryPlugin =
256
+ async ({
257
+ directory,
258
+ }) => {
259
+ /*
260
+ * CRITICAL:
261
+ * Never auto-create a ToolNet
262
+ * project just because OpenCode
263
+ * happens to start in /root,
264
+ * /tmp, $HOME, etc.
265
+ */
266
+ const projectRoot =
267
+ findProjectRoot(
268
+ directory
77
269
  )
78
270
 
79
- const text =
80
- await new Response(
81
- child.stdout
82
- ).text()
83
-
84
- await child.exited
271
+ if (!projectRoot) {
272
+ writeStatus({
273
+ active: false,
274
+ reason:
275
+ "no-toolnet-project",
276
+ directory,
277
+ })
85
278
 
86
- return text.trim()
87
- } catch {
88
- return ""
279
+ return {}
89
280
  }
90
- }
91
281
 
92
- function sync(sessionId, flag) {
93
- if (!sessionId) return
282
+ writeStatus({
283
+ active: true,
284
+ projectRoot,
285
+ state:
286
+ "plugin-loaded",
287
+ })
94
288
 
95
- try {
96
- const args = [
97
- binary,
98
- "session:opencode-sync",
99
- sessionId,
100
- "--project",
101
- directory,
102
- ]
289
+ let lastSessionId =
290
+ null
103
291
 
104
- if (flag) {
105
- args.push(flag)
106
- }
292
+ let syncChain =
293
+ Promise.resolve()
107
294
 
108
- const child = Bun.spawn(args, {
109
- stdin: "ignore",
110
- stdout: "ignore",
111
- stderr: "ignore",
112
- })
295
+ let contextCache = {
296
+ value: "",
297
+ expiresAt: 0,
298
+ }
299
+
300
+ async function readContext() {
301
+ const now =
302
+ Date.now()
113
303
 
114
304
  if (
115
- typeof child.unref === "function"
305
+ contextCache.value &&
306
+ now <
307
+ contextCache.expiresAt
116
308
  ) {
117
- child.unref()
309
+ return (
310
+ contextCache.value
311
+ )
118
312
  }
119
- } catch {
120
- // Memory capture must never break OpenCode.
121
- }
122
- }
123
313
 
124
- /*
125
- * Pull the latest project brief from ToolNet remote storage
126
- * before the new OpenCode session starts doing work.
127
- */
128
- await refreshContext()
314
+ try {
315
+ const child =
316
+ Bun.spawn(
317
+ [
318
+ TOOLNET_BINARY,
319
+ "context:print",
320
+ "--project",
321
+ projectRoot,
322
+ "--tokens",
323
+ String(
324
+ CONTEXT_MAX_TOKENS
325
+ ),
326
+ ],
327
+ {
328
+ stdin:
329
+ "ignore",
330
+ stdout:
331
+ "pipe",
332
+ stderr:
333
+ "ignore",
334
+ }
335
+ )
336
+
337
+ const textPromise =
338
+ new Response(
339
+ child.stdout
340
+ ).text()
341
+
342
+ const exitCode =
343
+ await child.exited
344
+
345
+ if (
346
+ exitCode !== 0
347
+ ) {
348
+ return ""
349
+ }
350
+
351
+ const text =
352
+ (
353
+ await textPromise
354
+ ).trim()
129
355
 
130
- return {
131
- event: async ({ event }) => {
132
- const sessionId =
133
- getSessionId(event)
356
+ contextCache = {
357
+ value: text,
358
+ expiresAt:
359
+ now +
360
+ CONTEXT_CACHE_MS,
361
+ }
362
+
363
+ return text
364
+ } catch {
365
+ return ""
366
+ }
367
+ }
134
368
 
369
+ async function syncNow(
370
+ sessionId,
371
+ flag,
372
+ reason
373
+ ) {
135
374
  if (!sessionId) {
136
375
  return
137
376
  }
138
377
 
139
- if (event.type === "session.idle") {
140
- sync(sessionId, "--idle")
141
- return
378
+ const args = [
379
+ TOOLNET_BINARY,
380
+ "session:opencode-sync",
381
+ sessionId,
382
+ "--project",
383
+ projectRoot,
384
+ ]
385
+
386
+ if (flag) {
387
+ args.push(
388
+ flag
389
+ )
142
390
  }
143
391
 
144
- if (event.type === "session.compacted") {
145
- sync(sessionId, "--compacted")
146
- return
392
+ try {
393
+ await runWithTimeout(
394
+ args
395
+ )
396
+
397
+ writeStatus({
398
+ active: true,
399
+ projectRoot,
400
+ sessionId,
401
+ reason,
402
+ state:
403
+ "sync-success",
404
+ })
405
+ } catch (error) {
406
+ writeStatus({
407
+ active: true,
408
+ projectRoot,
409
+ sessionId,
410
+ reason,
411
+ state:
412
+ "sync-failed",
413
+ error:
414
+ error instanceof
415
+ Error
416
+ ? error.message
417
+ : String(
418
+ error
419
+ ),
420
+ })
421
+
422
+ throw error
147
423
  }
424
+ }
148
425
 
149
- if (event.type === "session.error") {
150
- sync(sessionId, "--error")
426
+ function queueSync(
427
+ sessionId,
428
+ flag,
429
+ reason
430
+ ) {
431
+ if (!sessionId) {
432
+ return syncChain
151
433
  }
152
- },
434
+
435
+ lastSessionId =
436
+ sessionId
437
+
438
+ syncChain =
439
+ syncChain
440
+ .catch(
441
+ () =>
442
+ undefined
443
+ )
444
+ .then(
445
+ () =>
446
+ syncNow(
447
+ sessionId,
448
+ flag,
449
+ reason
450
+ )
451
+ )
452
+
453
+ return syncChain
454
+ }
153
455
 
154
456
  /*
155
- * Official OpenCode compaction hook.
156
- * Preserve ToolNet's semantic state after context compaction.
457
+ * Periodic durability checkpoint.
458
+ *
459
+ * This is intentionally independent
460
+ * from session.idle/dispose because
461
+ * abrupt SSH/process termination may
462
+ * skip shutdown hooks.
463
+ *
464
+ * unref() prevents the timer itself
465
+ * from keeping OpenCode alive.
157
466
  */
158
- "experimental.session.compacting": async (_input, output) => {
159
- const context =
160
- await readContext()
467
+ const periodic =
468
+ setInterval(
469
+ () => {
470
+ if (
471
+ lastSessionId
472
+ ) {
473
+ void queueSync(
474
+ lastSessionId,
475
+ null,
476
+ "periodic"
477
+ )
478
+ }
479
+ },
480
+ PERIODIC_SYNC_MS
481
+ )
161
482
 
162
- if (
163
- context &&
164
- Array.isArray(
165
- output.context
166
- )
167
- ) {
168
- output.context.push(
169
- context
483
+ if (
484
+ typeof periodic.unref ===
485
+ "function"
486
+ ) {
487
+ periodic.unref()
488
+ }
489
+
490
+ return {
491
+ event: async ({
492
+ event,
493
+ }) => {
494
+ const sessionId =
495
+ getSessionId(
496
+ event
497
+ )
498
+
499
+ if (sessionId) {
500
+ lastSessionId =
501
+ sessionId
502
+ }
503
+
504
+ if (
505
+ event.type ===
506
+ "session.idle"
507
+ ) {
508
+ await queueSync(
509
+ sessionId,
510
+ "--idle",
511
+ "session.idle"
512
+ )
513
+
514
+ return
515
+ }
516
+
517
+ if (
518
+ event.type ===
519
+ "session.compacted"
520
+ ) {
521
+ await queueSync(
522
+ sessionId,
523
+ "--compacted",
524
+ "session.compacted"
525
+ )
526
+
527
+ contextCache = {
528
+ value: "",
529
+ expiresAt: 0,
530
+ }
531
+
532
+ return
533
+ }
534
+
535
+ if (
536
+ event.type ===
537
+ "session.error"
538
+ ) {
539
+ await queueSync(
540
+ sessionId,
541
+ "--error",
542
+ "session.error"
543
+ )
544
+
545
+ return
546
+ }
547
+ },
548
+
549
+ /*
550
+ * VERIFIED on OpenCode 1.18.14:
551
+ * this hook fires on real model turns.
552
+ *
553
+ * It remains experimental, therefore
554
+ * AGENTS.md + MCP stay as fallbacks.
555
+ */
556
+ "experimental.chat.system.transform":
557
+ async (
558
+ _input,
559
+ output
560
+ ) => {
561
+ const context =
562
+ await readContext()
563
+
564
+ if (!context) {
565
+ return
566
+ }
567
+
568
+ if (
569
+ Array.isArray(
570
+ output?.system
571
+ )
572
+ ) {
573
+ output.system.push(
574
+ context
575
+ )
576
+
577
+ return
578
+ }
579
+
580
+ if (
581
+ typeof output?.system ===
582
+ "string"
583
+ ) {
584
+ output.system =
585
+ output.system
586
+ ? \`\${output.system}\\n\\n\${context}\`
587
+ : context
588
+ }
589
+ },
590
+
591
+ /*
592
+ * Optional compaction survival hook.
593
+ * We keep it as a supplementary path,
594
+ * never as the only continuity path.
595
+ */
596
+ "experimental.session.compacting":
597
+ async (
598
+ _input,
599
+ output
600
+ ) => {
601
+ const context =
602
+ await readContext()
603
+
604
+ if (
605
+ context &&
606
+ Array.isArray(
607
+ output?.context
608
+ )
609
+ ) {
610
+ output.context.push(
611
+ context
612
+ )
613
+ }
614
+ },
615
+
616
+ /*
617
+ * VERIFIED for clean OpenCode exits.
618
+ * Best effort only: kill -9 / crash /
619
+ * machine loss cannot guarantee this.
620
+ */
621
+ dispose: async () => {
622
+ clearInterval(
623
+ periodic
170
624
  )
171
- }
172
- },
625
+
626
+ if (
627
+ lastSessionId
628
+ ) {
629
+ try {
630
+ await queueSync(
631
+ lastSessionId,
632
+ "--idle",
633
+ "dispose"
634
+ )
635
+ } catch {
636
+ // Status already recorded.
637
+ }
638
+ }
639
+
640
+ try {
641
+ await syncChain
642
+ } catch {
643
+ // Do not block OpenCode shutdown.
644
+ }
645
+ },
646
+ }
173
647
  }
174
- }
175
- `;return D(o,n,{encoding:"utf8",mode:384}),o}import{existsSync as ie,mkdirSync as k,readFileSync as se,writeFileSync as b}from"node:fs";import{homedir as H}from"node:os";import{dirname as P,join as T}from"node:path";function ce(e){let t=[],o=/"((?:\\.|[^"\\])*)"|'([^']*)'/g,r;for(;r=o.exec(e);){let n=r[1]??r[2]??"";try{t.push(r[1]!==void 0?JSON.parse(`"${n}"`):n)}catch{t.push(n)}}return t}function L(e={}){let t=e.configFile??T(process.env.CODEX_HOME??T(H(),".codex"),"config.toml"),o=e.previousFile??T(H(),".config","toolnet-memory","codex-notify-previous.json");k(P(t),{recursive:!0}),k(P(o),{recursive:!0});let r=ie(t)?se(t,"utf8"):"",n=e.binary??"toolnet-memory",s=`notify = [${JSON.stringify(n)}, "session:codex-notify"]`,i=r.split(`
176
- `),a=i.findIndex(l=>/^\s*\[/.test(l));a<0&&(a=i.length);let f=-1,c=-1;for(let l=0;l<a;l+=1)if(/^\s*notify\s*=/.test(i[l])){if(f=l,c=l,i[l].includes("[")&&!i[l].includes("]"))for(;c+1<a&&(c+=1,!i[c].includes("]")););break}let g=[];if(f>=0){let l=i.slice(f,c+1).join(`
177
- `);g=ce(l),i.splice(f,c-f+1,s)}else a=i.findIndex(l=>/^\s*\[/.test(l)),a<0&&(a=i.length),i.splice(a,0,s);let E=g.length>=2&&g[g.length-1]==="session:codex-notify";return g.length>0&&!E&&b(o,JSON.stringify(g,null,2)+`
178
- `,{encoding:"utf8",mode:384}),r=i.join(`
648
+ `;return $(o,n.trimStart(),{encoding:"utf8",mode:384}),o}import{existsSync as se,mkdirSync as K,readFileSync as ie,writeFileSync as P}from"node:fs";import{homedir as k}from"node:os";import{dirname as H,join as O}from"node:path";function ce(e){let t=[],o=/"((?:\\.|[^"\\])*)"|'([^']*)'/g,r;for(;r=o.exec(e);){let n=r[1]??r[2]??"";try{t.push(r[1]!==void 0?JSON.parse(`"${n}"`):n)}catch{t.push(n)}}return t}function b(e={}){let t=e.configFile??O(process.env.CODEX_HOME??O(k(),".codex"),"config.toml"),o=e.previousFile??O(k(),".config","toolnet-memory","codex-notify-previous.json");K(H(t),{recursive:!0}),K(H(o),{recursive:!0});let r=se(t)?ie(t,"utf8"):"",n=e.binary??"toolnet-memory",i=`notify = [${JSON.stringify(n)}, "session:codex-notify"]`,s=r.split(`
649
+ `),a=s.findIndex(l=>/^\s*\[/.test(l));a<0&&(a=s.length);let f=-1,c=-1;for(let l=0;l<a;l+=1)if(/^\s*notify\s*=/.test(s[l])){if(f=l,c=l,s[l].includes("[")&&!s[l].includes("]"))for(;c+1<a&&(c+=1,!s[c].includes("]")););break}let u=[];if(f>=0){let l=s.slice(f,c+1).join(`
650
+ `);u=ce(l),s.splice(f,c-f+1,i)}else a=s.findIndex(l=>/^\s*\[/.test(l)),a<0&&(a=s.length),s.splice(a,0,i);let m=u.length>=2&&u[u.length-1]==="session:codex-notify";return u.length>0&&!m&&P(o,JSON.stringify(u,null,2)+`
651
+ `,{encoding:"utf8",mode:384}),r=s.join(`
179
652
  `),r.endsWith(`
180
653
  `)||(r+=`
181
- `),b(t,r,{encoding:"utf8",mode:384}),{configFile:t,previousFile:o,preservedPrevious:g.length>0&&!E}}import{existsSync as ae,mkdirSync as le,readFileSync as ge,writeFileSync as de}from"node:fs";import{homedir as ue}from"node:os";import{dirname as fe,join as U}from"node:path";function Ee(e){return`'${e.replace(/'/g,"'\\''")}'`}function v(e={}){let t=e.hooksFile??U(process.env.CODEX_HOME??U(ue(),".codex"),"hooks.json");le(fe(t),{recursive:!0});let o={};if(ae(t))try{o=JSON.parse(ge(t,"utf8"))}catch(a){throw new Error(`Invalid existing Codex hooks.json: ${a instanceof Error?a.message:String(a)}`)}let r=o.hooks&&typeof o.hooks=="object"&&!Array.isArray(o.hooks)?o.hooks:{};o.hooks=r;let s=(Array.isArray(r.SessionStart)?r.SessionStart:[]).filter(a=>{try{return!JSON.stringify(a).includes("session:codex-context")}catch{return!0}}),i=e.binary??"toolnet-memory";return s.push({matcher:"startup|resume|clear|compact",hooks:[{type:"command",command:`${Ee(i)} session:codex-context`,timeout:15,additionalContextLimit:1e3,statusMessage:"Loading ToolNet project continuity"}]}),r.SessionStart=s,de(t,JSON.stringify(o,null,2)+`
182
- `,{encoding:"utf8",mode:384}),t}function h(e){return me("sh",["-lc",`command -v ${JSON.stringify(e)} >/dev/null 2>&1`],{stdio:"ignore"}).status===0}function _e(){return h("agy")||A(N(R(),".gemini"))}function Se(){return h("opencode")||A(N(R(),".config","opencode"))}function pe(){return h("codex")||A(process.env.CODEX_HOME??N(R(),".codex"))}function I(e={}){let t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=[];if(!(e.force===!0||_e()))o.push({agent:"agy",detected:!1,installed:!1,targets:[]});else try{let n=Y({binary:t});o.push({agent:"agy",detected:!0,installed:!0,targets:[n]})}catch(n){o.push({agent:"agy",detected:!0,installed:!1,targets:[],error:n instanceof Error?n.message:String(n)})}if(!(e.force===!0||Se()))o.push({agent:"opencode",detected:!1,installed:!1,targets:[]});else try{let n=w({binary:t});o.push({agent:"opencode",detected:!0,installed:!0,targets:[n]})}catch(n){o.push({agent:"opencode",detected:!0,installed:!1,targets:[],error:n instanceof Error?n.message:String(n)})}if(!(e.force===!0||pe()))o.push({agent:"codex",detected:!1,installed:!1,targets:[]});else try{let n=L({binary:t}),s=v({binary:t}),i=[n.configFile,s];n.preservedPrevious&&i.push(n.previousFile),o.push({agent:"codex",detected:!0,installed:!0,targets:i})}catch(n){o.push({agent:"codex",detected:!0,installed:!1,targets:[],error:n instanceof Error?n.message:String(n)})}return o}function ye(e){console.log(""),console.log("ToolNet Memory AI Integrations"),console.log("=============================="),console.log("");for(let t of e){let o=t.agent==="agy"?"Agy / Antigravity":t.agent==="opencode"?"OpenCode":"Codex";if(!t.detected){console.log(`- ${o}: not detected`);continue}if(t.installed){console.log(`\u2713 ${o}: automatic memory enabled`);continue}console.log(`\u2717 ${o}: integration failed`),t.error&&console.log(` ${t.error}`)}console.log("")}async function Ce(){let e=process.argv.slice(2),t=e.includes("--all"),o=e.includes("--json"),r=I({force:t});if(o){console.log(JSON.stringify(r,null,2));return}ye(r)}var Oe=process.argv[1]&&(process.argv[1].endsWith("auto-integrate.js")||process.argv[1].endsWith("auto-integrate.ts"));Oe&&Ce().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exitCode=1});var x=q.join(Te.homedir(),".config","toolnet-memory"),_=q.join(x,".env");function Re(e){let t=new Map;for(let o of e.split(/\r?\n/)){let r=o.trim();if(!r||r.startsWith("#"))continue;let n=r.indexOf("=");n!==-1&&t.set(r.slice(0,n).trim(),r.slice(n+1).trim())}return t}function J(e){let t=e.get("MEMORY_STORAGE_PROVIDER")?.trim();return t==="r2"||t==="s3"||t==="local"||t==="huggingface"?t:e.get("R2_ACCOUNT_ID")&&e.get("R2_BUCKET")?"r2":e.get("S3_BUCKET")?"s3":e.get("HF_NAMESPACE")&&e.get("HF_BUCKET")?"huggingface":"r2"}function B(e){return e==="r2"?["R2_ACCOUNT_ID","R2_BUCKET","R2_ACCESS_KEY_ID","R2_SECRET_ACCESS_KEY"]:e==="s3"?["S3_BUCKET","S3_ACCESS_KEY_ID","S3_SECRET_ACCESS_KEY"]:e==="huggingface"?["HF_NAMESPACE","HF_BUCKET","HF_S3_ACCESS_KEY_ID","HF_S3_SECRET_ACCESS_KEY"]:[]}function y(e){return e==="r2"?"Cloudflare R2":e==="s3"?"S3-compatible storage":e==="huggingface"?"Hugging Face S3 (legacy)":"Local storage"}function j(e){m.mkdirSync(x,{recursive:!0,mode:448});let o=`# ==========================================================
654
+ `),P(t,r,{encoding:"utf8",mode:384}),{configFile:t,previousFile:o,preservedPrevious:u.length>0&&!m}}import{existsSync as ae,mkdirSync as le,readFileSync as ue,writeFileSync as ge}from"node:fs";import{homedir as de}from"node:os";import{dirname as fe,join as j}from"node:path";function me(e){return`'${e.replace(/'/g,"'\\''")}'`}function L(e={}){let t=e.hooksFile??j(process.env.CODEX_HOME??j(de(),".codex"),"hooks.json");le(fe(t),{recursive:!0});let o={};if(ae(t))try{o=JSON.parse(ue(t,"utf8"))}catch(a){throw new Error(`Invalid existing Codex hooks.json: ${a instanceof Error?a.message:String(a)}`)}let r=o.hooks&&typeof o.hooks=="object"&&!Array.isArray(o.hooks)?o.hooks:{};o.hooks=r;let i=(Array.isArray(r.SessionStart)?r.SessionStart:[]).filter(a=>{try{return!JSON.stringify(a).includes("session:codex-context")}catch{return!0}}),s=e.binary??"toolnet-memory";return i.push({matcher:"startup|resume|clear|compact",hooks:[{type:"command",command:`${me(s)} session:codex-context`,timeout:15,additionalContextLimit:1e3,statusMessage:"Loading ToolNet project continuity"}]}),r.SessionStart=i,ge(t,JSON.stringify(o,null,2)+`
655
+ `,{encoding:"utf8",mode:384}),t}function I(e){return Se("sh",["-lc",`command -v ${JSON.stringify(e)} >/dev/null 2>&1`],{stdio:"ignore"}).status===0}function pe(){return I("agy")||A(R(h(),".gemini"))}function Ee(){return I("opencode")||A(R(h(),".config","opencode"))}function _e(){return I("codex")||A(process.env.CODEX_HOME??R(h(),".codex"))}function N(e={}){let t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=[];if(!(e.force===!0||pe()))o.push({agent:"agy",detected:!1,installed:!1,targets:[]});else try{let n=F({binary:t});o.push({agent:"agy",detected:!0,installed:!0,targets:[n]})}catch(n){o.push({agent:"agy",detected:!0,installed:!1,targets:[],error:n instanceof Error?n.message:String(n)})}if(!(e.force===!0||Ee()))o.push({agent:"opencode",detected:!1,installed:!1,targets:[]});else try{let n=v({binary:t});o.push({agent:"opencode",detected:!0,installed:!0,targets:[n]})}catch(n){o.push({agent:"opencode",detected:!0,installed:!1,targets:[],error:n instanceof Error?n.message:String(n)})}if(!(e.force===!0||_e()))o.push({agent:"codex",detected:!1,installed:!1,targets:[]});else try{let n=b({binary:t}),i=L({binary:t}),s=[n.configFile,i];n.preservedPrevious&&s.push(n.previousFile),o.push({agent:"codex",detected:!0,installed:!0,targets:s})}catch(n){o.push({agent:"codex",detected:!0,installed:!1,targets:[],error:n instanceof Error?n.message:String(n)})}return o}function ye(e){console.log(""),console.log("ToolNet Memory AI Integrations"),console.log("=============================="),console.log("");for(let t of e){let o=t.agent==="agy"?"Agy / Antigravity":t.agent==="opencode"?"OpenCode":"Codex";if(!t.detected){console.log(`- ${o}: not detected`);continue}if(t.installed){console.log(`\u2713 ${o}: automatic memory enabled`);continue}console.log(`\u2717 ${o}: integration failed`),t.error&&console.log(` ${t.error}`)}console.log("")}async function Ce(){let e=process.argv.slice(2),t=e.includes("--all"),o=e.includes("--json"),r=N({force:t});if(o){console.log(JSON.stringify(r,null,2));return}ye(r)}var Te=process.argv[1]&&(process.argv[1].endsWith("auto-integrate.js")||process.argv[1].endsWith("auto-integrate.ts"));Te&&Ce().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exitCode=1});var x=G.join(Oe.homedir(),".config","toolnet-memory"),p=G.join(x,".env");function he(e){let t=new Map;for(let o of e.split(/\r?\n/)){let r=o.trim();if(!r||r.startsWith("#"))continue;let n=r.indexOf("=");n!==-1&&t.set(r.slice(0,n).trim(),r.slice(n+1).trim())}return t}function J(e){let t=e.get("MEMORY_STORAGE_PROVIDER")?.trim();return t==="r2"||t==="s3"||t==="local"||t==="huggingface"?t:e.get("R2_ACCOUNT_ID")&&e.get("R2_BUCKET")?"r2":e.get("S3_BUCKET")?"s3":e.get("HF_NAMESPACE")&&e.get("HF_BUCKET")?"huggingface":"r2"}function U(e){return e==="r2"?["R2_ACCOUNT_ID","R2_BUCKET","R2_ACCESS_KEY_ID","R2_SECRET_ACCESS_KEY"]:e==="s3"?["S3_BUCKET","S3_ACCESS_KEY_ID","S3_SECRET_ACCESS_KEY"]:e==="huggingface"?["HF_NAMESPACE","HF_BUCKET","HF_S3_ACCESS_KEY_ID","HF_S3_SECRET_ACCESS_KEY"]:[]}function y(e){return e==="r2"?"Cloudflare R2":e==="s3"?"S3-compatible storage":e==="huggingface"?"Hugging Face S3 (legacy)":"Local storage"}function B(e){S.mkdirSync(x,{recursive:!0,mode:448});let o=`# ==========================================================
183
656
  # TOOLNET MEMORY
184
657
  # ==========================================================
185
658
 
@@ -231,7 +704,7 @@ TOOLNET_SESSION_LEARNING=${e.get("TOOLNET_SESSION_LEARNING")??"1"}
231
704
  TOOLNET_WORK_CONTINUITY=${e.get("TOOLNET_WORK_CONTINUITY")??"1"}
232
705
  TOOLNET_SEMANTIC_CONTINUITY=${e.get("TOOLNET_SEMANTIC_CONTINUITY")??"1"}
233
706
  TOOLNET_SMART_HANDOFF=${e.get("TOOLNET_SMART_HANDOFF")??"1"}
234
- `;m.writeFileSync(_,o,{encoding:"utf8",mode:384}),m.chmodSync(x,448),m.chmodSync(_,384)}function G(e,t=!0){let o=e.trim().toLowerCase();return o?o==="y"||o==="yes":t}async function Ne(e){return d.isTTY?(S.write(e),new Promise(t=>{let o="",r=()=>{d.off("data",n),d.setRawMode?.(!1),d.pause(),S.write(`
235
- `),t(o)},n=s=>{for(let i of s.toString("utf8")){if(i==="\r"||i===`
236
- `){r();return}if(i===""&&(d.setRawMode?.(!1),S.write(`
237
- `),process.exit(130)),i==="\x7F"){o=o.slice(0,-1);continue}o+=i}};d.resume(),d.setRawMode?.(!0),d.on("data",n)})):""}function M(){try{let t=I().filter(o=>o.installed);if(t.length>0){console.log(""),console.log("Automatic AI memory:");for(let o of t){let r=o.agent==="agy"?"Agy / Antigravity":o.agent==="opencode"?"OpenCode":"Codex";console.log(` \u2713 ${r}`)}}}catch{}}function u(e,t,o,r){o.trim()?e.set(t,o.trim()):!e.get(t)&&r!==void 0&&e.set(t,r)}async function he(e,t){console.log("Storage provider:"),console.log(" 1. Cloudflare R2 (recommended)"),console.log(" 2. S3 / S3-compatible"),console.log(" 3. Local"),console.log(" 4. Hugging Face S3 (legacy)"),console.log("");let o=t==="s3"?"2":t==="local"?"3":t==="huggingface"?"4":"1",n=(await e.question(`Choose [${o}]: `)).trim()||o;return n==="2"?"s3":n==="3"?"local":n==="4"?"huggingface":"r2"}async function Ie(){let e=m.existsSync(_),t=e?Re(m.readFileSync(_,"utf8")):new Map,o=J(t),r=B(o),n=r.every(c=>!!t.get(c)?.trim());if(console.log(""),console.log("TOOLNET MEMORY SETUP"),console.log("===================="),console.log(""),console.log(`Config: ${_}`),console.log(""),!d.isTTY||!S.isTTY){e||(t.set("MEMORY_STORAGE_PROVIDER",o),j(t)),n?console.log(`\u2713 ${y(o)} already configured`):(console.log("Configuration pending."),console.log("Run:"),console.log(" toolnet-memory setup")),M();return}let s=Ae.createInterface({input:d,output:S});if(n){console.log(`\u2713 ${y(o)} already configured`),console.log("");let c=await s.question("Use existing storage configuration? (Y/n) [Y]: ");if(G(c)){s.close(),console.log(""),console.log(`\u2713 Existing ${y(o)} configuration kept`),console.log(""),M(),console.log("Next:"),console.log(" toolnet-memory doctor");return}console.log("")}o=await he(s,o),t.set("MEMORY_STORAGE_PROVIDER",o),console.log("");let i="",a="";if(o==="r2"){let c=await s.question(t.get("R2_ACCOUNT_ID")?`Cloudflare Account ID [${t.get("R2_ACCOUNT_ID")}]: `:"Cloudflare Account ID: "),g=await s.question(`R2 Bucket [${t.get("R2_BUCKET")||"toolnet-memory"}]: `),E=await s.question(t.get("R2_ACCESS_KEY_ID")?"R2 Access Key ID [configured]: ":"R2 Access Key ID: ");u(t,"R2_ACCOUNT_ID",c),u(t,"R2_BUCKET",g,"toolnet-memory"),u(t,"R2_ACCESS_KEY_ID",E),i="R2_SECRET_ACCESS_KEY",a=t.get(i)?"R2 Secret Access Key [configured]: ":"R2 Secret Access Key: "}else if(o==="s3"){let c=await s.question(t.get("S3_ENDPOINT")?`S3 Endpoint [${t.get("S3_ENDPOINT")}]: `:"S3 Endpoint [blank = AWS S3]: "),g=await s.question(`S3 Region [${t.get("S3_REGION")||"us-east-1"}]: `),E=await s.question(`S3 Bucket [${t.get("S3_BUCKET")||"toolnet-memory"}]: `),l=await s.question(t.get("S3_ACCESS_KEY_ID")?"S3 Access Key ID [configured]: ":"S3 Access Key ID: "),F=await s.question(`Force path-style? (y/N) [${t.get("S3_FORCE_PATH_STYLE")==="true"?"Y":"N"}]: `);c.trim()&&t.set("S3_ENDPOINT",c.trim()),u(t,"S3_REGION",g,"us-east-1"),u(t,"S3_BUCKET",E,"toolnet-memory"),u(t,"S3_ACCESS_KEY_ID",l),F.trim()?t.set("S3_FORCE_PATH_STYLE",G(F,!1)?"true":"false"):t.get("S3_FORCE_PATH_STYLE")||t.set("S3_FORCE_PATH_STYLE","false"),i="S3_SECRET_ACCESS_KEY",a=t.get(i)?"S3 Secret Access Key [configured]: ":"S3 Secret Access Key: "}else if(o==="huggingface"){let c=await s.question(t.get("HF_NAMESPACE")?`Hugging Face namespace [${t.get("HF_NAMESPACE")}]: `:"Hugging Face namespace: "),g=await s.question(`Bucket [${t.get("HF_BUCKET")||"toolnet-memory"}]: `),E=await s.question(t.get("HF_S3_ACCESS_KEY_ID")?"S3 Access Key ID [configured]: ":"S3 Access Key ID: ");u(t,"HF_NAMESPACE",c),u(t,"HF_BUCKET",g,"toolnet-memory"),u(t,"HF_S3_ACCESS_KEY_ID",E),i="HF_S3_SECRET_ACCESS_KEY",a=t.get(i)?"S3 Secret Access Key [configured]: ":"S3 Secret Access Key: "}if(s.close(),i){let c=await Ne(a);c.trim()&&t.set(i,c.trim())}j(t),r=B(o);let f=r.filter(c=>!t.get(c)?.trim());if(console.log(""),console.log("\u2713 Configuration saved"),console.log(` ${_}`),console.log(""),f.length){console.log("Missing configuration:");for(let c of f)console.log(` - ${c}`);console.log(""),console.log("Run setup again:"),console.log(" toolnet-memory setup");return}console.log(`\u2713 ${y(o)} configuration complete`),console.log(""),M(),console.log("Next:"),console.log(" toolnet-memory doctor")}Ie().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exit(1)});
707
+ `;S.writeFileSync(p,o,{encoding:"utf8",mode:384}),S.chmodSync(x,448),S.chmodSync(p,384)}function q(e,t=!0){let o=e.trim().toLowerCase();return o?o==="y"||o==="yes":t}async function Re(e){return g.isTTY?(E.write(e),new Promise(t=>{let o="",r=()=>{g.off("data",n),g.setRawMode?.(!1),g.pause(),E.write(`
708
+ `),t(o)},n=i=>{for(let s of i.toString("utf8")){if(s==="\r"||s===`
709
+ `){r();return}if(s===""&&(g.setRawMode?.(!1),E.write(`
710
+ `),process.exit(130)),s==="\x7F"){o=o.slice(0,-1);continue}o+=s}};g.resume(),g.setRawMode?.(!0),g.on("data",n)})):""}function M(){try{let t=N().filter(o=>o.installed);if(t.length>0){console.log(""),console.log("Automatic AI memory:");for(let o of t){let r=o.agent==="agy"?"Agy / Antigravity":o.agent==="opencode"?"OpenCode":"Codex";console.log(` \u2713 ${r}`)}}}catch{}}function d(e,t,o,r){o.trim()?e.set(t,o.trim()):!e.get(t)&&r!==void 0&&e.set(t,r)}async function Ie(e,t){console.log("Storage provider:"),console.log(" 1. Cloudflare R2 (recommended)"),console.log(" 2. S3 / S3-compatible"),console.log(" 3. Local"),console.log(" 4. Hugging Face S3 (legacy)"),console.log("");let o=t==="s3"?"2":t==="local"?"3":t==="huggingface"?"4":"1",n=(await e.question(`Choose [${o}]: `)).trim()||o;return n==="2"?"s3":n==="3"?"local":n==="4"?"huggingface":"r2"}async function Ne(){let e=S.existsSync(p),t=e?he(S.readFileSync(p,"utf8")):new Map,o=J(t),r=U(o),n=r.every(c=>!!t.get(c)?.trim());if(console.log(""),console.log("TOOLNET MEMORY SETUP"),console.log("===================="),console.log(""),console.log(`Config: ${p}`),console.log(""),!g.isTTY||!E.isTTY){e||(t.set("MEMORY_STORAGE_PROVIDER",o),B(t)),n?console.log(`\u2713 ${y(o)} already configured`):(console.log("Configuration pending."),console.log("Run:"),console.log(" toolnet-memory setup")),M();return}let i=Ae.createInterface({input:g,output:E});if(n){console.log(`\u2713 ${y(o)} already configured`),console.log("");let c=await i.question("Use existing storage configuration? (Y/n) [Y]: ");if(q(c)){i.close(),console.log(""),console.log(`\u2713 Existing ${y(o)} configuration kept`),console.log(""),M(),console.log("Next:"),console.log(" toolnet-memory doctor");return}console.log("")}o=await Ie(i,o),t.set("MEMORY_STORAGE_PROVIDER",o),console.log("");let s="",a="";if(o==="r2"){let c=await i.question(t.get("R2_ACCOUNT_ID")?`Cloudflare Account ID [${t.get("R2_ACCOUNT_ID")}]: `:"Cloudflare Account ID: "),u=await i.question(`R2 Bucket [${t.get("R2_BUCKET")||"toolnet-memory"}]: `),m=await i.question(t.get("R2_ACCESS_KEY_ID")?"R2 Access Key ID [configured]: ":"R2 Access Key ID: ");d(t,"R2_ACCOUNT_ID",c),d(t,"R2_BUCKET",u,"toolnet-memory"),d(t,"R2_ACCESS_KEY_ID",m),s="R2_SECRET_ACCESS_KEY",a=t.get(s)?"R2 Secret Access Key [configured]: ":"R2 Secret Access Key: "}else if(o==="s3"){let c=await i.question(t.get("S3_ENDPOINT")?`S3 Endpoint [${t.get("S3_ENDPOINT")}]: `:"S3 Endpoint [blank = AWS S3]: "),u=await i.question(`S3 Region [${t.get("S3_REGION")||"us-east-1"}]: `),m=await i.question(`S3 Bucket [${t.get("S3_BUCKET")||"toolnet-memory"}]: `),l=await i.question(t.get("S3_ACCESS_KEY_ID")?"S3 Access Key ID [configured]: ":"S3 Access Key ID: "),w=await i.question(`Force path-style? (y/N) [${t.get("S3_FORCE_PATH_STYLE")==="true"?"Y":"N"}]: `);c.trim()&&t.set("S3_ENDPOINT",c.trim()),d(t,"S3_REGION",u,"us-east-1"),d(t,"S3_BUCKET",m,"toolnet-memory"),d(t,"S3_ACCESS_KEY_ID",l),w.trim()?t.set("S3_FORCE_PATH_STYLE",q(w,!1)?"true":"false"):t.get("S3_FORCE_PATH_STYLE")||t.set("S3_FORCE_PATH_STYLE","false"),s="S3_SECRET_ACCESS_KEY",a=t.get(s)?"S3 Secret Access Key [configured]: ":"S3 Secret Access Key: "}else if(o==="huggingface"){let c=await i.question(t.get("HF_NAMESPACE")?`Hugging Face namespace [${t.get("HF_NAMESPACE")}]: `:"Hugging Face namespace: "),u=await i.question(`Bucket [${t.get("HF_BUCKET")||"toolnet-memory"}]: `),m=await i.question(t.get("HF_S3_ACCESS_KEY_ID")?"S3 Access Key ID [configured]: ":"S3 Access Key ID: ");d(t,"HF_NAMESPACE",c),d(t,"HF_BUCKET",u,"toolnet-memory"),d(t,"HF_S3_ACCESS_KEY_ID",m),s="HF_S3_SECRET_ACCESS_KEY",a=t.get(s)?"S3 Secret Access Key [configured]: ":"S3 Secret Access Key: "}if(i.close(),s){let c=await Re(a);c.trim()&&t.set(s,c.trim())}B(t),r=U(o);let f=r.filter(c=>!t.get(c)?.trim());if(console.log(""),console.log("\u2713 Configuration saved"),console.log(` ${p}`),console.log(""),f.length){console.log("Missing configuration:");for(let c of f)console.log(` - ${c}`);console.log(""),console.log("Run setup again:"),console.log(" toolnet-memory setup");return}console.log(`\u2713 ${y(o)} configuration complete`),console.log(""),M(),console.log("Next:"),console.log(" toolnet-memory doctor")}Ne().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exit(1)});