squish-memory 0.9.2 → 1.0.0

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.
Files changed (208) hide show
  1. package/{.env.mcp → .env.mcp.example} +23 -0
  2. package/CHANGELOG.md +102 -0
  3. package/QUICK-START.md +10 -4
  4. package/README.md +73 -4
  5. package/config/plugin-manifest.json +152 -0
  6. package/config/plugin-manifest.schema.json +244 -0
  7. package/dist/algorithms/{merge/analytics → analytics}/token-estimator.d.ts +1 -1
  8. package/dist/algorithms/analytics/token-estimator.d.ts.map +1 -0
  9. package/dist/algorithms/{merge/analytics → analytics}/token-estimator.js +3 -3
  10. package/dist/algorithms/analytics/token-estimator.js.map +1 -0
  11. package/dist/algorithms/detection/hash-filters.d.ts.map +1 -0
  12. package/dist/algorithms/detection/hash-filters.js.map +1 -0
  13. package/dist/algorithms/{merge/detection → detection}/semantic-ranker.d.ts +1 -1
  14. package/dist/algorithms/detection/semantic-ranker.d.ts.map +1 -0
  15. package/dist/algorithms/{merge/detection → detection}/semantic-ranker.js +1 -1
  16. package/dist/algorithms/detection/semantic-ranker.js.map +1 -0
  17. package/dist/algorithms/{merge/detection → detection}/two-stage-detector.d.ts +1 -1
  18. package/dist/algorithms/detection/two-stage-detector.d.ts.map +1 -0
  19. package/dist/algorithms/{merge/detection → detection}/two-stage-detector.js +4 -4
  20. package/dist/algorithms/detection/two-stage-detector.js.map +1 -0
  21. package/dist/algorithms/handlers/approve-merge.d.ts.map +1 -0
  22. package/dist/algorithms/{merge/handlers → handlers}/approve-merge.js +4 -4
  23. package/dist/algorithms/handlers/approve-merge.js.map +1 -0
  24. package/dist/algorithms/{merge/handlers → handlers}/detect-duplicates.d.ts +1 -1
  25. package/dist/algorithms/handlers/detect-duplicates.d.ts.map +1 -0
  26. package/dist/algorithms/{merge/handlers → handlers}/detect-duplicates.js +55 -75
  27. package/dist/algorithms/handlers/detect-duplicates.js.map +1 -0
  28. package/dist/algorithms/handlers/get-stats.d.ts.map +1 -0
  29. package/dist/algorithms/{merge/handlers → handlers}/get-stats.js +3 -3
  30. package/dist/algorithms/handlers/get-stats.js.map +1 -0
  31. package/dist/algorithms/handlers/list-proposals.d.ts.map +1 -0
  32. package/dist/algorithms/{merge/handlers → handlers}/list-proposals.js +3 -3
  33. package/dist/algorithms/handlers/list-proposals.js.map +1 -0
  34. package/dist/algorithms/handlers/preview-merge.d.ts.map +1 -0
  35. package/dist/algorithms/{merge/handlers → handlers}/preview-merge.js +3 -3
  36. package/dist/algorithms/handlers/preview-merge.js.map +1 -0
  37. package/dist/algorithms/handlers/reject-merge.d.ts.map +1 -0
  38. package/dist/algorithms/{merge/handlers → handlers}/reject-merge.js +3 -3
  39. package/dist/algorithms/handlers/reject-merge.js.map +1 -0
  40. package/dist/algorithms/handlers/reverse-merge.d.ts.map +1 -0
  41. package/dist/algorithms/{merge/handlers → handlers}/reverse-merge.js +3 -3
  42. package/dist/algorithms/handlers/reverse-merge.js.map +1 -0
  43. package/dist/algorithms/{merge/safety → safety}/safety-checks.d.ts +1 -1
  44. package/dist/algorithms/safety/safety-checks.d.ts.map +1 -0
  45. package/dist/algorithms/safety/safety-checks.js +179 -0
  46. package/dist/algorithms/safety/safety-checks.js.map +1 -0
  47. package/dist/algorithms/{merge/strategies → strategies}/merge-strategies.d.ts +1 -1
  48. package/dist/algorithms/strategies/merge-strategies.d.ts.map +1 -0
  49. package/dist/algorithms/strategies/merge-strategies.js.map +1 -0
  50. package/dist/algorithms/utils/response-builder.d.ts +28 -0
  51. package/dist/algorithms/utils/response-builder.d.ts.map +1 -0
  52. package/dist/algorithms/utils/response-builder.js +37 -0
  53. package/dist/algorithms/utils/response-builder.js.map +1 -0
  54. package/dist/api/web/web.d.ts.map +1 -1
  55. package/dist/api/web/web.js +0 -21
  56. package/dist/api/web/web.js.map +1 -1
  57. package/dist/commands/mcp-server.js +1 -1
  58. package/dist/config.d.ts +9 -1
  59. package/dist/config.d.ts.map +1 -1
  60. package/dist/config.js +35 -32
  61. package/dist/config.js.map +1 -1
  62. package/dist/core/associations.js +2 -2
  63. package/dist/core/associations.js.map +1 -1
  64. package/dist/core/core-memory.d.ts +5 -0
  65. package/dist/core/core-memory.d.ts.map +1 -1
  66. package/dist/core/core-memory.js +17 -5
  67. package/dist/core/core-memory.js.map +1 -1
  68. package/dist/core/embeddings.d.ts +9 -0
  69. package/dist/core/embeddings.d.ts.map +1 -1
  70. package/dist/core/embeddings.js +153 -16
  71. package/dist/core/embeddings.js.map +1 -1
  72. package/dist/core/layers/generator.d.ts +25 -0
  73. package/dist/core/layers/generator.d.ts.map +1 -0
  74. package/dist/core/layers/generator.js +76 -0
  75. package/dist/core/layers/generator.js.map +1 -0
  76. package/dist/core/mcp/tools.d.ts.map +1 -1
  77. package/dist/core/mcp/tools.js +71 -0
  78. package/dist/core/mcp/tools.js.map +1 -1
  79. package/dist/core/memory/hybrid-retrieval.d.ts.map +1 -1
  80. package/dist/core/memory/hybrid-retrieval.js +49 -1
  81. package/dist/core/memory/hybrid-retrieval.js.map +1 -1
  82. package/dist/core/memory/hybrid-search.d.ts.map +1 -1
  83. package/dist/core/memory/hybrid-search.js +0 -7
  84. package/dist/core/memory/hybrid-search.js.map +1 -1
  85. package/dist/core/memory/memories.d.ts.map +1 -1
  86. package/dist/core/memory/memories.js +47 -53
  87. package/dist/core/memory/memories.js.map +1 -1
  88. package/dist/core/memory/progressive-disclosure.d.ts.map +1 -1
  89. package/dist/core/memory/progressive-disclosure.js.map +1 -1
  90. package/dist/core/namespaces/index.d.ts +71 -0
  91. package/dist/core/namespaces/index.d.ts.map +1 -0
  92. package/dist/core/namespaces/index.js +296 -0
  93. package/dist/core/namespaces/index.js.map +1 -0
  94. package/dist/core/namespaces/uri-parser.d.ts +31 -0
  95. package/dist/core/namespaces/uri-parser.d.ts.map +1 -0
  96. package/dist/core/namespaces/uri-parser.js +74 -0
  97. package/dist/core/namespaces/uri-parser.js.map +1 -0
  98. package/dist/core/observations.d.ts.map +1 -1
  99. package/dist/core/observations.js +3 -12
  100. package/dist/core/observations.js.map +1 -1
  101. package/dist/core/projects.d.ts.map +1 -1
  102. package/dist/core/projects.js +0 -12
  103. package/dist/core/projects.js.map +1 -1
  104. package/dist/core/scheduler/cron-scheduler.d.ts.map +1 -1
  105. package/dist/core/scheduler/cron-scheduler.js +14 -0
  106. package/dist/core/scheduler/cron-scheduler.js.map +1 -1
  107. package/dist/core/session-hooks/self-iteration-job.d.ts +20 -0
  108. package/dist/core/session-hooks/self-iteration-job.d.ts.map +1 -0
  109. package/dist/core/session-hooks/self-iteration-job.js +278 -0
  110. package/dist/core/session-hooks/self-iteration-job.js.map +1 -0
  111. package/dist/core/session-hooks/session-hooks.d.ts +18 -0
  112. package/dist/core/session-hooks/session-hooks.d.ts.map +1 -0
  113. package/dist/core/session-hooks/session-hooks.js +55 -0
  114. package/dist/core/session-hooks/session-hooks.js.map +1 -0
  115. package/dist/core/tracing/collector.d.ts +111 -0
  116. package/dist/core/tracing/collector.d.ts.map +1 -0
  117. package/dist/core/tracing/collector.js +338 -0
  118. package/dist/core/tracing/collector.js.map +1 -0
  119. package/dist/core/tracing/visualizer.d.ts +32 -0
  120. package/dist/core/tracing/visualizer.d.ts.map +1 -0
  121. package/dist/core/tracing/visualizer.js +165 -0
  122. package/dist/core/tracing/visualizer.js.map +1 -0
  123. package/dist/db/bootstrap.d.ts.map +1 -1
  124. package/dist/db/bootstrap.js +33 -6
  125. package/dist/db/bootstrap.js.map +1 -1
  126. package/dist/drizzle/schema-sqlite.d.ts +401 -0
  127. package/dist/drizzle/schema-sqlite.d.ts.map +1 -1
  128. package/dist/drizzle/schema-sqlite.js +66 -0
  129. package/dist/drizzle/schema-sqlite.js.map +1 -1
  130. package/dist/drizzle/schema.d.ts +385 -0
  131. package/dist/drizzle/schema.d.ts.map +1 -1
  132. package/dist/drizzle/schema.js +64 -0
  133. package/dist/drizzle/schema.js.map +1 -1
  134. package/dist/index.d.ts +4 -4
  135. package/dist/index.js +101 -62
  136. package/dist/index.js.map +1 -1
  137. package/generated/mcp/manifest.json +23 -0
  138. package/generated/mcp/mcp-servers.json +25 -0
  139. package/generated/mcp/mcporter.json +34 -0
  140. package/generated/mcp/openclaw-memory-qmd.json +17 -0
  141. package/generated/mcp/runtime.json +12 -0
  142. package/package.json +68 -26
  143. package/packages/plugin-claude-code/README.md +73 -0
  144. package/packages/plugin-claude-code/dist/plugin-wrapper.d.ts +35 -0
  145. package/packages/plugin-claude-code/dist/plugin-wrapper.d.ts.map +1 -0
  146. package/packages/plugin-claude-code/dist/plugin-wrapper.js +191 -0
  147. package/packages/plugin-claude-code/dist/plugin-wrapper.js.map +1 -0
  148. package/packages/plugin-claude-code/package.json +31 -0
  149. package/packages/plugin-openclaw/README.md +70 -0
  150. package/packages/plugin-openclaw/dist/index.d.ts +49 -0
  151. package/packages/plugin-openclaw/dist/index.d.ts.map +1 -0
  152. package/packages/plugin-openclaw/dist/index.js +262 -0
  153. package/packages/plugin-openclaw/dist/index.js.map +1 -0
  154. package/packages/plugin-openclaw/openclaw.plugin.json +94 -0
  155. package/packages/plugin-openclaw/package.json +31 -0
  156. package/packages/plugin-opencode/install.mjs +217 -0
  157. package/packages/plugin-opencode/package.json +21 -0
  158. package/scripts/build-release.sh +20 -17
  159. package/scripts/check-secrets.js +132 -0
  160. package/scripts/dependency-manager.mjs +217 -0
  161. package/scripts/detect-clients.mjs +78 -0
  162. package/scripts/github-release.sh +43 -27
  163. package/scripts/install-interactive.mjs +674 -0
  164. package/scripts/install-plugin.mjs +415 -0
  165. package/scripts/test-interactive.mjs +131 -0
  166. package/commands/managed-sync.ts +0 -69
  167. package/commands/mcp-server.ts +0 -519
  168. package/dist/algorithms/merge/analytics/token-estimator.d.ts.map +0 -1
  169. package/dist/algorithms/merge/analytics/token-estimator.js.map +0 -1
  170. package/dist/algorithms/merge/detection/hash-filters.d.ts.map +0 -1
  171. package/dist/algorithms/merge/detection/hash-filters.js.map +0 -1
  172. package/dist/algorithms/merge/detection/semantic-ranker.d.ts.map +0 -1
  173. package/dist/algorithms/merge/detection/semantic-ranker.js.map +0 -1
  174. package/dist/algorithms/merge/detection/two-stage-detector.d.ts.map +0 -1
  175. package/dist/algorithms/merge/detection/two-stage-detector.js.map +0 -1
  176. package/dist/algorithms/merge/handlers/approve-merge.d.ts.map +0 -1
  177. package/dist/algorithms/merge/handlers/approve-merge.js.map +0 -1
  178. package/dist/algorithms/merge/handlers/detect-duplicates.d.ts.map +0 -1
  179. package/dist/algorithms/merge/handlers/detect-duplicates.js.map +0 -1
  180. package/dist/algorithms/merge/handlers/get-stats.d.ts.map +0 -1
  181. package/dist/algorithms/merge/handlers/get-stats.js.map +0 -1
  182. package/dist/algorithms/merge/handlers/list-proposals.d.ts.map +0 -1
  183. package/dist/algorithms/merge/handlers/list-proposals.js.map +0 -1
  184. package/dist/algorithms/merge/handlers/preview-merge.d.ts.map +0 -1
  185. package/dist/algorithms/merge/handlers/preview-merge.js.map +0 -1
  186. package/dist/algorithms/merge/handlers/reject-merge.d.ts.map +0 -1
  187. package/dist/algorithms/merge/handlers/reject-merge.js.map +0 -1
  188. package/dist/algorithms/merge/handlers/reverse-merge.d.ts.map +0 -1
  189. package/dist/algorithms/merge/handlers/reverse-merge.js.map +0 -1
  190. package/dist/algorithms/merge/safety/safety-checks.d.ts.map +0 -1
  191. package/dist/algorithms/merge/safety/safety-checks.js +0 -215
  192. package/dist/algorithms/merge/safety/safety-checks.js.map +0 -1
  193. package/dist/algorithms/merge/strategies/merge-strategies.d.ts.map +0 -1
  194. package/dist/algorithms/merge/strategies/merge-strategies.js.map +0 -1
  195. package/dist/core/embeddings/qmd-provider.d.ts +0 -65
  196. package/dist/core/embeddings/qmd-provider.d.ts.map +0 -1
  197. package/dist/core/embeddings/qmd-provider.js +0 -133
  198. package/dist/core/embeddings/qmd-provider.js.map +0 -1
  199. package/scripts/init-dirs.ts +0 -15
  200. /package/dist/algorithms/{merge/detection → detection}/hash-filters.d.ts +0 -0
  201. /package/dist/algorithms/{merge/detection → detection}/hash-filters.js +0 -0
  202. /package/dist/algorithms/{merge/handlers → handlers}/approve-merge.d.ts +0 -0
  203. /package/dist/algorithms/{merge/handlers → handlers}/get-stats.d.ts +0 -0
  204. /package/dist/algorithms/{merge/handlers → handlers}/list-proposals.d.ts +0 -0
  205. /package/dist/algorithms/{merge/handlers → handlers}/preview-merge.d.ts +0 -0
  206. /package/dist/algorithms/{merge/handlers → handlers}/reject-merge.d.ts +0 -0
  207. /package/dist/algorithms/{merge/handlers → handlers}/reverse-merge.d.ts +0 -0
  208. /package/dist/algorithms/{merge/strategies → strategies}/merge-strategies.js +0 -0
