toolnet-memory 0.3.6 → 0.3.8

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.
@@ -322,6 +322,20 @@ case "$COMMAND" in
322
322
  exit 0
323
323
  ;;
324
324
 
325
+ session:kiro-hook)
326
+ shift
327
+
328
+ if [ -f "$ROOT/bundle/kiro-hook.js" ]; then
329
+ exec node "$ROOT/bundle/kiro-hook.js" "$@"
330
+ fi
331
+
332
+ if [ -x "${TSX:-}" ] && [ -f "$ROOT/src/session/kiro/hook.ts" ]; then
333
+ exec "$TSX" "$ROOT/src/session/kiro/hook.ts" "$@"
334
+ fi
335
+
336
+ exit 0
337
+ ;;
338
+
325
339
  integrate:claude)
326
340
  shift
327
341
 
@@ -337,6 +351,21 @@ case "$COMMAND" in
337
351
  exit 1
338
352
  ;;
339
353
 
354
+ integrate:kiro)
355
+ shift
356
+
357
+ if [ -f "$ROOT/bundle/kiro.js" ]; then
358
+ exec node "$ROOT/bundle/kiro.js" install "$@"
359
+ fi
360
+
361
+ if [ -x "${TSX:-}" ] && [ -f "$ROOT/src/session/kiro/cli.ts" ]; then
362
+ exec "$TSX" "$ROOT/src/session/kiro/cli.ts" install "$@"
363
+ fi
364
+
365
+ echo "Kiro CLI integration installer not found."
366
+ exit 1
367
+ ;;
368
+
340
369
  session:codex-notify)
341
370
  shift
342
371
 
@@ -595,119 +624,52 @@ case "$COMMAND" in
595
624
  ;;
596
625
 
597
626
  help|--help|-h)
598
- cat <<'HELP'
599
- ToolNet Memory - Project Context & Memory System
600
-
601
- INTEGRATIONS:
602
- toolnet-memory integrate:detect Detect Agy, Codex and OpenCode
603
- toolnet-memory integrate:auto Detect and enable integrations
604
-
605
- PROJECT SETUP:
606
- toolnet-memory init [path] Initialize ToolNet for a project
607
- toolnet-memory init --project <path> Initialize an explicit project
608
- toolnet-memory init --json Print machine-readable result
609
- toolnet-memory init --no-integrate Skip automatic agent integration
610
-
611
- FAST CONTEXT (default):
612
- toolnet-memory Print fast project context (default)
613
- toolnet-memory context:print Print fast project context
614
- toolnet-memory context:sync Show context hash/size
615
- toolnet-memory profile:show Show .toolnet/profile.md
616
- toolnet-memory profile:sync Sync profile to GEMINI.md/AGENTS.md/CLAUDE.md
617
-
618
- MANUAL DEEP RECOVERY (do not run automatically):
619
- toolnet-memory context:refresh Rebuild deep startup brief cache
620
- toolnet-memory brief [--tokens N] Show full project brief
621
- toolnet-memory handoff:latest Show latest session handoff
622
- toolnet-memory session:agy-recover [--limit N] Recover Agy sessions
623
- toolnet-memory session:codex-recover [--limit N] Recover Codex sessions
624
- toolnet-memory session:opencode-recover [--limit N] Recover OpenCode sessions
625
-
626
- MEMORY OPERATIONS:
627
- toolnet-memory memory:review [--project PATH] [--session ID]
628
- toolnet-memory memory:reconcile [--project PATH]
629
-
630
- GUARD (Architecture & Rule Checking):
631
- toolnet-memory guard:check Check project rules and evidence
632
- toolnet-memory guard:check --file <path> Check if file violates rules
633
- toolnet-memory guard:check --command "<cmd>" Check if command is dangerous
634
- toolnet-memory guard:explain Show rules and evidence
635
- toolnet-memory guard:json Output guard result as JSON
636
-
637
- NOTE: Raw transcripts are not injected into prompts.
638
- Durable memory is promoted selectively based on importance scoring.
639
- Guard mode: warn (default), strict, or off (TOOLNET_GUARD_MODE)
640
-
641
- PROJECT MANUAL:
642
- toolnet-memory project:manual-init [--project PATH]
643
- toolnet-memory project:manual-show [--project PATH]
644
- toolnet-memory project:manual-sync [--project PATH]
645
-
646
- MEMORY AGENT QUERY:
647
- toolnet-memory ask "<question>" Ask concise project-memory questions
648
-
649
- WORK CONTINUITY:
650
- toolnet-memory work:status [--project PATH]
651
- toolnet-memory work:json [--project PATH]
652
- toolnet-memory work:reconcile [--project PATH]
653
-
654
- CODE INTELLIGENCE:
655
- toolnet-memory index
656
- toolnet-memory index:graph
657
- toolnet-memory incremental
658
- toolnet-memory semantic "query"
659
- toolnet-memory impact [file]
660
-
661
- SETUP & INTEGRATION:
662
- toolnet-memory --version
663
- toolnet-memory doctor
664
- toolnet-memory provider:list
665
- toolnet-memory provider:status
666
- toolnet-memory provider:test [llm|embedding]
667
- toolnet-memory setup
668
- toolnet-memory update
669
- toolnet-memory config get KEY
670
- toolnet-memory config set KEY VALUE
671
- toolnet-memory config open
672
- toolnet-memory integrate:auto
673
- toolnet-memory integrate:opencode
674
- toolnet-memory integrate:agy
675
- toolnet-memory integrate:codex
676
- toolnet-memory mcp
677
- toolnet-memory api
678
-
679
- OPTIONAL BACKGROUND SERVICE:
680
- toolnet-memory service:install Install/start daemon in PM2
681
- toolnet-memory service:start Start installed daemon
682
- toolnet-memory service:stop Stop daemon
683
- toolnet-memory service:restart Restart daemon after ToolNet updates
684
- toolnet-memory service:status Show daemon/cache status
685
- toolnet-memory service:remove Remove daemon from PM2
686
- toolnet-memory service Run daemon in foreground
687
-
688
- PRODUCTION:
689
- toolnet-memory production:certify Verify packaged production runtime
690
-
691
- SNAPSHOTS:
692
- toolnet-memory snapshot:list
693
- toolnet-memory snapshot:create "reason"
694
- toolnet-memory snapshot:restore <id>
695
- toolnet-memory recover
696
-
697
- SESSION SYNC (manual):
698
- toolnet-memory session:agy-sync <conversation-id> --transcript <path>
699
- toolnet-memory session:codex-sync <thread-id>
700
- toolnet-memory session:opencode-sync <session-id>
701
-
702
- IMPORTANT:
703
- Deep recovery commands (brief, handoff:latest, session:*-recover) are manual only.
704
- Do not run them automatically at startup. Use fast context commands instead.
705
- HELP
627
+ shift
628
+ run bundle/help-cli.js src/production/help-cli.ts "$@"
629
+ ;;
630
+
631
+ # Aliases
632
+ context)
633
+ shift
634
+ run bundle/context-runtime.js src/work-continuity/context-runtime-cli.ts print "$@"
635
+ ;;
636
+
637
+ work)
638
+ shift
639
+ if [ -f "$ROOT/bundle/work-continuity.js" ]; then
640
+ exec node "$ROOT/bundle/work-continuity.js" status "$@"
641
+ fi
642
+ exec "$TSX" "$ROOT/src/work-continuity/cli.ts" status "$@"
643
+ ;;
644
+
645
+ graph)
646
+ shift
647
+ run bundle/graph-cli.js src/production/graph-cli.ts "$@"
648
+ ;;
649
+
650
+ graph:ui)
651
+ shift
652
+ run bundle/graph-cli.js src/production/graph-cli.ts "$@"
653
+ ;;
654
+
655
+ model)
656
+ shift
657
+ run bundle/model-cli.js src/production/model-cli.ts "$@"
658
+ ;;
659
+
660
+ provider)
661
+ shift
662
+ run bundle/provider.js src/ai/provider-cli.ts status "$@"
706
663
  ;;
707
664
 
665
+ status)
666
+ shift
667
+ run bundle/status-cli.js src/production/status-cli.ts "$@"
668
+ ;;
669
+
670
+
708
671
  *)
709
- echo "Unknown command: $COMMAND"
710
- echo "Run: toolnet-memory help"
711
- exit 1
672
+ run bundle/help-cli.js src/production/help-cli.ts --unknown "$COMMAND"
712
673
  ;;
674
+
713
675
  esac
@@ -1,6 +1,6 @@
1
- import{existsSync as je}from"node:fs";import{homedir as Ae}from"node:os";import{join as Me}from"node:path";import{spawnSync as Fe}from"node:child_process";import{homedir as Te}from"node:os";import{join as p}from"node:path";function _(e={}){return p(e.home??Te(),".gemini")}function I(e={}){return p(_(e),"config")}function x(e={}){return p(I(e),"mcp_config.json")}function C(e={}){return p(I(e),"hooks.json")}function P(e={}){return p(_(e),"antigravity-cli")}function J(e="toolnet-memory",n={}){return p(P(n),"plugins",e)}function D(e={}){return[P(e),I(e)]}import{homedir as Ee}from"node:os";import{join as y}from"node:path";function m(e={}){let n=e.xdgConfigHome??process.env.XDG_CONFIG_HOME?.trim();return n?y(n,"opencode"):y(e.home??Ee(),".config","opencode")}function $(e={}){return y(m(e),"opencode.json")}function L(e={}){return y(m(e),"plugins")}function H(e={}){return y(m(e),"AGENTS.md")}import{homedir as B}from"node:os";import{join as k}from"node:path";function N(e={}){return k(e.home??B(),".claude")}function U(e={}){return k(N(e),"settings.json")}function Y(e={}){return k(e.home??B(),".claude.json")}function Re(e){return Fe("sh",["-lc",`command -v ${JSON.stringify(e)} >/dev/null 2>&1`],{stdio:"ignore"}).status===0}function S(e){let n=e.commandExists(e.command),t=e.configPaths.filter(s=>je(s)),o=t.length>0,i=[];n&&i.push(`command:${e.command}`);for(let s of t)i.push(`config:${s}`);return{agent:e.agent,detected:n||o,commandDetected:n,configDetected:o,evidence:i}}function q(e={}){let n=e.home??Ae(),t=e.commandExists??Re,o=e.codexHome??process.env.CODEX_HOME??Me(n,".codex");return[S({agent:"agy",command:"agy",commandExists:t,configPaths:D({home:n})}),S({agent:"opencode",command:"opencode",commandExists:t,configPaths:[m({home:n,xdgConfigHome:e.xdgConfigHome})]}),S({agent:"claude",command:"claude",commandExists:t,configPaths:[N({home:n})]}),S({agent:"codex",command:"codex",commandExists:t,configPaths:[o]})]}import{existsSync as w,mkdirSync as z,readFileSync as Q,renameSync as Ve,writeFileSync as ze}from"node:fs";import{dirname as Qe,join as b}from"node:path";import{existsSync as _e,mkdirSync as Pe,readFileSync as Je,renameSync as De,rmSync as $e,writeFileSync as Le}from"node:fs";import{dirname as He}from"node:path";function Be(e){return`'${e.replace(/'/g,"'\\''")}'`}function W(e={}){let n=e.hooksFile??C();Pe(He(n),{recursive:!0,mode:448});let t={};if(_e(n)){let r;try{r=JSON.parse(Je(n,"utf8"))}catch(c){throw new Error(`Invalid existing Agy hooks.json: ${c instanceof Error?c.message:String(c)}`)}if(typeof r!="object"||r===null||Array.isArray(r))throw new Error("Invalid existing Agy hooks.json: root must be a JSON object.");t=r}let o=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",i=`${Be(o)} session:agy-hook`;t["toolnet-memory"]={enabled:!0,PreToolUse:[{matcher:"view_file|list_dir|find_by_name|grep_search|run_command",hooks:[{type:"command",command:`${i} pre-tool`,timeout:5}]}],PreInvocation:[{type:"command",command:`${i} pre`,timeout:15}],PostInvocation:[{type:"command",command:`${i} post`,timeout:15}],Stop:[{type:"command",command:`${i} stop`,timeout:30}]};let s=`${n}.tmp-${process.pid}-${Date.now()}`;try{Le(s,JSON.stringify(t,null,2)+`
2
- `,{encoding:"utf8",mode:384}),De(s,n)}finally{$e(s,{force:!0})}return n}import{existsSync as Ue,mkdirSync as Ye,readFileSync as qe,renameSync as We,writeFileSync as Ge}from"node:fs";import{dirname as Xe}from"node:path";function h(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Ke(e,n){Ye(Xe(e),{recursive:!0,mode:448});let t=`${e}.tmp-${process.pid}-${Date.now()}`;Ge(t,`${JSON.stringify(n,null,2)}
3
- `,{encoding:"utf8",mode:384}),We(t,e)}function G(e){if(!Ue(e))return{};let n=qe(e,"utf8").trim();if(!n)return{};let t;try{t=JSON.parse(n)}catch(o){throw new Error(`Invalid existing Agy MCP config: ${o instanceof Error?o.message:String(o)}`)}if(!h(t))throw new Error("Invalid existing Agy MCP config: root must be a JSON object.");return t}function X(e,n){return h(e)?e.command===n&&Array.isArray(e.args)&&e.args.length===1&&e.args[0]==="mcp":!1}function K(e={}){let n=e.configFile??x(),t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=e.serverName??"toolnet-memory",i=G(n),s=i.mcpServers;if(s!==void 0&&!h(s))throw new Error("Invalid existing Agy MCP config: mcpServers must be an object.");let r=h(s)?{...s}:{},c=r[o];if(X(c,t))return{installed:!0,changed:!1,configFile:n,serverName:o,command:t,args:["mcp"]};r[o]={command:t,args:["mcp"]};let l={...i,mcpServers:r};Ke(n,l);let a=G(n).mcpServers;if(!h(a)||!X(a[o],t))throw new Error("Agy MCP configuration was written but verification failed.");return{installed:!0,changed:!0,configFile:n,serverName:o,command:t,args:["mcp"]}}var Ze=`# ToolNet Memory Continuity
1
+ import{existsSync as Ve}from"node:fs";import{homedir as ze}from"node:os";import{join as Qe}from"node:path";import{spawnSync as Ze}from"node:child_process";import{homedir as Ye}from"node:os";import{join as p}from"node:path";function H(e={}){return p(e.home??Ye(),".gemini")}function j(e={}){return p(H(e),"config")}function w(e={}){return p(j(e),"mcp_config.json")}function I(e={}){return p(j(e),"hooks.json")}function L(e={}){return p(H(e),"antigravity-cli")}function K(e="toolnet-memory",n={}){return p(L(n),"plugins",e)}function B(e={}){return[L(e),j(e)]}import{homedir as qe}from"node:os";import{join as h}from"node:path";function m(e={}){let n=e.xdgConfigHome??process.env.XDG_CONFIG_HOME?.trim();return n?h(n,"opencode"):h(e.home??qe(),".config","opencode")}function U(e={}){return h(m(e),"opencode.json")}function Y(e={}){return h(m(e),"plugins")}function q(e={}){return h(m(e),"AGENTS.md")}import{homedir as W}from"node:os";import{join as T}from"node:path";function A(e={}){return T(e.home??W(),".claude")}function G(e={}){return T(A(e),"settings.json")}function X(e={}){return T(e.home??W(),".claude.json")}import{homedir as We}from"node:os";import{join as O}from"node:path";function M(e={}){return e.kiroHome??process.env.KIRO_HOME??O(e.home??We(),".kiro")}function Ge(e={}){return O(M(e),"settings")}function V(e={}){return O(Ge(e),"mcp.json")}function Xe(e={}){return O(M(e),"hooks")}function z(e={}){return O(Xe(e),"toolnet-memory.json")}function Q(e={}){return[M(e)]}function et(e){return Ze("sh",["-lc",`command -v ${JSON.stringify(e)} >/dev/null 2>&1`],{stdio:"ignore"}).status===0}function v(e){let n=e.commandExists(e.command),t=e.configPaths.filter(i=>Ve(i)),o=t.length>0,s=[];n&&s.push(`command:${e.command}`);for(let i of t)s.push(`config:${i}`);return{agent:e.agent,detected:n||o,commandDetected:n,configDetected:o,evidence:s}}function Z(e={}){let n=e.home??ze(),t=e.commandExists??et,o=e.codexHome??process.env.CODEX_HOME??Qe(n,".codex");return[v({agent:"agy",command:"agy",commandExists:t,configPaths:B({home:n})}),v({agent:"opencode",command:"opencode",commandExists:t,configPaths:[m({home:n,xdgConfigHome:e.xdgConfigHome})]}),v({agent:"claude",command:"claude",commandExists:t,configPaths:[A({home:n})]}),v({agent:"kiro",command:"kiro-cli",commandExists:t,configPaths:Q({home:n,kiroHome:e.kiroHome})}),v({agent:"codex",command:"codex",commandExists:t,configPaths:[o]})]}import{existsSync as E,mkdirSync as ie,readFileSync as se,renameSync as yt,writeFileSync as ht}from"node:fs";import{dirname as Ot,join as N}from"node:path";import{existsSync as tt,mkdirSync as nt,readFileSync as ot,renameSync as rt,rmSync as it,writeFileSync as st}from"node:fs";import{dirname as ct}from"node:path";function at(e){return`'${e.replace(/'/g,"'\\''")}'`}function ee(e={}){let n=e.hooksFile??I();nt(ct(n),{recursive:!0,mode:448});let t={};if(tt(n)){let r;try{r=JSON.parse(ot(n,"utf8"))}catch(c){throw new Error(`Invalid existing Agy hooks.json: ${c instanceof Error?c.message:String(c)}`)}if(typeof r!="object"||r===null||Array.isArray(r))throw new Error("Invalid existing Agy hooks.json: root must be a JSON object.");t=r}let o=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",s=`${at(o)} session:agy-hook`;t["toolnet-memory"]={enabled:!0,PreToolUse:[{matcher:"view_file|list_dir|find_by_name|grep_search|run_command",hooks:[{type:"command",command:`${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=`${n}.tmp-${process.pid}-${Date.now()}`;try{st(i,JSON.stringify(t,null,2)+`
2
+ `,{encoding:"utf8",mode:384}),rt(i,n)}finally{it(i,{force:!0})}return n}import{existsSync as lt,mkdirSync as ut,readFileSync as dt,renameSync as mt,writeFileSync as gt}from"node:fs";import{dirname as pt}from"node:path";function x(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function ft(e,n){ut(pt(e),{recursive:!0,mode:448});let t=`${e}.tmp-${process.pid}-${Date.now()}`;gt(t,`${JSON.stringify(n,null,2)}
3
+ `,{encoding:"utf8",mode:384}),mt(t,e)}function te(e){if(!lt(e))return{};let n=dt(e,"utf8").trim();if(!n)return{};let t;try{t=JSON.parse(n)}catch(o){throw new Error(`Invalid existing Agy MCP config: ${o instanceof Error?o.message:String(o)}`)}if(!x(t))throw new Error("Invalid existing Agy MCP config: root must be a JSON object.");return t}function ne(e,n){return x(e)?e.command===n&&Array.isArray(e.args)&&e.args.length===1&&e.args[0]==="mcp":!1}function oe(e={}){let n=e.configFile??w(),t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=e.serverName??"toolnet-memory",s=te(n),i=s.mcpServers;if(i!==void 0&&!x(i))throw new Error("Invalid existing Agy MCP config: mcpServers must be an object.");let r=x(i)?{...i}:{},c=r[o];if(ne(c,t))return{installed:!0,changed:!1,configFile:n,serverName:o,command:t,args:["mcp"]};r[o]={command:t,args:["mcp"]};let l={...s,mcpServers:r};ft(n,l);let a=te(n).mcpServers;if(!x(a)||!ne(a[o],t))throw new Error("Agy MCP configuration was written but verification failed.");return{installed:!0,changed:!0,configFile:n,serverName:o,command:t,args:["mcp"]}}var vt=`# ToolNet Memory Continuity
4
4
 
5
5
  ToolNet Memory is the authoritative continuity layer for previous project work.
6
6
 
@@ -41,15 +41,15 @@ Use \`mode="ai"\` only when continuity is ambiguous or requires synthesis.
41
41
  Current repository evidence overrides stale memory after ToolNet has restored the working context.
42
42
 
43
43
  Do not ask the user to repeat context already available through ToolNet Memory.
44
- `;function Z(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function T(e,n){z(Qe(e),{recursive:!0});let t=`${e}.tmp-${process.pid}-${Date.now()}`;ze(t,n,{encoding:"utf8",mode:384}),Ve(t,e)}function V(e,n){w(e)&&Q(e,"utf8")===n||T(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],T(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"],T(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??J(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}),V(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
- `),K({configFile:s,binary:t,serverName:"toolnet-memory"}),W({hooksFile:r,binary:t}),V(c,`${Ze.trim()}
48
- `);let l=e.legacyMcpFile??x(),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`
44
+ `;function ce(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function F(e,n){ie(Ot(e),{recursive:!0});let t=`${e}.tmp-${process.pid}-${Date.now()}`;ht(t,n,{encoding:"utf8",mode:384}),yt(t,e)}function re(e,n){E(e)&&se(e,"utf8")===n||F(e,n)}function ae(e){if(!E(e))return{};let n=se(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(!ce(t))throw new Error(`Invalid legacy Antigravity config ${e}: root must be object`);return t}function xt(e,n){if(!E(e))return!1;let t=ae(e);if(!ce(t.mcpServers)||!Object.prototype.hasOwnProperty.call(t.mcpServers,n))return!1;let o={...t.mcpServers};return delete o[n],F(e,`${JSON.stringify({...t,mcpServers:o},null,2)}
45
+ `),!0}function bt(e){if(!E(e))return!1;let n=ae(e);if(!Object.prototype.hasOwnProperty.call(n,"toolnet-memory"))return!1;let t={...n};return delete t["toolnet-memory"],F(e,`${JSON.stringify(t,null,2)}
46
+ `),!0}function le(e={}){let n=e.pluginName??"toolnet-memory",t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=e.pluginRoot??K(n),s=N(o,"plugin.json"),i=N(o,"mcp_config.json"),r=N(o,"hooks.json"),c=N(o,"rules","toolnet-memory-continuity.md");ie(o,{recursive:!0,mode:448}),re(s,`${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
+ `),oe({configFile:i,binary:t,serverName:"toolnet-memory"}),ee({hooksFile:r,binary:t}),re(c,`${vt.trim()}
48
+ `);let l=e.legacyMcpFile??w(),u=e.legacyHooksFile??I(),a=[];return l!==i&&xt(l,"toolnet-memory")&&a.push(l),u!==r&&bt(u)&&a.push(u),{installed:!0,pluginRoot:o,files:[s,i,r,c],migratedLegacy:a}}import{existsSync as kt,mkdirSync as me,readFileSync as St,writeFileSync as ge}from"node:fs";import{join as wt}from"node:path";var Ct="memory_agent_ask";function ue(){return`
49
49
  [TOOLNET MEMORY AGENT]
50
50
 
51
51
  Tool available:
52
- - ${nt}
52
+ - ${Ct}
53
53
 
54
54
  Use it automatically BEFORE guessing when:
55
55
 
@@ -96,7 +96,7 @@ Rules:
96
96
  - After receiving the ToolNet answer, continue the task
97
97
  instead of asking the user to repeat known context.
98
98
  - If ToolNet says information is not recorded, say so.
99
- `.trim()}var oe="<!-- TOOLNET_MEMORY_BOOTSTRAP_START -->",E="<!-- TOOLNET_MEMORY_BOOTSTRAP_END -->";function it(){let e=H();re(m(),{recursive:!0});let n=`${oe}
99
+ `.trim()}var de="<!-- TOOLNET_MEMORY_BOOTSTRAP_START -->",R="<!-- TOOLNET_MEMORY_BOOTSTRAP_END -->";function It(){let e=q();me(m(),{recursive:!0});let n=`${de}
100
100
  ## ToolNet Memory
101
101
 
102
102
  ToolNet Memory is available for projects that already contain a valid \`.toolnet/project.json\`.
@@ -112,12 +112,12 @@ Rules:
112
112
  7. Current repository evidence has priority over stale memory when they conflict.
113
113
 
114
114
 
115
- ${ne()}
115
+ ${ue()}
116
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+=`
117
+ ${R}`,t=kt(e)?St(e,"utf8"):"",o=t.indexOf(de),s=t.indexOf(R);return o>=0&&s>=o?t=t.slice(0,o)+n+t.slice(s+R.length):(t=t.trimEnd(),t&&(t+=`
118
118
 
119
- `),t+=n),se(e,t.trimEnd()+`
120
- `,{encoding:"utf8",mode:384}),e}function ie(e={}){let n=e.directory??L();re(n,{recursive:!0}),it();let t=st(n,"toolnet-memory.js"),o=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",i=`
119
+ `),t+=n),ge(e,t.trimEnd()+`
120
+ `,{encoding:"utf8",mode:384}),e}function pe(e={}){let n=e.directory??Y();me(n,{recursive:!0}),It();let t=wt(n,"toolnet-memory.js"),o=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",s=`
121
121
  // Generated by ToolNet Memory.
122
122
  // OpenCode integration:
123
123
  // - project gated
@@ -1051,14 +1051,16 @@ export const ToolNetMemoryPlugin =
1051
1051
  },
1052
1052
  }
1053
1053
  }
1054
- `;return se(t,i.trimStart(),{encoding:"utf8",mode:384}),t}import{existsSync as le,mkdirSync as ct,readFileSync as at,renameSync as lt,writeFileSync as ut}from"node:fs";import{dirname as ue,join as dt}from"node:path";function f(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function mt(e,n){ct(ue(e),{recursive:!0});let t=`${e}.tmp-${process.pid}-${Date.now()}`;ut(t,`${JSON.stringify(n,null,2)}
1055
- `,{encoding:"utf8",mode:384}),lt(t,e)}function ce(e){if(!le(e))return{};let n=at(e,"utf8").trim();if(!n)return{};let t;try{t=JSON.parse(n)}catch(o){throw new Error(`Invalid existing OpenCode opencode.json: ${o instanceof Error?o.message:String(o)}`)}if(!f(t))throw new Error("Invalid existing OpenCode opencode.json: root must be a JSON object.");return t}function ae(e,n){if(!f(e))return!1;let t=e.command;return e.type==="local"&&e.enabled!==!1&&Array.isArray(t)&&t.length===2&&t[0]===n&&t[1]==="mcp"}function gt(e,n){let t=e.mcpServers;if(!f(t)||!Object.prototype.hasOwnProperty.call(t,n))return{root:e,changed:!1};let o={...t};return delete o[n],{root:{...e,mcpServers:o},changed:!0}}function de(e={}){let n=e.configFile??$(),t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=e.serverName??"toolnet-memory",i=dt(ue(n),"opencode.jsonc"),s=le(i)?i:void 0,r=ce(n),c=gt(r,o),l=c.root,u=l.mcp;if(u!==void 0&&!f(u))throw new Error("Invalid existing OpenCode config: mcp must be an object.");let a=f(u)?{...u}:{},g=a[o];if(ae(g,t)&&!c.changed)return{installed:!0,changed:!1,configFile:n,serverName:o,command:[t,"mcp"],preservedJsonc:s};a[o]={type:"local",command:[t,"mcp"],enabled:!0};let d={...l,mcp:a};mt(n,d);let R=ce(n);if(!f(R.mcp)||!ae(R.mcp[o],t))throw new Error("OpenCode MCP configuration was written but verification failed.");return{installed:!0,changed:!0,configFile:n,serverName:o,command:[t,"mcp"],preservedJsonc:s}}import{existsSync as pt,mkdirSync as me,readFileSync as ft,writeFileSync as ge}from"node:fs";import{homedir as pe}from"node:os";import{dirname as fe,join as j}from"node:path";function yt(e){let n=[],t=/"((?:\\.|[^"\\])*)"|'([^']*)'/g,o;for(;o=t.exec(e);){let i=o[1]??o[2]??"";try{n.push(o[1]!==void 0?JSON.parse(`"${i}"`):i)}catch{n.push(i)}}return n}function ye(e={}){let n=e.configFile??j(process.env.CODEX_HOME??j(pe(),".codex"),"config.toml"),t=e.previousFile??j(pe(),".config","toolnet-memory","codex-notify-previous.json");me(fe(n),{recursive:!0}),me(fe(t),{recursive:!0});let o=pt(n)?ft(n,"utf8"):"",i=e.binary??"toolnet-memory",s=`notify = [${JSON.stringify(i)}, "session:codex-notify"]`,r=o.split(`
1054
+ `;return ge(t,s.trimStart(),{encoding:"utf8",mode:384}),t}import{existsSync as he,mkdirSync as Nt,readFileSync as Et,renameSync as jt,writeFileSync as Tt}from"node:fs";import{dirname as Oe,join as At}from"node:path";function f(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Mt(e,n){Nt(Oe(e),{recursive:!0});let t=`${e}.tmp-${process.pid}-${Date.now()}`;Tt(t,`${JSON.stringify(n,null,2)}
1055
+ `,{encoding:"utf8",mode:384}),jt(t,e)}function fe(e){if(!he(e))return{};let n=Et(e,"utf8").trim();if(!n)return{};let t;try{t=JSON.parse(n)}catch(o){throw new Error(`Invalid existing OpenCode opencode.json: ${o instanceof Error?o.message:String(o)}`)}if(!f(t))throw new Error("Invalid existing OpenCode opencode.json: root must be a JSON object.");return t}function ye(e,n){if(!f(e))return!1;let t=e.command;return e.type==="local"&&e.enabled!==!1&&Array.isArray(t)&&t.length===2&&t[0]===n&&t[1]==="mcp"}function Ft(e,n){let t=e.mcpServers;if(!f(t)||!Object.prototype.hasOwnProperty.call(t,n))return{root:e,changed:!1};let o={...t};return delete o[n],{root:{...e,mcpServers:o},changed:!0}}function ve(e={}){let n=e.configFile??U(),t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=e.serverName??"toolnet-memory",s=At(Oe(n),"opencode.jsonc"),i=he(s)?s:void 0,r=fe(n),c=Ft(r,o),l=c.root,u=l.mcp;if(u!==void 0&&!f(u))throw new Error("Invalid existing OpenCode config: mcp must be an object.");let a=f(u)?{...u}:{},g=a[o];if(ye(g,t)&&!c.changed)return{installed:!0,changed:!1,configFile:n,serverName:o,command:[t,"mcp"],preservedJsonc:i};a[o]={type:"local",command:[t,"mcp"],enabled:!0};let d={...l,mcp:a};Mt(n,d);let D=fe(n);if(!f(D.mcp)||!ye(D.mcp[o],t))throw new Error("OpenCode MCP configuration was written but verification failed.");return{installed:!0,changed:!0,configFile:n,serverName:o,command:[t,"mcp"],preservedJsonc:i}}import{existsSync as Rt,mkdirSync as xe,readFileSync as Pt,writeFileSync as be}from"node:fs";import{homedir as Ce}from"node:os";import{dirname as ke,join as P}from"node:path";function _t(e){let n=[],t=/"((?:\\.|[^"\\])*)"|'([^']*)'/g,o;for(;o=t.exec(e);){let s=o[1]??o[2]??"";try{n.push(o[1]!==void 0?JSON.parse(`"${s}"`):s)}catch{n.push(s)}}return n}function Se(e={}){let n=e.configFile??P(process.env.CODEX_HOME??P(Ce(),".codex"),"config.toml"),t=e.previousFile??P(Ce(),".config","toolnet-memory","codex-notify-previous.json");xe(ke(n),{recursive:!0}),xe(ke(t),{recursive:!0});let o=Rt(n)?Pt(n,"utf8"):"",s=e.binary??"toolnet-memory",i=`notify = [${JSON.stringify(s)}, "session:codex-notify"]`,r=o.split(`
1056
1056
  `),c=r.findIndex(d=>/^\s*\[/.test(d));c<0&&(c=r.length);let l=-1,u=-1;for(let d=0;d<c;d+=1)if(/^\s*notify\s*=/.test(r[d])){if(l=d,u=d,r[d].includes("[")&&!r[d].includes("]"))for(;u+1<c&&(u+=1,!r[u].includes("]")););break}let a=[];if(l>=0){let d=r.slice(l,u+1).join(`
1057
- `);a=yt(d),r.splice(l,u-l+1,s)}else c=r.findIndex(d=>/^\s*\[/.test(d)),c<0&&(c=r.length),r.splice(c,0,s);let g=a.length>=2&&a[a.length-1]==="session:codex-notify";return a.length>0&&!g&&ge(t,JSON.stringify(a,null,2)+`
1057
+ `);a=_t(d),r.splice(l,u-l+1,i)}else c=r.findIndex(d=>/^\s*\[/.test(d)),c<0&&(c=r.length),r.splice(c,0,i);let g=a.length>=2&&a[a.length-1]==="session:codex-notify";return a.length>0&&!g&&be(t,JSON.stringify(a,null,2)+`
1058
1058
  `,{encoding:"utf8",mode:384}),o=r.join(`
1059
1059
  `),o.endsWith(`
1060
1060
  `)||(o+=`
1061
- `),ge(n,o,{encoding:"utf8",mode:384}),{configFile:n,previousFile:t,preservedPrevious:a.length>0&&!g}}import{existsSync as ht,mkdirSync as Ot,readFileSync as vt,writeFileSync as xt}from"node:fs";import{homedir as Ct}from"node:os";import{dirname as St,join as he}from"node:path";function bt(e){return`'${e.replace(/'/g,"'\\''")}'`}function Oe(e={}){let n=e.hooksFile??he(process.env.CODEX_HOME??he(Ct(),".codex"),"hooks.json");Ot(St(n),{recursive:!0});let t={};if(ht(n))try{t=JSON.parse(vt(n,"utf8"))}catch(c){throw new Error(`Invalid existing Codex hooks.json: ${c instanceof Error?c.message:String(c)}`)}let o=t.hooks&&typeof t.hooks=="object"&&!Array.isArray(t.hooks)?t.hooks:{};t.hooks=o;let s=(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 s.push({matcher:"startup|resume|clear|compact",hooks:[{type:"command",command:`${bt(r)} session:codex-context`,timeout:15,additionalContextLimit:1e3,statusMessage:"Loading ToolNet project continuity"}]}),o.SessionStart=s,xt(n,JSON.stringify(t,null,2)+`
1062
- `,{encoding:"utf8",mode:384}),n}import{spawnSync as wt}from"node:child_process";function A(e,n){return wt(e,n,{encoding:"utf8",stdio:["ignore","pipe","pipe"]})}function ve(e,n){let t=A(e,["mcp","get",n,"--json"]);if(t.status!==0||!t.stdout)return null;try{return JSON.parse(t.stdout)}catch{return null}}function xe(e,n){return e.enabled!==!1&&e.transport?.type==="stdio"&&e.transport?.command===n&&Array.isArray(e.transport?.args)&&e.transport?.args.length===1&&e.transport.args[0]==="mcp"}function Ce(e={}){let n=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",t=e.codexBinary??"codex",o=e.serverName??"toolnet-memory",i=ve(t,o);if(i&&xe(i,n))return{installed:!0,changed:!1,serverName:o,command:n,args:["mcp"]};if(i){let c=A(t,["mcp","remove",o]);if(c.status!==0)return{installed:!1,changed:!1,serverName:o,command:n,args:["mcp"],error:(c.stderr||c.stdout||"Unable to remove old ToolNet MCP configuration.").trim()}}let s=A(t,["mcp","add",o,"--",n,"mcp"]);if(s.status!==0)return{installed:!1,changed:!1,serverName:o,command:n,args:["mcp"],error:(s.stderr||s.stdout||"Unable to register ToolNet MCP.").trim()};let r=ve(t,o);return!r||!xe(r,n)?{installed:!1,changed:!0,serverName:o,command:n,args:["mcp"],error:"Codex accepted MCP registration but verification did not match expected ToolNet command."}:{installed:!0,changed:!0,serverName:o,command:n,args:["mcp"]}}import{existsSync as It,mkdirSync as kt,readFileSync as Nt,renameSync as Tt,rmSync as Et,writeFileSync as jt}from"node:fs";import{dirname as At}from"node:path";function O(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Mt(e){return/^[A-Za-z0-9_./:-]+$/u.test(e)?e:`'${e.replace(/'/gu,"'\\''")}'`}function Ft(e){if(!It(e))return{};let n;try{n=JSON.parse(Nt(e,"utf8"))}catch(t){throw new Error(`Invalid existing Claude settings.json: ${t instanceof Error?t.message:String(t)}`)}if(!O(n))throw new Error("Invalid existing Claude settings.json: root must be a JSON object.");return n}function M(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 n=[];for(let t of e){if(!O(t)){n.push(t);continue}let o=t.hooks;if(!Array.isArray(o)){n.push(t);continue}let i=o.filter(s=>{if(!O(s))return!0;let r=s.command;return!(typeof r=="string"&&r.includes("session:claude-hook"))});i.length!==0&&n.push({...t,hooks:i})}return n}function F(e){return{type:"command",command:e,timeout:10}}function Rt(e,n){kt(At(e),{recursive:!0,mode:448});let t=`${e}.toolnet-${process.pid}-${Date.now()}.tmp`;try{jt(t,JSON.stringify(n,null,2)+`
1063
- `,{encoding:"utf8",mode:384}),Tt(t,e)}finally{Et(t,{force:!0})}}function Se(e={}){let n=e.settingsFile??U(),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},g=JSON.stringify(o),d=JSON.stringify(a);return g===d?{settingsFile:n,changed:!1}:(Rt(n,a),{settingsFile:n,changed:!0})}import{existsSync as _t,mkdirSync as Pt,readFileSync as Jt,renameSync as Dt,rmSync as $t,writeFileSync as Lt}from"node:fs";import{dirname as Ht}from"node:path";function v(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function be(e){if(!_t(e))return{};let n;try{n=JSON.parse(Jt(e,"utf8"))}catch(t){throw new Error(`Invalid existing Claude Code config: ${t instanceof Error?t.message:String(t)}`)}if(!v(n))throw new Error("Invalid existing Claude Code config: root must be a JSON object.");return n}function we(e,n){if(!v(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){Pt(Ht(e),{recursive:!0});let t=`${e}.toolnet-${process.pid}-${Date.now()}.tmp`;try{Lt(t,JSON.stringify(n,null,2)+`
1064
- `,{encoding:"utf8",mode:384}),Dt(t,e)}finally{$t(t,{force:!0})}}function Ie(e={}){let n=e.stateFile??Y(),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&&!v(s))throw new Error("Invalid existing Claude Code config: mcpServers must be an object.");let r=v(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(!v(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 ke(e={}){let n=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",t=Se({binary:n,settingsFile:e.settingsFile}),o=Ie({binary:n,stateFile:e.stateFile});return{hooks:t,mcp:o,files:[t.settingsFile,o.configFile]}}function Ne(){return q()}function Ut(e={}){let n=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",t=[],o=Ne(),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=ke({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 Yt(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 Wt(){let e=process.argv.slice(2),n=e.includes("--all"),t=e.includes("--json");if(e.includes("--detect-only")){let s=Ne();if(t){console.log(JSON.stringify(s,null,2));return}Yt(s);return}let i=Ut({force:n});if(t){console.log(JSON.stringify(i,null,2));return}qt(i)}var Gt=process.argv[1]&&(process.argv[1].endsWith("auto-integrate.js")||process.argv[1].endsWith("auto-integrate.ts"));Gt&&Wt().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exitCode=1});export{Ne as detectAutoIntegrations,Ut as installAutoIntegrations};
1061
+ `),be(n,o,{encoding:"utf8",mode:384}),{configFile:n,previousFile:t,preservedPrevious:a.length>0&&!g}}import{existsSync as Jt,mkdirSync as $t,readFileSync as Dt,writeFileSync as Ht}from"node:fs";import{homedir as Lt}from"node:os";import{dirname as Kt,join as we}from"node:path";function Bt(e){return`'${e.replace(/'/g,"'\\''")}'`}function Ie(e={}){let n=e.hooksFile??we(process.env.CODEX_HOME??we(Lt(),".codex"),"hooks.json");$t(Kt(n),{recursive:!0});let t={};if(Jt(n))try{t=JSON.parse(Dt(n,"utf8"))}catch(c){throw new Error(`Invalid existing Codex hooks.json: ${c instanceof Error?c.message:String(c)}`)}let o=t.hooks&&typeof t.hooks=="object"&&!Array.isArray(t.hooks)?t.hooks:{};t.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:`${Bt(r)} session:codex-context`,timeout:15,additionalContextLimit:1e3,statusMessage:"Loading ToolNet project continuity"}]}),o.SessionStart=i,Ht(n,JSON.stringify(t,null,2)+`
1062
+ `,{encoding:"utf8",mode:384}),n}import{spawnSync as Ut}from"node:child_process";function _(e,n){return Ut(e,n,{encoding:"utf8",stdio:["ignore","pipe","pipe"]})}function Ne(e,n){let t=_(e,["mcp","get",n,"--json"]);if(t.status!==0||!t.stdout)return null;try{return JSON.parse(t.stdout)}catch{return null}}function Ee(e,n){return e.enabled!==!1&&e.transport?.type==="stdio"&&e.transport?.command===n&&Array.isArray(e.transport?.args)&&e.transport?.args.length===1&&e.transport.args[0]==="mcp"}function je(e={}){let n=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",t=e.codexBinary??"codex",o=e.serverName??"toolnet-memory",s=Ne(t,o);if(s&&Ee(s,n))return{installed:!0,changed:!1,serverName:o,command:n,args:["mcp"]};if(s){let c=_(t,["mcp","remove",o]);if(c.status!==0)return{installed:!1,changed:!1,serverName:o,command:n,args:["mcp"],error:(c.stderr||c.stdout||"Unable to remove old ToolNet MCP configuration.").trim()}}let i=_(t,["mcp","add",o,"--",n,"mcp"]);if(i.status!==0)return{installed:!1,changed:!1,serverName:o,command:n,args:["mcp"],error:(i.stderr||i.stdout||"Unable to register ToolNet MCP.").trim()};let r=Ne(t,o);return!r||!Ee(r,n)?{installed:!1,changed:!0,serverName:o,command:n,args:["mcp"],error:"Codex accepted MCP registration but verification did not match expected ToolNet command."}:{installed:!0,changed:!0,serverName:o,command:n,args:["mcp"]}}import{existsSync as Yt,mkdirSync as qt,readFileSync as Wt,renameSync as Gt,rmSync as Xt,writeFileSync as Vt}from"node:fs";import{dirname as zt}from"node:path";function b(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Qt(e){return/^[A-Za-z0-9_./:-]+$/u.test(e)?e:`'${e.replace(/'/gu,"'\\''")}'`}function Zt(e){if(!Yt(e))return{};let n;try{n=JSON.parse(Wt(e,"utf8"))}catch(t){throw new Error(`Invalid existing Claude settings.json: ${t instanceof Error?t.message:String(t)}`)}if(!b(n))throw new Error("Invalid existing Claude settings.json: root must be a JSON object.");return n}function J(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 n=[];for(let t of e){if(!b(t)){n.push(t);continue}let o=t.hooks;if(!Array.isArray(o)){n.push(t);continue}let s=o.filter(i=>{if(!b(i))return!0;let r=i.command;return!(typeof r=="string"&&r.includes("session:claude-hook"))});s.length!==0&&n.push({...t,hooks:s})}return n}function $(e){return{type:"command",command:e,timeout:10}}function en(e,n){qt(zt(e),{recursive:!0,mode:448});let t=`${e}.toolnet-${process.pid}-${Date.now()}.tmp`;try{Vt(t,JSON.stringify(n,null,2)+`
1063
+ `,{encoding:"utf8",mode:384}),Gt(t,e)}finally{Xt(t,{force:!0})}}function Te(e={}){let n=e.settingsFile??G(),t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=Zt(n),s=o.hooks;if(s!==void 0&&!b(s))throw new Error("Invalid existing Claude settings.json: hooks must be an object.");let i=b(s)?{...s}:{},r=`${Qt(t)} session:claude-hook`,c=J(i.SessionStart);c.push({matcher:"startup|resume|clear|compact",hooks:[$(r)]}),i.SessionStart=c;let l=J(i.PostToolUse);l.push({matcher:"Edit|Write",hooks:[$(r)]}),i.PostToolUse=l;let u=J(i.Stop);u.push({hooks:[$(r)]}),i.Stop=u;let a={...o,hooks:i},g=JSON.stringify(o),d=JSON.stringify(a);return g===d?{settingsFile:n,changed:!1}:(en(n,a),{settingsFile:n,changed:!0})}import{existsSync as tn,mkdirSync as nn,readFileSync as on,renameSync as rn,rmSync as sn,writeFileSync as cn}from"node:fs";import{dirname as an}from"node:path";function C(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Ae(e){if(!tn(e))return{};let n;try{n=JSON.parse(on(e,"utf8"))}catch(t){throw new Error(`Invalid existing Claude Code config: ${t instanceof Error?t.message:String(t)}`)}if(!C(n))throw new Error("Invalid existing Claude Code config: root must be a JSON object.");return n}function Me(e,n){if(!C(e))return!1;let t=e.args;return e.type==="stdio"&&e.command===n&&Array.isArray(t)&&t.length===1&&t[0]==="mcp"}function ln(e,n){nn(an(e),{recursive:!0});let t=`${e}.toolnet-${process.pid}-${Date.now()}.tmp`;try{cn(t,JSON.stringify(n,null,2)+`
1064
+ `,{encoding:"utf8",mode:384}),rn(t,e)}finally{sn(t,{force:!0})}}function Fe(e={}){let n=e.stateFile??X(),t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=e.serverName??"toolnet-memory",s=Ae(n),i=s.mcpServers;if(i!==void 0&&!C(i))throw new Error("Invalid existing Claude Code config: mcpServers must be an object.");let r=C(i)?{...i}:{},c=r[o];if(Me(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"]},ln(n,{...s,mcpServers:r});let a=Ae(n).mcpServers;if(!C(a)||!Me(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 Re(e={}){let n=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",t=Te({binary:n,settingsFile:e.settingsFile}),o=Fe({binary:n,stateFile:e.stateFile});return{hooks:t,mcp:o,files:[t.settingsFile,o.configFile]}}import{existsSync as un,mkdirSync as dn,readFileSync as mn,renameSync as gn,rmSync as pn,writeFileSync as fn}from"node:fs";import{dirname as yn}from"node:path";var y="ToolNet Memory - ";function Je(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function hn(e){return/^[A-Za-z0-9_./:-]+$/u.test(e)?e:`'${e.replace(/'/gu,"'\\''")}'`}function Pe(e){if(!un(e))return{};let n=mn(e,"utf8").trim();if(!n)return{};let t;try{t=JSON.parse(n)}catch(o){throw new Error(`Invalid existing Kiro hooks file: ${o instanceof Error?o.message:String(o)}`)}if(!Je(t))throw new Error("Invalid existing Kiro hooks file: root must be a JSON object.");return t}function _e(e){return Je(e)?typeof e.name=="string"&&e.name.startsWith(y):!1}function k(e){return{type:"command",command:e}}function On(e){return[{name:`${y}Session Start`,description:"Inject compact local ToolNet continuity and capture Kiro session activation.",trigger:"SessionStart",action:k(e),timeout:10,enabled:!0},{name:`${y}Prompt Continuity`,description:"Capture prompts and refresh ToolNet guidance only for resume/continue requests.",trigger:"UserPromptSubmit",action:k(e),timeout:10,enabled:!0},{name:`${y}Raw History Guard`,description:"Prevent Kiro from reconstructing continuity from raw ToolNet/agent session history.",trigger:"PreToolUse",matcher:"*",action:k(e),timeout:10,enabled:!0},{name:`${y}Tool Capture`,description:"Capture durable tool activity while filtering noisy read-only events.",trigger:"PostToolUse",matcher:"*",action:k(e),timeout:15,enabled:!0},{name:`${y}Final Flush`,description:"Flush pending Kiro WAL events when the assistant finishes a turn.",trigger:"Stop",action:k(e),timeout:30,enabled:!0}]}function vn(e,n){dn(yn(e),{recursive:!0,mode:448});let t=`${e}.toolnet-${process.pid}-${Date.now()}.tmp`;try{fn(t,JSON.stringify(n,null,2)+`
1065
+ `,{encoding:"utf8",mode:384}),gn(t,e)}finally{pn(t,{force:!0})}}function $e(e={}){let n=e.hooksFile??z(),t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=Pe(n);if(o.version!==void 0&&o.version!=="v1")throw new Error(`Unsupported existing Kiro hooks version: ${String(o.version)}`);let s=o.hooks;if(s!==void 0&&!Array.isArray(s))throw new Error("Invalid existing Kiro hooks file: hooks must be an array.");let i=Array.isArray(s)?s.filter(a=>!_e(a)):[],r=`${hn(t)} session:kiro-hook`,c=On(r),l={...o,version:"v1",hooks:[...i,...c]};if(JSON.stringify(o)===JSON.stringify(l))return{hooksFile:n,changed:!1,hookCount:c.length};vn(n,l);let u=Pe(n);if(u.version!=="v1"||!Array.isArray(u.hooks)||u.hooks.filter(_e).length!==c.length)throw new Error("Kiro hooks were written but verification failed.");return{hooksFile:n,changed:!0,hookCount:c.length}}import{existsSync as xn,mkdirSync as bn,readFileSync as Cn,renameSync as kn,rmSync as Sn,writeFileSync as wn}from"node:fs";import{dirname as In}from"node:path";function S(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function De(e){if(!xn(e))return{};let n=Cn(e,"utf8").trim();if(!n)return{};let t;try{t=JSON.parse(n)}catch(o){throw new Error(`Invalid existing Kiro MCP config: ${o instanceof Error?o.message:String(o)}`)}if(!S(t))throw new Error("Invalid existing Kiro MCP config: root must be a JSON object.");return t}function He(e,n){return S(e)?e.command===n&&Array.isArray(e.args)&&e.args.length===1&&e.args[0]==="mcp"&&e.disabled===!1:!1}function Nn(e,n){bn(In(e),{recursive:!0,mode:448});let t=`${e}.tmp-${process.pid}-${Date.now()}`;try{wn(t,`${JSON.stringify(n,null,2)}
1066
+ `,{encoding:"utf8",mode:384}),kn(t,e)}finally{Sn(t,{force:!0})}}function Le(e={}){let n=e.configFile??V(),t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=e.serverName??"toolnet-memory",s=De(n),i=s.mcpServers;if(i!==void 0&&!S(i))throw new Error("Invalid existing Kiro MCP config: mcpServers must be an object.");let r=S(i)?{...i}:{},c=r[o];if(He(c,t))return{installed:!0,changed:!1,configFile:n,serverName:o,command:t,args:["mcp"]};r[o]={command:t,args:["mcp"],disabled:!1};let l={...s,mcpServers:r};Nn(n,l);let a=De(n).mcpServers;if(!S(a)||!He(a[o],t))throw new Error("Kiro MCP configuration was written but verification failed.");return{installed:!0,changed:!0,configFile:n,serverName:o,command:t,args:["mcp"]}}function Ke(e={}){let n=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",t=Le({binary:n,configFile:e.configFile}),o=$e({binary:n,hooksFile:e.hooksFile});return{installed:t.installed,changed:t.changed||o.changed,mcp:t,hooks:o,files:[t.configFile,o.hooksFile]}}function Be(){return Z()}function En(e={}){let n=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",t=[],o=e.detections??Be(),s=new Map(o.map(i=>[i.agent,i.detected]));if(!(e.force===!0||s.get("agy")===!0))t.push({agent:"agy",detected:!1,installed:!1,targets:[]});else try{let r=le({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||s.get("opencode")===!0))t.push({agent:"opencode",detected:!1,installed:!1,targets:[]});else try{let r=pe({binary:n}),c=ve({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||s.get("claude")===!0))t.push({agent:"claude",detected:!1,installed:!1,targets:[]});else try{let r=Re({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||s.get("kiro")===!0))t.push({agent:"kiro",detected:!1,installed:!1,targets:[]});else try{let r=Ke({...e.kiro??{},binary:n});t.push({agent:"kiro",detected:!0,installed:!0,targets:[r.mcp.configFile,`mcp:${r.mcp.serverName}`,r.hooks.hooksFile]})}catch(r){t.push({agent:"kiro",detected:!0,installed:!1,targets:[],error:r instanceof Error?r.message:String(r)})}if(!(e.force===!0||s.get("codex")===!0))t.push({agent:"codex",detected:!1,installed:!1,targets:[]});else try{let r=Se({binary:n}),c=Ie({binary:n}),l=je({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 Ue(e){switch(e){case"agy":return"Agy / Antigravity";case"opencode":return"OpenCode";case"claude":return"Claude Code";case"kiro":return"Kiro CLI";case"codex":return"Codex"}}function jn(e){console.log(""),console.log("ToolNet Memory Integration Detection"),console.log("===================================="),console.log("");for(let n of e){let t=Ue(n.agent);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 Tn(e){console.log(""),console.log("ToolNet Memory AI Integrations"),console.log("=============================="),console.log("");for(let n of e){let t=Ue(n.agent);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 An(){let e=process.argv.slice(2),n=e.includes("--all"),t=e.includes("--json");if(e.includes("--detect-only")){let i=Be();if(t){console.log(JSON.stringify(i,null,2));return}jn(i);return}let s=En({force:n});if(t){console.log(JSON.stringify(s,null,2));return}Tn(s)}var Mn=process.argv[1]&&(process.argv[1].endsWith("auto-integrate.js")||process.argv[1].endsWith("auto-integrate.ts"));Mn&&An().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exitCode=1});export{Be as detectAutoIntegrations,En as installAutoIntegrations};
@@ -47,4 +47,4 @@ Answer the coding agent directly.
47
47
  `):e.trim(),l=C(e),d=y(n,p,{maxFacts:8,maxChars:1800}).facts.map(b=>({kind:b.kind,value:b.value,source:b.source})),g=St(a&&i?[...d,...i.focus]:d);return Et(n,{schema:"toolnet.memory-conversation.v1",version:1,projectId:n.id,updatedAt:new Date(r).toISOString(),lastIntent:l,focus:g}),{question:p,originalQuestion:e,usedPriorFocus:a,previousIntent:i?.lastIntent,currentIntent:l,priorFocus:i?.focus??[],focusCount:g.length}}var Dr={question:pe.string().min(2).max(4e3).describe("Question about previous project work, unfinished tasks, handoff, blockers, decisions, files or next actions."),mode:pe.enum(["ai","local"]).optional().describe("ai uses ToolNet Memory Agent with configured LLM fallback chain. local returns deterministic local memory only.")};async function fe(n,e){let t=me(n.project,e.question);if(e.mode==="local"){let o=H(t);if(o)return{answer:o.answer,mode:"local",usedAi:!1,source:o.source,intent:o.intent};let i=M(n.project,t.originalQuestion);return{answer:i.answer,mode:"local",usedAi:!1,source:i.source,intent:i.intent}}let r=await le(n.project,t.question);if(!r.usedAi){let o=H(t);if(o)return{answer:o.answer,mode:"ai",usedAi:!1,provider:r.provider,model:r.model}}return{answer:r.answer,mode:"ai",usedAi:r.usedAi,provider:r.provider,model:r.model}}var ge="RAW_TRANSCRIPT_MUST_NEVER_ENTER_CONTINUITY_CONTEXT_X1";function ye(n,e){he(n,JSON.stringify(e,null,2)+`
48
48
  `,"utf8")}function Rt(n,e,t,r,o,i,a){return{continuity:{schema:"toolnet.handoff.v2",version:2,project:{id:n,name:e},source:{agent:t,nativeSessionId:`${t}-x1-source`,sessionKey:`${t}:${t}-x1-source`,sequence:42,reason:"checkpoint"},capturedAt:"2026-08-13T05:30:00.000Z",current:{phase:{id:"x1",title:"Cross-Agent Continuity E2E",status:"in_progress"},task:{id:"x1-cross-agent",title:r,status:"in_progress"},file:o},completed:{phases:["A1 OpenCode Adapter","A2 Agy Adapter","A3 Claude Code Adapter"],tasks:["Build canonical handoff schema","Build intelligent memory retrieval"]},remaining:{phases:["X1 Cross-Agent Continuity E2E"],tasks:[r],todos:[i]},nextAction:a,blockers:[],decisions:["Canonical handoff must override stale session state","Never replay raw transcripts during continuity recovery"],files:{current:o,recent:[o]},tests:{status:"passing",recent:["A1-A3 adapter tests passed"]},attention:[],progress:{phasesTotal:3,phasesCompleted:2,tasksTotal:4,tasksCompleted:3,blocked:0},stateDigest:"a".repeat(64)}}}async function L(n,e){return fe({project:n},{question:e,mode:"local"})}async function Ft(n,e){let t=Tt(k(Ot(),`toolnet-x1-${n}-${e}-`));try{let r=`x1-${n}-${e}`,o=`x1-${n}-to-${e}`,i={id:r,name:o,rootPath:t,createdAt:"2026-08-13T05:30:00.000Z",updatedAt:"2026-08-13T05:30:00.000Z",graphVersion:1,memoryVersion:1},a=k(t,".toolnet","work"),p=k(t,".toolnet","context"),l=k(t,".toolnet","sessions",n,"raw-session-x1");G(a,{recursive:!0}),G(p,{recursive:!0}),G(l,{recursive:!0});let f=`X1 ${n} to ${e} canonical continuity`,d=`src/x1/${n}-to-${e}.ts`,g=`Implement ${e} continuation step`,b=`Continue ${f} in ${d}`;ye(k(a,"handoff-latest.json"),Rt(r,o,n,f,d,g,b)),ye(k(p,"session-origin.json"),{version:1,projectId:r,agent:"stale-agent",nativeSessionId:"stale-session",updatedAt:"2026-01-01T00:00:00.000Z",currentTask:"STALE TASK MUST NOT WIN",currentPhase:"STALE PHASE",lastTouchedFile:"src/stale/old.ts",latestNextAction:"STALE NEXT ACTION"}),he(k(l,"events.jsonl"),JSON.stringify({text:ge})+`
49
49
  `,"utf8");let be=y(i,"T\xF4i ph\u1EA3i l\xE0m g\xEC ti\u1EBFp?",{maxFacts:12,maxChars:3200}),D=await L(i,"Task hi\u1EC7n t\u1EA1i \u0111ang l\xE0m l\xE0 g\xEC?"),K=await L(i,"File hi\u1EC7n t\u1EA1i l\xE0 file n\xE0o?"),$=await L(i,"TODO c\xF2n l\u1EA1i l\xE0 g\xEC?"),U=await L(i,"T\xF4i ph\u1EA3i l\xE0m g\xEC ti\u1EBFp?"),ve=[D.answer,K.answer,$.answer,U.answer].join(`
50
- `),j=[D,K,$,U],Y={canonicalHandoffPreferred:be.facts[0]?.source==="handoff",taskRecovered:D.answer.includes(f),fileRecovered:K.answer.includes(d),todoRecovered:$.answer.includes(g),nextActionRecovered:U.answer.includes(b),localModeOnly:j.every(A=>A.mode==="local"),noAiRequired:j.every(A=>A.usedAi===!1),rawTranscriptIsolated:!ve.includes(ge),compact:j.every(A=>A.answer.length<1600)};return{from:n,to:e,passed:Object.values(Y).every(Boolean),checks:Y}}finally{Pt(t,{recursive:!0,force:!0})}}async function ke(){let n=[["agy","codex"],["codex","opencode"],["opencode","claude"],["claude","agy"]],e=[];for(let[r,o]of n)e.push(await Ft(r,o));let t=e.filter(r=>r.passed).length;return{passed:t===e.length,total:e.length,passedCount:t,cases:e}}var m={reset:"\x1B[0m",green:"\x1B[32m",red:"\x1B[31m",cyan:"\x1B[36m",gray:"\x1B[90m",white:"\x1B[97m"};function Nt(n){return n?`${m.green}\u2713${m.reset}`:`${m.red}\u2717${m.reset}`}function Lt(n){let e=`${n.from.padEnd(8)} \u2192 ${n.to.padEnd(8)}`;if(console.log(`\u2502 ${Nt(n.passed)} ${e}`),!n.passed)for(let[t,r]of Object.entries(n.checks))r||console.log(`\u2502 ${m.red}\u2717${m.reset} ${t}`)}async function Dt(){console.log(""),console.log(`${m.cyan}\u25C7${m.reset} ${m.white}ToolNet Cross-Agent Continuity Certification${m.reset}`),console.log(`${m.gray}\u2502${m.reset}`);let n=await ke();for(let e of n.cases)Lt(e);if(console.log(`${m.gray}\u2502${m.reset}`),n.passed){console.log(`${m.green}\u25C6 PASS${m.reset} ${n.passedCount}/${n.total} cross-agent transitions`),console.log(`${m.green}\u2713${m.reset} canonical handoff`),console.log(`${m.green}\u2713${m.reset} memory_agent_ask`),console.log(`${m.green}\u2713${m.reset} task / file / TODO / next action`),console.log(`${m.green}\u2713${m.reset} raw transcripts isolated`),console.log(`${m.green}\u2713${m.reset} AI not required for direct continuity`);return}console.log(`${m.red}\u25C6 FAIL${m.reset} ${n.passedCount}/${n.total} cross-agent transitions`),process.exitCode=1}Dt().catch(n=>{console.error(n instanceof Error?n.message:String(n)),process.exitCode=1});
50
+ `),j=[D,K,$,U],Y={canonicalHandoffPreferred:be.facts[0]?.source==="handoff",taskRecovered:D.answer.includes(f),fileRecovered:K.answer.includes(d),todoRecovered:$.answer.includes(g),nextActionRecovered:U.answer.includes(b),localModeOnly:j.every(A=>A.mode==="local"),noAiRequired:j.every(A=>A.usedAi===!1),rawTranscriptIsolated:!ve.includes(ge),compact:j.every(A=>A.answer.length<1600)};return{from:n,to:e,passed:Object.values(Y).every(Boolean),checks:Y}}finally{Pt(t,{recursive:!0,force:!0})}}async function ke(){let n=[["agy","codex"],["codex","opencode"],["opencode","claude"],["claude","kiro"],["kiro","agy"]],e=[];for(let[r,o]of n)e.push(await Ft(r,o));let t=e.filter(r=>r.passed).length;return{passed:t===e.length,total:e.length,passedCount:t,cases:e}}var m={reset:"\x1B[0m",green:"\x1B[32m",red:"\x1B[31m",cyan:"\x1B[36m",gray:"\x1B[90m",white:"\x1B[97m"};function Nt(n){return n?`${m.green}\u2713${m.reset}`:`${m.red}\u2717${m.reset}`}function Lt(n){let e=`${n.from.padEnd(8)} \u2192 ${n.to.padEnd(8)}`;if(console.log(`\u2502 ${Nt(n.passed)} ${e}`),!n.passed)for(let[t,r]of Object.entries(n.checks))r||console.log(`\u2502 ${m.red}\u2717${m.reset} ${t}`)}async function Dt(){console.log(""),console.log(`${m.cyan}\u25C7${m.reset} ${m.white}ToolNet Cross-Agent Continuity Certification${m.reset}`),console.log(`${m.gray}\u2502${m.reset}`);let n=await ke();for(let e of n.cases)Lt(e);if(console.log(`${m.gray}\u2502${m.reset}`),n.passed){console.log(`${m.green}\u25C6 PASS${m.reset} ${n.passedCount}/${n.total} cross-agent transitions`),console.log(`${m.green}\u2713${m.reset} canonical handoff`),console.log(`${m.green}\u2713${m.reset} memory_agent_ask`),console.log(`${m.green}\u2713${m.reset} task / file / TODO / next action`),console.log(`${m.green}\u2713${m.reset} raw transcripts isolated`),console.log(`${m.green}\u2713${m.reset} AI not required for direct continuity`);return}console.log(`${m.red}\u25C6 FAIL${m.reset} ${n.passedCount}/${n.total} cross-agent transitions`),process.exitCode=1}Dt().catch(n=>{console.error(n instanceof Error?n.message:String(n)),process.exitCode=1});
@@ -0,0 +1 @@
1
+ import{spawn as U}from"node:child_process";import{dirname as T,resolve as v}from"node:path";import{fileURLToPath as E}from"node:url";var h={spinner:["\xB7","\u2722","\u2733","\u2736","\u273B","\u273D"],barFilled:"\u2588",barEmpty:"\u2591",phaseDone:"\u25C6",phaseActive:"\u25C7",rail:"\u2502",branch:"\u251C",corner:"\u2514",dash:"\u2014"},C={spinner:[".","*","+","x","*","+"],barFilled:"#",barEmpty:"-",phaseDone:"[OK]",phaseActive:"[>>]",rail:"|",branch:"|",corner:"\\",dash:"-"};function b(){if(process.env.TOOLNET_ASCII==="1")return!1;if(process.env.TOOLNET_UNICODE==="1")return!0;if(process.platform==="win32"){let n=process.env.TERM_PROGRAM,r=process.env.WT_SESSION,e=process.env.VSCODE_PID,t=process.env.ConEmuPID;return!!(r||e||t||n==="vscode")}return process.env.TERM!=="linux"}var u;function a(){return u||(u=b()?h:C),u}var i={reset:"\x1B[0m",green:"\x1B[32m",cyan:"\x1B[36m",red:"\x1B[31m",yellow:"\x1B[33m",amber:"\x1B[38;5;214m",white:"\x1B[97m",dim:"\x1B[2m",bold:"\x1B[1m"};function x(n){return n.noColor||process.env.NO_COLOR?!1:n.tty??process.stdout.isTTY??!1}function l(n,r,e){return x(e)?`${r}${n}${i.reset}`:n}function c(n,r){return l(n,i.dim,r)}function g(n,r){return l(n,i.cyan,r)}function G(n,r){return l(n,i.green,r)}function y(n,r){return l(n,i.red,r)}function m(n,r,e){let t=a(),s=r?` ${c(`v${r}`,e)}`:"";return`${g(t.phaseDone,e)} ${n}${s}`}function d(n){let r=a();return G(r.phaseDone,n)}function O(n){return y("\u2717",n)}function S(n){return{tty:n.tty,noColor:n.noColor}}async function R(n){let r=S(n);console.log(""),console.log(m("Code Graph",void 0,r)),console.log("");let e=T(E(import.meta.url)),t=v(e,"graph-ui.js"),s=process.env.TOOLNET_GRAPH_PORT??"9749",f=process.env.TOOLNET_GRAPH_HOST??"127.0.0.1";console.log(c(" Starting visualization server...",r)),console.log("");let p=U(process.execPath,[t],{stdio:"inherit",env:{...process.env,TOOLNET_GRAPH_PORT:s,TOOLNET_GRAPH_HOST:f}});await new Promise(o=>setTimeout(o,1e3)),console.log(d(r)+" Graph UI started"),console.log(""),console.log(` ${g(`http://${f}:${s}`,r)}`),console.log(""),console.log(c(" Press Ctrl+C to stop",r)),console.log(""),process.on("SIGINT",()=>{p.kill("SIGTERM"),process.exit(0)}),process.on("SIGTERM",()=>{p.kill("SIGTERM"),process.exit(0)}),p.on("exit",o=>{o!==0&&o!==null&&(console.log(""),console.log(O(r)+" Graph UI stopped unexpectedly"),console.log(""),process.exit(o))})}async function $(){let n=process.argv.slice(2),r={tty:process.stdout.isTTY,noColor:process.env.NO_COLOR!==void 0||n.includes("--no-color")};await R(r)}$().catch(n=>{console.error(""),console.error(n instanceof Error?n.message:String(n)),console.error(""),process.exitCode=1});
@@ -0,0 +1,3 @@
1
+ import"dotenv/config";import{createReadStream as ue}from"node:fs";import{createServer as ut}from"node:http";import{dirname as mt,resolve as Q}from"node:path";import{fileURLToPath as ft}from"node:url";import{existsSync as ge,readFileSync as he}from"node:fs";import{homedir as ye}from"node:os";import{join as Se}from"node:path";function be(n){let t=n.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 xe(){let n=process.env.TOOLNET_GLOBAL_ENV??Se(ye(),".config","toolnet-memory",".env");if(!ge(n))return;let t=he(n,"utf8");for(let e of t.split(/\r?\n/)){let s=e.trim();if(!s||s.startsWith("#"))continue;s.startsWith("export ")&&(s=s.slice(7));let a=s.indexOf("=");if(a<=0)continue;let c=s.slice(0,a).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(c)&&process.env[c]===void 0&&(process.env[c]=be(s.slice(a+1)))}}xe();function N(n,t){return n===void 0?t:["1","true","yes","on"].includes(n.toLowerCase())}function L(n,t){if(!n)return t;let e=Number(n);return Number.isFinite(e)?e:t}function te(){return{memory:{autoCapture:N(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:N(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:N(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:N(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:L(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:L(process.env.MEMORY_RERANK_TOP,10),finalContext:L(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:L(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",r2:{accountId:process.env.R2_ACCOUNT_ID,bucket:process.env.R2_BUCKET,accessKeyId:process.env.R2_ACCESS_KEY_ID,secretAccessKey:process.env.R2_SECRET_ACCESS_KEY},s3:{endpoint:process.env.S3_ENDPOINT,region:process.env.S3_REGION,bucket:process.env.S3_BUCKET,accessKeyId:process.env.S3_ACCESS_KEY_ID,secretAccessKey:process.env.S3_SECRET_ACCESS_KEY,forcePathStyle:N(process.env.S3_FORCE_PATH_STYLE,!1)},huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:L(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as Ce}from"node:crypto";import{existsSync as J,mkdirSync as we,readFileSync as Ee,renameSync as ve,writeFileSync as Pe}from"node:fs";import{basename as ke,dirname as K,join as B,parse as oe,resolve as I}from"node:path";var ne=".toolnet",je="project.json";function Re(n){return Ce("sha256").update(n).digest("hex").slice(0,16)}function Y(n){return B(n,ne,je)}function Te(n){return J(Y(n))}function Ae(n,t){let e=I(n),s=oe(e).root;for(;;){if(Te(e))return e;if(e===s||t&&e===I(t))break;let a=K(e);if(a===e)break;e=a}return null}function Me(n){let t=I(n),e=oe(t).root,s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(s.some(c=>J(B(t,c))))return t;if(t===e)break;let a=K(t);if(a===t)break;t=a}return I(n)}function Ne(n){let t;try{t=JSON.parse(Ee(n,"utf8"))}catch(a){throw new Error(`Invalid ToolNet project manifest: ${n}: ${a instanceof Error?a.message:String(a)}`)}if(!t||typeof t!="object")throw new Error(`Invalid ToolNet project manifest: ${n}`);let e=t;if(typeof e.id!="string"||!e.id.trim())throw new Error(`ToolNet project manifest is missing id: ${n}`);if(typeof e.name!="string"||!e.name.trim())throw new Error(`ToolNet project manifest is missing name: ${n}`);let s=new Date().toISOString();return{version:1,id:e.id,name:e.name,remote:typeof e.remote=="string"&&e.remote.trim()?e.remote:e.name,rootPath:typeof e.rootPath=="string"?e.rootPath:K(K(n)),createdAt:typeof e.createdAt=="string"?e.createdAt:s,updatedAt:typeof e.updatedAt=="string"?e.updatedAt:s,graphVersion:typeof e.graphVersion=="number"?e.graphVersion:0,memoryVersion:typeof e.memoryVersion=="number"?e.memoryVersion:0,metadata:e.metadata&&typeof e.metadata=="object"?e.metadata:void 0}}function re(n,t){let e=B(n,ne);we(e,{recursive:!0});let s=Y(n),a=`${s}.tmp-${process.pid}`;Pe(a,JSON.stringify(t,null,2)+`
3
+ `,{encoding:"utf8",mode:384}),ve(a,s)}function se(n,t){return{id:n.id,name:n.name,remote:n.remote,rootPath:t,createdAt:n.createdAt,updatedAt:n.updatedAt,graphVersion:n.graphVersion,memoryVersion:n.memoryVersion,metadata:n.metadata}}var z=class{detect(t=process.cwd()){let e=I(t),s=Me(e),c=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(v=>J(B(s,v))),p=Ae(e,c?s:void 0);if(p){let v=Y(p),j=Ne(v);return j.rootPath!==p&&(j.rootPath=p,j.updatedAt=new Date().toISOString(),re(p,j)),se(j,p)}let k=new Date().toISOString(),b=ke(s),E={version:1,id:Re(s),name:b,remote:b,rootPath:s,createdAt:k,updatedAt:k,graphVersion:0,memoryVersion:0};return re(s,E),se(E,s)}};import*as w from"typescript";var A=class{symbols=new Map;edges=new Map;addSymbol(t){this.symbols.set(t.id,t)}addEdge(t){this.edges.set(t.id,t)}getSymbol(t){return this.symbols.get(t)}findByName(t,e){return[...this.symbols.values()].filter(s=>s.projectId===t&&(s.name===e||s.qualifiedName===e))}allSymbols(t){return[...this.symbols.values()].filter(e=>e.projectId===t)}allEdges(t){return[...this.edges.values()].filter(e=>e.projectId===t)}import(t,e){for(let s of t)this.addSymbol(s);for(let s of e)this.addEdge(s)}clearProject(t){for(let[e,s]of this.symbols)s.projectId===t&&this.symbols.delete(e);for(let[e,s]of this.edges)s.projectId===t&&this.edges.delete(e)}};import{execFile as $e}from"node:child_process";import{promisify as Fe}from"node:util";var kr=Fe($e);import*as P from"typescript";import*as m from"typescript";var Gs=new Set([m.SyntaxKind.EqualsToken,m.SyntaxKind.PlusEqualsToken,m.SyntaxKind.MinusEqualsToken,m.SyntaxKind.AsteriskEqualsToken,m.SyntaxKind.SlashEqualsToken,m.SyntaxKind.PercentEqualsToken,m.SyntaxKind.QuestionQuestionEqualsToken,m.SyntaxKind.AmpersandAmpersandEqualsToken,m.SyntaxKind.BarBarEqualsToken]);var H=class{constructor(t){this.graph=t}graph;build(t,e,s,a){let c=this.graph.allSymbols(t),p=this.graph.allEdges(t),k=new Map(e?.layers.map(o=>[o.filePath,o.layer])??[]),b=new Map;for(let o of e?.clusters??[])for(let r of o.files)b.set(r,{id:o.id,label:o.label});let E=new Map(e?.hotspots.map(o=>[o.filePath,o.score])??[]),v=new Map(s?.deadCode.map(o=>[o.symbolId,{confidence:o.confidence,score:o.score}])??[]),j=new Map,M=new Map;for(let o of p)M.set(o.from,(M.get(o.from)??0)+1),j.set(o.to,(j.get(o.to)??0)+1);let R=[];for(let o=0;o<c.length;o++){let r=c[o],l=b.get(r.filePath);R.push({id:r.id,name:r.name,qualifiedName:r.qualifiedName,type:r.type,filePath:r.filePath,layer:k.get(r.filePath),cluster:l?.id,clusterLabel:l?.label,hotspotScore:E.get(r.filePath),deadCode:v.get(r.id),incoming:j.get(r.id)??0,outgoing:M.get(r.id)??0}),((o+1)%100===0||o===c.length-1)&&a?.({current:o+1,total:c.length,phase:"nodes"})}let F=new Set(R.map(o=>o.id)),T=p.filter(o=>F.has(o.from)&&F.has(o.to)),D=[];for(let o=0;o<T.length;o++){let r=T[o];D.push({source:r.from,target:r.to,type:r.type,confidence:typeof r.metadata?.confidence=="string"?r.metadata.confidence:void 0,resolver:typeof r.metadata?.resolver=="string"?r.metadata.resolver:void 0}),((o+1)%100===0||o===T.length-1)&&a?.({current:o+1,total:T.length,phase:"links"})}return{version:1,projectId:t,generatedAt:new Date().toISOString(),summary:{nodes:R.length,links:D.length,files:new Set(R.map(o=>o.filePath)).size,clusters:new Set(R.map(o=>o.cluster).filter(Boolean)).size},nodes:R,links:D}}};import{homedir as ct}from"node:os";import{join as lt}from"node:path";import{DeleteObjectCommand as Ge,GetObjectCommand as Ke,HeadObjectCommand as ze,ListObjectsV2Command as Be,PutObjectCommand as He,S3Client as Ve}from"@aws-sdk/client-s3";import{getSignedUrl as Ue}from"@aws-sdk/s3-request-presigner";var V=class{name="huggingface";client;bucket;constructor(t){this.bucket=t.bucket,this.client=new Ve({region:"us-east-1",endpoint:`https://s3.hf.co/${t.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}})}async put(t,e,s="application/octet-stream"){let a=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new He({Bucket:this.bucket,Key:t,Body:a,ContentType:s}))}async get(t){let e=await Ue(this.client,new Ke({Bucket:this.bucket,Key:t}),{expiresIn:60}),s=await fetch(e,{redirect:"follow"});if(s.status===404)return null;if(!s.ok)throw new Error(`HF download failed: ${s.status} ${s.statusText}`);return new Uint8Array(await s.arrayBuffer())}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await this.client.send(new ze({Bucket:this.bucket,Key:t})),!0}catch(e){if(typeof e=="object"&&e!==null&&"$metadata"in e&&e.$metadata?.httpStatusCode===404)return!1;throw e}}async delete(t){await this.client.send(new Ge({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],s;do{let a=await this.client.send(new Be({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:s}));for(let c of a.Contents??[])c.Key&&e.push({key:c.Key,size:c.Size,updatedAt:c.LastModified?.toISOString()});s=a.IsTruncated?a.NextContinuationToken:void 0}while(s);return e}};import{access as ie,mkdir as We,readFile as Je,readdir as Ye,rm as qe,stat as ae,writeFile as Qe}from"node:fs/promises";import{dirname as Xe,join as Ze,relative as ce,resolve as et}from"node:path";var O=class{constructor(t){this.root=t}root;name="local";path(t){let e=t.replace(/^\/+/,"");return et(this.root,e)}async put(t,e){let s=this.path(t);await We(Xe(s),{recursive:!0}),await Qe(s,e)}async get(t){try{return await Je(this.path(t))}catch(e){if(typeof e=="object"&&e!==null&&"code"in e&&e.code==="ENOENT")return null;throw e}}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await ie(this.path(t)),!0}catch{return!1}}async delete(t){await qe(this.path(t),{force:!0})}async list(t=""){let e=this.path(t),s=[];try{await ie(e)}catch{return s}let a=async p=>{let k=await Ye(p,{withFileTypes:!0});for(let b of k){let E=Ze(p,b.name);if(b.isDirectory()){await a(E);continue}let v=await ae(E);s.push({key:ce(this.root,E),size:v.size,updatedAt:v.mtime.toISOString()})}},c=await ae(e);return c.isDirectory()?await a(e):s.push({key:ce(this.root,e),size:c.size,updatedAt:c.mtime.toISOString()}),s}};import{DeleteObjectCommand as tt,GetObjectCommand as rt,HeadObjectCommand as st,ListObjectsV2Command as ot,PutObjectCommand as nt,S3Client as it}from"@aws-sdk/client-s3";import{getSignedUrl as at}from"@aws-sdk/s3-request-presigner";var _=class{name;client;bucket;constructor(t){this.name=t.name??"s3",this.bucket=t.bucket,this.client=new it({region:t.region??"us-east-1",endpoint:t.endpoint||void 0,forcePathStyle:t.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}})}async put(t,e,s="application/octet-stream"){let a=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new nt({Bucket:this.bucket,Key:t,Body:a,ContentType:s}))}async get(t){let e=await at(this.client,new rt({Bucket:this.bucket,Key:t}),{expiresIn:60}),s=await fetch(e,{redirect:"follow"});if(s.status===404)return null;if(!s.ok)throw new Error(`${this.name} download failed: ${s.status} ${s.statusText}`);return new Uint8Array(await s.arrayBuffer())}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await this.client.send(new st({Bucket:this.bucket,Key:t})),!0}catch(e){if(typeof e=="object"&&e!==null&&"$metadata"in e&&e.$metadata?.httpStatusCode===404)return!1;throw e}}async delete(t){await this.client.send(new tt({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],s;do{let a=await this.client.send(new ot({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:s}));for(let c of a.Contents??[])c.Key&&e.push({key:c.Key,size:c.Size,updatedAt:c.LastModified?.toISOString()});s=a.IsTruncated?a.NextContinuationToken:void 0}while(s);return e}};function q(n,t){return console.warn(t),new O(n)}function le(n){let t=n.localRoot??lt(ct(),".toolnet-memory","storage");if(n.provider==="r2"){let e=n.r2;return e?.accountId&&e.bucket&&e.accessKeyId&&e.secretAccessKey?new _({name:"r2",endpoint:`https://${e.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:e.bucket,forcePathStyle:!0,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):q(t,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(n.provider==="s3"){let e=n.s3;return e?.bucket&&e.accessKeyId&&e.secretAccessKey?new _({name:"s3",endpoint:e.endpoint,region:e.region??"us-east-1",bucket:e.bucket,forcePathStyle:e.forcePathStyle??!1,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):q(t,"[storage] S3 credentials missing. Using local fallback.")}if(n.provider==="huggingface"){let e=n.huggingface;return e?.namespace&&e.bucket&&e.accessKeyId&&e.secretAccessKey?new V({namespace:e.namespace,bucket:e.bucket,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):q(t,"[storage] Hugging Face credentials missing. Using local fallback.")}return new O(t)}function dt(n){return new Promise(t=>setTimeout(t,n))}async function de(n,t={}){let e=Math.max(1,t.attempts??3),s=t.baseDelayMs??150,a=t.maxDelayMs??2e3,c;for(let p=1;p<=e;p++)try{return await n()}catch(k){if(c=k,p>=e)break;let b=Math.min(a,s*2**(p-1)),E=Math.floor(Math.random()*Math.max(1,b*.2));await dt(b+E)}throw c}var pt=new Set(["put","get","getText","delete","list"]);function pe(n,t={}){return new Proxy(n,{get(e,s){let a=Reflect.get(e,s,e);return typeof a!="function"?a:pt.has(s)?(...c)=>de(()=>Promise.resolve(a.apply(e,c)),t):a.bind(e)}})}var me=Number(process.env.TOOLNET_GRAPH_PORT??9749),fe=process.env.TOOLNET_GRAPH_HOST??"127.0.0.1";function gt(n){if(typeof n=="string")return n;if(!n||typeof n!="object")return null;let t=n;for(let e of["key","Key","path","name"])if(typeof t[e]=="string")return t[e];return null}async function ht(n,t){return(await n.list(t)).map(gt).filter(s=>!!s)}async function $(n,t){let e=await n.getText(t);if(!e)return null;try{return JSON.parse(e)}catch{return null}}async function yt(n){let t=await ht(n,"projects/"),e=[...new Set(t.filter(c=>/^projects\/[^/]+\/project\.json$/.test(c)).map(c=>c.split("/")[1]))],s=new Set(t),a=[];for(let c of e){let p=await $(n,`projects/${c}/project.json`);!p||typeof p.id!="string"||a.push({id:p.id,name:typeof p.name=="string"?p.name:c,remote:c,hasGraph:s.has(`projects/${c}/code/graph/current.json`),hasArchitecture:s.has(`projects/${c}/code/architecture/current.json`),hasAnalysis:s.has(`projects/${c}/code/analysis/current.json`),hasVisualization:s.has(`projects/${c}/code/visualization/graph.json`)})}return a.sort((c,p)=>c.name.localeCompare(p.name))}async function St(){let n=te(),t=new z().detect(),e=pe(le({provider:n.storage.provider,huggingface:n.storage.huggingface,localRoot:n.storage.localRoot}),{attempts:3});e.name!=="huggingface"&&console.warn("[graph-ui] Remote Hugging Face storage is not active.");let s=mt(ft(import.meta.url)),a=Q(s,"public"),c=Q(a,"vendor","3d-force-graph.min.js");async function p(){let o=await yt(e),r=t.remote??t.name,l=o.find(f=>f.remote===r&&f.hasGraph),S=o.find(f=>f.hasGraph);return{projects:o,defaultProject:l?.remote??S?.remote??null}}let k=new Map;function b(o){return String(typeof o=="object"&&o!==null?o.id:o)}function E(o){return o.clusterLabel||o.cluster||o.layer||"Unclustered"}function v(o,r={}){return{id:o.id,name:o.name,remote:o.remote,hasArchitecture:o.hasArchitecture,hasAnalysis:o.hasAnalysis,...r}}async function j(o){let r=`projects/${o.remote}`,l=await $(e,`${r}/code/graph/current.json`);if(!l||!Array.isArray(l.symbols)||!Array.isArray(l.edges))throw new Error(`Code graph missing for project ${o.name}`);let S=await $(e,`${r}/code/architecture/current.json`),f=await $(e,`${r}/code/analysis/current.json`),y=new A;y.import(l.symbols,l.edges);let x=new H(y).build(o.id,S,f);return await e.put(`${r}/code/visualization/graph.json`,JSON.stringify(x),"application/json"),{...x,dashboard:v(o)}}async function M(o,r=!1){r&&k.delete(o.remote);let l=k.get(o.remote);if(l)return l;let S=`projects/${o.remote}`,f=await $(e,`${S}/code/visualization/graph.json`);if(f&&Array.isArray(f.nodes)&&Array.isArray(f.links)){let x={...f,dashboard:v(o)};return k.set(o.remote,x),x}let y=await j(o);return k.set(o.remote,y),y}function R(o,r){let l=new Map,S=new Map;for(let d of r.nodes){let u=E(d),g=`cluster:${u}`;S.set(d.id,g);let i=l.get(g);i||(i={id:g,name:u,qualifiedName:u,type:"subsystem",filePath:"",clusterLabel:u,incoming:0,outgoing:0,memberCount:0,files:new Set,viewKind:"cluster"},l.set(g,i)),i.memberCount+=1,d.filePath&&i.files.add(d.filePath)}let f=new Map;for(let d of r.links){let u=S.get(b(d.source)),g=S.get(b(d.target));if(!u||!g||u===g)continue;let i=`${u}\0${g}`,h=f.get(i);h||(h={source:u,target:g,type:"SUBSYSTEM_DEPENDENCY",count:0},f.set(i,h)),h.count+=1}for(let d of f.values()){let u=l.get(d.source),g=l.get(d.target);u&&(u.outgoing+=d.count),g&&(g.incoming+=d.count)}let y=[...l.values()].map(d=>({...d,filePath:`${d.files.size} files \xB7 ${d.memberCount} symbols`,files:void 0})),x=[...f.values()];return{version:2,projectId:r.projectId,generatedAt:r.generatedAt,summary:{nodes:y.length,links:x.length,files:r.summary?.files??0,clusters:y.length},nodes:y,links:x,dashboard:v(o,{viewMode:"overview",totalNodes:r.nodes.length,totalLinks:r.links.length})}}function F(o,r,l){let S=r.nodes.filter(i=>!l||E(i)===l),f=new Set(S.map(i=>i.id)),y=new Map,x=new Map;for(let i of S){let h=i.filePath||"(unknown)";y.set(i.id,h);let C=x.get(h);C||(C={id:`file:${h}`,name:h.split("/").pop()||h,qualifiedName:h,type:"file",filePath:h,clusterLabel:E(i),incoming:0,outgoing:0,memberCount:0,viewKind:"file"},x.set(h,C)),C.memberCount+=1}let d=new Map;for(let i of r.links){let h=b(i.source),C=b(i.target);if(!f.has(h)||!f.has(C))continue;let U=y.get(h),W=y.get(C);if(!U||!W||U===W)continue;let X=`file:${U}`,Z=`file:${W}`,ee=`${X}\0${Z}`,G=d.get(ee);G||(G={source:X,target:Z,type:"FILE_DEPENDENCY",count:0},d.set(ee,G)),G.count+=1}for(let i of d.values()){let h=x.get(String(i.source).slice(5)),C=x.get(String(i.target).slice(5));h&&(h.outgoing+=i.count),C&&(C.incoming+=i.count)}let u=[...x.values()],g=[...d.values()];return{version:2,projectId:r.projectId,generatedAt:r.generatedAt,summary:{nodes:u.length,links:g.length,files:u.length,clusters:l?1:0},nodes:u,links:g,dashboard:v(o,{viewMode:"files",selection:l,totalNodes:r.nodes.length,totalLinks:r.links.length})}}function T(o,r,l){let y=r.nodes.filter(i=>i.filePath===l),x=new Set(y.map(i=>i.id)),d=new Set;for(let i of y.slice(0,1800))d.add(i.id);for(let i of r.links){if(d.size>=1800)break;let h=b(i.source),C=b(i.target);(x.has(h)||x.has(C))&&(d.add(h),d.size<1800&&d.add(C))}let u=r.nodes.filter(i=>d.has(i.id)).map(i=>({...i,viewKind:"symbol"})),g=[];for(let i of r.links){if(g.length>=12e3)break;let h=b(i.source),C=b(i.target);d.has(h)&&d.has(C)&&g.push({...i,source:h,target:C})}return{version:2,projectId:r.projectId,generatedAt:r.generatedAt,summary:{nodes:u.length,links:g.length,files:new Set(u.map(i=>i.filePath)).size,clusters:new Set(u.map(i=>E(i))).size},nodes:u,links:g,dashboard:v(o,{viewMode:"symbols",selection:l,focused:!0,truncated:u.length>=1800||g.length>=12e3,totalNodes:r.nodes.length,totalLinks:r.links.length})}}ut(async(o,r)=>{try{let l=new URL(o.url??"/",`http://${o.headers.host??"localhost"}`);if(l.pathname==="/api/health"){let S=await p();r.setHeader("content-type","application/json; charset=utf-8"),r.setHeader("cache-control","no-store"),r.end(JSON.stringify({ok:!0,mode:"multi-project",projects:S.projects.length,indexedProjects:S.projects.filter(f=>f.hasGraph).length,defaultProject:S.defaultProject}));return}if(l.pathname==="/api/projects"){let S=await p();r.setHeader("content-type","application/json; charset=utf-8"),r.setHeader("cache-control","no-store"),r.end(JSON.stringify(S));return}if(l.pathname==="/api/graph"){let S=await p(),f=l.searchParams.get("project")??S.defaultProject;if(!f){r.statusCode=404,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:"No indexed ToolNet project found."}));return}let y=S.projects.find(i=>i.remote===f);if(!y){r.statusCode=404,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:"Project not found.",project:f}));return}if(!y.hasGraph){r.statusCode=409,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:"Project is registered but has not been indexed yet.",project:y}));return}let x=l.searchParams.get("refresh")==="1",d=await M(y,x),u=l.searchParams.get("mode")??"full",g=d;if(u==="overview")g=R(y,d);else if(u==="files")g=F(y,d,l.searchParams.get("cluster")??"");else if(u==="symbols"){let i=l.searchParams.get("file");if(!i){r.statusCode=400,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:"file is required for symbols mode."}));return}g=T(y,d,i)}else if(u!=="full"){r.statusCode=400,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:`Unknown graph mode: ${u}`}));return}r.setHeader("content-type","application/json; charset=utf-8"),r.setHeader("cache-control","no-store"),r.end(JSON.stringify(g));return}if(l.pathname==="/vendor/3d-force-graph.min.js"){r.setHeader("content-type","application/javascript; charset=utf-8"),ue(c).pipe(r);return}if(l.pathname==="/"||l.pathname==="/index.html"){r.setHeader("content-type","text/html; charset=utf-8"),r.setHeader("cache-control","no-store"),ue(Q(a,"index.html")).pipe(r);return}r.statusCode=404,r.end("Not found")}catch(l){console.error(l),r.statusCode=500,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:l instanceof Error?l.message:String(l)}))}}).listen(me,fe,()=>{console.log("ToolNet Multi-Project 3D Graph"),console.log(`Listening: http://${fe}:${me}`)})}St().catch(n=>{console.error(n),process.exit(1)});
@@ -0,0 +1,4 @@
1
+ import{readFileSync as G}from"node:fs";import{dirname as D,resolve as E}from"node:path";import{fileURLToPath as j}from"node:url";var R={spinner:["\xB7","\u2722","\u2733","\u2736","\u273B","\u273D"],barFilled:"\u2588",barEmpty:"\u2591",phaseDone:"\u25C6",phaseActive:"\u25C7",rail:"\u2502",branch:"\u251C",corner:"\u2514",dash:"\u2014"},U={spinner:[".","*","+","x","*","+"],barFilled:"#",barEmpty:"-",phaseDone:"[OK]",phaseActive:"[>>]",rail:"|",branch:"|",corner:"\\",dash:"-"};function A(){if(process.env.TOOLNET_ASCII==="1")return!1;if(process.env.TOOLNET_UNICODE==="1")return!0;if(process.platform==="win32"){let t=process.env.TERM_PROGRAM,e=process.env.WT_SESSION,o=process.env.VSCODE_PID,n=process.env.ConEmuPID;return!!(e||o||n||t==="vscode")}return process.env.TERM!=="linux"}var h;function v(){return h||(h=A()?R:U),h}var d={reset:"\x1B[0m",green:"\x1B[32m",cyan:"\x1B[36m",red:"\x1B[31m",yellow:"\x1B[33m",amber:"\x1B[38;5;214m",white:"\x1B[97m",dim:"\x1B[2m",bold:"\x1B[1m"};function T(t){return t.noColor||process.env.NO_COLOR?!1:t.tty??process.stdout.isTTY??!1}function f(t,e,o){return T(o)?`${e}${t}${d.reset}`:t}function N(t,e){return f(t,d.dim,e)}function M(t,e){return f(t,d.bold,e)}function O(t,e){return f(t,d.cyan,e)}function u(t,e,o){let n=v(),i=e?` ${N(`v${e}`,o)}`:"";return`${O(n.phaseDone,o)} ${t}${i}`}function a(t,e){return M(t,e)}function l(t,e,o,n){let i=" ".repeat(Math.max(0,o-t.length));return` ${O(t,n)}${i} ${e}`}var m=[{name:"setup",description:"Configure ToolNet",category:"core"},{name:"init",description:"Initialize current project",usage:"toolnet-memory init [path]",category:"core"},{name:"doctor",description:"Check system health",category:"core"},{name:"ask",description:"Ask project memory",usage:"toolnet-memory ask <question>",category:"memory"},{name:"context",description:"Show current project context",category:"memory",aliases:["context:print"]},{name:"work",description:"Show current work state",category:"memory",aliases:["work:status"]},{name:"index",description:"Build code intelligence",category:"code",pipeline:["Scanning files","Parsing code","Type Resolution","Rich Graph","Semantic Code Index","Architecture Intelligence","Graph Analysis","3D Visualization Dataset"]},{name:"semantic",description:"Semantic code search",usage:"toolnet-memory semantic <query>",category:"code"},{name:"impact",description:"Analyze change impact",usage:"toolnet-memory impact <file>",category:"code"},{name:"graph",description:"Open code graph UI",category:"code"},{name:"graph:ui",description:"Open code graph UI",category:"code",hidden:!0},{name:"model",description:"View/change AI model",category:"ai"},{name:"provider",description:"Manage AI providers",category:"ai",aliases:["provider:status"]},{name:"status",description:"Show ToolNet status",category:"system"},{name:"update",description:"Update ToolNet Memory",category:"system"},{name:"context:print",description:"Print fast project context",category:"context",hidden:!0},{name:"context:sync",description:"Show context hash/size",category:"context"},{name:"context:refresh",description:"Rebuild deep startup brief cache",category:"context"},{name:"brief",description:"Show full project brief",category:"context"},{name:"brief:json",description:"Show brief as JSON",category:"context"},{name:"profile:show",description:"Show .toolnet/profile.md",category:"context"},{name:"profile:sync",description:"Sync profile to agent files",category:"context"},{name:"work:status",description:"Show current work state",category:"work",hidden:!0},{name:"work:json",description:"Show work state as JSON",category:"work"},{name:"work:reconcile",description:"Reconcile work continuity",category:"work"},{name:"handoff:latest",description:"Show latest session handoff",category:"work"},{name:"handoff:refresh",description:"Refresh handoff data",category:"work"},{name:"index:graph",description:"Build graph index only",category:"code"},{name:"incremental",description:"Incremental code indexing",category:"code"},{name:"config",description:"Manage configuration",usage:"toolnet-memory config <get|set|list|open>",category:"ai"},{name:"provider:list",description:"List available AI providers",category:"ai"},{name:"provider:status",description:"Show AI provider status",category:"ai",hidden:!0},{name:"provider:test",description:"Test AI provider connection",category:"ai"},{name:"integrate:detect",description:"Detect agent integrations",category:"integration"},{name:"integrate:auto",description:"Auto-enable integrations",category:"integration"},{name:"integrate:agy",description:"Install Agy hooks",category:"integration"},{name:"integrate:codex",description:"Install Codex integration",category:"integration"},{name:"integrate:opencode",description:"Install OpenCode plugin",category:"integration"},{name:"integrate:claude",description:"Install Claude Code integration",category:"integration"},{name:"integrate:kiro",description:"Install Kiro CLI integration",usage:"toolnet-memory integrate:kiro [--status] [--json]",category:"integration"},{name:"session:agy-sync",description:"Sync Agy session",category:"session"},{name:"session:agy-recover",description:"Recover Agy sessions",category:"session"},{name:"session:agy-hook",description:"Agy session hook",category:"session",hidden:!0},{name:"session:codex-sync",description:"Sync Codex session",category:"session"},{name:"session:codex-recover",description:"Recover Codex sessions",category:"session"},{name:"session:codex-notify",description:"Codex notification hook",category:"session",hidden:!0},{name:"session:codex-context",description:"Codex context hook",category:"session",hidden:!0},{name:"session:opencode-sync",description:"Sync OpenCode session",category:"session"},{name:"session:opencode-recover",description:"Recover OpenCode sessions",category:"session"},{name:"session:claude-hook",description:"Claude session hook",category:"session",hidden:!0},{name:"session:kiro-hook",description:"Kiro lifecycle hook",category:"session",hidden:!0},{name:"memory:review",description:"Review memory entries",category:"session"},{name:"memory:reconcile",description:"Reconcile memory state",category:"session"},{name:"guard:check",description:"Check project rules",category:"guard"},{name:"guard:explain",description:"Show rules and evidence",category:"guard"},{name:"guard:json",description:"Output guard result as JSON",category:"guard",hidden:!0},{name:"project:manual-init",description:"Initialize project manual",category:"project"},{name:"project:manual-show",description:"Show project manual",category:"project"},{name:"project:manual-sync",description:"Sync project manual",category:"project"},{name:"snapshot:list",description:"List snapshots",category:"snapshot"},{name:"snapshot:create",description:"Create snapshot",category:"snapshot"},{name:"snapshot:restore",description:"Restore snapshot",category:"snapshot"},{name:"recover",description:"Recover latest snapshot",category:"snapshot"},{name:"service",description:"Run daemon in foreground",category:"service"},{name:"service:install",description:"Install/start daemon",category:"service"},{name:"service:start",description:"Start daemon",category:"service"},{name:"service:stop",description:"Stop daemon",category:"service"},{name:"service:restart",description:"Restart daemon",category:"service"},{name:"service:status",description:"Show daemon status",category:"service"},{name:"service:remove",description:"Remove daemon",category:"service"},{name:"mcp",description:"Run MCP server",category:"advanced"},{name:"api",description:"Run API server",category:"advanced"},{name:"run",description:"Run ToolNet runtime",category:"advanced",hidden:!0},{name:"production:certify",description:"Verify production runtime",category:"advanced"},{name:"certify:continuity",description:"Certify work continuity",category:"advanced"},{name:"ask-ai",description:"Ask AI agent (advanced)",category:"advanced",hidden:!0}];function x(t){return{tty:t.tty,noColor:t.noColor}}function S(t){let e=[],o=t.version??"0.0.0",n=x(t);e.push(""),e.push(u("ToolNet Memory",o,n)),e.push(" Persistent memory & code intelligence for AI coding agents"),e.push(""),e.push(a("USAGE",n)),e.push(" toolnet-memory <command> [options]"),e.push("");let i=[{key:"core",title:"GET STARTED"},{key:"memory",title:"MEMORY"},{key:"code",title:"CODE"},{key:"ai",title:"AI"},{key:"system",title:"SYSTEM"}];for(let{key:s,title:g}of i){let c=m.filter(r=>r.category===s&&!r.hidden);if(c.length===0)continue;e.push(a(g,n));let y=Math.max(...c.map(r=>r.name.length));for(let r of c)e.push(l(r.name,r.description,y,n));e.push("")}return e.push(a("MORE",n)),e.push(l("help --all","Show all advanced commands",15,n)),e.push(l("help <command>","Show command details",15,n)),e.push(""),e.join(`
2
+ `)}function k(t){let e=[],o=t.version??"0.0.0",n=x(t);e.push(""),e.push(u("ToolNet Memory",o,n)),e.push(" Persistent memory & code intelligence for AI coding agents"),e.push(""),e.push(a("USAGE",n)),e.push(" toolnet-memory <command> [options]"),e.push("");let i=[{key:"core",title:"PROJECT SETUP"},{key:"memory",title:"MEMORY"},{key:"code",title:"CODE INTELLIGENCE"},{key:"ai",title:"AI / PROVIDERS"},{key:"system",title:"SYSTEM"},{key:"context",title:"CONTEXT & MEMORY"},{key:"work",title:"WORK CONTINUITY"},{key:"integration",title:"AGENT INTEGRATIONS"},{key:"session",title:"SESSIONS"},{key:"guard",title:"GUARD"},{key:"project",title:"PROJECT MANUAL"},{key:"snapshot",title:"SNAPSHOTS & RECOVERY"},{key:"service",title:"BACKGROUND SERVICE"},{key:"advanced",title:"DEVELOPER / PRODUCTION"}];for(let{key:s,title:g}of i){let c=m.filter(r=>r.category===s&&!r.hidden);if(c.length===0)continue;e.push(a(g,n));let y=Math.max(...c.map(r=>r.name.length));for(let r of c)e.push(l(r.name,r.description,y,n));e.push("")}return e.join(`
3
+ `)}function w(t,e){let o=m.find(s=>s.name===t||s.aliases?.includes(t));if(!o)return null;let n=[],i=x(e);if(n.push(""),n.push(u(o.name,void 0,i)),n.push(""),n.push(o.description),n.push(""),o.usage&&(n.push(a("USAGE",i)),n.push(` ${o.usage}`),n.push("")),o.aliases&&o.aliases.length>0){n.push(a("ALIASES",i));for(let s of o.aliases)n.push(` ${s}`);n.push("")}if(o.pipeline&&o.pipeline.length>0){n.push(a("PIPELINE",i));for(let s of o.pipeline)n.push(` ${s}`);n.push("")}if(o.examples&&o.examples.length>0){n.push(a("EXAMPLES",i));for(let s of o.examples)n.push(` ${s}`);n.push("")}return n.join(`
4
+ `)}function b(t){return m.flatMap(n=>[n.name,...n.aliases??[]]).filter(n=>n===t?!1:!!(n.startsWith(t)||t.length>=3&&n.includes(t))).slice(0,3)}function $(){let t=D(j(import.meta.url)),e=[E(t,"../package.json"),E(t,"../../package.json")];for(let o of e)try{let n=JSON.parse(G(o,"utf8"));if(n.version)return n.version}catch{}return process.env.npm_package_version??"unknown"}var p=process.argv.slice(2),C={version:$(),tty:!!process.stdout.isTTY};function I(t){let e=b(t);if(console.error(""),console.error(`\u2715 Unknown command: ${t}`),e.length>0){console.error(""),console.error(" Did you mean:");for(let o of e)console.error(` ${o}`)}console.error(""),console.error(" Run `toolnet-memory help` to see available commands."),console.error("")}if(p[0]==="--unknown")I(p[1]??""),process.exitCode=1;else if(p[0]==="--all")console.log(k(C));else if(p[0]){let t=w(p[0],C);t?console.log(t):(I(p[0]),process.exitCode=1)}else console.log(S(C));