@@ -0,0 +1,674 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Squish Interactive Plugin Installer
5
+ * Multi-step wizard with clack
6
+ */
7
+
8
+ import { intro, outro, confirm, multiselect, select, isCancel, cancel, spinner, note } from '@clack/prompts';
9
+ import picocolors from 'picocolors';
10
+ import fs from "node:fs";
11
+ import os from "node:os";
12
+ import path from "node:path";
13
+ import { spawnSync } from "node:child_process";
14
+
15
+ const root = process.cwd();
16
+ const manifestPath = path.join(root, "config", "plugin-manifest.json");
17
+
18
+ const c = picocolors;
19
+
20
+ const icons = {
21
+ squish: "🐙",
22
+ check: "✓",
23
+ cross: "✗",
24
+ package: "📦",
25
+ arrow: "→",
26
+ dot: "●",
27
+ circle: "○",
28
+ diamond: "◆",
29
+ pointer: "❯",
30
+ line: "│",
31
+ corner: "└",
32
+ section: "─",
33
+ cli: "⌨️",
34
+ mcp: "🔌",
35
+ plugin: "🔧",
36
+ settings: "⚙️",
37
+ local: "🏠",
38
+ remote: "☁️",
39
+ brain: "🧠",
40
+ cloud: "☁️"
41
+ };
42
+
43
+ const CLIENT_DIRS = {
44
+ "claude-code": path.join(os.homedir(), ".claude"),
45
+ opencode: path.join(os.homedir(), ".config", "opencode"),
46
+ codex: path.join(os.homedir(), ".codex"),
47
+ cursor: path.join(os.homedir(), ".cursor"),
48
+ vscode: path.join(os.homedir(), ".vscode", "mcp"),
49
+ windsurf: path.join(os.homedir(), ".windsurf"),
50
+ openclaw: path.join(os.homedir(), ".openclaw")
51
+ };
52
+
53
+ const PLUGINS_DIR = path.join(root, "packages");
54
+
55
+ function printLogo() {
56
+ console.log(c.cyan(`
57
+ ███████╗ ██████╗ ██╗ ██╗██╗███████╗██╗ ██╗
58
+ ██╔════╝██╔═══██╗██║ ██║██║██╔════╝██║ ██║
59
+ ███████╗██║ ██║██║ ██║██║███████╗███████║
60
+ ╚════██║██║ ██║██║ ██║██║╚════██║██╔══██║
61
+ ███████║╚██████╔╝╚██████╔╝██║███████║██║ ██║
62
+ ╚══════╝ ╚═════╝ ╚═════╝ ╚═╝╚══════╝╚═╝ ╚═╝
63
+ `));
64
+ console.log(c.gray(" Universal Memory System for AI Agents\n"));
65
+ }
66
+
67
+ function loadManifest() {
68
+ if (!fs.existsSync(manifestPath)) {
69
+ return null;
70
+ }
71
+ try {
72
+ return JSON.parse(fs.readFileSync(manifestPath, "utf8"));
73
+ } catch {
74
+ return null;
75
+ }
76
+ }
77
+
78
+ function detectInstalledClients() {
79
+ const installed = {};
80
+ for (const [client, dir] of Object.entries(CLIENT_DIRS)) {
81
+ try {
82
+ installed[client] = fs.existsSync(dir);
83
+ } catch {
84
+ installed[client] = false;
85
+ }
86
+ }
87
+ return installed;
88
+ }
89
+
90
+ function checkPluginSource(pluginId) {
91
+ const pluginDirs = {
92
+ "claude-code": path.join(PLUGINS_DIR, "plugin-claude-code"),
93
+ "openclaw": path.join(PLUGINS_DIR, "plugin-openclaw"),
94
+ "opencode": path.join(PLUGINS_DIR, "plugin-opencode")
95
+ };
96
+
97
+ const pluginDir = pluginDirs[pluginId];
98
+ if (!pluginDir) return false;
99
+
100
+ return fs.existsSync(pluginDir) && fs.existsSync(path.join(pluginDir, "package.json"));
101
+ }
102
+
103
+ function shouldUseNonInteractive() {
104
+ return process.env.CI === 'true' ||
105
+ process.env.NON_INTERACTIVE === '1' ||
106
+ process.env.AUTOMATION === 'true' ||
107
+ !process.stdin.isTTY;
108
+ }
109
+
110
+ function parseArgs(argv) {
111
+ const flags = {
112
+ auto: false,
113
+ select: [],
114
+ all: false,
115
+ list: false,
116
+ dryRun: false,
117
+ help: false,
118
+ verbose: false,
119
+ quick: false
120
+ };
121
+
122
+ for (let i = 2; i < argv.length; i++) {
123
+ const token = argv[i];
124
+
125
+ if (token === "--auto" || token === "-a") {
126
+ flags.auto = true;
127
+ } else if (token === "--all") {
128
+ flags.all = true;
129
+ } else if (token === "--list" || token === "-l") {
130
+ flags.list = true;
131
+ } else if (token === "--dry-run" || token === "-n") {
132
+ flags.dryRun = true;
133
+ } else if (token === "--verbose" || token === "-v") {
134
+ flags.verbose = true;
135
+ } else if (token === "--help" || token === "-h") {
136
+ flags.help = true;
137
+ } else if (token === "--quick" || token === "-q") {
138
+ flags.quick = true;
139
+ } else if (token.startsWith("--select=")) {
140
+ flags.select = token.slice(9).split(",").map(s => s.trim());
141
+ } else if (token === "--select") {
142
+ flags.select = [argv[i + 1]];
143
+ i++;
144
+ } else {
145
+ console.log(c.red(`Unknown flag: ${token}`));
146
+ console.log(`Run ${c.cyan("--help")} for usage information`);
147
+ process.exit(1);
148
+ }
149
+ }
150
+
151
+ return flags;
152
+ }
153
+
154
+ function printHelp() {
155
+ printLogo();
156
+ console.log(c.white("USAGE:"));
157
+ console.log(` ${c.cyan("bun run install:interactive")} [OPTIONS]\n`);
158
+
159
+ console.log(c.white("OPTIONS:"));
160
+ console.log(` ${c.cyan("--auto")}, ${c.cyan("-a")} Skip menu, install all available plugins`);
161
+ console.log(` ${c.cyan("--quick")}, ${c.cyan("-q")} Quick install (CLI + all plugins)`);
162
+ console.log(` ${c.cyan("--select")}=<list> Pre-select plugins (comma-separated)`);
163
+ console.log(` ${c.cyan("--all")} Install all available plugins`);
164
+ console.log(` ${c.cyan("--list")}, ${c.cyan("-l")} List available plugins and exit`);
165
+ console.log(` ${c.cyan("--dry-run")}, ${c.cyan("-n")} Preview changes without installing`);
166
+ console.log(` ${c.cyan("--verbose")}, ${c.cyan("-v")} Show detailed output`);
167
+ console.log(` ${c.cyan("--help")}, ${c.cyan("-h")} Show this help message\n`);
168
+
169
+ console.log(c.white("ENVIRONMENT VARIABLES:"));
170
+ console.log(` ${c.cyan("CI=true")} Force non-interactive mode`);
171
+ console.log(` ${c.cyan("NON_INTERACTIVE=1")} Force non-interactive mode`);
172
+ console.log(` ${c.cyan("AUTOMATION=true")} Force non-interactive mode\n`);
173
+
174
+ console.log(c.white("INTERACTIVE WIZARD:"));
175
+ console.log(` ${c.gray("1.")} Select components: CLI, MCP Server, Plugins`);
176
+ console.log(` ${c.gray("2.")} Choose plugins (if selected)`);
177
+ console.log(` ${c.gray("3.")} Configure mode (local/remote)`);
178
+ console.log(` ${c.gray("4.")} Review and install\n`);
179
+
180
+ console.log(c.white("EXAMPLES:"));
181
+ console.log(` ${c.gray("# Interactive wizard (default)")}`);
182
+ console.log(` ${c.gray("$")} bun run install:interactive\n`);
183
+ console.log(` ${c.gray("# Quick install (CLI + all plugins)")}`);
184
+ console.log(` ${c.gray("$")} bun run install:interactive --quick\n`);
185
+ console.log(` ${c.gray("# Non-interactive: install all")}`);
186
+ console.log(` ${c.gray("$")} bun run install:interactive --auto\n`);
187
+
188
+ console.log(c.gray("────────────────────────────────────────────────────"));
189
+ console.log(c.gray("Documentation: https://github.com/michielhdoteth/squish"));
190
+ }
191
+
192
+ function listPlugins() {
193
+ const manifest = loadManifest();
194
+ if (!manifest || !manifest.targets) {
195
+ console.log(c.red("Error: Plugin manifest not found"));
196
+ process.exit(1);
197
+ }
198
+
199
+ const installed = detectInstalledClients();
200
+ const clientNames = {
201
+ "claude-code": "Claude Code",
202
+ "openclaw": "OpenClaw",
203
+ "opencode": "OpenCode",
204
+ "codex": "Codex",
205
+ "cursor": "Cursor",
206
+ "vscode": "VS Code",
207
+ "windsurf": "Windsurf"
208
+ };
209
+
210
+ printLogo();
211
+ console.log(c.white("Available Plugins:"));
212
+ console.log(c.gray("────────────────────────────────────────────────────"));
213
+ console.log();
214
+
215
+ let i = 1;
216
+ for (const [client, config] of Object.entries(manifest.targets)) {
217
+ const isInstalled = installed[client];
218
+ const hasSource = checkPluginSource(client);
219
+
220
+ const status = isInstalled
221
+ ? `${c.green(icons.check)} installed`
222
+ : `${c.yellow(icons.dot)} not installed`;
223
+
224
+ const source = hasSource
225
+ ? `${c.cyan(icons.package)} source`
226
+ : `${c.red(icons.cross)} no source`;
227
+
228
+ console.log(` ${i}. ${c.white(clientNames[client] || client)}`);
229
+ console.log(` ${c.gray("Type:")} ${config.type || 'unknown'}`);
230
+ console.log(` ${status} ${source}`);
231
+ console.log();
232
+ i++;
233
+ }
234
+
235
+ console.log(c.gray("────────────────────────────────────────────────────"));
236
+ console.log(c.gray(`Total: ${i - 1} plugins available`));
237
+ }
238
+
239
+ function getPluginChoices() {
240
+ const manifest = loadManifest();
241
+ if (!manifest || !manifest.targets) {
242
+ return [];
243
+ }
244
+
245
+ const installed = detectInstalledClients();
246
+ const clientNames = {
247
+ "claude-code": "Claude Code",
248
+ "openclaw": "OpenClaw",
249
+ "opencode": "OpenCode",
250
+ "codex": "Codex",
251
+ "cursor": "Cursor",
252
+ "vscode": "VS Code",
253
+ "windsurf": "Windsurf"
254
+ };
255
+
256
+ const typeDescriptions = {
257
+ "hooks": "Session hooks for auto-memory",
258
+ "plugin-slot": "Memory slot via MCP bridge",
259
+ "mcp": "MCP server configuration"
260
+ };
261
+
262
+ return Object.entries(manifest.targets).map(([client, config]) => {
263
+ const isInstalled = installed[client];
264
+ const hasSource = checkPluginSource(client);
265
+
266
+ const name = clientNames[client] || client;
267
+ const type = typeDescriptions[config.type] || config.type || 'Plugin';
268
+
269
+ let label = name;
270
+ if (isInstalled) label += ` ${c.green(icons.check)}`;
271
+ if (hasSource) label += ` ${c.cyan(icons.package)}`;
272
+
273
+ return {
274
+ value: client,
275
+ label: label,
276
+ hint: type
277
+ };
278
+ });
279
+ }
280
+
281
+ async function wizardComponentSelection() {
282
+ const components = await multiselect({
283
+ message: 'What would you like to install?',
284
+ options: [
285
+ {
286
+ value: 'cli',
287
+ label: `${icons.cli} CLI - Command line interface`,
288
+ hint: 'squish command for terminal use'
289
+ },
290
+ {
291
+ value: 'plugins',
292
+ label: `${icons.plugin} AI Agent Plugins`,
293
+ hint: 'Claude Code, OpenClaw, OpenCode, etc.'
294
+ }
295
+ ],
296
+ required: false
297
+ });
298
+
299
+ if (isCancel(components)) {
300
+ cancel('Installation cancelled');
301
+ process.exit(0);
302
+ }
303
+
304
+ return components;
305
+ }
306
+
307
+ async function wizardPluginSelection() {
308
+ const choices = getPluginChoices();
309
+
310
+ if (choices.length === 0) {
311
+ console.log(c.yellow("No plugins available"));
312
+ return [];
313
+ }
314
+
315
+ const plugins = await multiselect({
316
+ message: 'Which AI agents do you want to integrate with?',
317
+ options: choices,
318
+ required: false
319
+ });
320
+
321
+ if (isCancel(plugins)) {
322
+ cancel('Installation cancelled');
323
+ process.exit(0);
324
+ }
325
+
326
+ return plugins;
327
+ }
328
+
329
+ async function wizardConfiguration() {
330
+ const mode = await select({
331
+ message: 'Select operation mode:',
332
+ options: [
333
+ {
334
+ value: 'local',
335
+ label: `${icons.local} Local Mode`,
336
+ hint: 'Everything runs locally (default, recommended)'
337
+ },
338
+ {
339
+ value: 'remote',
340
+ label: `${icons.remote} Remote Mode`,
341
+ hint: 'Connect to remote Squish server'
342
+ }
343
+ ]
344
+ });
345
+
346
+ if (isCancel(mode)) {
347
+ cancel('Installation cancelled');
348
+ process.exit(0);
349
+ }
350
+
351
+ const embeddings = await select({
352
+ message: 'Select embeddings provider:',
353
+ options: [
354
+ {
355
+ value: 'local',
356
+ label: `${icons.brain} Local Embeddings`,
357
+ hint: 'Uses local CPU (default, free, private)'
358
+ },
359
+ {
360
+ value: 'openai',
361
+ label: `${icons.cloud} OpenAI Embeddings`,
362
+ hint: 'Requires OPENAI_API_KEY (better quality)'
363
+ },
364
+ {
365
+ value: 'cohere',
366
+ label: `${icons.cloud} Cohere Embeddings`,
367
+ hint: 'Requires COHERE_API_KEY'
368
+ }
369
+ ]
370
+ });
371
+
372
+ if (isCancel(embeddings)) {
373
+ cancel('Installation cancelled');
374
+ process.exit(0);
375
+ }
376
+
377
+ return { mode, embeddings };
378
+ }
379
+
380
+ async function wizardReview(installConfig) {
381
+ const { components, plugins, config } = installConfig;
382
+
383
+ let summary = `${c.white("Installation Summary:")}\n\n`;
384
+
385
+ summary += `${c.cyan("Components:")}\n`;
386
+ if (components.includes('cli')) summary += ` ${icons.check} CLI\n`;
387
+ if (components.includes('mcp')) summary += ` ${icons.check} MCP Server\n`;
388
+ if (components.includes('plugins')) summary += ` ${icons.check} AI Agent Plugins\n`;
389
+
390
+ if (components.includes('plugins') && plugins.length > 0) {
391
+ summary += `\n${c.cyan("Plugins:")}\n`;
392
+ plugins.forEach(p => {
393
+ summary += ` ${icons.check} ${p}\n`;
394
+ });
395
+ }
396
+
397
+ summary += `\n${c.cyan("Configuration:")}\n`;
398
+ summary += ` ${icons.settings} Mode: ${config.mode}\n`;
399
+ summary += ` ${icons.brain} Embeddings: ${config.embeddings}\n`;
400
+
401
+ note(summary, 'Review');
402
+
403
+ const shouldInstall = await confirm({
404
+ message: 'Proceed with installation?',
405
+ initialValue: true
406
+ });
407
+
408
+ if (isCancel(shouldInstall) || !shouldInstall) {
409
+ cancel('Installation cancelled');
410
+ process.exit(0);
411
+ }
412
+
413
+ return shouldInstall;
414
+ }
415
+
416
+ async function performInstallation(installConfig, options = {}) {
417
+ const { components, plugins, config } = installConfig;
418
+ const s = spinner();
419
+
420
+ if (options.dryRun) {
421
+ console.log();
422
+ note(`${c.yellow("Dry-run mode - no changes made")}\n\nRemove --dry-run flag to perform actual installation.`, 'Preview');
423
+ return;
424
+ }
425
+
426
+ // Install dependencies
427
+ if (components.length > 0 || plugins.length > 0) {
428
+ s.start('Installing dependencies...');
429
+
430
+ const depResult = spawnSync(
431
+ process.execPath,
432
+ [path.join(root, "scripts", "dependency-manager.mjs")],
433
+ { encoding: "utf8", stdio: "pipe", timeout: 120000 }
434
+ );
435
+
436
+ if (depResult.status !== 0) {
437
+ s.stop(c.red(`${icons.cross} Dependency installation failed`));
438
+ if (options.verbose && depResult.stderr) {
439
+ console.log(c.gray(`Error: ${depResult.stderr}`));
440
+ }
441
+ process.exit(1);
442
+ }
443
+
444
+ s.stop(c.green(`${icons.check} Dependencies installed`));
445
+ }
446
+
447
+ // Install CLI
448
+ if (components.includes('cli')) {
449
+ s.start('Setting up CLI...');
450
+ // CLI is already available via npm install, but we could add global link
451
+ s.stop(c.green(`${icons.check} CLI ready`));
452
+ }
453
+
454
+ // Install plugins
455
+ if (components.includes('plugins') && plugins.length > 0) {
456
+ // Handle OpenCode specially - use its own installer
457
+ const opencodeIndex = plugins.indexOf('opencode');
458
+ if (opencodeIndex > -1) {
459
+ plugins.splice(opencodeIndex, 1);
460
+
461
+ s.start('Installing OpenCode plugin...');
462
+ const opencodeResult = spawnSync(
463
+ process.execPath,
464
+ [path.join(root, "packages", "plugin-opencode", "install.mjs")],
465
+ { encoding: "utf8", stdio: "pipe", timeout: 60000 }
466
+ );
467
+
468
+ if (opencodeResult.status !== 0) {
469
+ s.stop(c.red(`${icons.cross} OpenCode plugin installation failed`));
470
+ if (options.verbose && opencodeResult.stderr) {
471
+ console.log(c.gray(`Error: ${opencodeResult.stderr}`));
472
+ }
473
+ } else {
474
+ s.stop(c.green(`${icons.check} OpenCode plugin installed`));
475
+ }
476
+ }
477
+
478
+ // Install remaining plugins using general installer
479
+ if (plugins.length > 0) {
480
+ s.start(`Installing ${plugins.length} plugin(s)...`);
481
+
482
+ const installResult = spawnSync(
483
+ process.execPath,
484
+ [path.join(root, "scripts", "install-plugin.mjs"), "--client=" + plugins.join(",")],
485
+ { encoding: "utf8", stdio: "pipe", timeout: 300000 }
486
+ );
487
+
488
+ if (installResult.status !== 0) {
489
+ s.stop(c.red(`${icons.cross} Plugin installation failed`));
490
+ if (options.verbose && installResult.stderr) {
491
+ console.log(c.gray(`Error: ${installResult.stderr}`));
492
+ }
493
+ process.exit(1);
494
+ }
495
+
496
+ s.stop(c.green(`${icons.check} Plugins installed`));
497
+ }
498
+ }
499
+
500
+ // Save configuration
501
+ if (config) {
502
+ s.start('Saving configuration...');
503
+ // Save config to ~/.squish/config.json
504
+ const configPath = path.join(os.homedir(), '.squish', 'config.json');
505
+ const configDir = path.dirname(configPath);
506
+
507
+ if (!fs.existsSync(configDir)) {
508
+ fs.mkdirSync(configDir, { recursive: true });
509
+ }
510
+
511
+ fs.writeFileSync(configPath, JSON.stringify({
512
+ mode: config.mode,
513
+ embeddingsProvider: config.embeddings,
514
+ installedAt: new Date().toISOString(),
515
+ version: '1.0.0'
516
+ }, null, 2));
517
+
518
+ s.stop(c.green(`${icons.check} Configuration saved`));
519
+ }
520
+ }
521
+
522
+ async function runWizard(options = {}) {
523
+ printLogo();
524
+
525
+ intro(c.cyan(`${icons.squish} Squish Memory Installer`));
526
+
527
+ // Step 1: Component selection
528
+ const components = await wizardComponentSelection();
529
+
530
+ if (components.length === 0) {
531
+ cancel('No components selected. Exiting.');
532
+ process.exit(0);
533
+ }
534
+
535
+ // Step 2: Plugin selection (if chosen)
536
+ let plugins = [];
537
+ if (components.includes('plugins')) {
538
+ plugins = await wizardPluginSelection();
539
+ }
540
+
541
+ // Step 3: Configuration
542
+ const config = await wizardConfiguration();
543
+
544
+ // Step 4: Review
545
+ const installConfig = { components, plugins, config };
546
+ await wizardReview(installConfig);
547
+
548
+ // Step 5: Install
549
+ console.log();
550
+ await performInstallation(installConfig, options);
551
+
552
+ // Success
553
+ console.log();
554
+ outro(c.green(`${icons.check} Installation Complete!`));
555
+
556
+ // Next steps
557
+ console.log();
558
+ console.log(c.white("What's next?"));
559
+ console.log(` ${c.cyan(icons.arrow)} Restart your AI assistant(s)`);
560
+ console.log(` ${c.cyan(icons.arrow)} Try: ${c.gray("squish health")}`);
561
+ console.log(` ${c.cyan(icons.arrow)} Try: ${c.cyan("squish remember \"Your first memory\"")}`);
562
+ console.log();
563
+ console.log(c.gray("Documentation: https://github.com/michielhdoteth/squish"));
564
+ }
565
+
566
+ async function handleNonInteractive(flags, options) {
567
+ const choices = getPluginChoices();
568
+
569
+ if (choices.length === 0) {
570
+ console.log(c.red("No plugins available"));
571
+ process.exit(1);
572
+ }
573
+
574
+ let pluginIds = [];
575
+ let components = ['cli'];
576
+
577
+ if (flags.quick) {
578
+ // Quick install: CLI + all plugins
579
+ pluginIds = choices.map(c => c.value);
580
+ components = ['cli', 'plugins'];
581
+ if (options.verbose) {
582
+ printLogo();
583
+ console.log(`${c.cyan("[QUICK MODE]")} Installing CLI + all plugins...\n`);
584
+ }
585
+ } else if (flags.all) {
586
+ pluginIds = choices.map(c => c.value);
587
+ components = ['cli', 'mcp', 'plugins'];
588
+ if (options.verbose) {
589
+ printLogo();
590
+ console.log(`${c.cyan("[AUTO MODE]")} Installing all components...\n`);
591
+ }
592
+ } else if (flags.select.length > 0) {
593
+ const validIds = choices.map(c => c.value);
594
+ const invalid = flags.select.filter(s => !validIds.includes(s));
595
+
596
+ if (invalid.length > 0) {
597
+ console.log(c.red(`Invalid plugins: ${invalid.join(", ")}`));
598
+ console.log(c.gray(`Available: ${validIds.join(", ")}`));
599
+ process.exit(1);
600
+ }
601
+
602
+ pluginIds = flags.select;
603
+ components = ['cli', 'plugins'];
604
+ if (options.verbose) {
605
+ printLogo();
606
+ console.log(`${c.cyan("[AUTO MODE]")} Installing: ${pluginIds.join(", ")}...\n`);
607
+ }
608
+ } else {
609
+ console.log(c.yellow("Auto mode requires --all, --quick, or --select flag"));
610
+ const validIds = choices.map(c => c.value);
611
+ console.log(c.gray(`Available: ${validIds.join(", ")}`));
612
+ console.log(`Use ${c.cyan("--list")} to see all options`);
613
+ process.exit(1);
614
+ }
615
+
616
+ const installConfig = {
617
+ components,
618
+ plugins: pluginIds,
619
+ config: { mode: 'local', embeddings: 'local' }
620
+ };
621
+
622
+ await performInstallation(installConfig, options);
623
+ }
624
+
625
+ async function main() {
626
+ const flags = parseArgs(process.argv);
627
+
628
+ if (flags.help) {
629
+ printHelp();
630
+ process.exit(0);
631
+ }
632
+
633
+ if (flags.list) {
634
+ listPlugins();
635
+ process.exit(0);
636
+ }
637
+
638
+ const manifest = loadManifest();
639
+ if (!manifest) {
640
+ console.log(c.red("Error: Plugin manifest not found"));
641
+ console.log(c.gray(`Expected: ${manifestPath}`));
642
+ process.exit(1);
643
+ }
644
+
645
+ const options = {
646
+ dryRun: flags.dryRun,
647
+ verbose: flags.verbose
648
+ };
649
+
650
+ // Non-interactive mode
651
+ if (flags.auto || flags.quick || flags.select.length > 0 || shouldUseNonInteractive()) {
652
+ await handleNonInteractive(flags, options);
653
+ return;
654
+ }
655
+
656
+ // Interactive wizard mode
657
+ await runWizard(options);
658
+ }
659
+
660
+ process.on("SIGINT", () => {
661
+ console.log(`\n${c.yellow("Installation cancelled.")}`);
662
+ process.exit(0);
663
+ });
664
+
665
+ process.on("SIGTERM", () => {
666
+ console.log(`\n${c.yellow("Installation cancelled.")}`);
667
+ process.exit(0);
668
+ });
669
+
670
+ main().catch((err) => {
671
+ console.log(c.red(`Fatal error: ${err.message}`));
672
+ console.error(err);
673
+ process.exit(1);
674
+ });